Skip to main content

Game Art Cheatsheet

··7 mins
Table of Contents

Common glitches in game art and how to solve them. Troubleshooting tips for mesh issues, lighting bugs, particles, transparency, physics. The solutions can be applied in Unity, Unreal Engine, Godot and most other game engines.

Geometry
#

NameSymptomCauseSolutions
Aliased lines
Thin geo like cables or fences jitters, disappearsGeometry is thinner than a single pixel
  1. Make LODs with thicker geo, different texture
  2. Blend to a solid texture at far distance
  3. Use a different anti-aliasing algorithm
Flipped normals
Flipped normals
Mesh is inverted or partially invisibleBack faces of triangles are hidden by default (backface culling)
  1. Flip (reverse) vertex normals to face outside
  2. If both sides needed, enabled two-sided rendering in material
Import scale
Import scale
Model is huge or tiny after importingDifferent editors use 1 cm or 1 m as base unit
  1. Make sure to follow correct physical dimensions when modelling
  2. Set import or export scale to 0.01 or 100x
Temporal anti-aliasing
Ghosting. Meshes leave blurry trail when movingTemporal smoothing algorithms combine multiple frames over time for high quality
  1. Make sure game keeps good, stable framerate, high resolution
  2. If using vertex displacement in shader, add motion vectors support
  3. For stability but lower quality, switch to FXAA anti-aliasing
Z-Fighting
Flickering polygonsMultiple polys have equal Z-depth in the same pixel
  1. Make sure no polygons overlap on the same plane
  2. Increase near clip distance in camera
Split edges
Split edges
Holes appear in animated meshVertices have two modes: shared or split
  1. Merge vertices in your 3d software
  2. Lock vertex normals instead of physically splitting edges
  3. Use auto smooth groups
Visibility culling
Mesh or particle system disappearing from certain positionsFrustum or occlusion culling hides objects by their bounding boxes
  1. Check if occluders (geometry) are correct
  2. Increase bounding box size

Materials
#

NameSymptomCauseSolutions
Bilinear mipmap filtering
Bilinear mipmap filtering
Blurry textures at oblique anglesBilinear filtering in perspective projection underestimates required mipmap level of a texture
  1. Use anisotropic texture filtering, if performance tradeoff is acceptable
sRGB vs linear
sRGB vs linear
Textures are too dark or washed out after import, or roughness is wrongTextures come in (at least) two formats: linear color or sRGB. Linear is used for data, while sRGB for human-readable color
  1. In texturing programs, export albedo/diffuse as sRGB
  2. In game engine, enable sRGB in albedo/diffuse textures
  3. Disable sRGB (use linear) for data (roughness, masks)
Normal Y-axis sign
Normal Y-axis sign
Normal map looks invertedSome engines expect Y+ axis to mean “down”, while others consider it “up”
  1. Invert green channel of normal map
  2. In Unreal, tick Flip Green Channel in texture
Aliasing
Moire patterns, bloom flickeringResolution is not dense enough to represent (aka sample) the content
  1. Use smoother patterns in textures
  2. Add soft radius to light sources
  3. Blend to smoother texture by distance
  4. Use different anti-aliasing algorithm
Alpha bleeding
Alpha bleeding
White edges in textures with transparencyAlpha compositing
  1. In Substance Painter, use Dilation mode when exporting
  2. In Photoshop, fill the background by selecting alpha mask, inverting selection and use Fill > Content Aware
  3. Respect premultiplied alpha in shaders
Hard normals on foliage
Hard normals on foliage
Flat shading on foliage models, harshly disconnected from terrainFoliage mesh, made of big flat planes, is interpreted too literally by lighting
  1. Use manually edited, up-pointing vertex normals
  2. Read normal from terrain
Negative or NaN color
Negative or NaN color
Black or deeply saturated squares on screenPixel’s color value is negative or not a number. This corrupts some algorithms (bloom)
  1. Check for division-by-zero in shaders
  2. Clamp values to [0,1] in shaders
Edge bleedingColors from another island of UV space bleeding into the borders of given oneBilinear texture filtering picks up information from neighboring texels, as it’s unaware of UV seams or image atlas items
  1. Increase margins between UV islands
  2. Add 1 pixel of space between items in tileset/atlas
  3. In Substance Painter, use Dilation mode when exporting
Banding in display
Banding in display
Stepped gradients on screenTransition between certain colors can’t be represented, due to display’s color precision
  1. Add some film grain
  2. Use dithering
  3. Check if your post-process stack has HDR until very end
Banding in textures
Banding in textures
Stepped gradients in texturesLossy compression can’t represent subtle gradient
  1. Use more contrast in your source textures (e.g. stronger normal map), then reduce it in shader
Half precisionPixelated procedural materials on mobileMobile devices optimize some values by storing them in 16 bits instead of 32
  1. Make your effects less dependent on precision (smooth flow maps, bigger features on texture)
  2. Use modulo (%, fmod) on final time and UVs
  3. Force full precision on variable

Transparency
#

NameSymptomCauseSolutions
Transparents' sorting
Bad sorting of polygons
Triangles of meshes or particles appear in wrong orderGame engines employ per-object sorting instead of per-triangle, for speed
  1. Split big objects into smaller ones
  2. Move pivot closer to center of mesh
  3. Use dithered opacity or cutout alpha for semi-solid objects
  4. Hack: change draw priority (render queue) of shader
Depth of field vs. transparents
Depth of field vs. transparents
Transparent objects are not affected by depth of fieldDoF reads the depth of the scene to perform blur. Translucent materials don’t write single depth
  1. Use cutout alpha (masked opacity) instead
  2. In Unreal, enable Separate Translucency in material
  3. Sometimes you can’t avoid it

Lighting
#

NameSymptomCauseSolutions
Lightmap bleeding
Lightmap bleeding
Dark or wrong mesh edgesBilinear filtering picks up information from neighboring texels
  1. Increase margins between UV islands
  2. Increase mesh’s lightmap resolution
Shadow acne
Shadow acne
Stepped shadows at certain anglesShadow map’s resolution is not enough to represent the continuous surface
  1. Play with shadow bias parameter in light’s settings
  2. Increase shadow map resolution
Missing reflection probes
Missing reflection probes
Black reflections on metalsLack of reflection sources
  1. Make sure at least 1 reflection probe covers every area in the scene
Movable SkyLight
Movable SkyLight (UE4)
Black reflections on metalsSkyLight or all reflection probes have Mobility set to Movable
  1. Change their Mobility to Static
Overlapping polygonsBlack spots in lightmapsMultiple triangles are baked to the same texel
  1. Make sure no UV islands overlap
  2. Remove any overlapping geo in mesh & level
  3. Check for duplicate objects in level
Dynamic lights limitFlickering lightsRenderer reached a limit of light sources affecting the same pixel
  1. Reduce # of lights overlapping in that place (change radius, move, delete)
  2. Use static (baked) lights instead

Animation
#

NameSymptomCauseSolutions
Root motionCharacter “swims” or drifts far from pivot over timeRoot bone transform in animation is updated to match character movement in game
  1. Fix the loop, making sure it ends where it started
  2. Disable root motion

Physics
#

NameSymptomCauseSolutions
Tick rateFast objects fly through, skipping collisionPhysics frame rate was not sufficient for the system to detect collision
  1. Use thicker collider
  2. Enable continuous collision detection (CCD)

Node Graph Equivalents
#

Equivalent nodes across different software. Useful when translating tutorials or porting effects.

Functions
#

UE BPUE matUnity shaderBlender matDesignerHoudini VOPHou VEXHLSL
FInterpLerpLerpMixBlendMixlerplerp
LocationPositionPositionLocationPositionPositionv@PSV_Position
Branch😑If💚Branch😑Mix🤷‍♂️If…ElseIf💚ifif
Switch/SelectSwitchnonenoneSwitchSwitchswitchswitch
Literal FloatConstantFloatValueFloatConstantfloatconst float
Break VectorComponent Mask (or Convert)SplitSeparate XYZRGB-A SplitVector to Float.x.x
Make VectorAppend (or Convert)CombineCombine XYZRGB-A MergeFloat to Vectorsetfloat3
Transform VectorTransform 3x3 MatrixTransformnonenoneTransform Matrixmulmul
Map RangeRemapRemapMap RangeLevelsFitfit(x-vMin)/(vMax-vMin)
Dot ProductDotDot ProductVector Mathnone?Dot Productdotdot
noneOne MinusOne MinusInvertInvertComplement1-x1-x
Get Linear Color ValueCurve Atlas Row ParameterSample GradientColor RampGradient MapRamp Parameterchrampn/a
Sample Texture 2D At UV Positions*Texture SampleSample Texture 2DImage TextureBitmapTexturetexturetexture2d

* With the Geometry Script plugin

Concepts
#

UE BPUE matsBlender matsDesignerHou
Rect Lightn/aArea LightPlane LightArea/Grid Light
OutlinernoneOutlinerExplorerTree View
RerouteRerouteRerouteDot NodeDot Node
Local VariableNamed ReroutenonePortalnone

Shortcuts
#

ActionUE BPUE matsBlender matsDesignerHou
Graph: AddTab/RMBTab/RMBShift+ATab/SpacebarTab/RMB
Graph: DisconnectAlt+LMBAlt+LMBDrag awayAlt+LMBDrag→click
Graph: Pan viewRMB-dragRMB-dragMMB-dragMMB-dragMMB-drag

Credits, contributing
#

Oskar Świerad of Tech Art Aid is the original author and current maintainer.
Huge thanks to contibutors of examples from the Unity, Unreal Reddit threads and Twitter.

If you want something added or fixed, just add a new issue on GitHub.
I welcome pull requests too! (though quality is expected)

License
#

Feel free to share, copy, print and modify the document, under the terms of Apache License 2.0.