前言

UV流动与UV扰动一文中,简单演示了对于UV进行偏移得到的效果,本文接着说一下ScreenUV的原理及实现。

 

实现

ScreenUV指的是,模型上的纹理在屏幕上展开投射到物体上,模型的缩放、移动、旋转都不会影响纹理的展示效果,大概像下面这样。

这个效果很简单,既然纹理是与屏幕相关,那么先把默认的ObjectSpace物体空间的顶点位置转换到ViewSpace观察空间,再去对纹理进行采样就可以。

不过只是这样的话效果很呆,需要再进行畸变矫正让物体边缘处扭曲效果更加真实,并修复矫正畸变导致的纹理大小被锁定不能通过距离控制纹理大小的问题,增加通过Tilling控制屏幕纹理偏移和缩放,最终效果如下。

实际代码如下:

// shader的路径和名称
Shader "Shader Forge/ScreenUV" {
    // 暴露属性到面板
    Properties {
        _MainTex("RGB: Color  A: Alpha", 2d) = "gray"{}
        _Opacity("Opacity", range(0.0, 1.0)) = 1.0
        _ScreenMap("ScreenUV Textrue", 2d) = "white"{}
    }
    SubShader {
        Tags {
            "Queue"="Transparent"                //调整渲染顺序
            "RenderType"="Transparent"           //改为对应的
            "ForceNoShadowCasting"="True"        //关闭阴影投射
            "IgnoreProjector"="True"             //不响应投射器
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend One OneMinusSrcAlpha          //修改混合模式
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            
            // 声明变量
            uniform sampler2D _MainTex; 
            uniform half _Opacity;
            uniform sampler2D _ScreenMap; uniform float4 _ScreenMap_ST; //支持UV TillingOffset
            
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float2 screenUV : TEXCOORD1;
            };
            
            // 顶点shader
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;
                o.pos = UnityObjectToClipPos( v.vertex );
                o.uv = v.uv;

                float3 posVS = UnityObjectToViewPos( v.vertex ).xyz;  //得到顶点的观察空间位置
                float originDist = UnityObjectToViewPos( float3(0.0, 0.0, 0.0) ).z; //原点的观察空间位置深度
                
                o.screenUV = posVS.xy / posVS.z;       //畸变矫正
                o.screenUV *= originDist;              //修复畸变矫正带来的纹理大小被锁定问题,让纹理大小随距离变化
                o.screenUV = TRANSFORM_TEX(o.screenUV, _ScreenMap); //启用Tilling

                return o;
            }
            
            // 像素shader
            float4 frag(VertexOutput i) : COLOR {
                half4 var_MainTex = tex2D(_MainTex, i.uv);   
                half4 var_ScreenMap = tex2D(_ScreenMap, i.screenUV);
                
                half finalOpacity = var_MainTex.a * _Opacity * var_ScreenMap.r;  
                return half4(var_MainTex.rgb * _Opacity,finalOpacity);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

 

本文同样参考自B站庄懂的技术美术入门课,感谢。


人只要让步一次,
就会不断让步,
一次妥协势必导致一连串新的妥协。

——茨威格