【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。转换过程使用球的参数方程:

\[\left\{\begin{align*} & x_1 = \sin\left(\phi\right) \times \cos\left(\theta\right) \\ & y_1 = \sin\left(\phi\right) \times \sin\left(\theta\right) \\ & z_1 = \cos\left(\phi\right) \end{align*}\right. \]

其中\(\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)\)的直线可用两点式表示:

\[\frac{x - x_0}{x_1 - x_0} = \frac{y - y_0}{y_1 - y_0} = \frac{z - z_0}{z_1 - z_0} \]

由于射线从球的极点发出,有\(P_0\left(0, r, 0\right)\)。其中\(r\)为球的半径。将球视作单位球,即\(r = 1\)

并且,当该直线和赤道平面(即\(XOZ\)平面)相交时\(y = 0\)。一并带入上式化简得到:

\[\left\{\begin{align*} & x = - \frac{x_1}{y_1 - 1} \\ & z = - \frac{z_1}{y_1 - 1} \end{align*}\right. \]

如上,我们就得到了射线和平面交点的坐标\(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的旋转,就能得到♿说的道理或栗子头♿:

当然,你也可以用自己喜欢的图片进行创作。

后记

各位学到了没有?来!

(瞳子姐对不住了)

很惭愧,就做了一点微小的工作,谢谢大家。

posted @ 2023-03-23 01:11  GuyaWeiren  阅读(1427)  评论(0编辑  收藏  举报