先来看最终效果:
实现过程
本实例是基于cocoscreator2.4.8 创建一个场景roadScene.fire,在准备一张左右两边为透明像素的图片road.png, 图片的大小,要由项目情况而定,这里只是拿来做例子
由于packable 将图片设置为可动态合图,会使 Shader 中的 uv 是图集 uv,将导致计算错误),因此图片【属性检查器】中 packable 勾选状态为【取消】并应用。
将图片放到场景中:
这是正常默认shader显示的效果,显然不符合我们的要求。
这里需要自定义 Shader。因此我们右键创建一个 Shader,命名为 shaderRoad
为了使用此Shader,这里需要再创建一个材质并命名为matRoad
在 matRoad 的属性检查器中,将 Effect 属性设为 shaderRoad ,texture 属性设置为 iRoad 并应用。
将场景中道路节点的 Sprite 组件中的材质属性更改为 matRoad:
此时,我们成功使用了自定义的shader和材质来渲染Road精灵对象
编写shaderRoad.effect,代码如下:
CCEffect %{
techniques:
- passes:
- vert: vs
frag: fs
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties:
texture: { value: white }
alphaThreshold: { value: 0.5 }
x0: { value: 0.48 }
y0: { value: 0.8 }
dy: { value: 0.0 }
}%
CCProgram vs %{
precision highp float;
#include <cc-global>
#include <cc-local>
in vec3 a_position;
in vec4 a_color;
out vec4 v_color;
#if USE_TEXTURE
in vec2 a_uv0;
out vec2 v_uv0;
#endif
void main () {
vec4 pos = vec4(a_position, 1);
#if CC_USE_MODEL
pos = cc_matViewProj * cc_matWorld * pos;
#else
pos = cc_matViewProj * pos;
#endif
#if USE_TEXTURE
v_uv0 = a_uv0;
#endif
v_color = a_color;
gl_Position = pos;
}
}%
CCProgram fs %{
precision highp float;
#include <cc-global>
#include <alpha-test>
#include <texture>
in vec4 v_color;
uniform NumasConstant {
float x0;
float y0;
float dy;
};
#if USE_TEXTURE
in vec2 v_uv0;
uniform sampler2D texture;
#endif
void main () {
vec4 o = vec4(1, 1, 1, 1);
// 目标,重新计算 v_uv0 中的采样位置即可
vec2 uv = vec2(v_uv0.x, v_uv0.y);
if(uv.x <= 0.5) {
// 左侧边界的直线方程为 y = y0- (y0/x0)*x; x0 取值范围为 (0, 0.5) y0 取值范围为 (0, 正无穷)
// 计算当前渲染点对应左侧路边缘(直线)的值 s0
float s0 = - x0/y0 *uv.y + x0;
if(uv.x < s0) {
uv.x = 0.0;
}else{
uv.x = 0.5 * (uv.x - s0)/(0.5 - s0);
}
}else{
// 右侧边界的直线方程为 x = (1-x0) + x0*y/y0
float s0 = 1.0 - x0 + uv.y * x0/y0;
if(uv.x > s0) {
uv.x = 1.0;
}else{
uv.x = 0.5 * (s0 - uv.x)/(s0 - 0.5);
}
}
// 计算 v
// 保持 v 采样空间在 [0, 1]
uv.y = uv.y - dy;
if(uv.y <= 0.0) {
uv.y += 1.0;
}
#if USE_TEXTURE
CCTexture(texture, uv, o);
#endif
o *= v_color;
ALPHA_TEST(o);
gl_FragColor = o;
}
}%
创建Road组件,新建Road.ts
const {ccclass, property} = cc._decorator;
@ccclass
export default class Road extends cc.Component {
private dy: number = 0;
@property({type:cc.Sprite, visible:true}) private _sprite: cc.Sprite = null;
start () {
}
update (dt) {
this.dy += 0.01;
if(this.dy > 1) {
this.dy -= 1;
}
let _material = this._sprite.getMaterial(0);
_material.setProperty("dy", this.dy);
}
}
最后将Road.ts挂在场景中,设置_sprite,点击运行,就可以看到开头的效果了