Shaderlab 玻璃效果

第一种是冯乐乐版的

在这里插入图片描述

多附上了阴影和光照
基本原理:渲染队列为Transparent,也就是在所有不透明物体渲染完成之后再渲染毛玻璃,但不用开启混合,因为我们用GrabPass直接获取到渲染前的画面了,使用GrabPass保存玻璃渲染前的屏幕画面。
在shader中,首先在像素着色器中计算出当前像素在屏幕中的位置(范围[0,1]),然后将该位置沿着切线空间的(x,y)偏移,将偏移的结果作为在GrabPass中采样的位置,以实现画面扭曲的效果,得到扭曲后的像素,保存为一个颜色值。为什么不沿z偏移呢,因为世界空间下的高模中大部分顶点的法线不需要扰动,所以将其降为低模并映射到切线空间下的法线纹理后,大部分法线纹理的像素都趋近于(0, 0, 1),所以这种纹理大多偏向蓝色。使用z偏移屏幕坐标的话,效果不好。
此外,保存当前位置的立方体贴图,然后使用立方体映射得到另一个颜色值,然后用参数对以上两个颜色值做插值得到最后的扭曲结果。
最后可以再加上相应的光照和阴影,光照做加法即可。

  1. 计算屏幕坐标
    (我自己理解的二者关系)
    在这里插入图片描述
	// VS
	o.pos = UnityObjectToClipPos(v.vertex);
	
	float4 srcPos = o.pos * 0.5f;
   	srcPos.xy = float2(srcPos.x, srcPos.y * _ProjectionParams.x) + srcPos.w;
    srcPos.zw = o.pos.zw;
    o.srcPos = srcPos;

	// PS
	当前像素的实际uv是o.srcPos.xy / o.srcPos.w

_ProjectionParams.x表示directx和opengl的uv坐标系差异,两者的y轴正好相反,可以认为是投影矩阵反转的结果,这个x取值为1或者-1,对应着投影矩阵是否反转。
顶点着色器里实际上是做了①
在这里插入图片描述
为什么要这样呢?这个式子①代表什么呢?实际上,对于透视投影来说,将顶点做MVP变换的结果,就是将视锥挤压(缩放)成一个立方体,连带着所有顶点坐标一起缩放,坐标中心是摄像机,结果的每一个分量就是顶点在这个裁剪空间中的坐标。(裁剪是硬件做的,不用管)
在这里插入图片描述

但是这个值的w分量不再是1,所以要做齐次除法将w变为1,也就意味着这个坐标的xyz三个分量都要除以w。齐次除法之后,xyz的范围会变成[-1,1](unity),也就是NDC空间下的坐标,接下来只需要将这个范围进行视口变换放到屏幕空间下即可。也就是除以2加0.5。写出齐次除法和视口变换公式即:
在这里插入图片描述
那么再看式子①,在着色器中我们可以忽略屏幕宽度和长度,也就是不考虑乘pixelWidth和pixelHeight,但是问题来了,在顶点着色器中进行齐次除法的话,会破坏在像素着色器中的插值结果,因为裁剪空间是非线性空间,而插值是基于三角形重心坐标的线性插值,因此,如果在VS中不做齐次除法的话,插值的对象就是x,而做了的话,插值的对象就是x/w,引入了w这个新的变量,破坏了线性插值的条件,因此等在PS中插值完了再做齐次除法。
因此,①式在像素着色器中除以w,即屏幕坐标(uv)。

Shader "Custom/MyGlassShader"
{
    Properties
    {
        _MainTex ("Main Tex", 2D) = "white" {}
        _BumpTex ("Bump Tex", 2D) = "bump" {}
        _Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
        _Distortion ("Distortion", Range(0, 100)) = 10
        _RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0
        _LightAmount ("Light Amount", Range(0.0, 1.0)) = 1.0
    }
    SubShader
    {
        Tags { "LightMode" = "ForwardBase" "Queue" = "Transparent" "RenderType"="Opaque" }

        GrabPass { "_RefractionTex" }
           
        Pass
        {
            Cull Off
            CGPROGRAM

            #pragma multi_compile_fwdbase

            #pragma vertex VS
            #pragma fragment PS
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _BumpTex;
            float4 _BumpTex_ST;
            samplerCUBE _Cubemap;
            float _Distortion;
            float _RefractAmount;
            sampler2D _RefractionTex;
            float4 _RefractionTex_TexelSize;
            float _LightAmount;

            struct Input
            {
                float3 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float2 texcoord : TEXCOORD0;
            };

            struct Output
            {
                float4 pos : SV_POSITION;
                float4 srcPos : TEXCOORD0;
                float4 uv : TEXCOORD1;
                float3 posW : TEXCOORD2;
                float3 tanW : TEXCOORD3;
                float3 bitanW : TEXCOORD4;
                float3 normalW : TEXCOORD5;
                SHADOW_COORDS(6)
            };

            Output VS(Input v)
            {
                Output o;

                o.pos = UnityObjectToClipPos(v.vertex);
                o.posW = mul((float3x3)UNITY_MATRIX_M, v.vertex);

                //o.srcPos = ComputeGrabScreenPos(o.pos);  // 得到该顶点在屏幕上的采样坐标
                float4 srcPos = o.pos * 0.5f;
                srcPos.xy = float2(srcPos.x, srcPos.y * _ProjectionParams.x) + srcPos.w;
                srcPos.zw = o.pos.zw;
                o.srcPos = srcPos;

                o.uv.xy = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
                o.uv.zw = v.texcoord * _BumpTex_ST.xy + _BumpTex_ST.zw;

                o.normalW = normalize(mul((float3x3)unity_WorldToObject, v.normal));
                o.tanW = normalize(mul((float3x3)UNITY_MATRIX_M, v.tangent));
                o.bitanW = cross(o.normalW, o.tanW) * v.tangent.w;

                TRANSFER_SHADOW(o)
                
                return o;
            }

            fixed4 PS(Output o) : SV_TARGET
            {
                fixed3 bump = UnpackNormal(tex2D(_BumpTex, o.uv.zw));
               
                // Compute the offset in tangent space
                float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
                o.srcPos.xy = offset + o.srcPos.xy;  // 对屏幕采样坐标进行偏移
                fixed3 refrCol = tex2D(_RefractionTex, o.srcPos.xy / o.srcPos.w).rgb;  //_RefractionTex就是屏幕画面

                o.normalW = normalize(o.normalW);
                o.tanW = normalize(o.tanW - dot(o.normalW, o.tanW) * o.normalW);
                o.bitanW = normalize(o.bitanW);
                float3x3 t2w = float3x3(o.tanW, o.bitanW, o.normalW);
                bump = normalize(mul(bump, t2w));

                fixed3 f2Cam = normalize(_WorldSpaceCameraPos - o.posW);

                fixed3 reflDir = reflect(-f2Cam, bump);
                fixed4 texColor = tex2D(_MainTex, o.uv.xy);
                fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;

                fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;

        
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Unity中,可以使用ShaderLab语言编写自定义着色器。下面是一个简单的UI模糊效果Shader: ``` Shader "Custom/UIBlur" { Properties { _MainTex ("Texture", 2D) = "white" {} _BlurSize ("Blur Size", Range(0.0, 1.0)) = 0.01 } SubShader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} Pass { ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Cull Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; float _BlurSize; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col += tex2D(_MainTex, i.uv + float2(-0.5, -0.5) * _BlurSize); col += tex2D(_MainTex, i.uv + float2(-0.5, 0.5) * _BlurSize); col += tex2D(_MainTex, i.uv + float2(0.5, -0.5) * _BlurSize); col += tex2D(_MainTex, i.uv + float2(0.5, 0.5) * _BlurSize); col /= 5.0; return col; } ENDCG } } FallBack "UI/Default" } ``` 这个Shader中的`_MainTex`属性指定了需要模糊处理的纹理,`_BlurSize`属性指定了模糊的程度。在Fragment Shader中,使用了多次tex2D采样实现了简单的模糊效果。最后将采样到的颜色进行平均,返回最终的颜色。 在使用这个Shader时,只需要将它应用到需要模糊处理的UI元素的材质上即可。 ### 回答2: 要使用ShaderLab编写UI模糊效果,可以按照以下步骤进行: 1. 创建新的Shader文件:在Unity中创建一个新的着色器文件,并将其命名为BlurEffectUI。 2. 编写Shader语言代码:在新的着色器文件中,使用Shader语言编写模糊效果的代码。可以使用模糊算法,例如高斯模糊或均值模糊。下面是一个使用高斯模糊的例子: ``` Shader "Custom/BlurEffectUI" { Properties { _MainTex ("Texture", 2D) = "white" {} _BlurAmount ("Blur Amount", Range(0.0, 1.0)) = 0.5 } SubShader { Pass { Tags { "Queue" = "Transparent" "RenderType" = "Transparent" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; float _BlurAmount; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); col = tex2D(_MainTex, i.uv + float2(-1, -1) * _BlurAmount) * 0.25 + tex2D(_MainTex, i.uv + float2(-1, 1) * _BlurAmount) * 0.25 + tex2D(_MainTex, i.uv + float2(1, -1) * _BlurAmount) * 0.25 + tex2D(_MainTex, i.uv + float2(1, 1) * _BlurAmount) * 0.25; return col; } ENDCG } } } ``` 以上代码是一个示例模糊效果ShaderLab代码,使用了高斯模糊算法,模糊程度可以通过_BlurAmount参数进行调整。 3. 将着色器应用于UI上的材质:将新编写的着色器文件添加到Unity项目中,并在UI上的材质上应用该着色器。可以通过创建一个新材质,然后将其指定为UI元素上的材质来完成这一步骤。 4. 调整模糊效果:可以通过调整材质上的_BlurAmount属性来控制模糊效果的程度。根据需要,可以在材质上设置其他属性来满足特定的需求。 通过以上步骤,我们可以使用ShaderLab编写UI模糊效果,并将其应用于Unity中的UI元素上。这样,我们就可以在游戏或应用中实现想要的UI模糊效果了。 ### 回答3: 要使用ShaderLab编写UI模糊效果,首先需要了解ShaderLab语言和Unity引擎中UI组件的渲染方式。 ShaderLab是Unity引擎中用于编写着色器程序的语言。它具有一系列的内置语法和功能,可以用来编写各种各样的着色器效果。 UI组件在Unity中使用的是Canvas渲染器。要实现模糊效果,可以在ShaderLab中定义一个新的表面着色器(Surface Shader),并在其中添加模糊效果的实现代码。 在定义表面着色器时,需要使用[vertex]和[fragment]标记来指定顶点和片段着色器函数。可以通过在片段着色器中对像素进行模糊处理来实现模糊效果。 一种常见的实现模糊效果的方法是使用高斯模糊算法。该算法通过对像素周围的像素进行加权平均来创建模糊效果。可以在片段着色器中使用多个采样点,对周围的像素进行采样,并进行加权平均来计算最终的颜色值。 除了实现模糊效果的算法,还可以通过使用Uniform变量来控制模糊的程度。Uniform变量可以在C#脚本中进行设置,并在ShaderLab中进行调用。 完成了ShaderLab的编写后,需要将其附加到一个UI组件上。可以在Unity中创建一个新的材质,并将该材质应用到UI组件上。在材质中可以选择刚刚编写的模糊着色器。 通过上述步骤,就可以使用ShaderLab编写UI模糊效果。可以根据实际需求调整模糊程度和其他参数,以达到想要的效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值