《Unity Shader 入门精要》Chapter 5 — Reading Note
Basic Structure of Vertex/Fragment Shader
1 | Shader "MyShaderName"{ |
Basic Vertex Shader and Fragment Shader
1 | float4 vert(float4 v : POSITION) : SV_POSITION{ |
Semantics
Semantics is a special string corresponded to the input and output of the Shader, which tells Shader where to read data and where to write data. For example, POSITION tells Unity to fill the parameter with vertex coordinate.
Semantics supported by Unity: POSITION, TANGENT, NORMAL, TEXCOORD0, COLOR …
So where is these data from? In Unity, they are provided by the Mesh Renderer which is using this material. At each frame calling the Draw Call, Mesh Render will send the model data rendered by it to Unity Shader. As we know, a model usually contains a set of triangular facets. Each facets is composed by three vertex, and each vertex contains some other data like vertex position, tangent, normal, texture coordinate and color. By this way, we can access these model data in Vertex Shader.
Communication between Vertex Shader and Fragment Shader
We can construct a common structure as the output of Vertex Shader and the input of Fragment Shader.
Code will be shown at the bottom.
Properties
Properties allow us to easily control the parameters in Shader. We can modify these parameter in the materials which are using the Shader.
1 | Shader "MyShader"{ |
Debug
Ways to debug Shader:
- False-color image: Map the debug variables to [0, 1] and display it on the screen.
- Visual Studio - Graphics Debugger
- Unity - Frame Debugger
Some tips
float, half or fixed
- float: 32 bits, used for most of the desktop graphics.
- half: 16 bits, used for mobile graphics.
- fixed: 11 bits, used for some old graphics.
Although now most of the graphic devices can deal with float computation in a good perfromance, it’s a good habit to use float precision as low as possible (especially for mobile games).
Standardlize Syntax
DirectX platform has stricter requirement for Shader syntax, which means we should pay attention to syntax if we are going to publish our game on DirectX platform. For example, initialize variables using the right number of parameters.
Avoid Unnecessary Calculation
For different Shader Target and different Shader, the number of temporary registers and instructions we can use is different. One possible way is to use higher level Shader Target, but a better approach is to try using less calculation in Shader, or instead use pre-calculation.
Branch and Loop
Brach and loop is supported in Shader, but these will cause a significant performance reduction (GPU has a different realization for branch and loop). Therefore, it’s a good habit to use less branch and loop in Shader. However, if it’s unavoidable, there are some advices:
1. Use constant condition variables
2. Use less instructions in branch and loop
3. Use less nest
Don’t Divided by Zero
The result is unpredictable.
Two possible ways to solve the problem:
- Use a IF statement
- Replace zero by a tiny number (e.g. 0.000001)
Complete Code
1 | // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' |