Shader视差贴图

视差贴图相关理论介绍

粗略的视差效果:
在这里插入图片描述

本文就不陈述视差贴图相关描述了, 此处只记录说明一下两点

(1) 在求uv的偏移量的时候 :在切线空间下的 viewDir.xy / viewDir.z , 这里为什么要除以 .z

(2)以及直接使用上面链接中的“改进3”的 RayMarching (光线步进)实现精确的视差效果

(一)viewDir.xy / viewDir.z 的解释:

(1)求出BC: 最大的UV偏移值

AE, AG: 归一化后的 viewDir
AC的高度为:1
相似三角形: EF/ AF = BC / AC
即: viewDir.xy / viewDir.z = BC / 1

根据以上条件: BC = (viewDir.xy / View.z) * 1

因此 ,uv的偏移量 uvOffset = BC
uvOffset乘以很小的偏移系数,误差就会非常小。
(为防止.z太小,造成误差太大 通常: view.xy / (view.z + 0.42) )

在这里插入图片描述(2)精确的偏移值:BQ

人眼看到E点就被挡住了 B点的UV + BQ 才是 在Q点采样得到的高度 QE

在这里插入图片描述

(二)RayMatching 光线步进求相对精确的交点

在这里插入图片描述

Shader "Unlit/Parallax"
{
    
    
    Properties
    {
    
    
        _MainTex ("Texture", 2D) = "white" {
    
    }
        _ParallaxMap("Parallax",2D) = "white"{
    
    }
        _HeightScale("HeightScale",Range(0,0.1)) = 1
        _BumpMap("BumpMap",2D) = "Bump"{
    
    }
    }
    SubShader
    {
    
    
        Tags {
    
     "RenderType"="Opaque" }
        LOD 100

        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"
        #define PARALLAX_RAYMARCHING_STEPS 150
        #define PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS 10
            struct appdata
            {
    
    
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
            };

            struct v2f
            {
    
    
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
                float4 T2W0 : TEXCOORD1;
                float4 T2W1 : TEXCOORD2;
                float4 T2W2 : TEXCOORD3;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _ParallaxMap;
            float _HeightScale;
            
            sampler2D _BumpMap;           
            

            //  unity 的内置函数 
            //ParallaxOffset( )
            
            //-----------------RayMatching 封装的相关代码-----------------------------
            //  在函数中用到多次 tex2D采样 和 for循环,代价相对较大。。。。。。
            float GetParallaxHeight (float2 uv) {
    
    
                return tex2D(_ParallaxMap, uv).r ;
            }
float2 ParallaxRaymarching (float2 uv, float2 viewDir) {
    
    
    #if !defined(PARALLAX_RAYMARCHING_STEPS)
        #define PARALLAX_RAYMARCHING_STEPS 5
    #endif
    float2 uvOffset = 0;
    float stepSize = 1.0 / PARALLAX_RAYMARCHING_STEPS;
    float2 uvDelta = viewDir * stepSize;
    float stepHeight = 1;
    float surfaceHeight = GetParallaxHeight(uv);
    for (int i = 0; i < PARALLAX_RAYMARCHING_STEPS && stepHeight > surfaceHeight; ++i)
    {
    
    
        uvOffset -= uvDelta;
        stepHeight -= stepSize;
        surfaceHeight = GetParallaxHeight(uv + uvOffset);
    }
    #if !defined(PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS)
        #define PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS 2
    #endif
    for (int i = 0; i < PARALLAX_RAYMARCHING_BINARY_SEARCH_STEPS; i++) {
    
    
        uvDelta *= 0.5;
        stepSize *= 0.5;
        if (stepHeight < surfaceHeight) {
    
    
            uvOffset += uvDelta;
            stepHeight += stepSize;
        }else{
    
    
            uvOffset -= uvDelta;
            stepHeight -= stepSize;
        }
        surfaceHeight = GetParallaxHeight(uv + uvOffset);
    }
    return uvOffset;
}

float2 DoParallaxMap(float3 viewDir,float2 uv){
    
    

    viewDir = normalize(viewDir);
    viewDir.xy /=(viewDir.z + 0.41);
    viewDir.xy *= _HeightScale;
    uv += ParallaxRaymarching(uv.xy,viewDir.xy);
    return uv;
}
            
            //-----------------RayMatching end------------------------------
            
            


            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float3 normal =UnityObjectToWorldNormal(v.normal);
                float3 tangent = UnityObjectToWorldDir(v.tangent);
                float3 binormal = cross(normal,tangent) * v.tangent.w;
                float3 worldPos = mul(unity_ObjectToWorld,v.vertex);
                o.T2W0 = float4(tangent.x,binormal.x,normal.x,worldPos.x);
                o.T2W1 = float4(tangent.y,binormal.y,normal.y,worldPos.y);
                o.T2W2 = float4(tangent.z,binormal.z,normal.z,worldPos.z);               
                return o;
            }


            //   初始通用版本
            fixed4 frag (v2f i) : SV_Target
            {
    
    
                
                float3 worldPos = float3(i.T2W0.w,i.T2W1.w,i.T2W2.w);
                float3 normal = UnpackNormal(tex2D(_BumpMap,i.uv));
                
                normal = float3(dot(i.T2W0.xyz,normal),dot(i.T2W1.xyz,normal),dot(i.T2W2.xyz,normal));
                
                float3 view_ws =normalize(UnityWorldSpaceViewDir(worldPos));
                float3 t = float3(i.T2W0.x,i.T2W1.x,i.T2W2.x);
                float3 b = float3(i.T2W0.y,i.T2W1.y,i.T2W2.y);
                float3 n = float3(i.T2W0.z,i.T2W1.z,i.T2W2.z);
                                
                float3 view_ts = float3(dot(t,view_ws),dot(b,view_ws),dot(n,view_ws));
                  在视差贴图中  R,G,B, 三个分量的值相等,所以随便用一个就好
                float height = tex2D(_ParallaxMap,i.uv).r;
                  float类型的 ,如 1.0f  ,  不要写成1,  不然会被当成int处理,舍弃小数,造成精度不准;
                /// 0.5分界线高度图 来达到上下各异的视差
               height -= 0.5f;                
                // 此处的  .xy / .z     是利用相似三角形,求出精确的 xy 的偏移量
                float2 offsetUV = height*( view_ts.xy/ (view_ts.z + 0.42)) * _HeightScale;
                i.uv += offsetUV;
                
                float4 col = tex2D(_MainTex,i.uv);
                
               return col;
             
            }
            
            
          
            ENDCG
        }
    }
}

猜你喜欢

转载自blog.csdn.net/js0907/article/details/116308919