Location>code7788 >text

Light source types in Unity (forward render path for lighting calculations)

Popularity:117 ℃/2024-09-23 08:08:37

Light Source Types in Unity

There are 4 types of light sources supported in Unity:

  • parallel light
  • point source
  • spotlights
  • Surface light source (can be useful only during light baking)

Properties of the light source:

  • placement
  • Direction (to a point)
  • color
  • dissociation
  • Attenuation (decay to a point)
  1. parallel light

    The geometric definition of parallel light is the simplest; parallel light can illuminate an infinite range and has the same direction and intensity for all points in the scene. It appears in the scene as a character like the sun.

    img

  2. point source

    A point light source illuminates a finite amount of space, which is defined by a sphere in space. It can represent light emitted from a point that extends in all directions.

    img

    Note that the directional attribute of a point light source is a vector derived by subtracting the position of the point light source from a point, indicating the direction of the light from the point light source at that point. The point light source will decay, and as the object gradually principles the point light source, the intensity of light it receives will gradually decrease.

  3. spotlights

    The spotlight is the most complex of these 3 light source types. It illuminates the same limited space, but is no longer a simple sphere, but is defined by a conical area in space. A spotlight can be used to represent light that originates from a specific location and extends in a specific direction.

    img

The radius of this cone-shaped area is determined by the Range property in the panel, and the angle at which the cone opens is determined by the Spot Angle property. We can also modify the properties of the spotlight by dragging and dropping the wireframe of the spotlight (e.g. the yellow control point in the center and the yellow control points around it) directly in the Scene view. The position of the spotlight is also defined by the Position property of the Transform component. For the Direction property, we need to subtract the position of a point from the spotlight's position to get its direction to that point. The attenuation of the spotlight also decreases as the object moves away from the point source, with the intensity of the light being strongest at the apex of the cone, and zero at the boundary of the cone, where the attenuation value can be defined by a function, which is more complex than the point source attenuation formula, because we need to determine whether a point is within the cone or not.

Handling different lighting types in forward rendering

Shader "Custom/ForwardRanderingLearn"
{
    Properties{
        _Diffuse("Diffuse", Color) = (1,1,1,1) //diffuse reflective color
        _Specular("Specular",Color) = (1,1,1,1)//Highlight Reflective Color
        _Gloss("Gloss",Range(8.0,256)) = 20 //High light reflective intensity
    }
    
    SubShader{
        Tags { "RenderType" ="Opaque" }
        
        Pass
        {
            //Setting the rendering mode
            Tags{ "LightMode"="ForwardBase" }
            
            CGPROGRAM
            //Adding Macro References
            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag

            #include ""

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v v)
            {
                v2f o;
                 = UnityObjectToClipPos();
                 = UnityObjectToWorldNormal();
                 = mul(unity_ObjectToWorld,).xyz;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed3 worldNormal = normalize();
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //Direction of parallel light

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //ambient light

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0,dot(worldNormal,worldLightDir));

                //Calculating Highlight Reflections
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - );
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

                //Attenuation factor for parallel light
                fixed atten = 1.0;

                return fixed4(ambient + (diffuse + specular) * atten,1.0);
            }
            ENDCG
        }
        
        Pass
        {
            Tags {"LightMode" = "ForwardAdd"}
            
            //Turn on Mixed Mode
            Blend One One
            
            CGPROGRAM
            
            #pragma multi_compile_fwdadd

            #pragma vertex vert
            #pragma fragment frag

            #include ""
            #include ""

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v v)
            {
                v2f o;

                 = UnityObjectToClipPos();

                 = UnityObjectToWorldNormal();

                 = mul(unity_ObjectToWorld,).xyz;

                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {
                fixed3 worldNormal = normalize();

                //Determine the direction of the light source according to the type of light
                #ifdef USING_DIRECTIONAL_LIGHT
                //parallel light
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                #else
                //非parallel light
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - );
                #endif

                //diffuse reflection of light
                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0,dot(worldNormal,worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - );
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);

               
                //Set the attenuation function according to the type of light source
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed atten = 1.0;
                #else
                    #if defined(POINT)
                         float3 lightCoord = mul(unity_WorldToLight,float4(,1.0)).xyz;
                         fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #elif defined (SPOT)
                         float4 lightCoord = mul(unity_WorldToLight,float4(,1.0)).xyz;
                         fixed atten = ( > 0) * tex2D(_LightTexture0, / + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #else
                        fixed atten = 1.0;
                    #endif

                #endif

                return fixed4((diffuse + specular) * atten,1.0);
            }
            
            ENDCG
        }
    }
    FallBack "Specular"
   
}

In this shader, theBase PassThe most important parallel light in the scene is dealt with in the

There is only one parallel light in the scene, so the Base Pass will only be executed once. If the scene contains more than one parallel light, Unity will select the brightest parallel light and pass it to the Base Pass for pixel-by-pixel processing, while the others will be processed vertex-by-vertex or pixel-by-pixel in the Additional Pass.

If there isn't any parallel light in the scene, then Base Pass treats it as an all-black light source.

For Base Pass, the type of pixel-by-pixel light source it handles must be parallel light. We can use __WorldSpaceLightPos0 to get the direction of this parallel light (position has no meaning for parallel light), and use _LightColor0 to get its color and intensity (_LightColor0 is already the result of multiplying the color and the intensity), and since the parallel light can be considered as having no attenuation, so here we directly Since parallel light can be considered to have no attenuation, here we directly set the attenuation value to 1.0. The related code is as follows:

        //  Compute  diffuse  term
        fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

        ...

        //  Compute  specular  term
        fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)),
        _Gloss);

        //  The  attenuation  of  directional  light  is  always  1
        fixed  atten  =  1.0;

        return  fixed4(ambient  +  (diffuse  +  specular)  *  atten,  1.0);

Next, we need to define Additional Passes for the other pixel-by-pixel light sources in the scene, and to do this, we first need to set up the Pass's render path label:

        Pass  {
            //  Pass  for  other  pixel  lights
            Tags  {  "LightMode"="ForwardAdd"  }

            Blend  One  One

            CGPROGRAM

            //  Apparently  need  to  add  this  declaration
            #pragma  multi_compile_fwdadd

Unlike Base Pass, we also use the Blend command to turn on and set the blend mode. This is because we want the lighting result calculated by Additional Pass to be overlaid with the previous lighting result in the frame buffer. If the Blend command is not used, Additional Pass will directly overwrite the previous lighting results. In this example, the blend factor we chose is Blend One One, which is not required and can be set to any blend factor supported by Unity. The most common is Blend SrcAlpha One.

Typically, lighting is handled the same way in Additional Pass as it is in Base Pass, so all we need to do is paste the vertex and slice shader code from Base Pass into Additional Pass and modify it a little bit. These modifications are often to remove the ambient light, self-illumination, vertex-by-vertex lighting, and SH lighting portions of Base Pass, and to add some support for different light source types. Therefore, in the slice shader of the Additional Pass, we don't compute the ambient light in the scene anymore.

So in calculating the 5 attributes of the light source - thePosition, Direction, Color, Intensity, and AttenuationWhen we use _LightColor0, we can still get the color and intensity, but for the position, direction and attenuation properties, we need to calculate them separately according to the type of light source. First, let's see how to calculate the direction of different light sources:

        #ifdef  USING_DIRECTIONAL_LIGHT
              fixed3  worldLightDir  =  normalize(_WorldSpaceLightPos0.xyz);
        #else
              fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - );
        #endif

Dealing with the attenuation of different light sources:

        #ifdef  USING_DIRECTIONAL_LIGHT
            fixed  atten  =  1.0;
        #else
            float3  lightCoord  =  mul(_LightMatrix0,  float4(,  1)).xyz;
            fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
        #endif

We also determine the type of light source currently being processed by determining whether USING_DIRECTIONAL_LIGHT is defined. If it is a parallel light, the attenuation value is 1.0. If it is any other type of light source, then the processing is a bit more complicated. Although we could use mathematical expressions to calculate the attenuation of a given point relative to point and spot lights, these calculations tend to involve relatively computationally intensive operations such as root signing and dividing, so Unity chooses to use a texture as a Lookup Table (LUT) to get the attenuation of the light source in the slice shader. We first get the coordinates of the light source in space, and then use those coordinates to sample the attenuation texture to get the attenuation value.

image-20240923080110029

Note: This article is Feng Lele's Unity Shader Introductory Essentials Book Notes