Shader__常用效果

发布于 2020-04-20  35 次阅读


前言


在这里积累一些常见的效果,方便以后用到(站在巨人的肩膀上,不闭门造ju)。

正文

镜面反射效果

  • 创建Shader,赋给Mat。
  • 创建Camera,创建RenderTexture。
  • Camera赋值给RenderTexture,RenderTexture赋值给Mat。
  • Mat赋值给需要的物体。
Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex("Main Tex",2D) = "white"{}
    }
        SubShader
    {
        Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }

        Pass
    {
        Tags{ "LightMode" = "ForwardBase" }

        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

        //包含引用的内置文件  
#include "Lighting.cginc"  

        //声明properties中定义的属性  
        sampler2D _MainTex;

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

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

    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
    {
        //对渲染纹理进行采样和输出
        return tex2D(_MainTex,i.uv);

    }
        ENDCG
    }
    }
        Fallback"Reflective/VertexLit"
}

点击水波纹效果

WaterWaveEffect.shader

Shader "Unlit/WaterWaveEffect"
{
    Properties
    {
        _MainTex("Base (RGB)", 2D) = "white" {}
    }

        CGINCLUDE
#include "UnityCG.cginc"
        uniform sampler2D _MainTex;
    float4 _MainTex_TexelSize;
    uniform float _distanceFactor;
    uniform float _timeFactor;
    uniform float _totalFactor;
    uniform float _waveWidth;
    uniform float _curWaveDis;
    uniform float4 _startPos;

    fixed4 frag(v2f_img i) : SV_Target
    {
        //DX下纹理坐标反向问题
#if UNITY_UV_STARTS_AT_TOP
        if (_MainTex_TexelSize.y < 0)
        _startPos.y = 1 - _startPos.y;
#endif
    //计算uv到中间点的向量(向外扩,反过来就是向里缩)
    float2 dv = _startPos.xy - i.uv;
    //按照屏幕长宽比进行缩放
    dv = dv * float2(_ScreenParams.x / _ScreenParams.y, 1);
    //计算像素点距中点的距离
    float dis = sqrt(dv.x * dv.x + dv.y * dv.y);
    //用sin函数计算出波形的偏移值factor
    //dis在这里都是小于1的,所以我们需要乘以一个比较大的数,比如60,这样就有多个波峰波谷
    //sin函数是(-1,1)的值域,我们希望偏移值很小,所以这里我们缩小100倍,据说乘法比较快,so...
    float sinFactor = sin(dis * _distanceFactor + _Time.y * _timeFactor) * _totalFactor * 0.01;
    //距离当前波纹运动点的距离,如果小于waveWidth才予以保留,否则已经出了波纹范围,factor通过clamp设置为0
    float discardFactor = clamp(_waveWidth - abs(_curWaveDis - dis), 0, 1) / _waveWidth;
    //归一化
    float2 dv1 = normalize(dv);
    //计算每个像素uv的偏移值
    float2 offset = dv1 * sinFactor * discardFactor;
    //像素采样时偏移offset
    float2 uv = offset + i.uv;
    return tex2D(_MainTex, uv);
    }

        ENDCG

        SubShader
    {
        Pass
        {
            ZTest Always
            Cull Off
            ZWrite Off
            Fog{ Mode off }

            CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest 
            ENDCG
        }
    }
    Fallback off
}

PostEffectBase.cs 后处理 拖入shader生成对应材质球

using UnityEngine;
using System.Collections;

//非运行时也触发效果
[ExecuteInEditMode]
//屏幕后处理特效一般都需要绑定在摄像机上
[RequireComponent(typeof(Camera))]
//提供一个后处理的基类,主要功能在于直接通过Inspector面板拖入shader,生成shader对应的材质
public class PostEffectBase : MonoBehaviour
{
    //Inspector面板上直接拖入
    public Shader shader = null;
    private Material _material = null;
    public Material _Material
    {
        get
        {
            if (_material == null)
                _material = GenerateMaterial(shader);
            return _material;
        }
    }

    //根据shader创建用于屏幕特效的材质
    protected Material GenerateMaterial(Shader shader)
    {
        if (shader == null)
            return null;
        //需要判断shader是否支持
        if (shader.isSupported == false)
            return null;
        Material material = new Material(shader);
        material.hideFlags = HideFlags.DontSave;
        if (material)
            return material;
        return null;
    }

}

WaterWaveEffect.cs 点击触发


using UnityEngine;

public class WaterWaveEffect : PostEffectBase
{

    //距离系数
    public float distanceFactor = 60.0f;
    //时间系数
    public float timeFactor = -20.0f;
    //sin函数结果系数
    public float totalFactor = 1.0f;

    //波纹宽度
    public float waveWidth = 0.3f;
    //波纹扩散的速度
    public float waveSpeed = 1.0f;

    private float waveStartTime;
    private Vector4 startPos = new Vector4(0.5f, 0.5f, 0, 0);

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        //计算波纹移动的距离,根据enable到目前的时间*速度求解
        float curWaveDistance = (Time.time - waveStartTime) * waveSpeed;
        //设置一系列参数
        _Material.SetFloat("_distanceFactor", distanceFactor);
        _Material.SetFloat("_timeFactor", timeFactor);
        _Material.SetFloat("_totalFactor", totalFactor);
        _Material.SetFloat("_waveWidth", waveWidth);
        _Material.SetFloat("_curWaveDis", curWaveDistance);
        _Material.SetVector("_startPos", startPos);
        Graphics.Blit(source, destination, _Material);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Vector2 mousePos = Input.mousePosition;
            //将mousePos转化为(0,1)区间
            startPos = new Vector4(mousePos.x / Screen.width, mousePos.y / Screen.height, 0, 0);
            waveStartTime = Time.time;
        } 
    } 
}

消融效果

XiaoUnlitShader.shader

Shader "Unlit/XiaoUnlitShader"
{ 
    Properties
    {
        //控制消融程度
        _BurnAmount("Burn Amount", Range(0.0, 1.0)) = 0.0
            //控制模拟烧焦效果时的线宽
            _LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1
            //漫反射纹理
            _MainTex("Base (RGB)", 2D) = "white" {}
        //法线纹理
        _BumpMap("Normal Map", 2D) = "bump" {}
        //火焰边缘的两种颜色
        _BurnFirstColor("Burn First Color", Color) = (1, 0, 0, 1)
            _BurnSecondColor("Burn Second Color", Color) = (1, 0, 0, 1)
            //噪声纹理
            _BurnMap("Burn Map", 2D) = "white"{}
    }
    SubShader
    {
        Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }

        Pass
    {
        Tags{ "LightMode" = "ForwardBase" }
        //只渲染正面会出现错误的结果
        Cull Off

        CGPROGRAM

#include "Lighting.cginc"
#include "AutoLight.cginc"

#pragma multi_compile_fwdbase

#pragma vertex vert
#pragma fragment frag

        fixed _BurnAmount;
    fixed _LineWidth;
    sampler2D _MainTex;
    sampler2D _BumpMap;
    fixed4 _BurnFirstColor;
    fixed4 _BurnSecondColor;
    sampler2D _BurnMap;

    float4 _MainTex_ST;
    float4 _BumpMap_ST;
    float4 _BurnMap_ST;

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

    struct v2f
    {
        float4 pos : SV_POSITION;
        float2 uvMainTex : TEXCOORD0;
        float2 uvBumpMap : TEXCOORD1;
        float2 uvBurnMap : TEXCOORD2;
        float3 lightDir : TEXCOORD3;
        float3 worldPos : TEXCOORD4;
        SHADOW_COORDS(5)
    };

    v2f vert(a2v v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        //计算三张纹理对应纹理坐标,再把光源方向从模型空间变换到了切线空间
        o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
        o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
        o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);

        TANGENT_SPACE_ROTATION;
        o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;

        o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
        //为了得到阴影信息,计算了世界空间下的顶点位置和阴影纹理的采样坐标
        TRANSFER_SHADOW(o);

        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        //对噪声纹理采样
        fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
    //如果结果小于零就舍弃这个像素
    clip(burn.r - _BurnAmount);

    float3 tangentLightDir = normalize(i.lightDir);
    fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
    //得到材质的反射率
    fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;
    //计算环境光照
    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    //得到漫反射光照
    fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
    //平滑插值,计算混合系数
    //t为1说明该像素位于消融的边界处
    //t为0说明该像素为正常的模型颜色
    fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
    //计算烧焦颜色
    fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
    //使效果更加逼真(颜色加深?)
    burnColor = pow(burnColor, 5);

    UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
    fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));

    return fixed4(finalColor, 1);
    }

        ENDCG
    }

        // 为了让物体的阴影也能配合透明度测试产生正确的效果,需要自定义一个投射阴影Pass
        // 阴影投射重点是需要按正常Pass的处理来剔除片元或进行顶点动画
        // 以便阴影可以和物体正常渲染的结果相匹配
        Pass
    {
        Tags{ "LightMode" = "ShadowCaster" }

        CGPROGRAM

#pragma vertex vert
#pragma fragment frag

#pragma multi_compile_shadowcaster

#include "UnityCG.cginc"

        fixed _BurnAmount;
    sampler2D _BurnMap;
    float4 _BurnMap_ST;

    struct v2f
    {
        //定义阴影投射所需要定义的变量
        V2F_SHADOW_CASTER;
        float2 uvBurnMap : TEXCOORD1;
    };

    v2f vert(appdata_base v)
    {
        v2f o;
        //填充V2F_SHADOW_CASTER中的变量
        TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
            //计算噪声纹理的采样坐标
            o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);

        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
    //使用噪声纹理的采样结果剔除片元
    clip(burn.r - _BurnAmount);
    //完成阴影投射,把结果输出到深度图和阴影映射纹理中
    SHADOW_CASTER_FRAGMENT(i)
    }
        ENDCG
    }
    }
        FallBack "Diffuse"
}

BurnHelper.cs

using UnityEngine;

public class BurnHelper : MonoBehaviour
{  
    public Material material;
    [Range(0.01f, 1.0f)] public float burnSpeed = 0.3f;
    private float burnAmount = 0.0f;

    void Start()
    {
        if (material == null)
        {
            Renderer renderer = gameObject.GetComponentInChildren<Renderer>();
            if (renderer != null)
            {
                material = renderer.material;
            }
        }
        if (material == null)
        {
            this.enabled = false;
        }
        else
        {
            material.SetFloat("_BurnAmount", 0.0f);
        }
    }
    void Update()
    {
        burnAmount = Mathf.Repeat(Time.time * burnSpeed, 1.0f);
        material.SetFloat("_BurnAmount", burnAmount);
    }
}

水波效果

Shader "Unlit/WaterWav"
{  
    Properties
    {
        //水面颜色
        _Color("Main Color", Color) = (0, 0.15, 0.115, 1)
            //水面波纹材质纹理
            _MainTex("Base (RGB)", 2D) = "white" {}
        //噪声纹理生成的法线纹理
        _WaveMap("Wave Map", 2D) = "bump" {}
        //用于模拟反射的立方体纹理
        _Cubemap("Environment Cubemap", Cube) = "_Skybox" {}
        _WaveXSpeed("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01
            _WaveYSpeed("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01
            //控制模拟折射时图像扭曲程度
            _Distortion("Distortion", Range(0, 100)) = 10
    }

    SubShader
    {
        //Transparent确保该物体渲染时,其他所有不透明物体都已经被渲染到屏幕上了
        //Opaque使用着色器替换时物体可以在需要时被正确渲染
        //这通常发生在我们需要的到摄像机的深度和法线纹理时
        Tags{ "Queue" = "Transparent" "RenderType" = "Opaque" }

        Cull Off

        //决定抓取到的屏幕图像将会被存入哪个纹理中
        GrabPass{ "_RefractionTex" }

        Pass
    {
        Tags{ "LightMode" = "ForwardBase" }

        CGPROGRAM

#include "UnityCG.cginc"
#include "Lighting.cginc"

#pragma multi_compile_fwdbase

#pragma vertex vert
#pragma fragment frag

        fixed4 _Color;
    sampler2D _MainTex;
    float4 _MainTex_ST;
    sampler2D _WaveMap;
    float4 _WaveMap_ST;
    samplerCUBE _Cubemap;
    fixed _WaveXSpeed;
    fixed _WaveYSpeed;
    float _Distortion;
    sampler2D _RefractionTex;
    //纹素大小
    //一个256*512的纹理,纹素大小就是1/256*1/512
    //对屏幕图像的采样坐标进行偏移时使用该变量
    float4 _RefractionTex_TexelSize;

    struct a2v
    {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float4 tangent : TANGENT;
        float4 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, _WaveMap);

        float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
        //因为在片元着色器中需要把法线方向从切线空间变换到世界空间,以便对Cubemap采样
        //所以这里要求得切线空间到世界空间的变换矩阵
        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);

        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
        //得到视角方向
        fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
        //计算法线纹理当前偏移量
        float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);

        // 两次采样,模拟两层交叉水面波动效果
        fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb;
        fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb;
        //得到切线空间下的法线方向
        fixed3 bump = normalize(bump1 + bump2);

        //计算偏移量
        float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
        //与z相乘是为了模拟深度越大,折射程度越大的效果
        i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
        //scrPos透视除法
        fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;

        //把法线方向从切线空间变换到世界空间下
        bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));

        fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
        //得到视角方向相对于法线方向的反射方向
        fixed3 reflDir = reflect(-viewDir, bump);
        //使用反射方向对Cubemap进行采样
        fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb * _Color.rgb;
        //就三菲涅尔系数
        fixed fresnel = pow(1 - saturate(dot(viewDir, bump)), 4);
        //混合折射和反射颜色
        fixed3 finalColor = reflCol * fresnel + refrCol * (1 - fresnel);

        return fixed4(finalColor, 1);
    }

        ENDCG
    }
    }
        // Do not cast shadow
        FallBack Off
}

本文持续更新...


不积跬步,无以至千里;不积小流,无以成江海。