CocosCreator1.x实现水流动的效果
Cocos Creator版本:1.10.2
运行结果:(H5和原生都支持)
场景:
脚本:
HelloWorld.js:
let shader = require(`shader`);
cc.Class({
extends: cc.Component,
properties: {
water: cc.Node,
waterNormalMap: cc.SpriteFrame,
},
onLoad() {
// 加载所有着色器程序
shader.loadShaderPrograms();
// 初始化水的着色器程序
this._time = 0;
this._waterSpeed = 0.1;
shader.setGLProgramForNode(`water`, this.water);
shader.use(`water`);
shader.setUniformTexture(`water`, `normalMap`, this.waterNormalMap.getTexture());
shader.setUniformFloat(`water`, `v_offset`, 0);
},
update(dt) {
// 更新水的着色器程序
this._time += dt;
shader.use(`water`);
shader.setUniformFloat(`water`, `v_offset`, (this._time * this._waterSpeed) % 1);
},
});
shader.js
let WaterShader = require(`WaterShader`);
module.exports = {
// 加载所有的着色器程序
loadShaderPrograms() {
this.createGLProgram(`water`, WaterShader.vert, WaterShader.frag);
},
///////////////////////////////////////////////
// 存放所有着色器程序
gLProgramDict: {},
// 创建一个着色器程序
createGLProgram(glProgramName, vertStr, fragStr) {
let glProgram = this.gLProgramDict[glProgramName];
if (glProgram) {
cc.error(`${glProgramName}这个glProgram已经注册过了!!!`);
return;
}
glProgram = new cc.GLProgram();
if (cc.sys.isNative) {
glProgram.initWithString(vertStr, fragStr);
} else {
glProgram.initWithVertexShaderByteArray(vertStr, fragStr);
glProgram.addAttribute(cc.macro.ATTRIBUTE_NAME_POSITION, cc.macro.VERTEX_ATTRIB_POSITION);
glProgram.addAttribute(cc.macro.ATTRIBUTE_NAME_COLOR, cc.macro.VERTEX_ATTRIB_COLOR);
glProgram.addAttribute(cc.macro.ATTRIBUTE_NAME_TEX_COORD, cc.macro.VERTEX_ATTRIB_TEX_COORDS);
}
glProgram.link();
glProgram.updateUniforms();
glProgram.use();
this.gLProgramDict[glProgramName] = glProgram;
},
// 设置指定的着色器程序于指定node上
setGLProgramForNode(glProgramName, node) {
let glProgram = this.gLProgramDict[glProgramName];
if (!glProgram) {
cc.error(`${glProgramName}这个glProgram未注册!!!`);
return;
}
let sprite = node.getComponent(cc.Sprite);
if (!sprite) {
cc.error(`这个node没有Sprite组件啊!!!`);
return;
}
let sgNode = sprite._sgNode;
if (cc.sys.isNative) {
let glProgramState = cc.GLProgramState.getOrCreateWithGLProgram(glProgram);
sgNode.setGLProgramState(glProgramState);
} else {
sgNode.setShaderProgram(glProgram);
}
},
// 应用指定着色器程序
use(glProgramName) {
let glProgram = this.gLProgramDict[glProgramName];
if (!glProgram) {
cc.error(`${glProgramName}这个glProgram未注册!!!`);
return;
}
glProgram.use();
},
// 为指定着色器程序设置uniform值
setUniformFloat(glProgramName, uniformName, uniformValue) {
if (typeof uniformValue !== `number`) {
cc.error(`setUniformTexture 输入的值不是数字类型 @`, glProgramName, uniformName);
return;
}
this.setUniformCommon(glProgramName, uniformName, uniformValue, `setUniformFloat`, `setUniformLocationWith1f`);
},
setUniformTexture(glProgramName, uniformName, uniformValue) {
if (!(uniformValue instanceof cc.Texture2D)) {
cc.error(`setUniformTexture 输入的值不是纹理类型 @`, glProgramName, uniformName);
return;
}
if (cc.sys.isNative) {
this.setUniformCommon(glProgramName, uniformName, uniformValue, `setUniformTexture`, `setUniformLocationWith1f`);
} else {
let glProgram = this.gLProgramDict[glProgramName];
if (!glProgram) {
cc.error(`${glProgramName}这个glProgram未注册!!!`);
return;
}
let uniformLocation = glProgram.getUniformLocationForName(uniformName);
glProgram.setUniformLocationWith1i(uniformLocation, 1);
cc.gl.bindTexture2DN(1, uniformValue);
}
},
setUniformCommon(glProgramName, uniformName, uniformValue, jsbFnName, h5fnName) {
let glProgram = this.gLProgramDict[glProgramName];
if (!glProgram) {
cc.error(`${glProgramName}这个glProgram未注册!!!`);
return;
}
if (cc.sys.isNative) {
let glProgramState = cc.GLProgramState.getOrCreateWithGLProgram(glProgram);
glProgramState[jsbFnName](uniformName, uniformValue);
} else {
let uniformLocation = glProgram.getUniformLocationForName(uniformName);
glProgram[h5fnName](uniformLocation, uniformValue);
}
}
};
WaterShader.js
module.exports = {
vert: `
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
varying vec2 v_texCoord;
void main()
{
gl_Position = CC_PMatrix * a_position;
v_texCoord = a_texCoord;
}
`,
frag: `
varying vec2 v_texCoord;
uniform sampler2D normalMap; // 法线贴图
uniform float v_offset; // 法线贴图v坐标偏移
// 纠正纹理坐标 (当采样坐标超出范围时,从另一边出来,比如 1.1纠正为0.1,-0.1纠正为0.9 等)
vec2 get_fixuv_by_uv(vec2 uv) {
if (uv.x > 1.0) {
uv.x -= 1.0;
}
if (uv.x < 0.0) {
uv.x += 1.0;
}
if (uv.y > 1.0) {
uv.y -= 1.0;
}
if (uv.y < 0.0) {
uv.y += 1.0;
}
return uv;
}
// 获取法向
vec3 get_normal() {
vec2 uv = v_texCoord + vec2(0, v_offset);
uv = get_fixuv_by_uv(uv);
return normalize(texture2D(normalMap, uv).xyz * 2.0 - 1.0);
}
// 根据 折射方向 获取新的纹理坐标
vec2 get_uv_by_refract(vec3 refractVec) {
vec2 uv = v_texCoord + vec2(refractVec) * 0.5;
return uv;
}
void main() {
// 视点位置
vec3 eyePos = vec3(0.5, 0.5, 10.0);
// 入射方向
vec3 inVec = normalize(vec3(v_texCoord, 0.0) - eyePos);
// 获取法线
vec3 normal = get_normal();
// 根据 入射方向,法线,折射率 获取折射方向
vec3 refractVec = refract(inVec, normal, 0.7);
// 根据 折射方向 获取新的纹理坐标
vec2 v_texCoord2 = get_uv_by_refract(refractVec);
// 最终颜色
gl_FragColor = texture2D(CC_Texture0, v_texCoord2);
}
`,
};