基础知识
光源:实时渲染中,通常把光源当作一个没有体积的点,用l来表示它的方向。使用辐照度(irradiance)来量化光。
吸收和散射:光源与物体相交的结果:散射(scattering)/吸收(absorption)。
散射改变光线方向,但不改变光线密度和颜色:有两种方向,一是散射到物体内部(折射refraction/投射transmission);二是散射到物体外部(反射reflection)
吸收改变光线密度和颜色,但不改变光线方向。
对于不透明物体,,进入物体内部的光线还会和内部颗粒进行相交,其中一些光线最后会重新发射出物体表面。
对于散射的两种方向,在光照模型中采用不同的计算方式:高光反射(specular)——反射光线、漫反射(diffuse)——折射+吸收和散射出表面。
出射度(exitance):出射光线的数量和方向。
BRDF(Bidirectional Reflectance Distribution Function)光照模型:给定入射光线的方向和辐射度后,可以给出在某个出射方向上的光照能量分布。
标准光照模型——Phong光照模型——Blinn-Phong光照模型(各向同性)
只关心直接光照(direct light)——直接从光源发出照射到物体表面后经过一次反射进入摄像机的光线。
把这些光线分为四种:自发光(emissive)——若不使用全局光照(global illumination)技术,不会照亮周围+高光反射(specular)+漫反射(diffuse)+环境光(ambient)——其他所有的间接光照(indirect light)。
漫反射——兰伯特定律:
高光反射——Phong模型(经验模型),其中m^gloss是材质的光泽度(gloss)/反光度(shininess),值越大,亮点越小:
避免计算r——Blinn模型(经验模型)引入v 和l 的中间量h :
在逐像素或逐顶点中计算
片元着色器中计算:逐像素光照(per-pixel lighting):以像素为基础,得到法线(顶点法线插值/法线纹理),然后进行光照模型的计算——Phong着色/Phong插值/法线插值着色技术。
顶点着色器中计算:逐顶点光照(per-vertex lighting):在每个顶点上,计算光照,然后在图元内部进行线性插值,输出成像素颜色——高洛德着色
注意:顶点数小于像素数-》逐顶点计算量小于逐像素。但是由于逐顶点依赖线性插值得到像素光照,因此若光照模型中存在非线性计算(如高光反射),就会出现问题。而且逐顶点光照会导致图元内部颜色总是暗于顶点处的最高颜色值——产生棱角。
Unity中的环境光和自发光
自发光:在片元着色器最后输出的颜色前加上材质自发光的颜色即可。
在unity shader 中实现漫反射光照模型
逐顶点光照,法线计算需要使用原变换矩阵的逆转置矩阵计算才可以:
Shader "Custom/Diffuse_perVertex"
{
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 "Custom/Diffuse_perPixel"
{
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"
}
效果:
逐像素会更平滑,但是在光线到不了的地方将是全黑或者加了环境光但依旧明暗一样——半兰伯特光照模型:
Shader "Custom/Diffuse_perPixel"
{
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));
//使用半兰伯特模型,将分量从[-1,1]放缩到[0,1]
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"
}
效果:
三种对比:
在漫反射的基础上加入高光反射
逐顶点光照:
Shader "Custom/Specular_perVertex"
{
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;
// 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 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.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"
}
效果:
逐像素光照:
Shader "Custom/Specular_perPixel"
{
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;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
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);
// Transform the vertex from object spacet to world space
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
效果:
对Phong的修改——Blinn模型:
Shader "Custom/Specular_perPixel"
{
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;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
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);
// Transform the vertex from object spacet to world space
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// Compute specular term
//使用Phong模型计算高光
//fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
//使用Blinn模型计算高光
// Get the half direction in world space
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
效果:
三种对比:
在实际编写中,有很多内置函数可以用来计算,可以更加简化上面的代码。