02护盾效果
02护盾效果
本节主要记录一下,教程中护盾效果。其基础原理很简单,就是圆形几何运算。但其亮块区域的实现则有很多细节。
基础效果
最基础的效果,只要绘制一个过渡的圆即可。绘制圆只要结合distance和step函数即可。绘制过渡范围则需要结合使用smoothstep函数。具体来说计算如下
void fragment(){
// 坐标中心化 移动到中心为(0,0)
float radius_dis = distance(UV,vec2(0.5,0.5));
// 截出中心圆区域 半径为circle_radius
float circle = step(radius_dis,circle_radius);
// 用smoothStep做出 内径到外径的过渡数值变化
// smoothstep(a,b,c)将c的值按照[a,b]区间插值映射到[0,1]上
float circle_inner = smoothstep(circle_inner_start,1,radius_dis);
// 因为smoothstep在圈外部分给出 1 所以要用圆形范围截取一下
float circle_main_shape = circle*circle_inner;
}这段渲染效果如图

其中圆形范围筛选非常易于理解。smoothstep(a,b,c)根据官方文档相当于进行如下计算
t = clamp((c - a) / (b - a), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);https://registry.khronos.org/OpenGL-Refpages/gl4/html/smoothstep.xhtml
简单来说就是两步:一,线性映射从$[a,b]$区间映射到$[0,1]$上。二,用一个Hermite插值调整这个映射值,使其两端更平滑。
总体来说,就是期望在沿着圆的半径方向上在距离$[a,b]$区间逐步从$0$过渡到$1$。而在$a$以内,$b$以外的区域都为0。
对于smoothstep需要注意一点是。根据公式当$c=b$时,映射值必为$1$。当$c=a$时,映射值必为$0$。换句话说,如果$b\gt a$,那么从$a \to b$变化过程中会是从$0 \to 1$。如果$b\lt a$,那么从$a \to b$变化过程中会是从$1 \to 0$。也就是这两个映射图形大概可以用下图表示

还需要注意一点的是,对于代码来说,并不只有$c$一个自变量控制,实际上smoothstep(a,b,c)三个参数都可以动态控制,例如都随着时间变化。对应的都会有不同的表现效果。
剩下的制作圆环效果就比较简单:
- 给圆环添加颜色
只需用上面筛选好的范围值直接跟颜色相乘即可
uniform vec3 main_color : source_color;
vec3 shield_color = circle_main_shape*main_color;- 添加噪点流动
给一张噪音贴图,采样贴图并且添加UV随时间流动即可
uniform sampler2D noise : repeat_enable;
float texture_alpha = texture(noise,UV+vec2(TIME*0.1,0)).r;高光
最后一步,给圆环添加高光亮点。即有亮光爆闪一下的效果。这里其实就有很多实现制作细节方式。
- 方案1
给定一个点位置$(x,y)$,该点绘制一个带过渡的高亮圆形区域。把该区域跟前面的过渡圆形区域叠加即可。这也是视频中的方案。
其制作方式如下
float PointCircleColor(vec2 uv)
{
// 高光点 圆形范围筛选
float highlight_radius = distance(highlight_point,uv);
// smoothstep 过渡筛选出圆形区域 圆的边界就是第一个参数
float highlight_shape = smoothstep(1.0,0.0,highlight_radius);
// 通过幂函数 和 乘法 提亮
highlight_shape = pow(highlight_shape,highlight_power)*highlight_intensity;
return highlight_shape;
}这个方式简单直接,而且说实话,效果很不错。相当于一个曝光点,照亮了整个护盾。
但其有一些不方便点,一是需要给出一个高光点位置,也就是说每次要设置一个点,这个点距离圆形的相对位置要提前估计才能估计大概高亮效果。而且如果这个点位置非常远实际是看不到效果的。
不过这个可以通过参数设置方式来约束,例如采用极坐标即可。
- 方案2
给定一个角度值$\theta$,和一个范围值$\alpha$。在$(\theta-\alpha,\theta+\alpha)$区间范围内的区域进行一个亮度加强。
这个方案有一个关键点,就是筛选某个角度区域值。而这里就是对三角函数的应用,实际上后续教程中对三角函数部分也有涉及讲解。对于角度的判断依赖于反正切函数,即atan(y,x)函数。
https://registry.khronos.org/OpenGL-Refpages/gl4/html/atan.xhtml
官方文档中,其相当于给出$\arctan(y/x)$,但同时会使用$y,x$的符号来判断落在什么象限。所以其值域范围比标准的$\arctan$的函数要大,从$[-\pi,\pi]$。其范围映射值可以简单用下图来表示:

- 当$y\gt 0$,给出$0\to\pi$范围。
- 当$y\lt 0$,给出$-\pi\to 0$范围。
- 当$x\lt 0,y=0$时给出$\pi$,当$x\gt 0,y=0$时给出$0$。
所以直接将中心化UV传入后可以得如下图形:

这便是直接将点得角度作为alpha值赋值后结果。由于godot中心化后左上角坐标为$(-0.5,-0.5)$,右下角坐标为$(0.5,0.5)$。可以看到左下角区域整体为白色,沿着逆时针方向逐渐由白入黑,这正对应着角度值由$\pi \to -\pi$过程。其中左下角区域$y\gt 0,x\lt 0$正好对应$(\pi/2,\pi)$区间。所以可以清晰看到角度得变化过程,跟上面角度图的变化过程对应,恰好是上下颠倒。
下一步则是筛选出落在给定角度$(\theta-\alpha,\theta+\alpha)$内的值。
直接的想法似乎是判断一下目前点的角度$a=atan(y,x)$是否在区间$(\theta-\alpha,\theta+\alpha)$内即可。但是这样实现,会立即发现某些角度下,存在切边情况,有些边界情况并不能算出准确邻域。
例如给定$\theta=\pi,\alpha=0.1$。那么区间范围为$(\pi-0.1,\pi+0.1)$,这对应圆中最左侧一个夹角。但在图最左侧,实际存在跳变,下侧为$\pi$,上侧为$-\pi$。负数区间部分必然不在区间$(\pi-0.1,\pi+0.1)$内,于是判定为false而设置为0。
想到复分析中圆角度的变化跟黎曼曲面关系,这里需要一个周期变化函数来描述。而这个周期函数就是用去余函数,即mod函数。
https://registry.khronos.org/OpenGL-Refpages/gl4/html/mod.xhtml

这个函数带有先天的周期性质,其刚好跟周期跳变函数相对应。考虑其增长方向,恰好象征着沿着圆方向移动时的角度增长,每到边界边跳变一次。所以可以如下判定给定的值是否在区间内。给定一个圆角度区间例如$(\theta-\alpha,\theta+\alpha)$,给定一个数值$x$。
首先计算$y=\text{mod}(x-(\theta-\alpha),2\pi)$。相当于计算在$2\pi$区间内,数值$x$距离底边的角度距离。若该点在角度区间$(\theta-\alpha,\theta+\alpha)$内,相当于$y \lt 2\alpha$。由此可以判断精确的圆上邻域。由此可准确判定出朝某个方向角度邻域范围,如下图:

实际上带有周期性质的东西,很多都会用到mod函数。或者是类似函数结构。