以3D空间里的Sprite为例
思路:
- 使物体旋转到合适的位置
- 需要知道模型空间下的点(x,y,z)和旋转后的坐标系的方向向量在原来的模型空间下的值(i,j,k)
- 新的点在模型空间下为xi + yj + zk
注:
上面的i,j,k为矢量,x,y,z为标量
都是在物体原来的模型空间下完成的
- Sprite面对摄像机的点
详情看最下面的完整代码以及注释
- Sprite平行于屏幕平面,近大远小(类似于血条,饥荒中的物体的面片实现)
定点着色器核心代码
//化简版
float3 forwardDir = UNITY_MATRIX_MV[2].xyz;
float3 upDir = UNITY_MATRIX_MV[1].xyz;
float3 rightDir = UNITY_MATRIX_MV[0].xyz;
//完整版
//float3 forwardDir = mul(UNITY_MATRIX_T_MV, float4(0, 0, 1, 1)).xyz;
//float3 upDir = mul(UNITY_MATRIX_T_MV, float4(0, 1, 0, 1)).xyz;
//float3 rightDir = mul(UNITY_MATRIX_T_MV, float4(1, 0, 0, 1)).xyz;
UNITY_MATRIX_MV代表从模型空间向观察空间的转换矩阵,
UNITY_MATRIX_T_MV代表从观察空间向模型空间的转换矩阵,是UNITY_MATRIX_MV的转置矩阵,完整版就代表观察空间下的方向向量到模型空间的转换
下面是坐标系转换相关内容,来自于《Unity Shader入门精要》
第一部分代码来自于《Unity Shader 入门精要》第十一章
Shader "Test/Billboard" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {
}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {
"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
Pass {
Tags {
"LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _VerticalBillboarding;
v2f vert (a2v v) {
v2f o;
// 在这修改中心点
float3 center = float3(0, 0, 0);
// 摄像机的位置(模型空间)
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));
// 总体思路是将模型旋转,使其面对摄像机,则求得旋转后的方向向量,再结合模型坐标求出结果
// 向前的向量 由模型指向摄像机
float3 normalDir = viewer - center;
normalDir.y = normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
// Get the approximate up dir
// If normal dir is already towards up, then the up dir is towards front
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
//上面的是始终面对摄像机这个点,下面三行是始终平行于摄像机的平面(屏幕),适合制作血条
//float3 normalDir = UNITY_MATRIX_MV[2].xyz;
//float3 upDir = UNITY_MATRIX_MV[1].xyz;
//float3 rightDir = cross(upDir, normalDir);
// Use the three vectors to rotate the quad
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
//o.pos = UnityObjectToClipPos(float4(localPos, 1));
o.pos = UnityObjectToClipPos(float4(localPos, 1));
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
Shader "Test/Test" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {
}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (a2v v) {
v2f o;
//化简版
float3 forwardDir = UNITY_MATRIX_MV[2].xyz;
float3 upDir = UNITY_MATRIX_MV[1].xyz;
float3 rightDir = UNITY_MATRIX_MV[0].xyz;
//完整版
//float3 forwardDir = mul(UNITY_MATRIX_T_MV, float4(0, 0, 1, 1)).xyz;
//float3 upDir = mul(UNITY_MATRIX_T_MV, float4(0, 1, 0, 1)).xyz;
//float3 rightDir = mul(UNITY_MATRIX_T_MV, float4(1, 0, 0, 1)).xyz;
float3 localPos = rightDir * v.vertex.x + upDir * v.vertex.y + forwardDir * v.vertex.z;
o.pos = UnityObjectToClipPos(float4(localPos, 1));
//o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv);
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}