平移旋转缩放

平移

在顶点着色器中修改顶点位置的 xyz 值即可

缩放

为了防止朝负方向缩放,+1.0 作为基础值,使原点永远乘以正值

1
2
3
4
void Scale(inout float3 vertex)  
{
vertex.xyz *= 1.0 + _ScaleRange * sin(frac(_Time.z * _ScaleSpeed) * UNITY_TWO_PI);
}

旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 旋转方法  
void Rotate(inout float3 vertex)
{
//计算旋转角度
float angleY = _RotateRange * sin(frac(_Time.z * _RotateSpeed) * UNITY_TWO_PI);
//角度转为弧度
float radY = radians(angleY);

//sincos()方法节省性能,效果相同
//float sinY = sin(radY);
//float cosY = cos(radY);
sincos(radY, sinY, cosY);

// 这里就是应用旋转公式
vertex.xz = float2(
vertex.x * cosY - vertex.z * sinY,
vertex.x * sinY + vertex.z * cosY
);
}

广告牌 Billboarding

广告牌总是面对着摄像机,

广告牌技术的本质就是构建旋转矩阵,而我们知道一个变换矩阵需要 3 个基向量。广告牌技术使用的基向量通常就是表面法线 (normal)、指向上的方向 (up)以及指向右的方向(right) 除此之外,我们还需要指定一个锚点(anchor location),这个锚点在旋转过程中是固定不变的, 以此来确定多边形在空间中的位置。

构建 3 个相互正交的基向量。计算过程通常是,通过初始计算得到目标的表面法线(例如就是视角方向)和指向上的方向。两者不垂直但其中之一是固定的:

  • 当模拟草丛时,我们希望广告牌的指向上的方向永远是 (0,1,0),而法线方向应该随视角变化;
  • 当模拟粒子效果时,我们希望广告牌的法线方向是固定内,即总是指向视角方向,指向上的方向则可以发生变化。

这里我们**假设法线方向是固定的 (法线总是指向视角方向)

  1. 根据初始的表面法线和指向上的方向 $(0,1,0)$ 来 计算出目标方向的指向右的方向 (通过叉积操作): $right = cross(up,normal)$
  2. 归一化右向量,由法线和右向量叉积得出正交的向上方向 $up’=cross(normal,right)$
    Pasted image 20230630150252

Unity 模型要选择 Quad 而不能是 Plane,因为代码要求的多边形的顶点结构在模型空间下必须是竖直排列的。这样才能得到正确的相对于锚点的位置偏移量

fold title:广告牌
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
Shader "Custom/SimplerColor"
{
Properties
{
[MainTexture] _MainTex ("MainTex", 2D) = "white" {}
[MainColor] _BaseColor("BaseColor", Color) = (1,1,1,1)
_VerticalBillborading("垂直方向约束",Range(0,1)) = 1
}

SubShader
{
Tags
{
"RenderType"="Transparent"
"Queue" = "Transparent"
"RenderPipeline" = "UniversalPipeline"
}

HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float4 _MainTex_ST;
float _VerticalBillborading;
CBUFFER_END

TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);

struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;

};
ENDHLSL
//----------------------------------------------------
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off

Tags
{
"LightMode" = "UniversalForward"
}

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag


Varyings vert(Attributes input)
{
Varyings output = (Varyings)0;

//模型空间原点作为锚点
float3 centerOS = float3(0,0,0);
//获取模型空间摄像机位置
float3 viewDirOS = TransformWorldToObject(_WorldSpaceCameraPos);
//法线方向为锚点看向摄像机
float3 normalDirOS = viewDirOS - centerOS;

//_VerticalBillborading为1时,法线方向固定为视角方向。
//为0时,意味着向上向量固定为(0,1,0)
normalDirOS.y = normalDirOS.y* _VerticalBillborading;
normalDirOS = normalize(normalDirOS);

//对法线方向y分量判断,防止法线方向和向上向量平行
float3 upDir = abs(normalDirOS.y)>0.999?float3(0,0,1):float3(0,1,0);
float3 rightDir = normalize(cross(upDir,normalDirOS));
upDir = normalize(cross(normalDirOS,rightDir));

//根据原始位置相对于锚点的偏移以及三个正交基矢量,计算出新的顶点
float3 centerOffs = input.positionOS.xyz - centerOS;
float3 newPosOS = centerOS + centerOffs.x * rightDir + centerOffs.y * upDir + centerOffs.z * normalDirOS;

output.positionCS = TransformObjectToHClip(newPosOS);
output.uv =input.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

return output;
}

float4 frag(Varyings input) : SV_Target
{
float4 MainTex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
float4 finalColor = MainTex * _BaseColor;
return finalColor;
}
ENDHLSL
}
}
FallBack "Packages/com.unity.render-pipelines.universal/FallbackError"
}