更新 关于Unity macOS版本出现的bug
过了大半个月,发现另外需要用到_Texture_TexelSize的shader也出了同样的问题。后来去Google了一下这个问题发现是Unity里的一个bug,目前2023.3.1还是没有更新。
然后我换Windows试了一下发现没有问题。😅
10.2.2 玻璃效果
这一小节主要介绍GrabPass。拿玻璃效果的shader举例,玻璃的反射由对Cubemap的取样实现(前一节有讲)、折射由对GrabPass纹理取样实现。
折射效果的实现
在对GrabPass取样的时候并不是拿原本的屏幕映射坐标(Screen Position)进行取样,而是要对屏幕映射坐标(Screen Position)进行加工、偏移,从而实现光线折射的效果。
而取样坐标偏移offset的量,是由片元处bump map值(法线纹理定义的“凹凸度”)以及片元深度(很好理解,玻璃厚度越深对后方画面的折射效果越明显,光学原理实际是光程越长视线偏差越大)决定的。但在本书的shader代码里,offset还由GrabPass产生纹理的纹素大小texel size决定,官方的解释是:“_RefractionTex_TexelSize可以让我们得到该纹理的纹素大小,例如一个大小为256×512的纹理,它的纹素大小为(1/256, 1/512)。我们需要在对屏幕图像的采样坐标进行偏移时使用该变量”。
//Compute the offset (in tangent space)
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
//Use offset uv to get refraction value(by sampling refractive texture)
fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
出BUG
官方shader代码在项目里跑的时候出现bug。
可以看到即便distortion值调的很大,但shader没有出现任何的折射效果。
调试了一下发现是_RefractionTex_TexelSize值出了问题,在我的项目shader里的默认值为(or接近为)0。如果offset不考虑_RefractionTex_TexelSize的影响:
float2 offset = bump.xy * _Distortion;
这时折射扭曲的效果会出现,虽然offset整体值偏大有些失真:
总的来说我还是不清楚哪里出了问题导致_RefractionTex_TexelSize值出了问题,在pass的开头也做了如下声明:
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
但是没有在Unity面板上对GrabPass的参数进行任何设置(书里貌似也没有相应的内容),不知道到底是哪个环节遗失导致最终出bug。如果知道的友友请在评论区告诉我一声,感谢!下面是完整的shader代码:
Shader "Unlit/Glass"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {} //玻璃的材质纹理
_BumpMap ("Normal Map", 2D) = "bump" {} //玻璃的法线纹理
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {} //模拟反射的环境纹理
_Distortion ("Distortion", Range(0, 100)) = 10 //控制折射图像的扭曲程度
_RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0 //折射&反射的比率
}
SubShader
{
//We must be transparent, so other object are drawn before this one
Tags {
"Queue" = "Transparent"
"RenderType" = "Opaque" }
//This pass grabs the screen behind the object into a texture
//So that we can access the result in the next pass as _RefractionTex
GrabPass{"_RefractionTex"}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
//变量声明
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
samplerCUBE _Cubemap;
float _Distortion;
fixed _RefractAmount;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
float4 uv : TEXCOORD1;
float4 TtoW0 : TEXCOORD2;
float4 TtoW1 : TEXCOORD3;
float4 TtoW2 : TEXCOORD4;
};
v2f vert (a2v v)
{
v2f o;
//1 顶点变换
o.pos = UnityObjectToClipPos(v.vertex);
//2 获取GrabScreen的采样坐标
o.scrPos = ComputeGrabScreenPos(o.pos);
//3 处理纹理的采样坐标
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
//4 将顶点位置/法线/切线/副法线全部转换到世界空间中(-->这里要记住了,类似的操作都是在顶点着色器中完成的)
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
{
//1 位置
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
//2 视线方向(frag --> eye)
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//3 Get the normal in tangent space(先在法线空间对法线纹理进行采样)
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
//4 Compute the offset (in tangent space)
//float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
float2 offset = bump.xy * _Distortion ;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
//Use offset uv to get refraction value(by sampling refractive texture)
fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
//5 Convert the normal to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
//6 Compute the reflection direction
fixed3 reflDir = reflect(-worldViewDir, bump);
//7 Compute the reflection color(using Texture color * CubeMap)
fixed4 texColor = tex2D(_MainTex, i.uv.xy);
fixed3 reflColor = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;
//8 Mix refraction and reflection
fixed3 finalColor = reflColor * (1 - _RefractAmount) + refrCol * _RefractAmount;
return fixed4(finalColor, 1.0);
}
ENDCG
}
}
}