阿卡内酱
阿卡内酱
发布于 2026-04-14 / 1 阅读
0
0

06.Unity中的基础光照

渲染包含了两大部分:决定一个像素的可见性,决定这个像素上的光照计算。

6.1 基础概念

辐照度irradiance:光源照射到物体表面的点之间的距离。辐照度与这个距离成反比,与光源方向与物体表面法线方向夹角的cos成正比。可以通过光源方向 · 法线来计算cos

高光反射specular:描述物体如何直接反射光线,即镜面反射。
漫反射diffuse:表示多少物体会被折射、吸收和再次散射出表面
出射度:描述射出的光线的数量与方向。与辐照度满足线性关系,比值为材质的漫反射和高光反射属性。

着色shading:指的是,根据材质属性(如漫反射属性等)、光源信息(如光源方向、辐照度等),使用一个等式去计算沿某个观察方向的出射度的过程。我们也把这个等式称为光照模型 (Lighting Model) 。

BRDF光照模型(Bidirectional Reflectance Distribution Function):当给定入射光线的方向和辐照度后,BRDF可以给出在某个出射方向上的光照能量分布。

6.2 标准光照模型

phong模型:只关心直接光照(direct light),也就是那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线。
它的基本方法是,把进入到摄像机内的光线分为4个部分,每个部分使用一种方法来计算它的贡献度。这4个部分是。

  • 自发光 (emissive) 部分,使用 c emissive 来表示。这个部分用于描述当给定一个方向时,一个表面本身会向该方向发射多少辐射量。需要注意的是,如果没有使用全局光照(global illumination)技术,这些自发光的表面并不会真的照亮周围的物体,而是它本身看起来更亮了而已。

  • 高光反射 (specular) 部分,使用 c specular 来表示。这个部分用于描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。

  • 漫反射 (diffuse) 部分,使用 c diffuse 来表示。这个部分用于描述,当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。

  • 环境光 (ambient) 部分,使用 c ambient 来表示。它用于描述其他所有的间接光照。

6.2.1 环境光

C ambient = G ambient

6.2.2 漫反射

兰伯特定律Lambert’s law
c_{diffuse}=(c_{light}\,\cdot\,m_{diffuse})max(0,\hat n\cdot\hat l)c light表示光源颜色,m diffuse表示材质的漫反射颜色,n是表面法线,l是指向光源的单位法线。max防止取负防止被背后光照亮

6.2.3 高光反射

phong模型:
c_{specular}=(c_{light}\,\cdot\,m_{specular})max(0,\hat v\cdot\hat r)^{m_{gloss}}m gloss 是材质的光泽度 (gloss) ,也被称为反光度 (shininess) 。它用于控制高光区域的“亮点”有多宽, m gloss 越大,亮点就越小。 m specular 是材质的高光反射颜色,它用于控制该材质对于高光反射的强度和颜色。 c light 则是光源的颜色和强度。v视角方向

Blinn模型:h->halfDir

\begin{gather} \hat h=\frac{\hat v+\hat l}{|\hat v+\hat l|} \\ c_{specular}=(c_{light}\,\cdot\,m_{specular})max(0,\hat n\cdot\hat h)^{m_{gloss}} \end{gather}

6.2.4 逐像素/逐顶点

逐像素光照:在片元着色器中进行光照计算,有phong着色方法
逐顶点光照:被称为高洛德着色 (Gouraud shading)

6.3 Unity的环境光与自发光

在Unity中,场景中的环境光可以在Window -> Lighting ->Ambient Source/Ambient Color/Ambient Intensity 中控制,
在Shader中,我们只需要通过Unity的内置变量UNITY_LIGHTM ODEL_AMBIENT就可以得到环境光的颜色和强度信息。

要计算自发光也非常简单,只需要在片元着色器输出最后的颜色之前,把材质的自发光颜色添加到输出颜色上即可。

6.4漫反射实现

逐顶点光照示例代码:即在顶点着色器中进行颜色计算

Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
	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;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);
				
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Transform the normal from object space to world space
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				// Get the light direction in world space
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				// Compute diffuse term
				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"
}

逐像素光照代码:即在片元着色器中进行颜色计算

Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-Level" {
	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;
				float3 worldNormal : TEXCOORD0;
			};
			
			v2f vert(a2v v) {
				v2f o;
				// Transform the vertex from object space to projection space
				o.pos = UnityObjectToClipPos(v.vertex);

				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);

				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				// Get the normal in world space
				fixed3 worldNormal = normalize(i.worldNormal);
				// Get the light direction in world space
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				
				fixed3 color = ambient + diffuse;
				
				return fixed4(color, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

半兰伯特模型

公式:
c_{diffuse}=(c_{light}\,\cdot\,m_{diffuse})(\alpha(\hat n\cdot\hat l)+\beta)α与β一般取0.5,此时n·l就被直接映射到[ 0,1 ]

6.5 高光反射实现

逐顶点:

Shader "Tutorial/Chapter06/Ct6-Specular-Vertex-Level"
{
    Properties {
        _Diffuse ("Diffuse Color", color) = (1,1,1,1)
        _Specular ("Specular Color", 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 "UnityCG.cginc"
            #include "Lighting.cginc"

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

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

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);

                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(mul(worldNormal, worldLight));

                // Specular
                fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
                
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_WorldToObject, v.vertex));

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(mul(viewDir, reflectDir)), _Gloss);

                o.color = ambient + diffuse + specular;

                return o;
            }

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

            ENDCG
        }
    }
}

逐像素:

Shader "Tutorial/Chapter06/Ct6-Specular-Pixel-Level"
{
    Properties {
        _Diffuse ("Diffuse Color", color) = (1,1,1,1)
        _Specular ("Specular Color", 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 "UnityCG.cginc"
            #include "Lighting.cginc"

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

            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 viewDir : TEXCOORD1;
                fixed3 color : Color;
            };

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_WorldToObject, v.vertex));

                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET {
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(mul(i.worldNormal, worldLight));

                // Specular
                fixed3 reflectDir = normalize(reflect(-worldLight, i.worldNormal));

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(mul(i.viewDir, reflectDir)), _Gloss);

                i.color = ambient + diffuse + specular;

                return fixed4(i.color, 1);
            }

            ENDCG
        }
    }
}

Blinn-Phong模型

代码:根据blinn模型公式进行计算

Shader "Tutorial/Chapter06/Ct6-BlinnPhong"
{
    Properties {
        _Diffuse ("Diffuse Color", color) = (1,1,1,1)
        _Specular ("Specular Color", 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 "UnityCG.cginc"
            #include "Lighting.cginc"

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

            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 viewDir : TEXCOORD1;
                fixed3 color : Color;
            };

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));

                o.viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_WorldToObject, v.vertex));

                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET {
                fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(mul(i.worldNormal, worldLight));

                // Specular
                fixed3 h = normalize(i.viewDir + worldLight);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(mul(i.worldNormal, h)), _Gloss);

                i.color = ambient + diffuse + specular;

                return fixed4(i.color, 1);
            }

            ENDCG
        }
    }
}

实际渲染中绝大多数情况下选择使用Blinn-Phong模型。

6.6 一些内置函数

表6.1 UnityCG.cginc中一些常用的帮助函数

函 数 名

描 述

float3 WorldSpaceViewDir (float4 v)

输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向。内部实现使用了UnityWorldSpaceViewDir函数

float3 UnityWorldSpaceViewDir (float4 v)

输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向

float3 ObjSpaceViewDir (float4 v)

输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向

float3 WorldSpaceLightDir (float4 v)

仅可用于前向渲染中 。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。内部实现使用了UnityWorldSpaceLightDir函数。没有被归一化

float3 UnityWorldSpaceLightDir (float4 v)

仅可用于前向渲染中 。输入一个世界空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化

float3 ObjSpaceLightDir (float4 v)

仅可用于前向渲染中 。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有被归一化

float3 UnityObjectToWorldNormal (float3 norm)

把法线方向从模型空间转换到世界空间中

float3 UnityObjectToWorldDir (float3 dir)

把方向矢量从模型空间变换到世界空间中

float3 UnityWorldToObjectDir(float3 dir)

把方向矢量从世界空间变换到模型空间中


评论