求知若饥,虚心若愚
渲染包含了两大部分:决定一个像素的可见性,决定这个像素上的光照运算
我们是如何看到这个世界的
光源
辐照度:垂直于l的单位面积上单位时间内穿过的能量

在左图中,光是垂直照射到物体表面,因此光线之间的垂直距离保持不变;而在右图中,光是斜着照射到物体表面,在物体表面光线之间的距离是d/cosθ,因此单位面积上接收到的光线数目要少于左图
吸收和散射
- 散射只改变光的方向,不改变光线密度和颜色
- 吸收只改变光线密度和颜色,不改变光线方向
- 光线经过散射后有两种方向:折射或反射
- 漫反射:有多少光线会被折射、吸收和散射出表面
- 高光反射:物体表面如何反射光线
- 出射度:出射光线的数量和方向,与辐照度成线性关系

散射时,光线会发生折射和反射现象。对于不透明物体,折射的光线会在物体内部继续传播,最终有一部分光线会重新从物体表面被发射出去
着色
根据材质属性和光源信息,使用一个等式去计算沿某个观察方向的出射度的过程。而这个等式也称为光照模型
如果它看起来是对的,那么它就是对的。
标准光照模型
标准光照模型只关心直接光照(忽略物体间接光照),基本概念由裴祥风提出。标准光照模型不支持一些物理特性,比如菲涅尔反射,且它是各向同性的。
-
自发光,材质的自发光颜色,使用全局光照系统可以对其他物体造成影响。

-
高光反射,需要知道表面法线、视角方向、光源方向、反射方向等
Phong模型计算
max(0%2C%20%5Cvec%7Bv%7D%5Ccdot%20%5Cvec%7Br%7D)%5E%7Bm_%7Bgloss%7D%7D%0A)
Blinn-Phong模型计算
%5E%7Bm_%7Bgloss%7D%7D%0A)
-
漫反射,符合兰伯特定律:反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。
%0A)
-
环境光,通常是个全局变量,场景公用

Unity Shader中实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| Shader "UnityShadersLearn/Chapter6/DiffuseVertexLevel" { Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) } 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 i) { v2f o; o.pos = UnityObjectToClipPos(i.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 已知材质颜色和法线方向,还需要通过_WorldSpaceLightPos0和_LightColor0获取光照方向和颜色 fixed3 worldNormal = normalize(mul(unity_ObjectToWorld, i.normal)); fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldLightDir, worldNormal));
o.color = diffuse + ambient; return o; }
fixed4 frag(v2f i) : SV_Target { return fixed4(i.color, 1.0); } ENDCG } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| Shader "UnityShadersLearn/Chapter6/DiffusePixelLevel" { Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) } 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 i) { v2f o; o.pos = UnityObjectToClipPos(i.vertex); o.worldNormal = mul(unity_ObjectToWorld, i.normal); 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 = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldLightDir, worldNormal));
return fixed4(ambient + diffuse, 1.0); } ENDCG } } FallBack "Diffuse" }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| Shader "UnityShadersLearn/Chapter6/HalfLambert" { Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) } 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 i) { v2f o; o.pos = UnityObjectToClipPos(i.vertex); o.worldNormal = mul(unity_ObjectToWorld, i.normal); 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 halfLambert = 0.5 * dot(worldLightDir, worldNormal) + 0.5; fixed3 diffuse = _Diffuse.rgb * _LightColor0.rgb * halfLambert;
return fixed4(ambient + diffuse, 1.0); } ENDCG } } FallBack "Diffuse" }
|

逐顶点漫反射光照、逐像素漫反射光照、半兰伯特光照的对比效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| Shader "UnityShadersLearn/Chapter6/SpecularVertexLevel" { // 由于高光是非线性的,所以顶点计算高光的话线性插值到片元会很奇怪 Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0) _Gloss("Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #include "Lighting.cginc"
#pragma vertex vert; #pragma fragment frag;
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(unity_ObjectToWorld, v.normal)); fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal)); fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz); fixed3 specular = _Specular.rgb * _LightColor0.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
o.color = ambient + diffuse + specular; return o; }
fixed4 frag(v2f i) : SV_Target { return fixed4(i.color, 1.0); } ENDCG } } FallBack "Specular" }
|

CG的reflect函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| Shader "UnityShadersLearn/Chapter6/SpecularPixelLevel" { Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0) _Gloss("Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #include "Lighting.cginc"
#pragma vertex vert; #pragma fragment frag;
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; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = mul(unity_ObjectToWorld, v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); 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 = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal)); fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); fixed3 specular = _Specular.rgb * _LightColor0.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0); } ENDCG } } FallBack "Specular" }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| Shader "UnityShadersLearn/Chapter6/BlinnPhong" { Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0) _Gloss("Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #include "Lighting.cginc"
#pragma vertex vert; #pragma fragment frag;
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; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = mul(unity_ObjectToWorld, v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); 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 = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _Specular.rgb * _LightColor0.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0); } ENDCG } } FallBack "Specular" }
|

逐顶点的高光反射光照、逐像素的高光反射光照(Phong光照模型)和Blinn-Phong高光反射光照的对比结果
使用Unity内置函数优化
内置函数会考虑点光源等非平行光源的情况,且法线变换会保留垂直性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| Shader "UnityShadersLearn/Chapter6/BlinnPhongUseBuildInFunction" { Properties { _Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0) _Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0) _Gloss("Gloss", Range(8.0, 256)) = 20 } SubShader { Pass { Tags {"LightMode"="ForwardBase"} CGPROGRAM #include "Lighting.cginc"
#pragma vertex vert; #pragma fragment frag;
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; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; }
fixed4 frag(v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _Specular.rgb * _LightColor0.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0); } ENDCG } } FallBack "Specular" }
|