51.广告牌具体实现

  1. 51.动态效果-顶点动画-广告牌-具体实现
    1. 51.1 知识点
      1. 知识回顾
        1. 广告牌效果实现关键点
      2. 导入资源
      3. 广告牌效果具体实现
        1. 主要步骤
        2. 创建shader,保留骨架
        3. 声明属性和属性映射。主纹理、颜色叠加、垂直广告牌程度(0为垂直广告牌,1为全向广告牌)
        4. 设置透明Shader相关,设置渲染类型为透明,渲染队列为透明队列,忽略投影,禁用合批,并且要关闭深度写入,设置透明混合模式,关闭背面剔除使广告牌两面都可见
        5. 结构体相关无需修改,保留顶点和纹理坐标
        6. 顶点函数首先确定新坐标系的中心点,将摄像机位置转换到模型空间以计算Z轴方向(normal),再根据垂直广告牌的程度调整Z轴的y分量并单位化,随后定义旧的Y轴方向(old up)以避免与Z轴重合,通过Z轴和Y轴叉乘得到X轴方向(right),再用Z轴和X轴叉乘得到新的Y轴方向(up),接着计算顶点相对于中心点的偏移量,利用新中心点和3个轴方向得到顶点的新位置,并将其转换到裁剪空间,最后应用UV的缩放和偏移。
        7. 片元函数直接采样叠加颜色即可
        8. 创建材质赋值给四边形,可以看到一直面向我们的广告牌效果,如果调整广告牌程度为0可以看到变成垂直广告牌
    2. 51.2 知识点代码
      1. Lesson51_动态效果_顶点动画_广告牌_具体实现.cs
      2. Lesson51_Billboarding.shader

51.动态效果-顶点动画-广告牌-具体实现


51.1 知识点

知识回顾

广告牌效果实现关键点

  1. 新坐标系

    • 原点确定(一般为(0,0,0)
    • 坐标轴计算(XYZ
  2. 顶点计算

    • 偏移位置 = 顶点坐标 - Center
    • 新顶点位置 = Center + X轴 * 偏移位置.x + Y轴 * 偏移位置.y + Z轴 * 偏移位置.z
  3. 全向广告牌与垂直广告牌区别

    • 计算normal轴时,若Y为0则为垂直广告牌

导入资源

广告牌效果具体实现

主要步骤

  1. 新建 Shader
    删除无用代码。

  2. 声明属性与属性映射

    • 主纹理
    • 颜色叠加
    • 垂直广告牌程度(0 为垂直广告牌,1 为全向广告牌)。
  3. 透明 Shader 相关设置

    • 注意:关闭批处理,并使其两面渲染。
  4. 结构体相关

    • 包括顶点和纹理坐标。
  5. 顶点着色器

    1. 确定新坐标中心点。
    2. 计算 Z 轴(normal),将摄像机坐标转到模型空间。
    3. 用垂直广告牌程度调整 Z 轴的 y 分量后,单位化。
    4. 声明 Y 轴(old up)。
    5. 利用 Z 轴(normal)和 Y 轴(old up)叉乘计算出 X 轴(right)。
    6. 利用 Z 轴(normal)和 X 轴(right)叉乘计算出新的 Y 轴(up)。
    7. 得到顶点相对于新坐标系中心点的偏移位置。
    8. 利用新中心点和三个轴计算出顶点的新位置。
    9. 将新顶点转换到裁剪空间。
    10. 应用 UV 的缩放和偏移。
  6. 片元着色器

    • 直接采样并叠加颜色即可。

创建shader,保留骨架

Shader "Unlit/Lesson51_Billboarding"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
    
        Pass
        {
    

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            sampler2D _MainTex;
            float4 _MainTex_ST;

        
            ENDCG
        }
    }
}

声明属性和属性映射。主纹理、颜色叠加、垂直广告牌程度(0为垂直广告牌,1为全向广告牌)

Properties
{
    // 纹理属性,用于给广告牌指定一个纹理
    _MainTex ("Texture", 2D) = "white" {}

    // 颜色属性,用于给广告牌指定一个基础颜色
    _Color("Color", Color) = (1,1,1,1)

    // 用于控制广告牌的类型:0表示全向广告牌,1表示垂直广告牌
    _VerticalBillboarding("VerticalBillboarding", Range(0,1)) = 1
}
// 纹理采样器
sampler2D _MainTex;
float4 _MainTex_ST; // 纹理的缩放和偏移
fixed4 _Color; // 基础颜色
float _VerticalBillboarding; // 广告牌类型控制

设置透明Shader相关,设置渲染类型为透明,渲染队列为透明队列,忽略投影,禁用合批,并且要关闭深度写入,设置透明混合模式,关闭背面剔除使广告牌两面都可见

// 设置渲染类型为透明,渲染队列为透明队列,忽略投影,禁用合批
Tags
{
    "RenderType"="Transparent"
    "Queue"="Transparent"
    "IgnoreProjector"="True"
    "DisableBatching"="True"
}

Pass
{
    // 关闭深度写入,使广告牌在透明物体上正确显示
    ZWrite Off
    // 设置透明混合模式:源颜色乘以透明度,目标颜色乘以(1 - 透明度)
    Blend SrcAlpha OneMinusSrcAlpha
    // 关闭背面剔除,使广告牌两面都可见
    Cull Off
    //...
}

结构体相关无需修改,保留顶点和纹理坐标

struct v2f
{
    float2 uv : TEXCOORD0; // 纹理坐标
    float4 vertex : SV_POSITION; // 裁剪空间的顶点位置
};

顶点函数首先确定新坐标系的中心点,将摄像机位置转换到模型空间以计算Z轴方向(normal),再根据垂直广告牌的程度调整Z轴的y分量并单位化,随后定义旧的Y轴方向(old up)以避免与Z轴重合,通过Z轴和Y轴叉乘得到X轴方向(right),再用Z轴和X轴叉乘得到新的Y轴方向(up),接着计算顶点相对于中心点的偏移量,利用新中心点和3个轴方向得到顶点的新位置,并将其转换到裁剪空间,最后应用UV的缩放和偏移。

// 顶点着色器
v2f vert(appdata_base appdata_base)
{
    v2f v2f;

    // 新坐标系的中心点(默认为模型空间原点)
    float3 center = float3(0, 0, 0);

    // 计算摄像机位置在模型空间的坐标  float4(_WorldSpaceCameraPos, 1)是把三维向量构建成四维
    float3 cameraInObjectPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));

    // 计算Z轴的方向(广告牌朝向摄像机的方向)
    float3 normalDir = cameraInObjectPos - center;

    // 控制广告牌类型:相当于把y往下压
    // _VerticalBillboarding为0时,Z轴方向被压到XZ平面;为1时保持原始视角方向
    normalDir.y *= _VerticalBillboarding;

    // 单位化Z轴
    normalDir = normalize(normalDir);

    // 设置模型空间中的Y轴正方向,避免Z轴与Y轴重合导致叉乘结果为0
    float3 upDir = normalDir.y > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);

    // 通过叉乘计算X轴的方向
    float3 rightDir = normalize(cross(upDir, normalDir));

    // 再次使用叉乘计算新的Y轴方向
    upDir = normalize(cross(normalDir, rightDir));

    // 计算顶点相对于新坐标系中心点的偏移量
    float3 centerOffset = appdata_base.vertex.xyz - center;

    // 使用新的坐标系的三个轴重新计算顶点位置
    float3 newVertexPos = center + rightDir * centerOffset.x + upDir * centerOffset.y + normalDir *
        centerOffset.z;

    // 将新的顶点位置转换到裁剪空间
    v2f.vertex = UnityObjectToClipPos(float4(newVertexPos, 1));

    // 计算纹理坐标的缩放和偏移
    v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

    return v2f;
}

片元函数直接采样叠加颜色即可

// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
    // 从纹理中采样颜色
    float4 color = tex2D(_MainTex, v2f.uv);

    // 乘以基础颜色
    color.rgb *= _Color.rgb;

    return color;
}

创建材质赋值给四边形,可以看到一直面向我们的广告牌效果,如果调整广告牌程度为0可以看到变成垂直广告牌



51.2 知识点代码

Lesson51_动态效果_顶点动画_广告牌_具体实现.cs

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

public class Lesson51_动态效果_顶点动画_广告牌_具体实现 : MonoBehaviour
{
    void Start()
    {
        #region 知识回顾

        //广告牌效果实现关键点
        //1.新坐标系
        //  原点确定(一般0,0,0)
        //  坐标轴计算(x,y,z)
        //2.顶点计算
        //  偏移位置 = 顶点坐标– Center
        //  新顶点位置 = Center + X轴 * 偏移位置.x + Y轴 * 偏移位置.y + Z轴 * 偏移位置.z
        //3.全向广告牌和垂直广告牌区别
        //  计算normal轴时,y为0则为垂直广告牌

        #endregion

        #region 导入资源

        #endregion

        #region 知识点 广告牌效果 具体实现

        //1.新建Shader,删除无用代码
        //2.声明属性,属性映射
        //  主纹理、颜色叠加、垂直广告牌程度(0为垂直广告牌,1为全向广告牌)
        //3.透明Shader相关
        //  注意:关闭批处理,并让其两面渲染
        //4.结构体相关
        //  顶点和纹理坐标
        //5.顶点着色器
        //  5-1:确定新坐标中心点
        //  5-2:计算Z轴(normal),将摄像机坐标转到模型空间
        //  5-3:用垂直广告牌程度改变Z轴y值后,单位化
        //  5-4:声明Y轴(old up)
        //  5-5:利用Z轴(normal)和Y轴(old up)叉乘计算出X轴(right)
        //  5-6:利用Z轴(normal)和X轴(right)叉乘计算出Y轴(up)
        //  5-7:得到顶点相对于新坐标系中心点的偏移位置
        //  5-8:利用新中心点和3轴计算出顶点新位置
        //  5-9:新顶点转到裁剪空间
        //  5-10:UV缩放偏移
        //6:片元着色器
        //  直接采样 叠加颜色即可

        #endregion
    }
}

Lesson51_Billboarding.shader

Shader "Unlit/Lesson51_Billboarding"
{
    Properties
    {
        // 纹理属性,用于给广告牌指定一个纹理
        _MainTex ("Texture", 2D) = "white" {}

        // 颜色属性,用于给广告牌指定一个基础颜色
        _Color("Color", Color) = (1,1,1,1)

        // 用于控制广告牌的类型:0表示全向广告牌,1表示垂直广告牌
        _VerticalBillboarding("VerticalBillboarding", Range(0,1)) = 1
    }
    SubShader
    {
        // 设置渲染类型为透明,渲染队列为透明队列,忽略投影,禁用合批
        Tags
        {
            "RenderType"="Transparent"
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "DisableBatching"="True"
        }

        Pass
        {
            // 关闭深度写入,使广告牌在透明物体上正确显示
            ZWrite Off
            // 设置透明混合模式:源颜色乘以透明度,目标颜色乘以(1 - 透明度)
            Blend SrcAlpha OneMinusSrcAlpha
            // 关闭背面剔除,使广告牌两面都可见
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            // 纹理采样器
            sampler2D _MainTex;
            float4 _MainTex_ST; // 纹理的缩放和偏移
            fixed4 _Color; // 基础颜色
            float _VerticalBillboarding; // 广告牌类型控制

            struct v2f
            {
                float2 uv : TEXCOORD0; // 纹理坐标
                float4 vertex : SV_POSITION; // 裁剪空间的顶点位置
            };

            // 顶点着色器
            v2f vert(appdata_base appdata_base)
            {
                v2f v2f;

                // 新坐标系的中心点(默认为模型空间原点)
                float3 center = float3(0, 0, 0);

                // 计算摄像机位置在模型空间的坐标  float4(_WorldSpaceCameraPos, 1)是把三维向量构建成四维
                float3 cameraInObjectPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));

                // 计算Z轴的方向(广告牌朝向摄像机的方向)
                float3 normalDir = cameraInObjectPos - center;

                // 控制广告牌类型:相当于把y往下压
                // _VerticalBillboarding为0时,Z轴方向被压到XZ平面;为1时保持原始视角方向
                normalDir.y *= _VerticalBillboarding;

                // 单位化Z轴
                normalDir = normalize(normalDir);

                // 设置模型空间中的Y轴正方向,避免Z轴与Y轴重合导致叉乘结果为0
                float3 upDir = normalDir.y > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);

                // 通过叉乘计算X轴的方向
                float3 rightDir = normalize(cross(upDir, normalDir));

                // 再次使用叉乘计算新的Y轴方向
                upDir = normalize(cross(normalDir, rightDir));

                // 计算顶点相对于新坐标系中心点的偏移量
                float3 centerOffset = appdata_base.vertex.xyz - center;

                // 使用新的坐标系的三个轴重新计算顶点位置
                float3 newVertexPos = center + rightDir * centerOffset.x + upDir * centerOffset.y + normalDir *
                    centerOffset.z;

                // 将新的顶点位置转换到裁剪空间
                v2f.vertex = UnityObjectToClipPos(float4(newVertexPos, 1));

                // 计算纹理坐标的缩放和偏移
                v2f.uv = appdata_base.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return v2f;
            }

            // 片段着色器
            fixed4 frag(v2f v2f) : SV_Target
            {
                // 从纹理中采样颜色
                float4 color = tex2D(_MainTex, v2f.uv);

                // 乘以基础颜色
                color.rgb *= _Color.rgb;

                return color;
            }
            ENDCG
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏