用Shader实现隧道效果

记录分享下最近的学习成果。
LoopSprite
在之前的项目中,遇到了需要类似的伪3D效果的需求,当时的做法是让美术提供隧道单节的图片,然后通过shader对图片进行缩放叠加,但实现过程自己也没理清楚。最近重新学习了白玉大佬的分型着色器和GT大佬的螺旋图,重新把逻辑和这个效果实现了一遍。

主要原理详见GT大佬文章:
https://mp.weixin.qq.com/s/6U7yVVDiD14360JGi7i2ew
白玉无冰分型着色器:
https://mp.weixin.qq.com/s/OuQaI18LwX3Lw7aRcKjDOw
对uv反复转换,映射出新的小图叠加,由于此处内外图片不需要相互拼接,所以把复数C设为1,同心圆旋转角设为0。
原项目中隧道为六边形,故将原圆形遮罩替换为多边形遮罩。

 float mask = Circle(spiralUV, origin, r, blur);
// 替换为
float mask = Polygon(spiralUV, N, Rm, blur);

float Circle(vec2 uv, vec2 o, float r, float blur) {
  return smoothstep(0., blur, distance(uv, o) - r);
}

// n: 多边形边的数量 r: 正多边形内接圆半径
float Polygon(vec2 uv, int n, float r, float blur) {
  // Angle and radius from the current pixel
  float a = atan(uv.x, uv.y) + PI;
  float rad = 2. * PI / float(n);
  // Shaping function that modulate the distance
  float d = cos(floor(0.5 + a / rad) * rad - a) * length(uv);
  return smoothstep(0., blur, d - r);
}

由于没找到合适的图片,就简单画了一个矩形隧道代替图片。

vec4 PolygonColor(vec2 uv, int n, float r, float blur) {
  // Angle and radius from the current pixel
  float a = atan(uv.x, uv.y) + PI;
  float rad = 2. * PI / float(n);
  // Shaping function that modulate the distance
  float d = cos(floor(0.5 + a / rad) * rad - a) * length(uv);
  vec3 color = vec3(0.2, 1., 0.4);
  color *= smoothstep(r - 0.01, r, d) * smoothstep(r + 0.01, r, d);
  color += vec3(0.2, 0., 0.4) * smoothstep(r + 0.02 + blur, r + 0.02, d) * step(0., d - r - 0.02);
  float pct = smoothstep(0., 0.01, abs(abs(uv.x) - abs(uv.y)));
  color = mix(vec3(0.2, 1., 0.4), color, pct);
  return vec4(color, 1.);

此时效果基本实现,但中心区域会存在闪烁的现象,原因不清楚,暂时对中心区域颜色进行由暗到明的处理。
代码详见:
https://github.com/RicardoZhou/CreatorStudy/blob/c06e87242c63866358bb2163ca0d11c230c12dfb/assets/Menu/LoopSprite/Loop.effect

ps:没写过帖子,欢迎各位大佬指点书写技巧,或者代码规范、优化等等,谢谢。

8赞

感觉在玩魂斗罗第二关 :slight_smile:

1赞

这么一说是有点像:grin:

pretty cool