UnityShader基础(九)——噪声纹理

       图形噪声,是计算机图形学中一类随机算法,经常用来模拟自然界中的各种纹理材质,如云、山脉等,都是通过噪声算法模拟出来的。通过不同的噪声算法,作用在物体纹理和材质细节,我们可以模拟不同类型的材质。        

以上节选自图形噪声 

        噪声纹理一般用于模拟看似随机的随机,使得随机更加自然合理,噪声纹理可以被看作是程序纹理的一种。

一、消融效果

        Shader:        

Shader "Custom/Test0"
{
    Properties
    {
        _MainTex("主帖图",2D)="white"{}
        _NormalTex("法线贴图",2D)="bump"{}
        
        _BurnArea("烧毁面积",Range(0,1))=0.1
        _BurnColor("烧毁颜色",Color)=(1,0,0,1)
        _BurnColor1("烧焦颜色",Color)=(1,0,0,1)
        
        _NoiseTex("噪声纹理",2D)="white"{}
        
    }
    SubShader
    {
        Pass
        {
            Cull off
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityLightingCommon.cginc"
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _NormalTex;
            float4 _NormalTex_ST;
            sampler2D _NoiseTex;
            float4 _NoiseTex_ST;

            fixed _BurnArea;
            fixed4 _BurnColor;
            fixed4 _BurnColor1;
            struct a2v
            {
                float4 vertex:POSITION;
 
                float3 normal:NORMAL;
                //切线不同于法线,需要用4个分量表示,因为切线的w属性用于指明副切线的方向
                float4 tangent:TANGENT;
                
                float4 texcoord:TEXCOORD0;
            };
 
            struct v2f
            {
                float4 pos:SV_POSITION;
 
                float3 tangentLightDir :TEXCOORD0;                
 
                float3 tangentViewDir:TEXCOORD1;
                //使用float4,前两个值存放基础纹理坐标,后两个值存放法线纹理坐标
                float4 uv:TEXCOORD2;

                float2 uv_Burn:TEXCOORD3; 
                      
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                
                o.pos=UnityObjectToClipPos(v.vertex);
                 
                TANGENT_SPACE_ROTATION;
 
                o.tangentLightDir=mul(rotation,ObjSpaceLightDir(v.vertex));
 
                o.tangentViewDir=mul(rotation,ObjSpaceViewDir(v.vertex));
                                                              
                o.uv.xy=TRANSFORM_TEX(v.texcoord,_MainTex);
                
                o.uv.zw=TRANSFORM_TEX(v.texcoord,_NormalTex);

                o.uv_Burn=TRANSFORM_TEX(v.texcoord,_NoiseTex);
                
                return o;
                
            }
 
            fixed4 frag(v2f i):SV_Target
            {
                //剔除已经烧毁的区域
                fixed3 noise_texResult=tex2D(_NoiseTex,i.uv_Burn);

                clip(noise_texResult.r-_BurnArea);

                //正常漫反射
                half3 tangentNormal=UnpackNormal(tex2D(_NormalTex,i.uv.zw));                 
                
                half3 tangentLightDir=normalize(i.tangentLightDir);
                 
                //纹理采样
                fixed3 texResult=tex2D(_MainTex,i.uv.xy);
 
                fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                fixed3 diffuse=_LightColor0*texResult
                            *saturate(dot(tangentLightDir,tangentNormal)*0.5+0.5);

                //烧焦区域计算
                fixed shaojiao=1-smoothstep(0,_BurnArea,noise_texResult.r-_BurnArea);

                //烧焦颜色计算,感觉只用一个会更好调一些
                fixed3 shaojiaoColor=lerp(_BurnColor,_BurnColor1,shaojiao);
                shaojiaoColor=pow(shaojiaoColor,5);

                //最终颜色计算
                fixed3 color=lerp(ambient+diffuse,shaojiaoColor,shaojiao*step(0.0001,_BurnArea));
                                                                                                
                return fixed4(color,1.0);
            }
            
            ENDCG
        }
    }
}

        

         效果还行。        

二、水面波动

        和之前玻璃的写法大差不差,主要区别就是反射用了菲涅尔,同时加入噪声纹理模拟水面的法线方向,营造出水面漂浮的感觉。

        我们使用噪声纹理代替平常的法线贴图做计算,噪声纹理需要设置成从灰度生成法线。

        相比于玻璃,我们需要让噪声纹理流动起来营造水面飘动,同时对噪声纹理进行采样用于计算 其次,使用菲涅尔反射控制折射和反射的比例。

        计算反射时,必须在世界空间下进行,因为需要采样立方体纹理。                

Shader "Custom/Test0"
{
    Properties
    {
        _MainTex("主要纹理",2D)="white"{}
        _NormalTex("法线贴图",2D)="bump"{}
        _CubeMap("立方体纹理",Cube)="_Skybox"{}
        _RefractLevel("折射程度",Range(0,100))=50
        _WaterSpeedX("水流X速度",float)=0.01
        _WaterSpeedY("水流Y速度",float)=0.01
    }
    SubShader
    {
        Tags
        {
            "Queue"="Transparent" "RenderType"="Opaque"
        }

        GrabPass
        {
            "_RefractionTex"
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _NormalTex;
            float4 _NormalTex_ST;

            sampler2D _RefractionTex;
            float4 _RefractionTex_TexelSize;

            samplerCUBE _CubeMap;

            fixed _RefractLevel;
            fixed _WaterSpeedX;
            fixed _WaterSpeedY;

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

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

            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, _NormalTex);

                //矩阵计算
                float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
                fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
                fixed3 worldBinormal = cross(worldNormal, worldTangent) * 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);

                o.worldNormal=worldNormal;

                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {
                float2 speed=_Time.y*float2(_WaterSpeedX*0.1,_WaterSpeedY*0.1);
                //营造出浮动的感觉
                fixed3 tangentNormal1 = UnpackNormal(tex2D(_NormalTex, i.uv.zw+speed));
                fixed3 tangentNormal2 = UnpackNormal(tex2D(_NormalTex, i.uv.zw-speed));
                fixed3 tangentNormal = normalize(tangentNormal1+tangentNormal2);
                
                //折射部分
                fixed2 offset = tangentNormal.xy * _RefractLevel * _RefractionTex_TexelSize.xy;
                i.scrPos.xy = offset + i.scrPos.xy;
                fixed3 refract = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w);

                //反射部分
                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                fixed3 worldNormal = normalize(half3(dot(i.TtoW0.xyz, tangentNormal)
                                                     , dot(i.TtoW1.xyz, tangentNormal),
                                                     dot(i.TtoW2.xyz, tangentNormal)));
                fixed3 reflectionDir = reflect(-worldViewDir,worldNormal);
                fixed4 texColor = tex2D(_MainTex, i.uv.xy);
                fixed3 reflection = texCUBE(_CubeMap, reflectionDir) * texColor;//*_MainColor;
                //如果和书上一样用噪声纹理的法线,效果有点差,无法实现书中示例的样子
                //不清楚是不是版本问题
                fixed fresnel = pow(1 - saturate(dot(worldViewDir, i.worldNormal)), 4);
                //最终结果
                fixed3 finalColor = reflection * fresnel + refract *(1-fresnel);

                return fixed4(finalColor, 1);
            }
            ENDCG
        }
    }
}

         结果也是怪怪的感觉,尤其是这个噪声的表现,不知道是不是个人写法问题什么的,而且书中的写法在我的2020版本中也是有问题,无法表现的和书中实例一样 。而且还有个问题,离水面越近,扰动就越剧烈,想了想好像是视角问题,拉近水面的时候噪声的偏扰占比加大,就和大风中离的远树摇晃的不狠,离的近看感觉挺晃的,应该是这个道理。            

三、非均匀雾效 

                脚本:                

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class RenderImage : MonoBehaviour
{
    public Shader shader;

    private Camera _camera;

    private Material _material;


    [Header("雾强度")] public float fogStrength = 0.5f;
    [Header("雾颜色")] public Color fogColor = Color.white;
    [Header("雾起始高度")] public float fogStartDis = -3;
    [Header("雾结束高度")] public float fogEndDis = 2;
    [Header("启用雾")] public bool key;
    [Header("噪声系数")] public float noiseStrength = 1;
    public float noiseSpeedX;
    public float noiseSpeedY;

    // Start is called before the first frame update
    void Awake()
    {
        if (shader == null)
        {
            Debug.Log("Shader为空");
        }
        else
        {
            _camera = GetComponent<Camera>();
            _camera.depthTextureMode |= DepthTextureMode.Depth;
            //_material = new Material(shader);
        }
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (!key)
        {
            _material = null;
        }
        else
        {
            _material = new Material(shader);
        }

        if (_material != null)
        {
            //计算四个向量的前期准备
            float temp = _camera.nearClipPlane * Mathf.Tan(_camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
            Vector3 up = _camera.transform.up * temp;
            Vector3 right = _camera.transform.right * _camera.aspect * temp;

            //开始计算四个向量,但此时向量只有方向信息
            var nearClipPlane = _camera.nearClipPlane;
            Vector3 temp1 = _camera.transform.forward * nearClipPlane;

            //左上
            Vector3 LT = temp1 + up - right;
            //左下
            Vector3 LB = temp1 - up - right;
            //右上
            Vector3 RT = temp1 + up + right;
            //右下
            Vector3 RB = temp1 - up + right;


            //计算真正的深度比例,送给片元着色器用于得到准确的深度
            //知道一个像素的深度,我们可以根据相似三角形计算比例
            float scale = LT.magnitude / nearClipPlane;

            //对四个向量进行深度比例的处理,这样的向量就包含了真正的距离和方向
            LT.Normalize();
            LT *= scale;

            LB.Normalize();
            LB *= scale;

            RT.Normalize();
            RT *= scale;

            RB.Normalize();
            RB *= scale;

            //我们利用一个矩阵存储四个向量,先初始化为单位矩阵
            Matrix4x4 temp2 = Matrix4x4.identity;

            temp2.SetRow(0, LT);
            temp2.SetRow(1, LB);
            temp2.SetRow(2, RT);
            temp2.SetRow(3, RB);

            //材质值传递
            _material.SetMatrix("_CornerVector", temp2);


            _material.SetFloat("_FogStrenth", fogStrength);
            _material.SetColor("_FogColor", fogColor);
            _material.SetFloat("_FogStart", fogStartDis);
            _material.SetFloat("_FogEnd", fogEndDis);
            _material.SetFloat("_NoiseSpeedX", noiseSpeedX);
            _material.SetFloat("_NoiseSpeedY", noiseSpeedY);
            _material.SetFloat("_NoiseStrngth", noiseStrength);

            Graphics.Blit(src, dest, _material);
        }
        else
        {
            Graphics.Blit(src, dest);
        }
    }
}

         Shader:        

Shader "Custom/Test0"
{
    Properties
    {
        //不写这句话就是灰,真的搞不懂Unity开发者写的什么代码,试了半小时给试出来了
        //偏偏别的都能过,就纹理必须写。。。
        _MainTex("主帖图",2D)="white"{}
        _NoiseTex("噪声贴图",2D)="white"{}

    }
    SubShader
    {
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _MainTex;
            sampler2D _NoiseTex;
            float4x4 _CornerVector;

            float _FogStrenth;
            float4 _FogColor;
            float _FogStart;
            float _FogEnd;
            float _NoiseSpeedX;
            float _NoiseSpeedY;
            float _NoiseStrngth;
            //声明获取深度纹理
            sampler2D _CameraDepthTexture;


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

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

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                int index;
                //关系对应好
                if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5)
                {
                    //左下
                    index = 1;
                }
                else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5)
                {
                    //右下
                    index = 3;
                }
                else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5)
                {
                    //右上
                    index = 2;
                }
                else
                {
                    //左上
                    index = 0;
                }
                o.dir = _CornerVector[index];
                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {
                
                //解析深度
                float depth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv));
                //世界坐标计算
                float3 worldPos = _WorldSpaceCameraPos + depth * i.dir;

                //噪声计算
                float2 speed=_Time.y*float2(_NoiseSpeedX,_NoiseSpeedY);
                float noise=(tex2D(_NoiseTex,i.uv+speed).r-0.5)*_NoiseStrngth;
                //雾效公式,(高度雾,参数是y
                //这种写法如果y值越低,雾越浓,即便是y值超过了起始点
                float fog = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
                //在后面再乘一个值把上面的影响消掉
                //但为了防止对式子结果产生影响,雾的起始高度要够低    
                fog = saturate(fog* _FogStrenth*(1+noise)) ; //*saturate(worldPos.y-_FogStart);

                fixed4 color = tex2D(_MainTex, i.uv);
                color.rgb = (1 - fog) * color.rgb + fog * _FogColor.rgb; // lerp(color.rgb, _FogColor.rgb, fog);
                return color;
            }
            ENDCG
        }
    }
}

       

结果还行,在采样上加个时间变化雾就可以飘着移动了。

猜你喜欢

转载自blog.csdn.net/SliverAsh_/article/details/127349341