Lighting Performance
Per-pixel dynamic lighting will add significant cost to every affected pixel and can lead to rendering object in multiple passes. Avoid having more than one Pixel Light affecting any single object, prefer it to be a directional light. Note that Pixel Light is a light which has a Render Mode setting set to Important.
Per-vertex dynamic lighting can add significant cost to vertex transformations. Avoid multiple lights affecting single objects. Bake lighting for static objects.
퍼 픽셀 라이트는 픽셀마다 중대한 영향을 끼치고 여러 패스로 그리게 될 수도 있으니까 하나만 쓰라는 얘기. 그것도 디렉셔널 라이트로. 셋팅은 important 셋팅으로 (제대로 번역하기 귀찮…)
버텍스 라이트는 트렌스폼 코스트에 영향 끼친다… 당연하겠지. 어쨌건 결론적으로 라이트는 하나만 쓰라는 말씀.
Optimize Model Geometry
When optimizing the geometry of a model, there are two basic rules:
- Don’t use excessive amount of faces if you don’t have to* 필요없는 면 쓰지 마* Keep the number of UV mapping seams and hard edges as low as possible
- UV 단순하게 짤러. 그리고 스무스 옵션으로 부드럽게 해. (주:노말과 UV 줄이라는 말임)
Note that the actual number of vertices that graphics hardware has to process is usually not the same as what is displayed in a 3D application. Modeling applications usually display the geometric vertex count, i.e. number of points that make up a model.
그래픽 툴에서 보이는 버텍스 숫자는 그래픽 하드웨어에서 인식하는 숫자랑 다르다는것 주의. 모델링 프로그램은 보이는 버텍스만 카운트 하걸랑.
For a graphics card however, some vertices have to be split into separate ones. If a vertex has multiple normals (it’s on a “hard edge”), or has multiple UV coordinates, or has multiple vertex colors, it has to be split. So the vertex count you see in Unity is almost always different from the one displayed in 3D application.
그래픽 카드에서는 버텍스를 똑같은 놈으로 잘라 (복사) 해 버리거든요. 버텍스 노말이 많으면 (꺾인면이 있으면) 또는 UV좌표가 많으면, 혹은 버텍스 칼라가 많으면 버텍스가 복사 되어요. 그래서 버텍스 카운트가 늘어나요. 그래서 그래픽 프로그램과는 데이터가 달라져요 (요건 글로 한번 써야겠다. 좋은 말인데?)
Texture Compression
All Android devices with support for OpenGL ES 2.0 also support the ETC1 compression format; it’s therefore encouraged to whenever possible use ETC1 as the prefered texture format. Using compressed textures is important not only to decrease the size of your textures (resulting in faster load times and smaller memory footprint), but can also increase your rendering performance dramatically! Compressed textures require only a fraction of memory bandwidth compared to full blown 32bit RGBA textures.
If targeting a specific graphics architecture, such as the Nvidia Tegra or Qualcomm Snapdragon, it may be worth considering using the proprietary compression formats available on those architectures. The Android Market also allows filtering based on supported texture compression format, meaning a distribution archive (.apk) with for example DXT compressed textures can be prevented for download on a device which doesn’t support it.
Enable Mip Maps
As a rule of thumb, always have Generate Mip Maps enabled. In the same way Texture Compression can help limit the amount of texture data transfered when the GPU is rendering, a mip mapped texture will enable the GPU to use a lower-resolution texture for smaller triangles. The only exception to this rule is when a texel (texture pixel) is known to map 1:1 to the rendered screen pixel, as with UI elements or in a pure 2D game.
Tips for writing well performing shaders
Although all Android OpenGL ES 2.0 GPUs fully support pixel and vertex shaders, do not expect to grab a desktop shader with complex per-pixel functionality and run it on Android device at 30 frames per second. Most often shaders will have to be hand optimized, calculations and texture reads kept to a minimum in order to achieve good frame rates.
Complex arithmetic operations
Arithmetic operations such as pow, exp, log, cos, sin, tan etc heavily tax GPU. Rule of thumb is to have not more than one such operation per fragment. Consider that sometimes lookup textures could be a better alternative.
Do NOT try to roll your own normalize, dot, inversesqrt operations however. Always use built-in ones – this was driver will generate much better code for you.
Keep in mind that discard operation will make your fragments slower.
Floating point operations
Always specify precision of the floating point variables while writing custom shaders. It is crucial to pick smallest possible format in order to achieve best performance.
If shader is written in GLSL ES, then precision is specified as following:
- highp - full 32 bits floating point format, well suitable for vertex transformations, slowest* mediump - reduced 16 bits floating point format, well suitable for texture UV coordinates, roughly x2 faster than highp* lowp - 10 bits fixed point format, well suitable for colors, lighting calculation and other high performant operations, roughly x4 faster than highp
If shader is written in CG or it is a surface shader, then precision is specified as following:
- float - analogous to highp in GLSL ES, slowest* half - analogous to mediump in GLSL ES, roughly x2 faster than float* fixed - analogous to lowp in GLSL ES, roughly x4 faster than float
For more details about general shader performance, please read the Shader Performance page. Quoted performance figures are based on the PowerVR graphics architecture, available in devices such as the Samsung Nexus S. Other hardware architectures may experience less (or more) benefit from using reduced register precision.
Bake Lighting into Lightmaps
Bake your scene static lighting into textures using Unity built-in Lightmapper. The process of generating a lightmapped environment takes only a little longer than just placing a light in the scene in Unity, but:
- It is going to run a lot faster (2-3 times for eg. 2 pixel lights)* And look a lot better since you can bake global illumination and the lightmapper can smooth the results
Share Materials
If a number of objects being rendered by the same camera uses the same material, then Unity Android will be able to employ a large variety of internal optimizations such as:
- Avoiding setting various render states to OpenGL ES.* Avoiding calculation of different parameters required to setup vertex and pixel processing* Batching small moving objects to reduce draw calls* Batching both big and small objects with enabled “static” property to reduce draw calls
All these optimizations will save you precious CPU cycles. Therefore, putting extra work to combine textures into single atlas and making number of objects to use the same material will always pay off. Do it!
Simple Checklist to make Your Game Faster
- If you’re using built-in shaders, peek ones from Mobile category. Keep in mind that Mobile/VertexLit is currently the fastest shader.* Keep the number of different materials per scene low - share as many materials between different objects as possible.* Set Static property on a non-moving objects to allow internal optimizations.* Use ETC1 format for textures when possible, otherwise choose 16bit textures over 32bit for uncompressed texture data.* Use mipmaps.* Use combiners or pixel shaders to mix several textures per fragment instead of multi-pass approach.* If writing custom shaders, always use smallest possible floating point format: + fixed / lowp – perfect for color, lighting information and normals,+ half / mediump – for texture UV coordinates,+ float / highp – avoid in pixel shaders, fine to use in vertex shader for vertex position calculations.* Minimize use of complex mathematical operations such as pow, sin, cos etc in pixel shaders.* Do not use Pixel Lights when it is not necessary – choose to have only a single (preferably directional) pixel light affecting your geometry.* Do not use dynamic lights when it is not necessary – choose to bake lighting instead.* Choose to use less textures per fragment.* Avoid alpha-testing, choose alpha-blending instead.* Do not use fog when it is not necessary.* Learn benefits of Occlusion culling and use it to reduce amount of visible geometry and draw-calls in case of complex static scenes with lots of occlusion. Plan your levels to benefit from Occlusion culling.* Use skyboxes to “fake” distant geometry.
See Also
- iPhone Optimizing Graphics Performance (for when the graphics architecture is known to be Imagination Tech’s PowerVR.)