学习资料:《Unity Shader入门精要》第10章
源代码:Github
立方体纹理
菲涅尔反射
视线与法线夹角越大,反射越强
Schlick菲涅尔近似等式:
F
s
c
h
l
i
c
k
(
v
⃗
,
n
⃗
)
=
F
0
+
(
1
−
F
0
)
(
1
−
v
⃗
⋅
n
⃗
)
5
F_{schlick}(\vec{v},\vec{n})=F_{0}+(1-F_{0})(1-\vec{v}\cdot \vec{n})^5
Fschlick(v,n)=F0+(1−F0)(1−v⋅n)5
v:视角方向,n:表面法线
渲染纹理
渲染目标纹理:把整个三维场景渲染到中间缓冲,即渲染目标纹理(Render Target Texture, RTT)
多重渲染目标(MRT):把场景同时渲染到多个渲染目标纹理中
延迟渲染
镜子效果
创建渲染纹理(Create -> Render Texture),并设置为摄像机的Target Texture,使摄像机把渲染结果更新到这个渲染纹理中。把摄像机放在适当的位置(镜子的中间),调整裁剪平面和视角等,使摄像机看到的就是镜子所照到的,只是左右相反。
使用渲染纹理作为Shader的输入属性,左右翻转后显示到物体上即可:
Mirror的Main Tex使用由渲染相机得到的渲染纹理Mirror Texture,渲染相机挂载在Mirror下面,Target Texture选择Mirror Texture。
// 左右翻转
o.uv = v.texcoord;
o.uv.x = 1 - o.uv.x;
效果展示:
玻璃效果
GrabPass:把当前屏幕的图像绘制在一张纹理中,供后续Pass使用,通常用于渲染透明物体
反射:CubeMap 环境映射纹理
折射:使用GrabPass获取玻璃后面的屏幕图像,使用切线空间下的法线对屏幕纹理坐标偏移后,再对屏幕图像采样得到近似的折射效果
// This pass grabs the screen behind the object into a texture.
// We can access the result in the next pass as _RefractionTex
GrabPass { "_RefractionTex" }
v2f vert (a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// 得到被抓取的屏幕图像的采样坐标
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
// 切线空间变换到世界空间
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag (v2f i) : SV_Target {
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
// 采样法线纹理,得到切线空间的法线方向
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
// 计算折射的偏移
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
// 法线从切线空间变换到世界空间
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed3 reflDir = reflect(-worldViewDir, bump);
fixed4 texColor = tex2D(_MainTex, i.uv.xy);
// 混合得到反射颜色
fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;
fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;
return fixed4(finalColor, 1);
}
效果展示:
渲染纹理 & GrabPass
-
GrabPass:实现简单
-
渲染纹理:手动创建渲染纹理、额外摄像机,把渲染纹理传递给对应的shader
效率:渲染纹理好于GrabPass,渲染纹理可以自定义大小,GrabPass的分辨率和显示屏幕一致,在移动设备上GrabPass需要CPU直接读取back buffer的数据