Cocos Creator 3.8 开发2D水面波纹Shader

发布时间 2024-01-08 16:32:50作者: 游戏开发阿博
使用cocos Creator 3.8做了一个游戏开中常用的2D的波浪水面,把技术点给记录一下,并提供完整的Shader代码。先上效果:
 
添加图片注释,不超过 140 字(可选)
2D 波浪的基本技术原理
2D 水面波纹的主要原理就是给定一个正选波的边界,在范围内的片元uv就显示,在范围外的片元uv就不显示。同时利用正弦波表达式:
y = A * sin(w * t+offset)
将片元的uv坐标uv.x 带入上面的公式offset,算出y0值。Cocos 的uv 左上角为(0, 0),我们要在图片的上方做水波纹,那么片元的uv.y < y0的片元就丢弃, uv.y >= y0的就正常显示。
对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。
 
添加图片注释,不超过 140 字(可选)
为了让水波能动起来,我们就要不断的改变同一个位置offset, 对应的y0的值,那么我们加一个 w * t, 角速度*时间。这样随着时间的推移同一个uv(x, y),会得到不同的y0的值。有了不同的边界,这样就会决定不同的片元的显示和丢弃。
具体编写Shader
第001步: 新建一个标准2D Shader 模板,能正常显示图片
Cocos Creator的版本不一样,可能导致以前编写的Shader无法正常运行,这种其实很好解决,我们在开发Shader的时候,基于现在Cocos Creator的版本先创建一个现有的模板出来,然后再将关键代码移植过去。我们做一个2D的Shader,所以复制一个buildin-sprite的Shader模板。然后创建一个材质选好我们新创建的Shader文件,将材质设置到Sprite组件上,能正常显示图片。
 
添加图片注释,不超过 140 字(可选)
第002步: 定义好uniform给用户的控制参数:
baseLine: 基准线,该参数用于控制波浪在图片的位置(波形的基准线);
A: 振幅,该参数用于控制波浪线的高度;
wSpeed:角速度,该参数用来控制一个波形的长度;
 
添加图片注释,不超过 140 字(可选)
第003步: 修改Frag片元着色的Shader代码
完成基准线功能,编写代码基准线以上的片元直接丢弃,基准线一下的片元保留,代码如下:
float value = 0;
if(uv0.y < baseLine + value) {
discard;
}
这样基准线baseLine以上的,就不显示,baseLine以下的显示。
第004步:基于A*sin(offset)来将基准线做正弦波处理
基准线确定好后,还需给基准线加上正弦波,offset为uv.x, value = A*sin(offset), baseLine + value, 这样就把baseLine做成了正弦波的分界线。
float value = A * sin(uv0.x * 30.0);
if(uv0.y < baseLine + value) {
discard;
}
第005步:让正弦波动起来
为了让正弦波动起来,我们在sin加上w*t, 在cocos ccreator里面cc_time是一个vec4的向量, cc_time.x是从启动后到现在的时间,随着时间不断地变化,value = A * sin(uv0.x * 30.0 + wSpeed * cc_time.x); value地值也成周期变化,这样波形就动起来了。
float value = A * sin(uv0.x * 30.0 + wSpeed * cc_time.x);
if(uv0.y < baseLine + value) {
discard;
}
完整Shader代码
最后上代码(Cocos Creator 3.8.0):
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
- passes:
- vert: sprite-vs:vert
frag: sprite-fs:frag
depthStencilState:
depthTest: false
depthWrite: false
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
blendDstAlpha: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
alphaThreshold: { value: 0.5 }
baseLine: { value: 0.5}
wSpeed: { value: 10.0 }
A: { value: 0.01}
}%
CCProgram sprite-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#if USE_LOCAL
#include <builtin/uniforms/cc-local>
#endif
#if SAMPLE_FROM_RT
#include <common/common-define>
#endif
in vec3 a_position;
in vec2 a_texCoord;
in vec4 a_color;
out vec4 color;
out vec2 uv0;
vec4 vert () {
vec4 pos = vec4(a_position, 1);
#if USE_LOCAL
pos = cc_matWorld * pos;
#endif
#if USE_PIXEL_ALIGNMENT
pos = cc_matView * pos;
pos.xyz = floor(pos.xyz);
pos = cc_matProj * pos;
#else
pos = cc_matViewProj * pos;
#endif
uv0 = a_texCoord;
#if SAMPLE_FROM_RT
CC_HANDLE_RT_SAMPLE_FLIP(uv0);
#endif
color = a_color;
return pos;
}
}%
CCProgram sprite-fs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#include <builtin/internal/embedded-alpha>
#include <builtin/internal/alpha-test>
in vec4 color;
#if USE_TEXTURE
in vec2 uv0;
#pragma builtin(local)
layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture;
#endif
uniform MyUniform {
float baseLine;
float wSpeed;
float A;
};
vec4 frag () {
vec4 o = vec4(1, 1, 1, 1);
#if USE_TEXTURE
o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
#endif
float value = A * sin(uv0.x * 30.0 + wSpeed * cc_time.x);
if(uv0.y < baseLine + value) {
discard;
}
o *= color;
return o;
}
}%