需求
实现一个Quad模型,在忽略任何角度旋转的billboard模式(公告板,永远面向摄像机)下,在shader中填入一个参数(旋转角度),使得最后的图像显示,在其本身平面上,绕中心点旋转。
要求效果如下:
实现
Amplify:
总体思路: 对模型的所有顶点绕Z轴,模型中心旋转。
大体解释一下这张图,主要是输出到VertexOffset之前的部分。
Amplify官方文档:http://wiki.amplify.pt/index.php?title=Unity_Products:Amplify_Shader_Editor/Rotate_About_Axis
RotateAboutAxis四个输入:
参数 |
注释 |
Normalized Rotation Axis |
绕哪根轴旋转,输入值需要归一化 |
Rotation Angle |
旋转角(弧度制) |
Pivot Point |
旋转中心点 |
Position |
当前顶点值 |
翻译成代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| float3 RotateAroundAxis( float3 center, float3 original, float3 u, float angle ) { original -= center; float C = cos( angle ); float S = sin( angle ); float t = 1 - C; float m00 = t * u.x * u.x + C; float m01 = t * u.x * u.y - S * u.z; float m02 = t * u.x * u.z + S * u.y; float m10 = t * u.x * u.y + S * u.z; float m11 = t * u.y * u.y + C; float m12 = t * u.y * u.z - S * u.x; float m20 = t * u.x * u.z - S * u.y; float m21 = t * u.y * u.z + S * u.x; float m22 = t * u.z * u.z + C; float3x3 finalMatrix = float3x3( m00, m01, m02, m10, m11, m12, m20, m21, m22 ); return mul( finalMatrix, original ) + center; }
|
1 2 3 4
| float3 axis = fixed3(0,0,1); fixed3 _rotatePoint = fixed3(0.25,0.25,0); float3 rotatedValue = RotateAroundAxis( _rotatePoint, saturate( v.vertex.xyz ), axis, _RotateAngle ); v.vertex.xyz = ( rotatedValue - _rotatePoint );
|
问题
再放大一下amplify版仔细观察一下。
这里有一点很鬼畜我始终不得其解,主要就是RotateAboutAxis的旋转中心点_rotatePoint需要设成(0.25,0.25,0),旋转后再减去这个_rotatePoint就是正确结果。
用Amplify之所以要再减去vertexPosition是因为最终输出是vertex offset。
于是我作了个测试,测试shader我把顶点位置作为颜色值直接输出,得到这样的测试图:
根据测试图的颜色可知,Unity自带的Quad模型顶点范围为(-0.5.-0.5,0)~(0.5,0.5,0)。中心点怎么看都应该是(0,0,0)对吧?
但如果把_rotatePoint设成(0,0,0) 结果就会如下图这么旋转
体会到我的绝望了吗???
验证RotateAroundAxis
好,这时候就会想了,是不是amplify提供RotateAroundAxis方法有问题啊?shader单点调试有点烦。
于是我把这个函数放到unity里实现了一遍看输出有没有问题……
丢代码丢代码
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
| private Vector3 RotateAroundAxis(Vector3 center, Vector3 original, Vector3 axis, float angle) { original -= center; Vector3 u = axis; float C = Mathf.Cos(angle * Mathf.Deg2Rad); float S = Mathf.Sin(angle * Mathf.Deg2Rad); float t = 1 - C; float m00 = t * u.x * u.x + C; float m01 = t * u.x * u.y - S * u.z; float m02 = t * u.x * u.z + S * u.y; float m10 = t * u.x * u.y + S * u.z; float m11 = t * u.y * u.y + C; float m12 = t * u.y * u.z - S * u.x; float m20 = t * u.x * u.z - S * u.y; float m21 = t * u.y * u.z + S * u.x; float m22 = t * u.z * u.z + C;
Matrix4x4 matrix4X4 = new Matrix4x4( new Vector4(m00, m01, m02), new Vector4(m10, m11, m12), new Vector4(m20, m21, m22), new Vector4(0, 0, 0) );
Vector4 mul = matrix4X4 * original; return new Vector3(mul.x,mul.y,mul.z) + center; }
public Vector3 center = new Vector3(0, 0, 0); public float angle = 90;
[ContextMenu("TestRotate")] public void TestRotate() { Vector3 origin = new Vector3(-0.5f, -0.5f, 0); Vector3 normal = new Vector3(0, 0, 1);
Vector3 result = RotateAroundAxis(center, origin, normal, angle); LogMgr.LogDebug(result);
Vector3 expectResult = new Vector3(0.5f, -0.5f, 0); }
|
事实证明,这个函数!
它!
木有问题!
GG,所以我还是不知道为什么。
现在是2021/2/3,上面的问题破案了……
朋友今天看到我的博客后问了灵魂拷问,你为什么要saturate。
所以其实我把(-0.5.-0.5,0)~(0.5,0.5,0) 这个区间给坍塌到了(0,0) ~ (0.5,0.5)……所以中点变为了(0.25,0.25)
再给个最终的简单的答案吧……无语子。