unity urp 视差卡牌

总体效果大概四层,从后往前排序为:卡背、背景、画像、边框

首先卡背比较简单,只要判断如果网格的背面就直接采样卡背图片展示即可

资源准备:

unity urp 视差卡牌_第1张图片

然后是背景,网上找到一张这样的图。

unity urp 视差卡牌_第2张图片

但他还不符合要求,我们的背景需要四方接续否则在视差下会看到不连续的接痕。关于如何处理四方接续的图。

首先是把这张图拽进ps里,裁切到512X512,然后在滤镜-其他-位移,将其水平垂直都移动512/2个像素,随后修补接痕位置使其连续即可。

unity urp 视差卡牌_第3张图片

然后是画像,这张图看过我上一个文章的应该很熟悉,而且我认为是很有趣的一件事,推荐去看一眼。Unity图片导入趣事随笔-CSDN博客

unity urp 视差卡牌_第4张图片

最后是边框,直接加在最顶层,这也是图片背景使用黑色的理由。

unity urp 视差卡牌_第5张图片

资源完毕,还需要视差的思路。红色为原本物体的uv位置,绿色是视觉上存在深度后所延长到的uv位置,理论上不需要计算z向量,但为了方便计算,原本物体的uv位置的z分量定为0。在红色上顶点的切线空间中,是右手坐标系,z向量向外,所以深度向量一定为(0,0,-1)根据这两个向量点乘可以求出深度向量和视角向量的cosθ,就可以根据深度反推出视角向量延长的距离,那么原本物体的uv位置 + 视角向量延长的距离就是视觉上存在深度后所延长到的uv位置,随后舍弃z分量就是视觉上存在深度后的uv位置。

unity urp 视差卡牌_第6张图片

这个方法根据输入的原uv坐标、视角向量(切线空间下)和深度来返回视觉上存在深度后的uv坐标

            float2 CalculateRealUVAfterDepth(float2 originUV, float3 viewDirTS, float depth)
            {
                //计算视角方向和深度方向的cos值
                float cosTheta = dot(normalize(viewDirTS), float3(0,0,-1));  //  一般来讲unity是左手坐标系,但在切线和观察空间较为特殊是右手坐标系,不过这并不影响z轴方向的判断
                //根据深度差算出两点间距离
                float dis = depth / cosTheta;
                //算出应用深度差后对应点位
                float3 originUVPoint = float3(originUV, 0);
                float3 afrerDepthUVPoint = originUVPoint + normalize(viewDirTS) * dis;
                //返回应用深度差后对应UV
                return afrerDepthUVPoint.xy;
            }

根据这些视差过后的uv坐标采样图片并叠加,就能做出视觉上有距离感的图片结构。

Shader "Kerzh/Tarol"
{
    Properties
    {
        [Space(50)]
        _FrameTexture("FrameTexture",2D) = "Black"{}
        
        [Space(50)]
        _ParallaxTexture1("Parallax Texture1",2D) = "Black"{}
        _ParallaxDepth1("Parallax Depth1",Float) = 0
        
        [Space(50)]
        _ParallaxTexture2("Parallax Texture2",2D) = "Black"{}
        _ParallaxDepth2("Parallax Depth2",Float) = 0
        _FlowVector2AndSpeed("FlowVector2AndSpeed",Vector) = (0,0,0,0)
        
        [Space(50)]
        _BackTexture("_BackTexture",2D) = "Black"{}
    }
    SubShader
    {
        Tags {"RenderPipeline" = "UniversalRenderPipeline" "Queue" = "Geometry" "RenderType" = "ReplaceMePlease" "ForceNoShadowCasting" = "false" "DisableBatching" = "False" "IgnoreProjector" = "False" "PreviewType" = "Plane"}
        LOD 100

        Pass
        {
            Cull Off
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include  "CommonCgInclude.cginc"

            V2FData vert (MeshData input)
            {
                V2FData output = FillBaseV2FData(input);
                return output;
            }

            sampler2D _FrameTexture;
            float4 _FrameTexture_ST;
            
            sampler2D _ParallaxTexture1;
            float4 _ParallaxTexture1_ST;
            float _ParallaxDepth1;
            
            sampler2D _ParallaxTexture2;
            float4 _ParallaxTexture2_ST;
            float _ParallaxDepth2;
            vector _FlowVector2AndSpeed;

            sampler2D _BackTexture;
            float4 _BackTexture_ST;

            float2 CalculateRealUVAfterDepth(float2 originUV, float3 viewDirTS, float depth)
            {
                //计算视角方向和深度方向的cos值
                float cosTheta = dot(normalize(viewDirTS), float3(0,0,-1));  //  一般来讲unity是左手坐标系,但在切线和观察空间较为特殊是右手坐标系,不过这并不影响z轴方向的判断
                //根据深度差算出两点间距离
                float dis = depth / cosTheta;
                //算出应用深度差后对应点位
                float3 originUVPoint = float3(originUV, 0);
                float3 afrerDepthUVPoint = originUVPoint + normalize(viewDirTS) * dis;
                //返回应用深度差后对应UV
                return afrerDepthUVPoint.xy;
            }
            

            fixed4 frag (V2FData input,float backFace:VFace) : SV_Target
            {
                
                float3 tangentWS = normalize(input.tangentWS);
                float3 normalWS = normalize(input.normalWS);
                float3 bitangentWS = normalize(input.bitangentWS);

                float3 lightDirWS = normalize(UnityWorldSpaceLightDir(input.posWS.xyz));
                float3 viewDirWS = normalize(UnityWorldSpaceViewDir(input.posWS.xyz));

                float2 uv = input.uv;

                //背面处理
                if(backFace<0)
                {
                    float2 backUV = float2(1-uv.x,uv.y);
                    float4 backTextureSample = tex2D(_BackTexture,backUV*_BackTexture_ST.xy + _BackTexture_ST.zw);
                    return backTextureSample;
                }

                //下面都是正面的了
                float3x3 TBN_WS2TS = float3x3(tangentWS,bitangentWS,normalWS);  //  世界-》切线变换矩阵
                float3 viewDirTS = mul(TBN_WS2TS, viewDirWS);
                //为什么需要在切线空间计算呢?因为深度值是相对于切线空间定义的
                //计算经深度值影响过后的uv
                float2 uv1AfterDepth = CalculateRealUVAfterDepth(uv, viewDirTS, _ParallaxDepth1);
                float2 uv2AfterDepth = CalculateRealUVAfterDepth(uv, viewDirTS, _ParallaxDepth2);

                //采样视差图1
                uv1AfterDepth = saturate(uv1AfterDepth);
                float4 _ParallaxTexture1Sample = tex2D(_ParallaxTexture1,uv1AfterDepth*_ParallaxTexture1_ST.xy + _ParallaxTexture1_ST.zw);

                //采样视差图2
                uv2AfterDepth += -_FlowVector2AndSpeed.xy * _Time.y * _FlowVector2AndSpeed.z;
                float4 _ParallaxTexture2Sample = tex2D(_ParallaxTexture2,uv2AfterDepth*_ParallaxTexture2_ST.xy + _ParallaxTexture2_ST.zw);

                //采样边框
                float4 _FrameTextureSample = tex2D(_FrameTexture,uv*_FrameTexture_ST.xy + _FrameTexture_ST.zw);
                
                float4 finalCol = float4(0,0,0,1);
                finalCol = _ParallaxTexture2Sample;
                finalCol = lerp(finalCol,_ParallaxTexture1Sample,_ParallaxTexture1Sample.a);
                finalCol += _FrameTextureSample;

                return finalCol;
            }
            ENDCG
        }
    }
}

库文件:CommonCGinclude.cginc

#ifndef COMMONCGINCLUDE
#define COMMONCGINCLUDE

struct MeshData
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    float2 uv2 : TEXCOORD1;
    float4 tangentOS :TANGENT;
    float3 normalOS : NORMAL;
    float4 vertexColor : COLOR;
};

struct V2FData
{
    float4 pos : SV_POSITION; // 必须命名为pos ,因为 TRANSFER_VERTEX_TO_FRAGMENT 是这么命名的,为了正确地获取到Shadow
    float2 uv : TEXCOORD0;
    float3 tangentWS : TEXCOORD1;
    float3 bitangentWS : TEXCOORD2;
    float3 normalWS : TEXCOORD3;
    float3 posWS : TEXCOORD4;
    float3 posOS : TEXCOORD5;
    float3 normalOS : TEXCOORD6;
    float4 vertexColor : TEXCOORD7;
    float2 uv2 : TEXCOORD8;
};

V2FData FillBaseV2FData(MeshData input)
{
    V2FData output;
    output.pos = UnityObjectToClipPos(input.vertex);
    output.uv = input.uv;
    output.uv2 = input.uv2;
    output.normalWS = normalize(UnityObjectToWorldNormal(input.normalOS));
    output.posWS = mul(unity_ObjectToWorld, input.vertex);
    output.posOS = input.vertex.xyz;
    output.tangentWS = normalize(UnityObjectToWorldDir(input.tangentOS));
    output.bitangentWS = cross(output.normalWS, output.tangentWS) * input.tangentOS.w; //乘上input.tangentOS.w 是unity引擎的bug,有的模型是 1 有的模型是 -1,必须这么写
    output.normalOS = input.normalOS;
    output.vertexColor = input.vertexColor;
    return output;
}

#endif

在面板上这样赋值即可获得这样的效果

unity urp 视差卡牌_第7张图片

你可能感兴趣的:(unity,着色器,学习,线性代数,游戏引擎)