乐趣区

关于游戏开发:用-Shader-写个完美的波浪

前言

皮皮最近接到了一个小需要:

???? 美术小姐姐:皮皮皮皮,你能不能做奶茶?

???? 我:???

???? 美术小姐姐:就是那种,奶茶的轮廓加上动静水波纹~

???? 我:吓死我还认为让我做喝的奶茶 …

???? 美术小姐姐:炒鸡多图片都须要这种成果,用动画的话工作量太大了!

???? 我:波浪成果是吧,小意思,一个月的奶茶就够了,或者扫码提需要~

???? 美术小姐姐:皮?????????????

???? 我:卒~

俗话说:遇事不决,量子力学 写虽得儿。

依据我多年喝奶茶的教训,像这种成果用 Shader 做就再简略不过了,最终的成果如下:

趁此机会,本篇文章就来与小伙伴们分享动静波浪 Shader 的原理和制作思路吧。

要留神的是,这是一篇偏入门的文章,写得会绝对比拟具体,尽量让不懂 Shader 的小白也能够看懂,这也是我写文章的一贯格调。

好了,话不多说,进入正题~


注释

???? 整体思路

看到波浪的体现特点我第一工夫想到的就是正弦曲线(或者说是正弦波,又让我想起了示波器)。

???? 正弦曲线(Sinusoid)

正弦曲线 是三角函数中的一种正弦(Sine)比例的曲线。正弦曲线体现为一条波浪线,形态犹如海上完满的波浪。

规范的正弦函数公式为:

y = sin(x)

正弦函数属于周期函数,其值域为 [-1, 1]

如下图就是一个纯正规范的正弦曲线:

而个别咱们罕用的正弦曲线公式为:

y = A · sin(ωx ± φ) + k

这条公式比规范公式多了几个常数,含意如下:

  • A振幅(Amplitude),曲线最高点与最低点的差值,体现为曲线的整体高度
  • ω角速度(Angular Velocity),管制曲线的周期,体现为曲线的严密水平
  • φ初相(Initial Phase),即当 x = 0 时的相位,体现为曲线在坐标系上的程度地位
  • k偏距(Offset),体现为曲线在坐标系上的垂直地位

相位(Phase):上方公式中的 ωx±φ 局部称为相位,相位产生在周期性的静止之中,最间接的了解就是角度。

☕稍加考虑

有了公式之后,咱们能够尝试调整其中的常数来扭转函数曲线的状态。

在查看下方的示例时,请尝试将 曲线状态的变动 图中右上角公式的变动 关联起来。

扭转曲线的高度

咱们能够调整常数 A(振幅)来扭转曲线的值域(值域为 [-A, A]):

扭转曲线的周期

咱们能够调整常数 ω(角速度)来扭转曲线的周期:

扭转曲线的程度地位

咱们能够调整常数 φ(初相)来扭转曲线的程度地位:

多说一句

其实对于“曲线的程度地位”这个形容是不太精确的,因为初相实际上扭转的是当 x = 0 时的相位,也就间接影响函数曲线在 x = 0 处的地位。

所以说曲线的地位并没有真正扭转,而只是曲线的状态产生了扭转。

然而因为正弦曲线的周期性特点,曲线的这种状态变动看起来像是曲线进行了位移。

扭转曲线的垂直地位

咱们能够调整常数 k(偏距)来扭转曲线的垂直地位:

???? 入手实现

明确了正弦曲线的个性之后,接下来咱们须要做的就是在代码中使用正弦函数。

慢着!正弦曲线的确如海上完满的波浪般柔美,然而正弦曲线是动态的,咱们要的波浪是动静的啊!

???? 如何让曲线动起来

别慌!还记得咱们能够调整 初相 来扭转曲线的“程度地位”吗?

既然如此,咱们能够给初相退出 工夫因素 ,使得 y 值能够随着工夫的减少产生 周期性变动,看起来就像是曲线在进行“程度位移”。

就像这样:

失去新的公式

退出工夫因素 t 后的曲线公式:

y = A · sin(ωx ± φt) + k

????On Shadertoy

小贴士:因为 GLSL ES 没有方法进行调试,所以写 Shader 时能够先在 Shadertoy 中编写并在线预览,显著提高效率。

所有尽在正文中,简略具体且直观。

主函数代码如下:

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{// 将像素坐标归一化(区间 [0.0, 1.0])// iResolution 是 Shadertoy 提供的视口分辨率全局变量(类型:vec3)vec2 uv = fragCoord / iResolution.xy;
    
    // 振幅(管制波浪顶端和底端的高度)float amplitude = 0.05;
    
    // 角速度(管制波浪的周期)float angularVelocity = 10.0;
    
    // 频率(管制波浪挪动的速度)float frequency = 10.0;
    
    // 偏距(设为 0.5 使得波浪垂直居中于屏幕)float offset = 0.5;
    
    // 初相位(正值体现为向左挪动,负值则体现为向右挪动)// iTime 是 Shadertoy 提供的运行工夫全局变量(类型:float)float initialPhase = frequency * iTime;
    
    // 代入正弦曲线公式计算 y 值
    // y = Asin(ωx ± φt) + k
    float y = amplitude * sin((angularVelocity * uv.x) + initialPhase) + offset;
    
    // 辨别 y 值高低局部,设置不同色彩
    vec4 color = uv.y > y ? vec4(0.0, 0.0, 0.0, 1.0) : vec4(0.0, 0.7, 0.9, 1.0);
    
    // 输入到屏幕
    fragColor = color;
}

预览成果如下(???? 是不是有内味儿了):

在线预览:https://www.shadertoy.com/view/ttSfRh

????On Cocos Creator

咱们次要关注片段着色器局部,这里就不展现整个 Effect 文件的代码了,间接上传送门吧。

Effect 文件:https://gitee.com/ifaswind/eazax-ccc/blob/master/resources/effects/eazax-sine-wave.effect

代码外围其实就是套用了公式,咱们代码正文一起看吧。

所有尽在正文中,简略具体且直观。

片段着色器代码如下:

CCProgram fs %{
  precision highp float;

  // 引入 Cocos Creator 内置的全副变量
  #include <cc-global>
  
  // 顶点色彩(来自顶点着色器)in vec4 v_color;
  // UV 坐标(来自顶点着色器)in vec2 v_uv0;

  // 纹理
  uniform sampler2D texture;  

  // 自定义属性
  uniform Properties {
    float amplitude;        // 振幅
    float angularVelocity;  // 角速度
    float frequency;        // 频率
    float offset;           // 偏距
  };

  void main () {
    // 保留顶点色彩
    vec4 color = v_color;
    
    // 叠加纹理色彩
    color *= texture(texture, v_uv0);
    
    // 间接抛弃本来就通明的像素
    if (color.a == 0.0) discard;
    
    // 初相位(正值体现为向左挪动,负值则体现为向右挪动)// cc_time 是 Cocos Creator 提供的运行工夫全局变量(类型:vec4)float initiaPhase = frequency * cc_time.x;
    
    // 代入正弦曲线公式计算 y 值
    // y = Asin(ωx ± φt) + k
    float y = amplitude * sin(angularVelocity * v_uv0.x + initiaPhase) + offset;
    
    // 抛弃 y 值以上的像素(左上角为原点 [0.0, 0.0])if (v_uv0.y < y) discard;
    
    // 输入色彩
    gl_FragColor = color;
  }
}%

运行成果如下:

应用 cc.tween 动静扭转高度(偏距)实现波浪进度条:

cc.tween(this.sineWave)
    .to(3, { height: 1})
    .to(0.5, { amplitude: 0})
    .start();

在线预览:https://ifaswind.gitee.io/eazax-cases?case=sineWave

SineWave 组件:https://gitee.com/ifaswind/eazax-ccc/blob/master/components/effects/SineWave.ts


专题

《一起学 Shader》这个专题断更了一段时间,很对不起小伙伴们,是时候续上了????

《一起学 Shader》

《Shader 入门:GLSL ES(简介和根本语法)》

《Shader 入门:GLSL ES(数据类型)》

《Shader 入门:GLSL ES(运算符和限定符)》


传送门

微信推文版本

集体博客:菜鸟小栈

开源主页:陈皮皮

Eazax-CCC 游戏开发脚手架

Eazax-CCC 示例在线预览


更多分享

《为什么抉择应用 TypeScript?》

《高斯含糊 Shader》

《一文看懂 YAML》

《Cocos Creator 性能优化:DrawCall》

《互联网经营术语扫盲》

《在 Cocos Creator 里画个炫酷的雷达图》


公众号

???? 菜鸟小栈

???? 我是陈皮皮,这是我的集体公众号,专一但不仅限于游戏开发、前端和后端技术记录与分享。

???? 每一篇原创都十分用心,你的关注就是我原创的能源!

Input and output.

退出移动版