渲染包含了两大部分:决定一个像素的可见性,决定这个像素上的光照计算。
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中一些常用的帮助函数