【Unity Shader入门精要学习】高级纹理(三)

渲染纹理

现代的GPU允许把整个三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture RTT),而不是传统的帧缓冲或是后备缓冲(backbuffer)。与之相关的是多重渲染目标(Multiple Render Target, MRT),这种技术指的是GPU允许我们把场景同时渲染到多个渲染目标纹理中,而不再需要为每个渲染目标纹理单独渲染完整的场景。延迟渲染就是使用多重渲染目标的一个应用。
Unity为渲染目标纹理定义了一种专门的纹理类型——渲染纹理(Render Texture)。Unity渲染纹理有两种使用方法:
1、在Project下创建一个渲染纹理,然后将其赋给某个摄像机,创建的渲染纹理可以调整分标率,滤波模式。这样摄像机渲染的结果会实时更新到这张纹理下。(这种方式也可以通过写代码的方式)
2、在屏幕后处理时使用GrabPass,命令,或是OnRenderImage函数获取当前屏幕图像。Unity会把这个屏幕图像放到一张和屏幕分辨率等同的渲染纹理中,然后就可以在Pass中把它当成普通的纹理使用就可以,用来实现各种屏幕特效。

镜子效果

其实镜子效果的实现就是在“镜子”的后面放置一个摄像机,然后把这个摄像机看到的东西画在“镜子”上(就是讲这个摄像机的RenderTexure当成正常的纹理画在某个面上),需要注意的是“镜子”与主相机看到的实际是镜面的,也就是反过来的,所以UV坐标需要在X方向上翻转一下。


镜子效果
Shader "Unlit/Mirror"
{
    Properties
    {
        _MainTex ("Render Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct a2v
            {
                float4 vertex : POSITION;
                float3 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos:SV_POSITION;
                float2 uv:TEXCOORD0;
            };

            sampler2D _MainTex;

            v2f vert (a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                o.uv.x = 1-o.uv.x;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

玻璃效果

当我们在Shader中定义一个GrabPass之后,Unity会把当前屏幕的图像绘制在一张纹理中,我们可以再后续的Pass中访问这张纹理。通常会使用GrabPass来实现诸如玻璃等透明材质的模拟,使用GrabPass可以对该物体后面的图像进行更复杂的操作,如使用法线模拟折射效果。
在使用GrabPass时需要注意物体的渲染队列设置,GrabPass通常用于渲染透明物体,尽管代码里面并不包含混合指令,但仍需要把渲染队列设置成透明队列:

Tags{"RenderType"="Opaque" "Queue"="Transparent"}

这样能保证在画这个物体时其他不透明的物体已经绘制完了。


玻璃
Shader "Unlit/GlassRefraction"
{
    Properties{
        _MainTex("Main Tex",2D)="white"{}
        _BumpTex("Bump Tex",2D)="bump"{}
        _Cubemap("Envirenment Tex",Cube)="_SkyBox"{}
        _Distortion("Distortion",Range(0,100))=50
        _RefractionAmount("Refraction Amount",Range(0,1))=0.5
    }

    SubShader{
        Tags{"RenderType"="Opaque" "Queue"="Transparent"}

        GrabPass{"_RefractionTex"}
        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _BumpTex;
            float4 _BumpTex_ST;

            samplerCUBE _Cubemap;

            float _Distortion;
            fixed _RefractionAmount;

            sampler2D _RefractionTex;
            float4 _RefractionTex_TexelSize;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 scrPos : TEXCOORD0;
                float4 uv : TEXCOORD1;
                float4 TtoW0 : TEXCOORD2;
                float4 TtoW1 : TEXCOORD3;
                float4 TtoW2 : TEXCOORD4;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.scrPos = ComputeGrabScreenPos(o.pos);
                o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpTex);

                float3 worldPos = mul(unity_ObjectToWorld,v.vertex);
                float3 worldNormal = UnityObjectToWorldNormal(v.normal);
                float3 worldTangent = UnityObjectToWorldDir(v.tangent);
                float3 worldBinormal = cross(normalize(worldTangent),normalize(worldNormal))*v.tangent.w;

                o.TtoW0 = float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
                o.TtoW1 = float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
                o.TtoW2 = float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);

                return o;
            }

            fixed4 frag(v2f i):SV_TARGET
            {
                float3 worldPos = float3(i.TtoW0.z,i.TtoW1.z,i.TtoW2.z);
                float3 worldViewDir = UnityWorldSpaceViewDir(worldPos);

                //切线空间
                fixed3 bump = UnpackNormal(tex2D(_BumpTex,i.uv.zw));
                //在切线空间对法线进行偏移,得出偏移的像素数量,_RefractionTex_TexelSize代表素纹大小
                //如256X512的纹理,素纹大小就是(1/256,1/512)
                float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
                //然后对屏幕空间XY值进行偏移,很疑惑为什么还要乘一次Z值,感觉不乘也可以
                i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;

                fixed3 refractColor = tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w).rgb;
                fixed3 diffuseColor = tex2D(_MainTex,i.uv.xy).rgb;

                fixed3 worldBump = normalize(half3(dot(bump,i.TtoW0.xyz),dot(bump,i.TtoW1.xyz),dot(bump,i.TtoW2.xyz)));
                fixed3 reflectDir = reflect(-worldViewDir,worldBump);

                fixed3 reflectColor = texCUBE(_Cubemap,reflectDir).rgb * diffuseColor;

                fixed3 finalColor = reflectColor * (1-_RefractionAmount) + refractColor * _RefractionAmount;

                return fixed4(finalColor,1.0);
            }

            ENDCG
        }
    }
}

你可能感兴趣的:(【Unity Shader入门精要学习】高级纹理(三))