Shader in Unity grabs the screen and achieves distortion effect (optimization)


Preface

Optimize the shader implemented in the previous article


1. In the previous input of the vertex shader, the structure was abandoned and the parameters were passed in directly from the application stage. If written in this way, it would be inconvenient for the program to expand, so it needs to be modified.

accomplish

1. Define a structure to pass in the vertex coordinate system

struct appdata
{ float4 vertex : POSITION; //From the input of the application stage, add one more uv for sampling the distorted texture float2 uv : TEXCOORD; };



2. Because UnityObjectToClipPos is converted from local space to clipping space, but perspective division is not performed, so perspective division needs to be performed. Perspective division can be performed using the xyz/w of the converted result.

v2f vert (appdata v)
{ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv,_DistortTex) + _Distort.xy * _Time.y; //Convert the local space to the next The result after clipping the space is passed to screenUV after perspective division o.screenUV.xyz = o.pos.xyz / o.pos.w; return o; }







3. Because the origin of the screen coordinates is generally in the upper left corner (DirectX) or lower left corner (OpenGL) (mine is a DirectX platform, so it is in the upper left corner.), it will cause the displayed position to be different from the position we need, so it needs to be corrected. It performs calculation translation and scaling processing

Insert image description here

DirectX平台:fixed2 uv = fixed2(i.screenUV.x * 0.5,i.screenUV.y * -0.5) + 0.5;
OpenGL平台:fixed2 uv = i.screenUV * 0.5 + 0.5;

Change to the vertex shader to calculate

DirectX平台:
o.screenUV.x = o.screenUV.x * 0.5 + 0.5;
o.screenUV.y = o.screenUV.y * -0.5 + 0.5;
OpenGL平台:
o.screenUV.x = o.screenUV * 0.5 + 0.5;

Insert image description here

However, this interpolation calculation will have errors and flaws, so it is better to calculate it in the fragment shader.

DirectX平台:
fixed2 uv = i.screenUV.xy / i.screenUV.w;
uv.x = uv.x * 0.5 +0.5;
uv.y = uv.y * -0.5 + 0.5;


2. Switch to the methods provided by Unity (interoperability between platforms)

ComputeScreenPos(float4 pos)
pos is the coordinate position in the clipping space, and returns the screen coordinate position under a certain projection point.
Since the coordinate value returned by this function is not divided by the homogeneous coordinates, if the return value of the function is used directly , you need to use: tex2Dproj(_ScreenTexture, uv.xyw);
you can also handle the secondary coordinates yourself, use: tex2D(_ScreenTexture, uv.xy / uv.w);

In the vertex shader: o.screenUV = ComputeScreenPos(o.pos);
In the fragment shader: fixed4 grabTex = tex2Dproj(_GrabTex,i.screenUV);


3. Finally add the twist

Shader "MyShader/P0_10_5"
{
    Properties
    {
        //实现扭曲,就需要传入贴图来实现扰度
        _DistortTex("DistortTex",2D) = "white"{}
        
        _Distort("SpeedX(X) SpeedY(y) Distort(Z)",vector) = (0,0,0,0)
    }
    SubShader
    {
        Tags{"Queue" = "Transparent"}
        //屏幕抓取需要单独使用一个Pass —— GrabPass{} 里面什么都不写,或者GrabPass{"_GrabTex"}
        GrabPass{"_GrabTex"}
        //使用Cull off 让两面都有扭曲
        Cull Off
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                //从应用程序阶段的输入,多加一个uv,用于对扭曲纹理的采样
                float2 uv : TEXCOORD;
                
            };
            
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
                float4 screenUV:TEXCOORD1;
            };

            //在使用抓取的屏幕前,需要像使用属性一样定义一下,_GrabTexture这个名字是Unity定义好的
            sampler2D _GrabTex;
            sampler2D _DistortTex;float4 _DistortTex_ST;
            float4 _Distort;

            
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv,_DistortTex) + _Distort.xy * _Time.y;
                //pos为裁剪空间下的坐标位置,返回的是某个投影点下的屏幕坐标位置
                o.screenUV = ComputeScreenPos(o.pos);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //DirectX平台:
                /*fixed2 uv = i.screenUV.xy / i.screenUV.w;
                uv.x = uv.x * 0.5  +0.5;
                uv.y = uv.y * -0.5 + 0.5;*/
                
                fixed4 distortTex = tex2D(_DistortTex,i.uv);

                //使用线性插值来控制UV的扭曲程度
                float2 uv = lerp(i.screenUV.xy/i.screenUV.w,distortTex,_Distort.z);
                //对抓取的屏幕进行采样
                fixed4 grabTex = tex2D(_GrabTex,uv);
                return grabTex;
                
            }
            ENDCG
        }
    }
}


Effect:
Please add image description

Guess you like

Origin blog.csdn.net/qq_51603875/article/details/132796248