Basic lighting in Unity

Generally speaking, we need to consider three physical phenomena to simulate the real lighting environment to generate an image.

First, light is emitted from the light source.

Then, the light intersects with some objects in the scene: some light is absorbed by the object, while others are scattered in other directions.

Finally, the camera absorbs some light and produces an image.

light source

In real-time rendering, we usually regard the light source as a point without volume, and use L to represent its direction. So how do we measure how much light a light source emits? That is, how do we quantify light? In optics, we use irradiance to quantify light. For directional light, its irradiance can be obtained by calculating the energy passing through per unit time on a unit area perpendicular to L. When calculating the illumination model, we need to know the irradiance of an object surface, and the object surface is often not perpendicular to L, so how to calculate such surface irradiance? We can use the cosine of the angle between the direction l of the light source and the surface normal n to get it. It should be noted that the modulus of the default direction vector here is 1.  

Absorption and scattering

After the light is emitted from the light source, it will intersect with some objects. Usually, there are two intersecting results, scattering and absorption.

Scattering only changes the direction of light. After light is scattered on the surface of an object, there are two directions: one is to scatter it into the interior of the object, which is called refraction or transmission; The other will scatter to the outside, which is called reflection. For opaque objects, the light refracted into the interior of the object will continue to intersect with the internal particles, some of which will finally be emitted from the surface of the object, and others will be absorbed by the object. The light re emitted from the surface of these objects will have a different direction distribution and color from the incident light.  

In order to distinguish two different scattering methods, we use different parts in the lighting model to calculate them: the specular part represents how the object surface reflects light, while the diffuse part represents how much light will be refracted, absorbed and scattered out of the surface. According to the number and direction of incident light, we can calculate the number and direction of outgoing light. We usually use emittance to describe it. There is a linear relationship between irradiance and emittance, and the ratio between them is the material and specular properties.

to color

Shading refers to the process of using an equation to calculate the emittance along a viewing direction according to the material properties and light source information. We also call this equation the illumination model. Different lighting models have different purposes.

BRDF illumination model

When we know the position and direction of the light source and the direction of the viewing angle, we need to know that a surface interacts with the light. For example, when light strikes a surface from a certain direction, how much light is reflected? What are the directions of reflection? BRDF is used to answer these questions. When a point on a model surface is given, BRDF contains a complete description of the appearance of the point. In graphics, BRDF mostly uses a mathematical formula to express, and provides some parameters to adjust the material properties. Generally speaking, when the direction and irradiance of the incident light are given, BRDF can give the light energy distribution in a certain emission direction.

The first law of computer graphics: if he looks right, he is right.

Standard illumination model

The standard illumination model only cares about direct illumination, that is, the light directly emitted from the light source that shines on the object surface and directly enters the camera after one reflection through the object surface.

His basic method is to divide the light entering the camera into four parts. Each part uses a method to calculate its contribution. These four parts are;

Self luminous part. This section is used to describe how much radiation a surface itself emits back in a given direction. It should be noted that if global illumination technology is not used, these self luminous surfaces will not really illuminate the surrounding objects, but they will look brighter.

Specular part, which is used to describe how much radiation is scattered by the surface in the direction of complete specular reflection when light shines on the surface of the model from the light source.

diffuse part, which is used to describe how much radiation the surface will scatter in each direction when light shines on the surface of the model from the light source.

The ambient section is used to describe all other indirect lighting.

Ambient light

Although the focus of the standard care model is to describe direct lighting, in the real world, objects can also be illuminated by the introduction lighting theory. Indirect illumination means that light is usually reflected between multiple objects and finally enters the camera, that is, it is reflected by objects more than once before it enters the camera.

In the standard illumination model, we use a part called environmental view to approximately simulate indirect illumination. The calculation of ambient light is very simple. It is usually a global variable, that is, all objects in the scene use this ambient light.

Self luminous

Light can also be emitted directly from the light source into the camera instead of being reflected by any object. The standard illumination model uses self illumination to calculate the contribution of this part.

Usually, in real-time rendering, the self luminous surface will not illuminate the surrounding surface, that is, the object will not be used as a light source.

diffuse reflection

Diffuse illumination is used to model the radiance scattered randomly by the object surface in all directions. In diffuse reflection, the position of the viewing angle is not important, because the reflection is completely random, so it can be considered that the part in any reflection direction is the same. However, the angle of incident light is important.

Diffuse reflection takes care of compound Lambert's Law: the intensity of reflected light is directly proportional to the cosine of the angle between the surface normal and the direction of the light source.

Specular reflection

The specular reflection here is an empirical model, that is to say, it is not completely consistent with the specular reflection phenomenon in the real world. It can be used to calculate the light that is not reflected in the direction of full specular reflection, which can make objects look shiny, such as metal materials.

The calculation of specular reflection requires a lot of information, such as the normal of the incoming surface, the direction of viewing angle, the direction of light source, the direction of reflection, etc.

Pixel by pixel or vertex by vertex

Where do we calculate the illumination model? Generally speaking, we have two choices: calculate in the slice shader, also known as per pixel lighting; Calculated in vertex shaders, also known as per vertex lighting.

In pixel by pixel illumination, we will get its normal based on each pixel, and then calculate the illumination simulation. This technique of interpolating vertex normals between patches is called Phong shading, also known as Phong interpolation or normal interpolation shading. This is different from the Phong lighting model we talked about earlier.

The opposite is vertex by vertex care, also known as Gauld coloring. In per vertex lighting, we calculate the lighting on each vertex, then perform linear interpolation within the rendering element, and finally output the imager color. Because the number of vertices is often much less than the number of pixels, the amount of calculation of per vertex illumination is often less than that of per pixel illumination. However, because per vertex illumination depends on linear interpolation to obtain pixel illumination, there will be problems when there are nonlinear calculations in the illumination model.

summary

Although the standard lighting mode is only an empirical mode, that is, it does not fully match the lighting in the real world. However, it is still widely used because of its ease of use, calculation speed and good effect. Second, due to its wide use, this standard lighting mode has many different names, also known as Phong lighting mode.

But this model has many limitations. First, there are many important physical phenomena that cannot be expressed by this model, such as Fresnel reflection. Secondly, this model is isotropic, that is, when we rotate the surface with a fixed angle of view and the direction of the light source, the reflection will not change. However, some surfaces have anisotropic reflection properties, such as brushed metal, hair and so on.

Ambient and self illumination in Unity

Ambient light

Self luminous

Diffuse illumination model

From left to right, there are vertex by vertex lighting, pixel by pixel lighting and half Lambert model

per-vertex lighting

Shader "Custom/manfanse"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
    }
    SubShader
    {
        Pass{
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"
            fixed4 _Diffuse;

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

            struct v2f{
                float4 pos:SV_POSITION;
                fixed3 color:COLOR;
            };


            v2f vert(a2v v){
                v2f o;
                //Model space to crop space
                o.pos=UnityObjectToClipPos(v.vertex);
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));

                o.color=ambient+diffuse;
                return o;

            }

            fixed4 frag(v2f i):SV_TARGET{
                return fixed4(i.color,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

Per pixel illumination

Shader "Custom/manfanse"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
    }
    SubShader
    {
        Pass{
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"
            fixed4 _Diffuse;

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

            struct v2f{
                float4 pos:SV_POSITION;
                fixed3 worldNormal:TEXCOORD0;
            };


            v2f vert(a2v v){
                v2f o;
                //Model space to crop space
                o.pos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                return o;

            }

            fixed4 frag(v2f i):SV_TARGET{
              fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

              fixed3 worldNormal=normalize(i.worldNormal);

              fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);

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

              fixed3 color=ambient+diffuse;

              return fixed4(color,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

Semi Lambert model

Shader "Custom/manfanse"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
    }
    SubShader
    {
        Pass{
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag 
            #include "Lighting.cginc"
            fixed4 _Diffuse;

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

            struct v2f{
                float4 pos:SV_POSITION;
                fixed3 worldNormal:TEXCOORD0;
            };


            v2f vert(a2v v){
                v2f o;
                //Model space to crop space
                o.pos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                return o;

            }

            fixed4 frag(v2f i):SV_TARGET{
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 worldNormal=normalize(i.worldNormal);

                fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);

                fixed halfLambert=dot(worldNormal,worldLightDir)*0.5+0.5;

                fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halfLambert;

                fixed3 color=ambient+diffuse;

                return fixed4(color,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

Specular reflection model

From left to right, there are vertex by vertex lighting, pixel by pixel lighting and Blinn Phong lighting model

per-vertex lighting

Shader "Custom/gaoguagn"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8.0,256))=20
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

            struct v2f{
                float4 pos:SV_POSITION;
                fixed3 color:COLOR;
            };

            v2f vert(a2v v){
                v2f o;

                o.pos=UnityObjectToClipPos(v.vertex);
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                fixed3 woeldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse =_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,woeldLightDir));
                fixed3 refletDir=normalize(reflect(-woeldLightDir,worldNormal));
                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz);
                fixed3 specular= _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(refletDir,viewDir)),_Gloss);

                o.color=ambient+diffuse+specular;

                return o;
            }

            fixed4 frag(v2f i):SV_TARGET{
                return fixed4 (i.color ,1.0);
            }

            ENDCG
        }
    }
    Fallback "Specular"
}

Per pixel illumination

Shader "Custom/gaoguagn"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8.0,256))=20
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

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

            v2f vert(a2v v){
                v2f o;

                o.pos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;

                return o;


            }

            fixed4 frag(v2f i):SV_TARGET{
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal=normalize(i.worldNormal);
                fixed3 woeldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse =_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,woeldLightDir));
                fixed3 refletDir=normalize(reflect(-woeldLightDir,worldNormal));
                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
                fixed3 specular= _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(refletDir,viewDir)),_Gloss);

                return fixed4(ambient+diffuse+specular,1.0);
            }

            ENDCG
        }
    }
    Fallback "Specular"
}

Blinn Phong illumination model

Shader "Custom/gaoguagn_2"
{
    Properties
    {
        _Diffuse("Diffuse",Color)=(1,1,1,1)
        _Specular("Specular",Color)=(1,1,1,1)
        _Gloss("Gloss",Range(8.0,256))=20
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

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

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

            v2f vert(a2v v){
                v2f o;

                o.pos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
                o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;

                return o;

            }

            fixed4 frag(v2f i):SV_TARGET{
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
                fixed3 worldNormal=normalize(i.worldNormal);
                fixed3 woeldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                fixed3 diffuse =_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,woeldLightDir));
 
                fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
                fixed3 halfDir=normalize(woeldLightDir*viewDir);
                fixed3 specular= _LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);

                return fixed4(ambient+diffuse+specular,1.0);
            }

            ENDCG
        }
    }
    Fallback "Specular"
}

Keywords: Unity 3d

Added by wesley1189 on Thu, 27 Jan 2022 15:02:18 +0200