概述
之前是使用OpenGL练习图形学项目的,现在要转向在Unity 3d中练习图像学项目。由此,才有了这一系列的项目。本篇将从比较简单的顶点动画说起,开启图形学的篇章。
原理
主要的原理,就是在顶点着色器中,对顶点进行各种偏移。这种偏移,可以根据自己的需要,在模型空间、世界空间、裁剪空间等空间中进行。
1、压扁效果
开放两个属性,TopY和BottomY,代表物体的上部和底部(世界空间中的数值)。再开放一个滑动属性用来控制顶点运动的幅度。
以TopY为基准,先计算出每个顶点的世界坐标,然后对顶点的Y坐标进行归一化处理。
1 | float GetNormalizeDist(float worldY) |
然后使用Control滑动属性控制相应的顶点进行位移。(在模型空间移动,之后再转到裁剪空间。)
1 | float3 positionWS = TransformObjectToWorld(input.positionOS.xyz); |
2、被门吸收
与上面的压扁效果的原理类似,只是移动的方向变了,同时还要将顶点的世界坐标传递给片元着色器。
1 | float3 positionWS = TransformObjectToWorld(input.positionOS.xyz); |
还要做一些特殊处理,对超过“门”的部分进行\(Clip\)操作,这是通过比较顶点的世界坐标的\(Y\)值和\(TopY\)来判断的。
1 | clip(_TopY - input.positionWS.y); |
3、黑洞吸收
与上面的原理类似,只是顶点的移动方向不再是上面的\(Y\)轴,而是朝向某个“黑洞”点。这里需要开放“黑洞”位置的属性接口。同时,也需要传递顶点的世界坐标给片元着色器。
1 | float3 positionWS = TransformObjectToWorld( .positionOS.xyz); |
在片元着色器中,比较顶点的\(X\)坐标和“黑洞”的\(X\)坐标,进行\(Clip\)操作。
1 | clip(_BlackHolePos. - .positionWS. ); |
4、残影
这里的实现原理是,一个\(Pass\)用来渲染本体,另一个\(Pass\)用来渲染残影。这里有一点需要注意,渲染本体的\(Pass\)的\(LightMode\)是\(UniversalForward\),要想让渲染残影的\(Pass\)也能正常渲染,需要设置该\(Pass\)的\(LightMode\)为\(SRPDefaultUnlit\)。
残影主要有两个实现点,一个是偏离本位,一个是残影自身的抖动。
首先是残影偏离本位,向特定方向整体偏移残影即可。
1 | input.positionOS += _Offset * cos(_Time.y * _ShakeSpeed) * _ShakeDir * _Control; |
接着是残影的抖动。这里的做法是先把顶点坐标的\(X\)放大\(10\)倍,再向下取整,然后求取对于\(2\)的余数,对奇数部分的顶点进行偏移。
1 | float yOffset = 0.5 * (floor(input.positionOS.x * 10) % 2); |
5、折纸
这里的原理是,对顶点以折叠处为中心进行旋转。同时要注意的是,为了同时渲染纸张的正面和背面,需要两个\(Pass\),每个\(Pass\)都要设置正确的\(Cull\)模式。
这里是主要的重新计算折叠后的顶点位置的过程。
1 | float angle = _FoldAngle; |
注意事项
上面的这几个小项目,只是练习用的,在真正用到项目中的时候,可能会有一些问题。在《\(Unity Shader入门精要\)》一书的\(11.3 顶点动画\)一节中,提到了一些注意事项。一个问题是批处理对顶点动画的影响;一个是对有顶点动画的物体添加阴影的问题。这里不再展开,有需要的可以在书中找到答案。
参考
- [1] Unity Shader - 一些玩具Shader
- [2] 《\(Unity Shader入门精要\)》