51.动态效果-顶点动画-广告牌-具体实现
51.1 知识点
知识回顾
广告牌效果实现关键点
新坐标系
- 原点确定(一般为
(0,0,0)
) - 坐标轴计算(
X
,Y
,Z
)
- 原点确定(一般为
顶点计算
- 偏移位置 = 顶点坐标 -
Center
- 新顶点位置 =
Center
+X
轴 * 偏移位置.x +Y
轴 * 偏移位置.y +Z
轴 * 偏移位置.z
- 偏移位置 = 顶点坐标 -
全向广告牌与垂直广告牌区别
- 计算
normal
轴时,若Y
为0则为垂直广告牌
- 计算
导入资源
广告牌效果具体实现
主要步骤
新建 Shader
删除无用代码。声明属性与属性映射
- 主纹理
- 颜色叠加
- 垂直广告牌程度(
0
为垂直广告牌,1
为全向广告牌)。
透明 Shader 相关设置
- 注意:关闭批处理,并使其两面渲染。
结构体相关
- 包括顶点和纹理坐标。
顶点着色器
- 确定新坐标中心点。
- 计算 Z 轴(
normal
),将摄像机坐标转到模型空间。 - 用垂直广告牌程度调整 Z 轴的 y 分量后,单位化。
- 声明 Y 轴(
old up
)。 - 利用 Z 轴(
normal
)和 Y 轴(old up
)叉乘计算出 X 轴(right
)。 - 利用 Z 轴(
normal
)和 X 轴(right
)叉乘计算出新的 Y 轴(up
)。 - 得到顶点相对于新坐标系中心点的偏移位置。
- 利用新中心点和三个轴计算出顶点的新位置。
- 将新顶点转换到裁剪空间。
- 应用 UV 的缩放和偏移。
片元着色器
- 直接采样并叠加颜色即可。
创建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