Shader攻占笔记(六)顶点涟漪与遮罩

飘动旗帜

顶点动画

  1. 需求:旗子波浪式飘动(动画);越接近旗杆处飘动越受限(遮罩)
  2. 波浪式飘动(三角函数配合_Time使用),将其附加到需要的方向上;
  3. 确定需要上下起伏的轴(y);确定作为变量的轴(z)
    在这里插入图片描述
//addValue = sin((timer + 顶点的z值) * 周期的倒数)* 幅度;
float timer = _Time[3] * _Frequency;
float addValue = sin((timer + v.vertex.z)*3)* _Amplitude;

遮罩
将一般顶点动画的变化量乘以一个由 Mask 图提供的“牵连程度”信息

float maskTex = tex2Dlod(_MaskTex, float4(v.texcoord.xy, 0, 0)).r;
  • 因为这个例子中只需要一位信息,所以只取了r值
  • 顶点着色器中引入纹理需要注意使用 tex2Dlod, 而非 tex2D
    在这里插入图片描述
    遮罩位置如上,r值越接近0的地方摆动的幅度越弱。最后将显示的部分换成原来的贴图即可。

效果
在这里插入图片描述
顶点着色器代码:

void vert(inout appdata_full v)
        {
            float maskTex = tex2Dlod(_MaskTex, float4(v.texcoord.xy, 0, 0)).r;   //遮罩引入
            float4 timer = _Time[3] * _Freq;                                             //加入动量
            float addValue = sin((timer + v.vertex.z)*3)* _Ampl;
            addValue *= maskTex;                                                //遮罩因子
            v.vertex.y += addValue;
        }

人物呼吸 猴子瞪眼

基本原理
1.需求:使用遮罩控制角色顶点膨胀程度,大致原理与飘扬旗帜相同
2.然而我没有合适的整块人物模型,于是改成了猴子瞪眼

操作过程
在这里插入图片描述
这是那只经典的CG猴子的遮罩,在 blender 里只描了眼睛的白色。我希望的效果是各个面片之间是含有缝隙的,因此使用朝法线方向挤出的方式(两种挤出方式见卡通着色那一章)。使用了两个CGPROGRAM块:
一个在内部,负责提供裂缝中的颜色以及瞪出的眼;

float WaveValue = pow(sin(timer), 8); //浮动函数塑造
WaveValue = WaveValue* _Amplitude * mask;//最终浮动量
v.vertex.xyz += normalize(v.normal) * WaveValue;//沿着法线方向挤出(有裂开的效果)

另一个负责形成一个稍微向外裂出的壳,以露出内部的内容。

v.vertex.xyz += normalize(v.normal) * 0.0002;

效果
在这里插入图片描述
内部的颜色使用了[HDR]颜色,发光,好看。
完整代码:

Shader "Lesson/sd_MonkeyBlink"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        _MaskTex("Mask Tex", 2D) = "white"{}
        _Freq("_Frequency", Range(0, 1)) = 1
        _Ampl("Amplitude", Range(0, 1)) = 1
        [HDR]_InnerColor("Inner Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Cull Off
        LOD 200
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        #pragma vertex vert
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _MaskTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)
        void vert(inout appdata_full v)
        {
            v.vertex.xyz += normalize(v.normal) * 0.0002;                   //沿着法线方向裂开一点
        }

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG

        CGPROGRAM
        #pragma surface surf Lambert
        #pragma vertex vert
        #pragma target 3.0
        sampler2D _MaskTex;
        struct Input
        {
            float2 uv_MaskTex;
        };
        float _Freq;
        float _Ampl;
        fixed4 _InnerColor;
        float _Emission;
        void vert(inout appdata_full v)
        {
            float mask = tex2Dlod(_MaskTex, float4(v.texcoord.xy, 0, 0)).r; //黑白,只取一个值即可
            float timer = _Time[2] * _Freq;                                 //时间参数
            float WaveValue = pow(sin(timer), 8);                           //浮动函数塑造
            _Ampl *= 0.01;                                                  //调整幅度值
            WaveValue = WaveValue* _Ampl * mask;                            //最终浮动量
            v.vertex.xyz += normalize(v.normal) * WaveValue;                //沿着法线方向挤出(有裂开的效果)
        }
        void surf(Input IN, inout SurfaceOutput o)
        {
            o.Albedo = _InnerColor;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

鼠标交互涟漪

一块果冻!!
在这里插入图片描述
基本原理

  1. 需求:可指定中心点的涟漪(球形浮动);浮动的数值随时间变化而降低(脚本控制);因首次扰动而掉落的小旗(脚本控制,可有可无);
  2. 模型空间与世界空间的转换:unity_ObjectToWorld 矩阵的使用;
  3. 朝顶点方向挤出,否则可能会裂开(见下图)
  4. 用脚本将射线击中点送入着色器中改变波动中点;
  5. 用脚本使每次波动的幅度都线性降低,直到下一次击中;

在这里插入图片描述

另外如果要实现史莱姆的效果,需要调低涟漪层数以及摇晃幅度 ,且模型顶点数足够才能Q弹。

实现过程
shader顶点部分:

void vert(inout appdata_full v)
        {
            //波纹高度图制作----------------------------
            float3 worldPos = mul(unity_ObjectToWorld, v.vertex.xyz).xyz;//转为世界坐标系
            float dis = distance(worldPos, _RippleCenter);//世界坐标下与波动中心的距离
            dis *= _RippleAmount;
            dis = 1 - dis;//默认情况是边缘->中心,改成中心->边缘
            float timer = _Time[2] * _Speed;//时间因子

            //波纹
            float waveValue = cos(timer + dis);//基本偏移量
            waveValue *= _Amplitude * 0.001;

            //v.vertex.xyz += normalize(v.normal) * waveValue;//如果使用法线依旧会裂开
            v.vertex.xyz += normalize(v.vertex.xyz) * waveValue;
        }

脚本部分:

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

public class ClickVertex : MonoBehaviour
{
    [Header("射线变量")]
    private Ray ray;
    private RaycastHit hit; //point, not obj 
    [SerializeField]
    private GameObject obj;
    private Material mat;
    [Header("摇晃")]
    public FlagDrop flag;
    public float amplitude = 1.6f;
    [SerializeField]
    private bool isWaving;
    [SerializeField]
    private float ampl = 0f;
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if(Physics.Raycast(ray, out hit))
            {
                obj = hit.collider.gameObject;
                if(obj.name == "Milk Cube")
                {
                    flag.FirstClick();              //检测是否第一次击中
                    isWaving = true;
                    ampl = amplitude;

                    mat = obj.GetComponent<Renderer>().material;
                    mat.SetVector("_RippleCenter", hit.point);
                    mat.SetFloat("_Amplitude", amplitude);
                }
            }
        }

        if(isWaving)
        {
            ampl -= Time.deltaTime;
            mat.SetFloat("_Amplitude", ampl);
            if(ampl <= 0)
            {
                isWaving = false;
                mat.SetFloat("_Amplitude", 0); //避免直接减到负数部分
            }
        }
    }
}

然后你就可以获得一块可爱的史莱姆啦!
完整shader:

Shader "Lesson/sd_ClickVert"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic("Metatllic", Range(0, 1)) = 0.5
        _RippleCenter("涟漪中心", Vector) = (0,0,0,0)
        _RippleAmount("涟漪数量", float) = 2
        _Speed("涟漪速度", float) = 1.5
        _Amplitude("幅度", Range(0, 2)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Transparent"
               "Queue" = "Transparent"
               "IgnoreProjector" = "True" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows //alpha 
        #pragma vertex vert
        #pragma target 3.0

        sampler2D _MainTex;
        struct Input
        {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float4 _RippleCenter;
        float _RippleAmount;
        float _Speed;
        float _Amplitude;

        UNITY_INSTANCING_BUFFER_START(Props)
        UNITY_INSTANCING_BUFFER_END(Props)
        void vert(inout appdata_full v)
        {
            //波纹高度图制作----------------------------
            float3 worldPos = mul(unity_ObjectToWorld, v.vertex.xyz).xyz;   //转为世界坐标
            float dis = distance(worldPos, _RippleCenter);                  //世界坐标下与波动中心的距离
            dis *= _RippleAmount;
            dis = 1 - dis;                                                  //默认情况是边缘->中心,改成中心->边缘
            float timer = _Time[2] * _Speed;                                //时间因子

            //波纹
            float waveValue = cos(timer + dis);                             //基本偏移量
            waveValue *= _Amplitude * 0.001;

            //v.vertex.xyz += normalize(v.normal) * waveValue;              //如果使用法线依旧会裂开
            v.vertex.xyz += normalize(v.vertex.xyz) * waveValue;
        }
        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Smoothness = _Glossiness;
            o.Metallic = _Metallic;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值