UnityShader第十章 高级纹理

文章描述了在Unity的macOS版本中,使用含有_Texture_TexelSize的shader时遇到的bug,该问题在2023.3.1版本仍未解决。作者通过在Windows环境下测试发现没有此问题。问题的关键在于_RefractionTex_TexelSize的值异常,导致折射效果无法正常显示。作者提供了shader代码并指出即使调整_Distortion值,由于_RefractionTex_TexelSize接近0,折射效果仍无法显现。解决方案尚未明确,作者期望社区中的其他开发者能提供帮助。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

更新 关于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
        }
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值