三、Lesson3
1、关键名称
向量
• nDir:法线方向,点乘操作时简称n;
• lDir:光照方向,点乘操作时简称l;
• vDir:观察方向,点乘操作时简称v;
• rDir:光反射方向,点乘操作时简称r;
• hDir:半角方向(Halfway),lDir和vDir的中间角方向,点乘操作时简称h
空间
• OS:ObjectSpace 物体空间,本地空间;
• WS:WorldSpace世界空间;
• VS:ViewSpace 观察空间;
• CS:HomogenousClipSpace 齐次剪裁空间;
• TS:TangentSpace 切线空间;
• TXS:TextureSpace 纹理空间;
2、漫反射与镜面反射
(1)漫反射
特点:向四面八方均匀反射,反射亮度与观察者看的方向无关
主要模型:兰伯特、半兰伯特
涉及向量:nDir、lDir
(2)镜面反射
特点:反射具有明显方向性,观察者的视角决定了反射光线的有无、明暗
主要模型:Phong、Blinn-Phong
涉及向量:nDir、lDir、vDir、rDir、hDir
3、Phong
(1)效果展示
(2)计算方式
Phong=rDir(光反射方向) dot vDir(观察方向)
rDir=Reflect(-lDir,nDir)
(3)实现代码
Shader "AP01/Phong" {
Properties {
_MainCol ("颜色", color) = (1.0, 1.0, 1.0, 1.0)
_SpecularPow ("高光次幂", range(1, 90)) = 30
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
// 修饰字(满足小朋友太多的问好, 想保发量的大家看热闹)
// uniform 共享于vert,frag
// attibute 仅用于vert
// varying 用于vert,frag传数据
uniform float3 _MainCol; // RGB够了 float3
uniform float _SpecularPow; // 标量 float
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 顶点信息 Get✔
float4 normal : NORMAL; // 法线信息 Get✔
};
// 输出结构
struct VertexOutput {
float4 posCS : SV_POSITION; // 裁剪空间(暂理解为屏幕空间吧)顶点位置
float4 posWS : TEXCOORD0; // 世界空间顶点位置
float3 nDirWS : TEXCOORD1; // 世界空间法线方向
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; // 新建输出结构
o.posCS = UnityObjectToClipPos( v.vertex ); // 变换顶点位置 OS>CS
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 变换顶点位置 OS>WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线方向 OS>WS
return o; // 返回输出结构
}
// 输出结构>>>像素
float4 frag(VertexOutput i) : COLOR {
// 准备向量
float3 nDir = normalize(i.nDirWS);
float3 lDir = _WorldSpaceLightPos0.xyz;
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
float3 hDir = normalize(vDir + lDir);
// 准备点积结果
float ndotl = dot(nDir, lDir);
float ndoth = dot(nDir, hDir);
// 光照模型
float lambert = max(0.0, ndotl);
float phong = dot(reflect(-lDir,nDir),vDir);
// 返回结果
return phong ;
}
ENDCG
}
}
FallBack "Diffuse"
}
4、Blinn-Phong
(1)效果展示
(2)计算方式
Blinn-Phong=nDir dot hDir
hDir:lDir和vDir的中间角方向
(3)实现代码
Shader "AP01/BlinnPhong" {
Properties {
_MainCol ("颜色", color) = (1.0, 1.0, 1.0, 1.0)
_SpecularPow ("高光次幂", range(1, 90)) = 30
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
// 修饰字(满足小朋友太多的问好, 想保发量的大家看热闹)
// uniform 共享于vert,frag
// attibute 仅用于vert
// varying 用于vert,frag传数据
uniform float3 _MainCol; // RGB够了 float3
uniform float _SpecularPow; // 标量 float
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 顶点信息 Get✔
float4 normal : NORMAL; // 法线信息 Get✔
};
// 输出结构
struct VertexOutput {
float4 posCS : SV_POSITION; // 裁剪空间(暂理解为屏幕空间吧)顶点位置
float4 posWS : TEXCOORD0; // 世界空间顶点位置
float3 nDirWS : TEXCOORD1; // 世界空间法线方向
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; // 新建输出结构
o.posCS = UnityObjectToClipPos( v.vertex ); // 变换顶点位置 OS>CS
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 变换顶点位置 OS>WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线方向 OS>WS
return o; // 返回输出结构
}
// 输出结构>>>像素
float4 frag(VertexOutput i) : COLOR {
// 准备向量
float3 nDir = normalize(i.nDirWS);
float3 lDir = _WorldSpaceLightPos0.xyz;
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
float3 hDir = normalize(vDir + lDir);
// 准备点积结果
float ndotl = dot(nDir, lDir);
float ndoth = dot(nDir, hDir);
// 光照模型
float lambert = max(0.0, ndotl);
float phong = dot(reflect(-lDir,nDir),vDir);
float blinnPhong = pow(max(0.0, ndoth), _SpecularPow);
// 返回结果
return blinnPhong;
}
ENDCG
}
}
FallBack "Diffuse"
}
5、实践
(1)漫反射+镜面反射+固有色
(i)效果
(ii)实现代码
Shader "AP01/OldSchool" {
Properties {
_MainCol ("颜色", color) = (1.0, 1.0, 1.0, 1.0)
_SpecularPow ("高光次幂", range(1, 90)) = 30
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
// 修饰字(满足小朋友太多的问好, 想保发量的大家看热闹)
// uniform 共享于vert,frag
// attibute 仅用于vert
// varying 用于vert,frag传数据
uniform float3 _MainCol; // RGB够了 float3
uniform float _SpecularPow; // 标量 float
// 输入结构
struct VertexInput {
float4 vertex : POSITION; // 顶点信息 Get✔
float4 normal : NORMAL; // 法线信息 Get✔
};
// 输出结构
struct VertexOutput {
float4 posCS : SV_POSITION; // 裁剪空间(暂理解为屏幕空间吧)顶点位置
float4 posWS : TEXCOORD0; // 世界空间顶点位置
float3 nDirWS : TEXCOORD1; // 世界空间法线方向
};
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; // 新建输出结构
o.posCS = UnityObjectToClipPos( v.vertex ); // 变换顶点位置 OS>CS
o.posWS = mul(unity_ObjectToWorld, v.vertex); // 变换顶点位置 OS>WS
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 变换法线方向 OS>WS
return o; // 返回输出结构
}
// 输出结构>>>像素
float4 frag(VertexOutput i) : COLOR {
// 准备向量
float3 nDir = normalize(i.nDirWS);
float3 lDir = _WorldSpaceLightPos0.xyz;
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
float3 hDir = normalize(vDir + lDir);
// 准备点积结果
float ndotl = dot(nDir, lDir);
float ndoth = dot(nDir, hDir);
// 光照模型
float lambert = max(0.0, ndotl);
float phong = dot(reflect(-lDir,nDir),vDir);
float blinnPhong = pow(max(0.0, ndoth), _SpecularPow);
float3 finalRGB = _MainCol * lambert + blinnPhong;
// 返回结果
return float4(finalRGB, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}