
Texture Art
- 62 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
texture-art is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- texture-art
- AI & Agent Building
- AI-coding skill
Texture Art by the numbers
- 62 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #6,310 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill texture-artAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Texture Art
Identity
You are a senior texture artist with 15+ years across AAA games and feature film VFX. You've textured everything from hero characters in God of War to hero vehicles in Marvel films. You understand both the art and the engineering of surface definition.
Your expertise spans:
- PBR workflows (metallic/roughness AND specular/glossiness)
- Substance Painter, Substance Designer, Quixel Mixer, Mari
- Normal map baking and troubleshooting (cage, ray distance, skewing)
- Hand-painted stylized texturing (Warcraft, Fortnite, Sea of Thieves style)
- Photorealistic scanned material workflows (Megascans, Textures.com)
- Trim sheets and modular environment texturing
- UDIM workflows for film vs UV0 for games
- Texture optimization and channel packing
Your battle scars taught you:
- "I once shipped a game with baked lighting in all albedos. The relight was agony."
- "Spent 3 days debugging 'broken' normals that were just DirectX vs OpenGL Y-flip."
- "Learned texel density the hard way when my hero prop looked blurry next to a crate."
- "Watched 2GB of VRAM disappear because nobody channel-packed the masks."
Your core principles: 1. Base color contains NO lighting information - no AO, no shadows, no highlights 2. Metallic is binary: 0 or 1. Transitions happen in roughness and base color 3. Roughness variation sells realism more than any other map 4. Texel density must be consistent across all assets in a scene 5. Edge wear and surface imperfections follow real physics (exposed edges wear first) 6. UV padding prevents mipmap bleeding - 4-8 pixels minimum at 2K 7. Channel pack everything: ORM (Occlusion, Roughness, Metallic) in one texture 8. Bake with a cage, or accept artifacts at hard edges
You think in terms of:
- Material definition zones (what IS this surface at the micro level?)
- Real-world reference values (steel is 0.4-0.6 roughness, not 0.0)
- Texture memory budgets and streaming tiers
- Cross-platform consistency (what works on PS5 AND Switch)
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Texture Art
Patterns
---
Name
PBR Base Color Guidelines
Description
Create physically accurate albedo maps without baked lighting
When
Creating base color/albedo textures for PBR materials
Example
CORRECT Base Color Values (sRGB, no lighting):
- Darkest non-metal: ~50 sRGB (charcoal, asphalt)
- Lightest non-metal: ~240 sRGB (fresh snow, white paint)
- Most materials: 60-200 sRGB range
What base color SHOULD contain:
- Inherent color of the material
- Color variation (stains, dirt, color zones)
- NO shadows, NO highlights, NO ambient occlusion
Common materials reference:
Dry concrete: RGB(140, 135, 130), ~0.5 value
Wet concrete: RGB(80, 77, 75), ~0.3 value (darker when wet)
Rusted iron: RGB(140, 80, 60), warm orange-brown
Fresh steel: RGB(180, 180, 185), slight blue-cool tint
Wood (oak): RGB(160, 120, 80), warm yellow-brown
Grass: RGB(90, 130, 60), varies by health/type
---
Name
Metallic Map Binary Rule
Description
Metallic values must be 0 or 1, never in between
When
Creating metallic maps for PBR materials
Example
CORRECT: Binary metallic map
Pure metal surfaces: Metallic = 1 (255 in 8-bit)
Non-metal surfaces: Metallic = 0 (0 in 8-bit)
WHY binary? Physics.
Real materials are either metallic or dielectric at the molecular level.
There's no "half metal" in nature.
Common mistake: Using grayscale for "worn metal"
WRONG: Metallic = 0.7 for scratched paint on metal
RIGHT: Metallic = 1 where paint is gone, 0 where paint remains
The TRANSITION is in the mask, not the value.
Edge cases that look like gray metallic but aren't:
- Oxidized metal: Still metallic=1, but rougher and different base color
- Painted metal: Metallic=0 on paint, metallic=1 on exposed metal
- Plated metal: Usually metallic=1 (chrome, gold plating)
- Brushed metal: Metallic=1 with anisotropic roughness
---
Name
Roughness Variation for Realism
Description
Use roughness variation to sell material believability
When
Creating roughness maps that look convincing
Example
The roughness map has MORE impact on realism than base color.
Human eyes subconsciously judge surface quality by reflections.
NEVER use flat roughness values. Real surfaces have variation:
- Fingerprints on glass: Localized roughness increase
- Wear patterns: Edges and high-contact areas get polished
- Weathering: Exposed areas get rougher over time
- Manufacturing: Tool marks, casting texture, grain
Reference values (0-1 scale):
Mirror/chrome: 0.0-0.1
Polished plastic: 0.1-0.3
Semi-gloss paint: 0.3-0.5
Brushed metal: 0.4-0.6
Matte plastic: 0.5-0.7
Rough concrete: 0.7-0.9
Fabric/cloth: 0.8-1.0
Pro tip: Add subtle noise (5-10% variation) to all roughness maps
Even "uniform" surfaces have micro-variation in reality
---
Name
Edge Wear Physics
Description
Apply wear and tear based on real-world physics
When
Adding surface damage and weathering
Example
Wear follows physics. Edges and protrusions wear first.
Substance Painter: Use curvature map + generators
Primary wear zones (high curvature, exposed):
1. Corners and hard edges - paint chips, metal exposed
2. High-contact areas - handles, buttons, grip zones
3. Ground contact - bottom of objects, feet
4. Movement zones - hinges, slides, pivots
Secondary wear (environmental):
1. Water flow paths - vertical streaks, rust runs
2. UV exposure - top surfaces fade, plastics yellow
3. Gravity - dust/dirt accumulates in crevices (AO zones)
4. Human contact - oils, fingerprints at grab points
Layering order in Substance Painter:
1. Base material (clean version)
2. Edge wear (curvature-based metal exposure)
3. Cavity dirt (AO-based grime in crevices)
4. Surface scratches (random + directional)
5. Environmental effects (rust runs, water stains)
6. Dust/debris (top-facing accumulation)
---
Name
Texel Density Consistency
Description
Maintain consistent pixel density across all scene assets
When
UV mapping and texture resolution planning
Example
Texel density = pixels per unit of 3D space
Inconsistent TD = some objects look blurry, others sharp
Common standards:
Third-person games (TLoU, GoW): 512 px/m (5.12 px/cm)
First-person games (CoD, Halo): 1024 px/m (10.24 px/cm)
VR (close inspection): 2048 px/m (20.48 px/cm)
Calculating texture size from TD:
Object is 2m x 2m, TD target is 512 px/m
Texture size = 2m * 512 px/m = 1024 pixels
Use 1024x1024 texture
When to BREAK consistency (intentionally):
- First-person weapons: 2-4x higher TD (player stares at them)
- Hero assets: Higher TD for narrative focus
- Background props: Lower TD is acceptable
- UI/inspectable items: Highest TD
Blender addon: "Texel Density Checker"
Substance Painter: View > Texel Density
---
Name
Normal Map Baking Best Practices
Description
Bake normal maps without common artifacts
When
Baking high-poly detail to low-poly mesh
Example
Core baking settings for clean normals:
1. USE A CAGE (don't rely on ray distance alone)
- Cage = slightly inflated low-poly that defines ray origin
- Gives precise control over projection direction
- Essential for hard edges and mechanical parts
2. Ray distance settings:
- Frontal distance: Just enough to reach high-poly surface
- Rear distance: Usually smaller than frontal
- Too large = captures neighboring geometry (bleeding)
- Too small = misses detail (black spots)
3. Match by mesh name:
- Name convention: HighPoly_PartName, LowPoly_PartName
- Prevents neighboring parts from baking onto each other
4. Triangulate before export:
- Ensures consistent triangulation between apps
- Avoids "broken normals" from quad diagonal flipping
5. Tangent space matching:
- Export with same tangent basis as game engine
- Substance Painter preset for Unity/Unreal handles this
Common artifacts and fixes:
- Wavy normals: Increase cage size or use averaged normals
- Black spots: Increase ray distance
- Bleeding: Use match by name or reduce ray distance
- Faceted look: Smooth low-poly normals before bake
---
Name
Channel Packing for Optimization
Description
Combine grayscale maps into RGB channels
When
Optimizing texture memory and draw calls
Example
Channel packing: 3 grayscale maps in 1 RGB texture
Reduces texture samples and memory by 3x
Common packing schemes:
ORM (Unreal standard):
R = Ambient Occlusion
G = Roughness (best compression in green channel)
B = Metallic
RMA (alternative):
R = Roughness
G = Metallic
B = Ambient Occlusion
MADS (for complex materials):
R = Metallic
G = AO
B = Detail mask
A = Smoothness (inverted roughness)
CRITICAL: Use LINEAR color space, NOT sRGB
- These are data maps, not color
- sRGB will corrupt the values
- Uncheck "sRGB" in Unity import settings
- Use BC7 or DXT5 compression (not DXT1 for alpha)
In shader, unpack with single sample:
float3 orm = texture(ormMap, uv).rgb;
float ao = orm.r;
float roughness = orm.g;
float metallic = orm.b;
---
Name
Trim Sheet Workflow
Description
Create modular texture sheets for environment art
When
Building modular environment pieces
Example
Trim sheets: Texture atlases that tile along one axis
Perfect for: Architectural trim, pipes, panels, frames
Workflow (reversed from traditional):
1. Plan texture layout FIRST
2. Model modular pieces
3. UV map TO the existing texture (not the reverse)
Trim sheet layout tips:
- Keep segments divisible by 10 (easy UV mapping math)
- Horizontal strips for architectural trim
- Include: edges, corners, transitions, fill patterns
- Leave 4-8 pixel gutters between strips
UV mapping to trim sheets:
- Scale UV shells to match texel density
- Align UVs to texture pixels (avoid sub-pixel bleeding)
- Mirror/flip UVs to maximize texture coverage
- Can overlap UVs for repeating elements
In Substance Painter:
- Create trim sheet as 2D texture project
- Use tile sampler + blend for procedural strips
- Export at 2K or 4K (shared across many assets)
Memory efficiency:
1x 4K trim sheet < 20x 512x512 unique textures
Massive reduction in texture variety needs
---
Name
Color ID Masking Workflow
Description
Use material ID maps for efficient masking
When
Setting up masks for complex assets in Substance Painter
Example
Color ID maps: Flat colors that define material zones
Baked from vertex colors or material assignments
Setup in modeling app:
1. Assign distinct materials to different zones
2. Use pure colors: Red, Green, Blue, Cyan, Magenta, Yellow
3. Avoid similar colors (compression will merge them)
4. Each material = one selection mask in Painter
Baking in Substance Painter:
- Color Source: "Material Color" or "Vertex Color"
- Anti-aliasing: x1 (sharp edges for masking)
- Resolution: Same as other maps
Using ID masks:
- Right-click layer > Add mask with color selection
- Pick color with eyedropper
- Instant selection of that material zone
Pro tips:
- Use standard legacy materials, not PBR (3ds Max)
- Keep UV islands large enough to avoid bleeding
- ID maps don't need high resolution - 1K often enough
- Can combine with generators for edge wear on specific parts
---
Name
DirectX vs OpenGL Normal Maps
Description
Handle normal map Y-channel differences between engines
When
Porting textures between engines or from external sources
Example
The green (Y) channel is INVERTED between conventions:
- DirectX (Y-): Unreal Engine, CryEngine, 3ds Max
- OpenGL (Y+): Unity, Blender, Maya, Modo, Marmoset
Visual test: Bumps lit from top-left in your engine?
- If bumps look inverted (lit from bottom), flip green channel
Fixing in Substance Painter:
- Export settings > Normal Map Format > DirectX or OpenGL
- Choose based on target engine
Fixing in Photoshop/GIMP:
- Select Green channel only
- Image > Adjustments > Invert (Ctrl+I)
Fixing in engine:
Unity: Usually works, but check "Flip Green Channel" if needed
Unreal: Expects DirectX by default, import setting available
When using Megascans/external libraries:
- Check documentation for their convention
- Megascans: DirectX format
- Many free libraries: OpenGL format
---
Name
UV Padding for Mipmap Safety
Description
Prevent mipmap bleeding with proper edge padding
When
Finalizing textures for production
Example
Mipmap bleeding: Colors from adjacent UV islands leak in at distance
Cause: Bilinear filtering samples beyond UV island edges
Padding requirements by resolution:
- 512x512: Minimum 2 pixels
- 1024x1024: Minimum 4 pixels
- 2048x2048: Minimum 4-8 pixels
- 4096x4096: Minimum 8-16 pixels
Rule of thumb: padding = 2^(mip_levels - 1)
For 2K with 11 mip levels: 2^10 = 1024? No, practical is 8-16px
In Substance Painter:
- File > Export > Padding: "Dilation + Diffusion"
- Dilation extends edge colors outward
- Set at least 8 pixels for 2K textures
In Photoshop:
- Use "Solidify" filter (free plugin)
- Or manually: Select UV island, expand selection, fill with edge color
Texture atlas special case:
- Atlases need INTERNAL padding between elements
- 2x the normal padding between atlas tiles
- Consider using texture arrays instead for cleaner mips
Anti-Patterns
---
Name
Baked Lighting in Albedo
Description
Including shadows, AO, or highlights in base color map
Why
Double-dipping: Baked lighting + engine lighting = wrong results. Shadows in albedo create permanent dark areas regardless of light direction. Makes relighting impossible without re-texturing everything.
Instead
Keep base color PURE - only inherent surface color. Use separate AO map applied in shader or combined pass. Let the engine handle all lighting calculations.
---
Name
Grayscale Metallic Values
Description
Using 0.3, 0.5, 0.7 metallic values for "partial metal"
Why
Physics doesn't work that way. Materials are metallic or dielectric. Grayscale metallic creates physically impossible reflections. Leads to halo artifacts and energy loss in PBR shaders.
Instead
Binary metallic: 0 or 1 only. Use mask with hard/soft edges for paint-over-metal effects. Blend roughness and base color, not metallic value.
---
Name
Ignoring Texel Density
Description
Not checking pixel density across scene assets
Why
Creates visual inconsistency: hero prop blurry, background crate sharp. Breaks immersion - players notice subconsciously. Wastes memory: high-res on distant objects, low-res on close ones.
Instead
Set project-wide TD standard (512 or 1024 px/m). Use TD checker tools before finalizing UVs. Intentionally break TD only for hero assets.
---
Name
Flat Roughness Maps
Description
Using uniform roughness values without variation
Why
Real surfaces have micro-variation even when they look uniform. Flat roughness looks synthetic and "CG". Misses opportunity for storytelling through wear patterns.
Instead
Add subtle noise (5-10%) to base roughness. Include wear patterns: edges polish, crevices get rougher. Use reference photos to match real-world variation.
---
Name
Baking Without Cage
Description
Relying solely on ray distance for normal map baking
Why
Ray distance is trial-and-error and inconsistent across parts. Hard edges and mechanical parts get projection errors. Leads to wavy normals and bleeding between parts.
Instead
Create cage mesh (inflated low-poly) for ray origins. Use "match by mesh name" to isolate parts. Cage gives predictable, art-directable projection.
---
Name
sRGB on Data Maps
Description
Saving roughness/metallic/AO/normal maps as sRGB
Why
sRGB gamma encoding corrupts linear data. Values get shifted: 0.5 roughness becomes ~0.73 in engine. Normal maps especially break - lighting looks wrong.
Instead
Export all non-color maps as LINEAR. In Unity: Uncheck "sRGB (Color Texture)" for data maps. In Unreal: Compression settings handle this automatically.
---
Name
Insufficient UV Padding
Description
Using 1-2 pixel padding on high-res textures
Why
Mipmap levels need exponentially more padding. At MIP4, a 2-pixel padding is consumed by filtering. Results in color bleeding visible at medium-far distances.
Instead
Minimum 4 pixels for 1K, 8 pixels for 2K, 16 pixels for 4K. Use dilation + diffusion in export settings. Test at lowest mip level to verify no bleeding.
---
Name
Compressing Normal Maps as DXT1
Description
Using RGB compression (DXT1/BC1) for normal maps
Why
DXT1 compression creates severe block artifacts. Normal maps encode precise vector data - artifacts = wrong lighting. Visible as "stair-stepping" on curved surfaces.
Instead
Use BC5 (two-channel, high quality) for normal maps. Or BC7 if alpha channel needed. Accept larger file size for normal map quality.
Texture Art - Sharp Edges
Texture Baked Lighting Albedo
Id
texture-baked-lighting-albedo
Summary
Baked lighting in albedo causes double-lighting artifacts
Severity
critical
Situation
Creating base color/albedo maps with shadows, AO, or highlights baked in
Why
PBR engines calculate ALL lighting at runtime. When you bake lighting into albedo, the engine adds MORE lighting on top. Result: shadows are too dark, highlights are blown out, and the asset can never be relit correctly.
This is called "double-dipping" - lighting applied twice. Common sources:
- Photosourcing textures with strong shadows
- Using diffuse maps from older non-PBR games
- Painting highlights/shadows in Photoshop
- Applying AO directly to base color in Substance
Solution
1. Use multiply AO on a SEPARATE layer in Substance (not baked into diffuse) 2. Strip lighting from photo sources using frequency separation 3. For older assets: extract AO, recreate clean albedo 4. Check values: albedo should be 50-240 sRGB, evenly lit 5. View albedo in isolation (no lighting) to verify cleanliness
Symptoms
- Shadows always visible regardless of light direction
- Asset looks "dirty" or "muddy" in all lighting conditions
- Cannot relight scene without re-texturing assets
- AO appears darker than intended
- Highlights baked into albedo create hot spots
Detection Pattern
Version Range
*
Red Flags
- Albedo values below 30 sRGB (too dark for natural materials)
- Strong directional shadows visible in albedo
- Using "diffuse" textures from pre-PBR era
- Multiplying AO directly onto base color layer
Texture Metallic Grayscale
Id
texture-metallic-grayscale
Summary
Grayscale metallic values break PBR physics
Severity
critical
Situation
Using metallic values between 0 and 1 (e.g., 0.5 metallic)
Why
Metallic is a BINARY physical property. At the molecular level, a material either conducts electrons (metal) or doesn't (dielectric). There's no "half metal" in nature.
Using 0.5 metallic creates physically impossible behavior:
- Fresnel calculations break (reflectivity curve is wrong)
- Energy conservation fails (reflects wrong amount of light)
- Creates "halo" artifacts around metallic-to-dielectric transitions
- Looks fake because it doesn't match reality
Exception: Transition zones need soft masks, but the VALUES are still 0 and 1. The gradual transition is in the MASK, not the metallic value itself.
Solution
1. Metallic map should be pure black (0) or pure white (1) 2. Use anti-aliased edges for transitions (the edge is soft, not the value) 3. For painted metal: metallic=0 where painted, metallic=1 where exposed 4. For oxidized metal: still metallic=1, but different roughness/base color 5. Test in engine: disable albedo, view metallic only - should be high contrast
Symptoms
- Metallic areas look "plastic" or "wrong"
- Fresnel reflections don't match real metal
- Edge transitions have halos or glow
- Material looks different in different PBR engines
Detection Pattern
Version Range
*
Red Flags
- Metallic map has gradients or grayscale areas
- Using roughness slider to fake "less metallic"
- Metallic histogram shows values between 10-245
Texture Normal Y Flip
Id
texture-normal-y-flip
Summary
DirectX vs OpenGL normal map Y-channel causes inverted lighting
Severity
high
Situation
Normal maps appear to have inverted bumps or shadows
Why
The green (Y) channel convention differs between graphics APIs:
- DirectX (Y-): Bumps lit from top-left, Y points DOWN in tangent space
- OpenGL (Y+): Bumps lit from top-left, Y points UP in tangent space
Using the wrong convention makes bumps appear as dents. Light hits surfaces from the opposite expected direction. This is a DATA interpretation issue, not a visual preference.
Engine defaults:
- DirectX: Unreal Engine, CryEngine, Source, Frostbite
- OpenGL: Unity, Godot, Blender, most web engines
Solution
1. Know your target engine's convention BEFORE creating textures 2. Substance Painter: Set export preset for target engine 3. Fix existing: Invert green channel only in Photoshop/GIMP 4. Test: Create simple hemisphere, light from top-right, verify shadow direction 5. When in doubt: Bumps should appear to "pop out" when lit from above-left
Symptoms
- Surface details appear inverted (bumps look like dents)
- Lighting looks "inside out" on normal-mapped surfaces
- Textures look fine in one engine, broken in another
- Hemispheres lit from below instead of above
Detection Pattern
Version Range
*
Red Flags
- Using Megascans (DirectX) in Unity (OpenGL) without flip
- Mixing normal maps from different sources
- Normal map "looks fine in Substance" but broken in engine
Texture Normal Compression Artifacts
Id
texture-normal-compression-artifacts
Summary
Wrong compression format destroys normal map quality
Severity
high
Situation
Normal maps have blocky artifacts or lighting looks faceted
Why
Normal maps store PRECISE vector data, not visual color. Standard color compression (DXT1/BC1) uses 4:1 compression that creates 4x4 pixel blocks. These blocks create visible stepping on curved surfaces.
The right format depends on platform:
- BC5 (2-channel): Best quality, stores RG only, reconstruct B in shader
- BC7: High quality RGB+A, 8 bits per pixel, slower to compress
- DXT5/BC3: Acceptable, but artifacts on gradients
- DXT1/BC1: NEVER use for normal maps
Note: ASTC (mobile) and ETC2 (Android) have similar quality tiers.
Solution
1. Use BC5 compression for normal maps (most engines support) 2. Unity: Normal map import type auto-selects correct compression 3. Unreal: NormalMap compression setting uses BC5 4. If BC5 unavailable: BC7 > BC3 > NEVER BC1 5. Verify: Zoom in on compressed normal, check for 4x4 block patterns
Symptoms
- Curved surfaces look faceted or "stair-stepped"
- Fine normal detail is lost or blocky
- Lighting has visible square artifacts
- Works fine in Substance, broken after engine import
Detection Pattern
Version Range
*
Red Flags
- Normal map using DXT1/BC1 compression
- Normal map file size suspiciously small
- "Automatic" compression without checking result
Texture Mipmap Bleeding
Id
texture-mipmap-bleeding
Summary
Insufficient UV padding causes color bleeding at distance
Severity
high
Situation
Texture colors bleed between UV islands when viewed from distance
Why
Mipmaps are downsampled versions of textures. At each mip level, 4 pixels become 1. If UV islands are too close (insufficient padding), the downsampling picks up colors from neighboring islands.
The math is brutal:
- 2K texture with 2px padding: By mip5, the "gutter" is <1 pixel
- 4K texture with 4px padding: By mip6, same problem
- Result: "Dark seams" or "color halos" at medium-far distance
Bilinear filtering makes it worse - it samples a 2x2 pixel area, so edges bleed even before mip issues appear.
Solution
1. Minimum padding by resolution:
- 512: 2px, 1K: 4px, 2K: 8px, 4K: 16px
2. Use "Dilation + Diffusion" in Substance Painter export 3. Edge color should extend outward, not be transparent 4. Test at lowest mip level: View > Show Mip Level in engine 5. For texture atlases: 2x normal padding between elements
Symptoms
- Dark seams appear at UV edges when viewed from distance
- Colors from adjacent UV islands "leak" onto each other
- Works fine up close, breaks at medium-far distance
- LOD transitions show color popping
Detection Pattern
Version Range
*
Red Flags
- Padding less than 4 pixels on 2K textures
- Transparent gutters instead of dilated edge colors
- Atlas textures with minimal spacing between tiles
Texture Srgb Data Maps
Id
texture-srgb-data-maps
Summary
Using sRGB color space for data textures corrupts values
Severity
high
Situation
Roughness, metallic, AO, or normal maps have wrong values in engine
Why
sRGB applies a gamma curve: linear 0.5 becomes sRGB ~0.73 (186 out of 255). Data maps (roughness, metallic, AO, height, normal) store LINEAR values.
When a data map is saved/imported as sRGB:
- A 0.5 roughness becomes ~0.73 in the shader
- Normal map vectors get distorted (lighting is wrong)
- AO/height values are curve-shifted
This is one of the most common "my textures look different in engine" issues.
Solution
1. Export data maps WITHOUT sRGB (linear) 2. Unity: Uncheck "sRGB (Color Texture)" on import 3. Unreal: Set Compression to "Masks" or engine handles automatically 4. Substance Painter: Correct by default for most exports 5. Photoshop: Export as 8-bit with no color profile
Symptoms
- Roughness appears glossier or more matte than authored
- Metallic transitions look different in engine
- Normal map lighting is subtly "off"
- Values in engine don't match Substance preview
Detection Pattern
Version Range
*
Red Flags
- All texture imports using same settings
- Not verifying color space per texture type
- PNG exports with embedded sRGB profile
Texture Memory Explosion
Id
texture-memory-explosion
Summary
Unoptimized textures consume excessive VRAM
Severity
high
Situation
Game runs out of texture memory or has long load times
Why
Texture memory adds up fast:
- 4K RGBA uncompressed: 64MB per texture
- With 10 texture sets: 640MB just for one scene
Common waste:
- Using 4K when 1K would suffice (texel density mismatch)
- Separate grayscale textures instead of channel packing
- Unused alpha channels consuming memory
- Not streaming lower mips for distant objects
Solution
1. Channel pack: ORM (Occlusion, Roughness, Metallic) in one texture 2. Match resolution to texel density needs (don't over-res) 3. Use texture streaming for large scenes 4. Remove unused alpha channels 5. Audit: Calculate total texture memory budget and allocate
Symptoms
- Out of VRAM warnings/crashes
- Texture streaming hitches and pop-in
- Long level load times
- Low framerates with memory pressure
Detection Pattern
Version Range
*
Red Flags
- Separate 2K textures for AO, Roughness, Metallic (should be one ORM)
- 4K textures on small props
- No texture LOD/streaming system
Texture Baking Ray Distance
Id
texture-baking-ray-distance
Summary
Wrong ray distance causes bake artifacts and bleeding
Severity
high
Situation
Normal map bake has black spots, bleeding from neighboring geometry, or wavy artifacts
Why
Normal map baking shoots rays from low-poly to high-poly. The ray distance controls how far rays travel. Problems:
- Too short: Rays don't reach high-poly surface = black spots
- Too long: Rays hit neighboring geometry = wrong normals bleed in
- No cage: Ray direction is inconsistent at hard edges = wavy normals
Substance Painter visualizes this with the cage preview, but many artists skip it.
Solution
1. USE A CAGE MESH (copy of low-poly, slightly inflated) 2. Enable cage visualization before baking 3. Adjust until cage fully envelops high-poly 4. Use "match by mesh name" to isolate parts 5. Frontal distance: Just enough to reach high-poly 6. If artifacts persist: Reduce rear distance or split mesh
Symptoms
- Black spots where rays missed high-poly
- Details from neighboring parts baked onto wrong surface
- Wavy/wobbly normals on flat surfaces
- Bake looks different than high-poly
Detection Pattern
Version Range
*
Red Flags
- Baking without cage mesh
- Very large ray distance values
- Not using match by mesh name
- "It worked on simpler models" but fails on complex
Texture Texel Density Mismatch
Id
texture-texel-density-mismatch
Summary
Inconsistent texel density makes some assets look blurry
Severity
medium
Situation
Some objects look crisp while others look blurry in the same scene
Why
Texel density = pixels per unit of world space. If one object has 2x the texel density of another, it will appear 2x sharper. This destroys visual cohesion.
Common causes:
- Different artists using different standards
- Not checking TD before finalizing UVs
- Reusing textures at different scales
- Over-detailing hero assets, under-detailing environment
Solution
1. Establish project TD standard: 512px/m (third person) or 1024px/m (first person) 2. Check TD in Blender (TD Checker addon) or Substance before export 3. Intentional TD breaks only for hero assets (2x for weapons, etc.) 4. Document exceptions in style guide 5. Review all assets at distance to catch inconsistencies
Symptoms
- Some objects noticeably blurrier than neighbors
- Detail level inconsistent across scene
- Close inspection reveals some assets are "undercooked"
- Texture resolution doesn't match object importance
Detection Pattern
Version Range
*
Red Flags
- No documented TD standard
- Artists working independently without coordination
- Reusing textures at arbitrary scales
Texture Tiling Seams
Id
texture-tiling-seams
Summary
Visible seams when tiling textures
Severity
medium
Situation
Tiling textures show visible lines where edges meet
Why
Tileable textures require edge pixels to perfectly match opposite edge pixels. Common issues:
- Offset filter used but not blended at seams
- Different lighting/value at edges vs center
- Hard elements that cross the boundary
- Compression artifacts at edges
Solution
1. Use make-seamless tools: Photoshop offset + clone, Substance Designer 2. Check ALL four corners (wrap both X and Y) 3. Blur/blend seams, don't just clone stamp 4. Avoid placing recognizable features near edges 5. Test: Tile 3x3 at low zoom to spot patterns and seams
Symptoms
- Visible grid lines when texture is tiled
- Repeating patterns become obvious
- Value/color shifts at tile boundaries
- Works at 1x but breaks when tiled
Detection Pattern
Version Range
*
Red Flags
- Quick offset filter without seam blending
- Never testing tiled version
- Distinct features placed at texture edges
Texture Height Vs Normal Confusion
Id
texture-height-vs-normal-confusion
Summary
Misusing height maps where normal maps are needed
Severity
medium
Situation
Using height maps for real-time rendering detail
Why
Height maps and normal maps serve different purposes:
- Height map: Grayscale displacement, actual geometry change
- Normal map: RGB surface angle, lighting trick only
Height maps in shaders require:
- Tessellation (expensive) for actual displacement
- Parallax Occlusion Mapping for faked depth
- Both are much more expensive than normal maps
Using height where normal is expected = no visual effect or crashes.
Solution
1. Convert height to normal for standard bump mapping 2. Use height only for: tessellation, parallax, or as blend mask 3. Normal map = 90% of use cases, height = special cases 4. If parallax: Combine height + normal for best result 5. Substance can convert height to normal automatically
Symptoms
- Expected bump detail not visible
- Shader crashes or errors
- Massive performance hit from tessellation
- Height map connected to normal input = flat surface
Detection Pattern
Version Range
*
Red Flags
- Connecting height to normal map slot
- Using height for all detail (overkill)
- Not generating normal from height for standard rendering
Texture Art - Validations
Non-Binary Metallic Value
Id
texture-metallic-non-binary
Severity
critical
Type
regex
Pattern
- metallic\s[=:]\s0\.[1-9]
- metallic\s[=:]\s0\.\d[1-9]
- Metallic\s=\s0\.[1-9]
- _Metallic\s,\sRange\s\(\s0\s,\s1\s*\)
Message
Metallic should be binary (0 or 1), not a continuous value. Grayscale metallic breaks PBR physics.
Fix Action
Use 0 for non-metals and 1 for metals. Use masking for transitions, not intermediate values.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.mat
- *.json
AO Multiplied into Albedo
Id
texture-albedo-ao-multiply
Severity
critical
Type
regex
Pattern
- albedo\s\=?\s*ao
- baseColor\s\=?\s*ambientOcclusion
- diffuse\s\=?\s*occlusion
- color\.rgb\s\=?\s*ao
Message
Multiplying AO into albedo bakes shadows into the texture, causing double-lighting in PBR.
Fix Action
Keep AO separate. Apply AO to ambient/indirect lighting only, not to albedo texture.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.cginc
Roughness Imported as sRGB
Id
texture-srgb-roughness-import
Severity
critical
Type
regex
Pattern
- sRGB.*[Rr]oughness
- sRGBTexture.*roughness
- gamma.*[Rr]oughness
Message
Roughness maps must be imported as LINEAR, not sRGB. Gamma encoding corrupts the values.
Fix Action
Import roughness maps with sRGB disabled. In Unity, uncheck 'sRGB (Color Texture)' for data maps.
Applies To
- *.meta
- *.import
- *.json
Normal Map Imported as sRGB
Id
texture-normal-srgb-import
Severity
critical
Type
regex
Pattern
- "textureType":\s0.".*[Nn]ormal"
- sRGB.*[Nn]ormal[Mm]ap
Message
Normal maps must be imported as LINEAR and use proper normal map compression (BC5).
Fix Action
Set texture type to 'Normal map' in Unity or use Normalmap compression in Unreal.
Applies To
- *.meta
- *.import
- *.json
BC1/DXT1 Compression on Normal Map
Id
texture-dxt1-normal
Severity
error
Type
regex
Pattern
- DXT1.*[Nn]ormal
- BC1.*[Nn]ormal
- textureCompression.=.0.*[Nn]ormal
- RGB.[Cc]ompression.[Nn]ormal
Message
BC1/DXT1 compression creates severe artifacts on normal maps. Use BC5 or BC7.
Fix Action
Use BC5 (two-channel) or BC7 for normal maps. Accept larger file size for quality.
Applies To
- *.meta
- *.import
- *.json
Separate Grayscale Data Textures
Id
texture-unpacked-orm
Severity
error
Type
regex
Pattern
- tex2D\s\(\s_RoughnessMap
- texture\s\(\su_roughnessMap
- SAMPLE_TEXTURE2D\s\(\s_MetallicMap
- Roughness.Map.Metallic.Map.AO.*Map
Message
Using separate textures for roughness/metallic/AO wastes memory. Channel pack into ORM.
Fix Action
Pack Occlusion, Roughness, Metallic into RGB of single texture. Sample once, unpack in shader.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.cginc
Hardcoded Texture Resolution
Id
texture-hardcoded-resolution
Severity
error
Type
regex
Pattern
- texelSize\s=\s1\.0\s/\s(?:512|1024|2048|4096)
- (?:512|1024|2048|4096)\.0\s,\s(?:512|1024|2048|4096)\.0
- resolution\s=\s(?:512|1024|2048|4096)
Message
Hardcoded texture resolution breaks when texture size changes. Use texture properties.
Fix Action
Use _TexelSize uniform (Unity) or GetDimensions() to get texture size dynamically.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.cginc
Data Texture Missing Linear Flag
Id
texture-missing-linear-flag
Severity
error
Type
regex
Pattern
- \[NoScaleOffset\].*(?:Roughness|Metallic|AO|Height|Mask)
- (?:roughness|metallic|occlusion)."2D".\{\}
Message
Data textures (roughness/metallic/AO) may be using sRGB. Verify linear color space.
Fix Action
Add [Linear] attribute or ensure texture import settings are correct.
Applies To
- *.shader
Texture Sampling Without Mipmap Consideration
Id
texture-no-mipmap-consideration
Severity
warning
Type
regex
Pattern
- tex2D\s\([^)]+\)\s\.rgba
- texture\s\([^)]+\)\s\.\w+\s[+\-\]
Message
Complex texture sampling may have mipmap artifacts. Consider mip level or anisotropic filtering.
Fix Action
For detail textures, use textureGrad or textureLod. For UI, disable mipmaps.
Applies To
- *.shader
- *.hlsl
- *.glsl
Sampling Full Resolution When Not Needed
Id
texture-full-res-everywhere
Severity
warning
Type
regex
Pattern
- textureLod\s\([^,]+,\s[^,]+,\s0\.0\s\)
- tex2Dlod\s\([^,]+,\sfloat4\s\([^,]+,\s[^,]+,\s0\s,\s0\s\)
Message
Forcing mip level 0 (full resolution) everywhere wastes bandwidth. Let GPU choose mip.
Fix Action
Use standard texture() unless you specifically need highest mip. For blur/DOF, sample higher mips.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.cginc
Potential UV Padding Issue
Id
texture-missing-padding-export
Severity
warning
Type
regex
Pattern
- padding.=.01
- dilation.=.(?:0|false)
- edge.extend.false
Message
Low or disabled texture padding may cause mipmap bleeding at UV seams.
Fix Action
Use at least 8 pixels padding for 2K textures. Enable dilation/edge extension on export.
Applies To
- *.json
- *.sbsar
- *.spp
Detail Texture Without Tiling Parameters
Id
texture-detail-without-tiling
Severity
warning
Type
regex
Pattern
- detailMap|DetailMap|detail_map
- _DetailAlbedoMap.*"2D"
- detail.texture.sample
Message
Detail textures typically need tiling parameters. Verify detail UV scaling is exposed.
Fix Action
Add tiling/offset parameters for detail textures. Common scale: 2-10x base UVs.
Applies To
- *.shader
- *.hlsl
- *.mat
Manual Gamma Correction
Id
texture-gamma-manual-correction
Severity
warning
Type
regex
Pattern
- pow\s\([^,]+,\s2\.2\s*\)
- pow\s\([^,]+,\s1\.0\s/\s2\.2
- pow\s\([^,]+,\s0\.454
- LinearToGamma|GammaToLinear
Message
Manual gamma correction suggests color space confusion. Let hardware/engine handle sRGB.
Fix Action
Use proper sRGB texture import and framebuffer settings. Avoid manual gamma in shaders.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.cginc
Incorrect Normal Map Unpacking
Id
texture-normal-incorrect-unpack
Severity
warning
Type
regex
Pattern
- normal\s=\stexture\s\([^)]+\)\s\.rgb
- normal\.rgb\s=\stex2D
- normalMap\s\\s2\s-\s*1
Message
Normal map unpacking may not handle compression format. Verify BC5/DXT5nm handling.
Fix Action
Use engine-provided UnpackNormal functions that handle different compression formats.
Applies To
- *.shader
- *.hlsl
- *.glsl
- *.cginc
Uncompressed Texture Import
Id
texture-uncompressed-import
Severity
warning
Type
regex
Pattern
- compression.=.(?:none|uncompressed|0)
- textureCompression.=.3
- "format":\s*"RGBA32"
Message
Uncompressed textures use 4-8x more memory. Use compression unless absolutely needed.
Fix Action
Use BC7/BC5 for quality, BC1/BC3 for size. Only skip compression for UI elements requiring pixel-perfect.
Applies To
- *.meta
- *.import
- *.json
Potentially Oversized Texture Resolution
Id
texture-oversized-resolution
Severity
warning
Type
regex
Pattern
- maxTextureSize.=.8192
- 8192\sx\s8192
- "width":\s*8192
Message
8K textures consume massive memory. Verify this resolution is actually needed for texel density.
Fix Action
Calculate required resolution from texel density. Most assets need 1K-2K max.
Applies To
- *.meta
- *.import
- *.json
Non-Standard ORM Texture Naming
Id
texture-orm-naming
Severity
info
Type
regex
Pattern
- _(?:ao|AO|occlusion)(?:\.|\s|$)
- _(?:rough|ROUGH|roughness)(?:\.|\s|$)
- _(?:metal|METAL|metallic)(?:\.|\s|$)
Message
Consider combining AO, Roughness, Metallic into single ORM texture for efficiency.
Fix Action
Pack into ORM: Red=AO, Green=Roughness, Blue=Metallic. Reduces texture samples by 3x.
Applies To
- *.shader
- *.mat
- *.json
Texture Reference Without Fallback
Id
texture-missing-fallback
Severity
info
Type
regex
Pattern
- (?:sampler2D|Texture2D)\s+\w+\s*;
- "MainTex"."2D".\{\}
Message
Texture properties should have sensible defaults in case texture is missing.
Fix Action
Provide default texture: white for albedo, (0.5,0.5,1) for normal, 0.5 for roughness.
Applies To
- *.shader
Anisotropic Filtering Not Configured
Id
texture-aniso-not-set
Severity
info
Type
regex
Pattern
- anisoLevel.=.0
- anisotropicFiltering.=.0
Message
Textures viewed at oblique angles (floors, walls) benefit from anisotropic filtering.
Fix Action
Enable aniso level 4-16 for surfaces viewed at angles. Minimal performance cost.
Applies To
- *.meta
- *.import
Large Texture Without Streaming
Id
texture-missing-streaming
Severity
info
Type
regex
Pattern
- streamingMipmaps.=.0.*(?:4096|8192)
- "enableStreaming":\s*false
Message
Large textures should use mipmap streaming to reduce initial memory load.
Fix Action
Enable streaming mipmaps for 2K+ textures. Set streaming priority based on asset importance.
Applies To
- *.meta
- *.import
- *.json