本部分案例将对正方体中的顶点位置进行变换以实现软糖扭动的效果。
一、基本原理
介绍本案例的具体开发之前首先需要了解实现软糖扭动的基本原理,具体情况如下图所示:
扭曲线框图:
从扭曲线框图中可以看出,模型实际上是由很多层小矩形叠加而成。在同一帧中,随着 y 坐标的不断升高,此层的顶点绕中心轴扭曲的角度越大。因此,实现扭动软糖的效果只要将代表软糖的长方体中各层顶点的 x、 z 坐标按照一定的规则根据顶点的 y 坐标以及当前帧的控制参数进行变换即可,具体的计算思路如(扭曲原理图1)、(扭曲原理图2)与(向量旋转图) 所示。
扭曲原理图1:
扭曲原理图2:
向量旋转图:
具体的计算步骤如下:
- 首先如(扭曲原理图1)所示,需要计算出当前顶点 y 坐标与最下层顶点 y 坐标的差值。
- 接着根据 y 坐标的差值、角度换算比例以及本帧的总扭曲角度换算出当前顶点的扭曲角度,计算公式为: currAngle=(currY-yStart)/ySpan×angleSpan。
- 最后根据当前顶点的 x、 z 坐标以及扭曲的角度计算出变换后顶点的 x、 z 坐标,此步的计算思路如(扭曲原理图2)与(向量旋转图)所示。
从(扭曲原理图2)与(向量旋转图)中可以看出,将顶点绕中心轴扭曲(旋转)实际上可以看成是将从中心点出发到变换前顶点的向量旋转指定的角度。旋转后得到的新向量的终点位置即所求的变换后顶点的位置,因此具体的计算公式如下。
- x’=xcosα-zsinα
- z’=xsinα+zcosα
上述公式中的α为需要旋转(扭曲)的角度,实际计算时采用前面步骤计算出来的变量 currAngle 的值即可。
二、开发实现.
本场景实现了两个正方体来处理不同的参数,基础代码不在赘述,直接来看顶点着色器实现:
#version 450
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec2 inUV;
layout (location = 3) in vec3 inColor;
layout (set = 0, binding = 0) uniform UBOMatrices {
mat4 projection;
mat4 view;
mat4 model;
float angleSpan;//本帧扭曲总角度
float yStart;//Y坐标起始点
float ySpan;//Y坐标总跨度
} uboMatrices;
layout (location = 0) out vec3 outNormal;
layout (location = 1) out vec3 outColor;
layout (location = 2) out vec2 outUV;
out gl_PerVertex {
vec4 gl_Position;
};
void main()
{
outNormal = inNormal;
outColor = inColor;
outUV = inUV;
float angleSpan = uboMatrices.angleSpan;//本帧扭曲总角度
float yStart = uboMatrices.yStart;//Y坐标起始点
float ySpan = uboMatrices.ySpan;//Y坐标总跨度
float tempAS = angleSpan*(inPos.y-yStart)/ySpan;//计算当前顶点扭动(绕中心点选择)的角度
vec3 tPosition = inPos;
//若不是最下面一层的顶点则计算扭动后的X、Z坐标
if(inPos.y>yStart)
{
tPosition.x = (cos(tempAS)*inPos.x-sin(tempAS)*inPos.z);
tPosition.z = (sin(tempAS)*inPos.x+cos(tempAS)*inPos.z);
}
gl_Position = uboMatrices.projection * uboMatrices.view * uboMatrices.model * vec4(tPosition.xyz, 1.0);
}
在shader外部,我们在RenderLoop中循环控制了初始扭曲总角度和Y坐标起点和总跨度来控制扭曲效果,编译运行后可见文章顶部的效果。