Unity Rendering Principle (14) Unity's Light Source
A world with only one parallel light is nice, but we really just need to deal with more complex light types and a larger number of light sources in Unity Shader.
Unity supports a total of 4 light sources, parallel light, point light, spotlight and surface light. Surface light is only useful when baking.
Due to the different geometric definitions of each light source, their corresponding light source properties are also different, which requires us to treat them differently. Fortunately, Unity provides us with many functions to handle them.
What is the impact of the type of light source
The type of light source will have an impact on the Shader. We can consider which properties of the light source are used by the Shader. The most commonly used are the position, direction, color, intensity and attenuation of the light source, and these five properties are closely related to their geometric information.
Parallel light
The geometric definition of parallel light is the simplest, and its illumination range is unlimited, usually appearing in the scene as a character such as the sun.
The reason why parallel light is simple is that it does not have a unique position, that is to say, it can be placed anywhere in the scene, and its geometric properties are only directions. We can adjust the Rotation property of the Transform of the parallel light to change it. The direction of the light source, and the direction of the parallel light to all points in the scene is the same. In addition, since the parallel light does not have a specific location, there is no concept of attenuation, that is, the light intensity does not change with distance.
Point light source
The point light source illuminates a limited space, which is defined by a sphere in space. A point light source can represent the light emitted by a point and extending in all directions, that is, its illumination range is a sphere.
- The radius of the sphere can be adjusted by the Range property of the panel
- Its location information can also be modified by the Transform component
- Direction property, we need to subtract the position of a point from the position of the point light source to get the direction of the point
- The color and intensity of the light source can be adjusted in the Light component
- At the same time, the point light source will attenuate. As the object gradually moves away from the point light source, the light intensity it receives will gradually decrease. The light intensity at the center of the sphere of the point light source is the strongest, and the boundary is the weakest. The attenuation value in the middle can be calculated by function.
Spotlight
Its illuminated space is limited, but it is no longer a simple sphere, but a cone. Its property calculation method is generally similar to that of the point light source, but the attenuation calculation function of the light source needs to determine whether it is within the range of the vertebral body.
Handling different types of light sources in forward rendering
After understanding the geometric definition of light sources in 3, let’s take a look at how to access their five properties in Unity Shader, position, direction, color, intensity, and attenuation
Shader
(1) First, we need to define the first Pass - Base Pass. For this, we need to set the label of the render path of the Pass
1 | Pass { |
In addition to setting the render path, we also used the’prgama 'compile directive, which ensures that we can use lighting attenuation and other care variables in the Shader to be correctly assigned, which is indispensable.
(2) In the slice element shader of Base Pass, we first calculate the ambient light in the scene
1 | fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; |
We want the ambient light to be calculated once, so this part will not be calculated in the Additional Pass later. Similarly, there is a self-luminous object, but we do not consider self-luminous for the time being
(3) Then, we processed the most important parallel light in the scene in Base Pass. In this example, there is only one parallel light in the scene. If there are multiple parallel lights, Unity will select the brightest parallel light and pass it to Base Pass for pixel-by-pixel processing, and other parallel lights will be processed vertex-by-vertex or pixel-by-pixel in Additional Pass. If there are no parallel lights in the scene, Base Pass will be treated as an all-black light source.
We mentioned that each light source has 5 properties, position, direction, color, intensity and attenuation. For Base Pass, the pixel-by-pixel light source type it handles must be parallel light, we can use _WorldSpaceLightPos0 to get the direction of this parallel light (position does not make sense for parallel light), use _LightColor0 to get its color and intensity (_LightColor0 is already the result of multiplying color and intensity), since parallel light can be considered unattenuated, so we directly make the attenuation value 1
1 | fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; |
At this point, the work of Base Pass is complete
(4) Next, we need to define Additional Pass for other pixel-by-pixel light sources in the scene. For this, we prefer to set the render path label of Pass
1 | Pass { |
In addition to setting the render path, we also used the #pragma multi_compile_fwdadd command to ensure that we can access the correct lighting variables in Additional Pass.
Unlike Base Pass, we also enable the Blend command and set the Blend Mode, because we want the lighting results calculated by the Additional Pass to be superimposed on the previous lighting results in the frame cache. If the Blend command is not used, the Additional Pass will directly overwrite the previous lighting results. The blending factor we are using now is’Blend One One ', it is not necessary, we can set it to any coefficient supported by Unity
(5) Generally speaking, the lighting processing of Additional Pass is the same as that of Base Pass, so we only need to paste the code of Base Pass into Additonal Pass and modify it slightly. These modifications are often to remove the ambient light in Base Pass, self-luminous, point-by-point lighting, SH lighting, etc., and add support for some different light sources.
Therefore, we did not calculate the ambient light in the scene. Since the type of light source processed by Additional Pass may be parallel light, point light or spotlight, we can still use _LightColor0 to calculate the 5 properties of the light source - position, direction, color, intensity and attenuation, but for properties such as position, direction and attenuation, we need to calculate them separately according to the type of light source.
First, let’s take a look at how to calculate the direction of different light sources
1 |
|
The above code first determines the type of pixel-by-pixel light source currently processed, which is obtained by using the ‘#ifdef’ command to determine whether USING_DIRECTIONAL_LIGHT ‘is defined. If the light type processed by the current forward rendering Pass is parallel light, Unity’s underlying rendering engine will define USING_DIRECTIONAL_LIGHT. If it is determined to be parallel light, the light source direction can be obtained directly from the’ _ WorldSpaceLightPos0.xyz ‘. If it is a point light source or spotlight, then the’ _ WorldSpaceLigthPos0.xyz 'represents the position of the light source in world space. To get the direction of the light source, you need to subtract the position of the vertices in world space from this position
Finally, we need to deal with the attenuation of different light sources
1 |
|
We also use USING_DIRECTIONAL_LIGHT to determine whether it is parallel light, and the attenuation value of non-parallel light. Although mathematical expressions can be used to calculate the attenuation of a given point relative to the point light source and spotlight, these calculations often involve relatively large calculations such as square root, division, etc. Large operations, so Unity chooses to use a texture as a Look up table (Lookup Table, LUT) to obtain the attenuation of the light source in the chip element shader. We first obtain the coordinates in the light source space, and then use the coordinates to sample the attenuation texture to obtain the attenuation value
The final code is as follows:
1 | Shader "Unlit/ForwardRendering" |
The above code only applies to the Standard pipeline, for URP pipeline is no longer applicable, the specific transformation method can refer to this blog: https://zhuanlan.zhihu.com/p/336428407
Base
We create a parallel light and 4 point light sources in the scene
When we create a light source, its Render Mode is Auto by default, which means Unity will determine for us which light sources will be processed pixel by pixel and which will be processed point by point or SH. Since we didn’t change the value in’Piexl Light Color ', by default an object can accept the 4 brightest pixel-by-pixel lights other than parallel lights. In this example, there are 5 light sources in the scene, one of which is Parallel light, it will be processed pixel-by-pixel in Base Pass, and the other 4 point lights will be processed pixel-by-pixel in Additional Pass.
Unity processes these light sources in order of importance. All point light sources in our scene have the same color and intensity, so their importance depends on how close they are to the capsule.
For an object in the scene, if it is not within the influence range of the light source, Unity will not call Pass for this object.
If there are many pixel-by-pixel light sources in the scene, the Additonal Pass of the object will be called multiple times, which will affect performance, so we can set the Render Mode of the light source to “Not Important”, so that Unity will not treat the light source as pixel-by-pixel.
In our example, the vertex shader does not handle the light source, so if you set 4 point light sources to be “Not Important”, the result is that these 4 light sources will not have an effect.