【ylg速成教程】在Unity中制作♿说的道理和栗子头♿
前言
大家好啊,我是狗猥,今天来点大家想看的东西。
说的道理和栗子头是吉吉王国答辩文化中不可磨灭的两颗璀璨明珠,它是用视频编辑软件Vegas中的Tiny Planet效果制作的。
DJGun出过一个视频讲解了实现原理,但细节部分几乎没有。
视频是这个:DJGun教你制作栗子头和说的道理
于是我在摸鱼的时候研究搞定了,把原理和实现过程分享给大家,祝大家早日成为ylg,像文老爷子一样把答辩拉得到处都是。
本项目使用Unity 2021.3.13f1制作,完整工程请移步GitHub:https://github.com/RenChiyu/UnityChestnutHead
转载请注明出处:https://www.cnblogs.com/GuyaWeiren/p/17204417.html
(这种一拖四的文章除了机器人应该没人会转吧)
原理
一. 平面图片变成球面
视频中是将图片贴在球上,所以要把图片的平面mesh转化成球面mesh。转换过程使用球的参数方程:
其中\(\theta = u \times2\pi\),\(\phi = v \times\pi\)。\(u\)和\(v\)即每个顶点的uv。
二. 投影的公式推导
通过视频我们能知道,说的道理是将球投影到平面所形成的。
其过程是,从球的极点开始,向球面任意点发射一条射线,射线与赤道平面所相交的点就是投影点。
这里以二维的圆来作为范例(三维的做起来麻烦,偷个懒):
极点为\(P_0\),圆上的任意点\(P_1\),与x轴交点为\(P\)。可以看到球面每个点和x轴的交点都是一一对应的。并且,我们可以用公式来描述这个映射。
首先需要知道射线的方程。三维空间中过点\(P_0\left(x_0, y_0, z_0\right)\)和点\(P_1\left(x_1, y_1, z_1\right)\)的直线可用两点式表示:
由于射线从球的极点发出,有\(P_0\left(0, r, 0\right)\)。其中\(r\)为球的半径。将球视作单位球,即\(r = 1\)。
并且,当该直线和赤道平面(即\(XOZ\)平面)相交时\(y = 0\)。一并带入上式化简得到:
如上,我们就得到了射线和平面交点的坐标\(P\left(x, 0, z\right)\)。
三. 栗子头的变换
栗子头是保持极点和赤道平面不变,只旋转球体本身,旋转到特定角度时投影的结果。
如上图的gif,想象鼠标拖动\(P_1\)的过程并不是\(P_1\)在圆上移动,而是拖动圆让它绕原点旋转。
Unity实现
这里可以在C#层实现,也可以用Shader。我选择了Shader。所有的变换都可以放在顶点着色器中。
由于直接设置transform
的旋转会导致整个模型发生旋转,为了保持极点和赤道平面不变,需要在顶点着色器中进行旋转变换。
在C#层使用Matrix4x4.TRS(Vector3 pos, Quaternion q, Vector3 s)
即可构建变换矩阵。
v2f vert (appdata v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// 平面变换为球
float theta = v.uv.x * UNITY_TWO_PI;
float phi = v.uv.y * UNITY_PI;
float x1 = sin(phi) * cos(theta);
float y1 = sin(phi) * sin(theta);
float z1 = cos(phi);
// 应用旋转矩阵
float4 p = mul(_TRSMatrix, float4(x1, y1, z1, v.vertex.w));
x1 = p.x;
y1 = p.y;
z1 = p.z;
// 计算出投影后的坐标
p.x = -x1 / (y1 - 1);
p.y = 0;
p.z = -z1 / (y1 - 1);
o.vertex = UnityObjectToClipPos(p);
return o;
}
上述代码中_TRSMatrix
即为C#层传入的变换矩阵。
Unity自带的Plane mesh的格子是10*10的,数量不够生成的图会有明显的扭曲。这里我用了一个自己生成的很密的mesh。
然后建立材质,贴上电棍的图片,通过调整xyz的旋转,就能得到♿说的道理或栗子头♿:
当然,你也可以用自己喜欢的图片进行创作。
后记
各位学到了没有?来!
(瞳子姐对不住了)
很惭愧,就做了一点微小的工作,谢谢大家。