手写Unity Shader 热扭曲效果

2022-3-26 09:12BLYS 1011 0

大家好,我是老莫,今天分享一个热扭曲效果的实现。首先看一下效果。

场景资源地址 Lighting Optimisation Tutorial | Tutorial Projects | Unity Asset Store

首先列一下可能需要的属性

        _DistortTex  ("Texture", 2D)              = "white" {}
        _DistortValue("DistortValue",Range(0,1))  = 1
        _DistortSpeed("DistortSpeed",float)       = 1

GrabPass

在unityshader中,我们可以使用GrabPass实现屏幕抓取。有两种写法。


GrabPass{}
**********
************
sampler2D _GrabTex;

1、GrabPass{} 抓取当前屏幕存储到_GrabTexture中,然后我们就可以直接调用_GrabTexture,抓取当前屏幕。但是这样写有个弊端,就是每一个使用了此shader 的物体都睡抓取一次屏幕,消耗很大。可以在FrameDebug中看到。

2。我们一般用第二种方法,GrabPass{"GrabTex"} {}中的纹理名称可以自定义。使用了指定的纹理名称时,不管场景中有多少物体(同意shader),都只会抓取一次屏幕,大大减少运算量。

所以我们这里使用第二种方法

GrabPass{"_GrabTex"}

定义GrabPass{"GrabTex"} 之后,需要写入sampler2D _GrabTex; 用于后面的调用

和模型采样贴图一样,有了纹理,我们还需要UV。unity中屏幕UV的获取方式为ComputeScreenPos(o.pos),注意这里的pos是进行了齐次剪裁后的值,原理是在齐次剪裁空间坐标下获取每个像素在屏幕中的UV坐标。ComputeScreenPos可以自动根据不同平台匹配屏幕坐标,因为DX和OpenGL的坐标轴起始点位置是不同的。这里更深入的知识点后面我会单独写一篇文章详述。

o. pos     = UnityObjectToClipPos(v.vertex);   
o.screenUV = ComputeScreenPos(o.pos);

接下来采样我们抓取到的纹理看一下效果

 fixed4 grabTex = tex2Dproj(_GrabTex,1-i.screenUV);

抓取成功,接下来我们让抓取到的纹理动起来,并且加上扭曲效果。

采样一张noise图,不用太讲究,网上随便下载一张

这里在片段着色器中做一个常规的uv移动,采样屏幕纹理时,在uv上加一个简单的lerp

fixed4 distortTex = tex2D(_DistortTex,i.uv.xy + _Time.xy * _DistortSpeed );
fixed4 grabTex = tex2Dproj(_GrabTex,lerp(i.screenUV,distortTex ,_DistortValue));

这时候我们所要的功能基本都已经实现了:屏幕抓取,UV扭曲+移动,但是边缘过于生硬,需要进一步优化一下。我们只需要中间火团的部分有热扭曲效果,那么我们需要得到一张这样的蒙版,可以自己在PS做一个,也可以在shader中生成,这里我随便拿公式写一个。

              float fade = pow(1-length(i.maskUV-0.5),_Radius);

               fixed4 grabTex = tex2Dproj(_GrabTex,lerp(i.screenUV,distortTex * fade,_DistortValue));

加上蒙版后再看一下效果,和没有加热扭曲效果的火把对比一下。

到这里今天的热扭曲效果就制作完成了,游戏中我们经常能在不经意中看到这种小细节,而往往是这种容易被忽略的细枝末节,最能表现出制作人的用心,也最能给玩家一种“此物从何来,对之惊且喜”之感。我是老莫,希望大家喜欢此次分享,不吝赐教!

以下是完整shader代码

Shader "Unlit/Distort04"
{
    Properties
    {  
        _DistortTex  ("Texture", 2D)              = "white" {}
        _DistortValue("DistortValue",Range(0,1))  = 1
        _DistortSpeed("DistortSpeed",float)       = 1
        _Radius ( "_Radius",float ) =1
    }
    SubShader
    {
        Tags { "Queue" = "Transparent"}
        LOD 100
        Cull Off

        GrabPass{"_GrabTex"}//如果使用仅GrabPass{} 则每个使用了抓取shader的物体都会调用一次抓取,消耗大

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdate{
                float2 uv       : TEXCOORD0;
                float4 vertex   : POSITION;
                
            };

            struct v2f
            {
                float2 uv       : TEXCOORD0;
                float2 maskUV   : TEXCOORD1;
                fixed4 pos      : SV_POSITION;
                float4 screenUV : TEXCOORD2;
            };

            sampler2D _GrabTex;
            sampler2D _DistortTex; float4 _DistortTex_ST;
            fixed _DistortValue;
            float _DistortSpeed;
            float _Radius;
            

            v2f vert (appdate v )
            {
                v2f o;
                
                o.uv = TRANSFORM_TEX(v.uv,_DistortTex) ;
                o.maskUV = v.uv;
                o. pos = UnityObjectToClipPos(v.vertex);
                o.screenUV = ComputeScreenPos(o.pos);//根据不同平台匹配屏幕坐标(DX或opengl)
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            
            {

               fixed4 distortTex = tex2D(_DistortTex,i.uv.xy + _Time.xy * _DistortSpeed );

               float fade = pow(1-length(i.maskUV-0.5),_Radius);

               fixed4 grabTex = tex2Dproj(_GrabTex,lerp(i.screenUV,distortTex * fade,_DistortValue));//和下面的结果一样
       
               return grabTex;

            }
            ENDCG
        }
    }
}


鲜花

握手

雷人

路过

鸡蛋

最新评论

QQ|手机版|小黑屋|九艺游戏动画论坛 ( 津ICP备2022000452号-1 )

GMT+8, 2024-4-25 02:36 , Processed in 0.059736 second(s), 18 queries .

Powered by Discuz! X3.4  © 2001-2017 Discuz Team.