cocos creator 1.x 中使用自定义shader

为了减少游戏资源,游戏中免不了会使用一些shader,下面是一个将图片变灰shader,以此来看看如何在cocos creator中使用和管理shader。

shader文件管理

在assets中新建一个文件夹resources,然后在resources中新建一个Shader文件夹。将所shader文件放在Shader文件夹中。

现在在Shader文件夹中新建两个文件gray.vert.js和gray.frag.js,内容如下:

// gray.vert.js
module.exports =
`
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
varying vec4 v_fragmentColor; 
varying vec2 v_texCoord; 
void main() 
{ 
	gl_Position = CC_PMatrix * a_position;
	v_fragmentColor = a_color; 
	v_texCoord = a_texCoord; 
}
`

// gray.frag.js
module.exports =
`
#ifdef GL_ES
precision lowp float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
	vec4 c = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
	gl_FragColor.xyz = vec3(0.2126*c.r + 0.7152*c.g + 0.0722*c.b);
	gl_FragColor.w = c.w;
}
`

shader使用

为了方便使用,写一个ShaderUtils,使用时只需要传入修改shader的Sprite和shader的名字。

// ShaderUtils.js
var ShaderUtils = {
	shaderPrograms: {},

	setShader: function(sprite, shaderName) {
		var glProgram = this.shaderPrograms[shaderName];
		if (!glProgram) {
			glProgram = new cc.GLProgram();
			var vert = require(cc.js.formatStr("%s.vert", shaderName));
			var frag = require(cc.js.formatStr("%s.frag", shaderName));
			glProgram.initWithString(vert, frag);
			if (!cc.sys.isNative) {  
				glProgram.initWithVertexShaderByteArray(vert, frag);
				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();
			this.shaderPrograms[shaderName] = glProgram;
		}
		sprite._sgNode.setShaderProgram(glProgram);
		return glProgram;
	},
};

module.exports = ShaderUtils;

为了避免同一个shader程序多次创建,这里做了一个缓冲,若是已有的就不再创建,直接使用。

方便对比,在HelloWorld的场景中再加一个Sprite,绑定到spGray,在spGray上使用上面写的shader。

// HelloWorld.js
var ShaderUtils = require("ShaderUtils");


cc.Class({
	extends: cc.Component,

	properties: {
		spGray: cc.Sprite
	},

	onLoad: function () {
		ShaderUtils.setShader(this.spGray, "gray");
	},

	update: function (dt) {

	},
});

效果如图:

效果图

spine上使用shader

有人在论坛里问,怎么把shader用在脊骨上,这里补充下。

var ShaderUtils = require("ShaderUtils");

cc.Class({
    extends: cc.Component,

    properties: {
        sp: sp.Skeleton
    },

    onLoad: function () {
        ShaderUtils.setShader(this.sp, "gray");
    },
});

如图,下编辑器中加入龙骨,骨骼动画直接用的官方实例。

这里写图片描述

运行效果:

这里写图片描述


2018,06.25更新

异步方法

之前的方法是把shader放在js文件中,好处是可以同步加载;不好的地方就是,没法使用通用的glsl文件,书写时无法利于ide对glsl语言的语法提示,下面是扩展了一种异步的加载方式,可以使用标准的glsl的shader文件。

通用的glsl shader 文件:

attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{ 
	gl_Position = CC_PMatrix * a_position;
	v_fragmentColor = a_color;
	v_texCoord = a_texCoord;
}

#ifdef GL_ES
precision lowp float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
	vec4 c = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
	gl_FragColor.xyz = vec3(0.2126*c.r + 0.7152*c.g + 0.0722*c.b);
	gl_FragColor.w = c.w;
}

扩展之后的ShaderUtils文件:

/**
 * used to change the shader of sprite
 * e.g. ShaderUtils.useShader(cc.Sprite, shader name);
 */

var ShaderUtils = {
	shaderMap: {}, // shader string map
	shaderPrograms: {},	//shader propgram map

	getGlPropgram: function(vert, frag) {
		var glProgram = new cc.GLProgram();
		glProgram.initWithString(vert, frag);
		if (!cc.sys.isNative) {  
			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();
		return glProgram;
	},

	// sync
	setShader: function(sprite, shaderName) {
		var glProgram = this.shaderPrograms[shaderName];
		if (!glProgram) {
			var vert = require(cc.js.formatStr("%s.vert", shaderName));
			var frag = require(cc.js.formatStr("%s.frag", shaderName));
			glProgram = this.getGlPropgram(vert, frag);
			this.shaderPrograms[shaderName] = glProgram;
		}
		sprite._sgNode.setShaderProgram(glProgram);
		return glProgram;
	},

	// async
	useShader: function(sprite, shaderName, cb) {
		var glProgram = this.shaderPrograms[shaderName];
		if (glProgram) {
			sprite._sgNode.setShaderProgram(glProgram);
			if (cb) {
				cb(glProgram);
			}
			return;
		}

		// vart shader
		var vert = cc.js.formatStr("shader/%s.vert", shaderName);
		if (this.shaderMap[vert]) {
			this.onLoadShader(vert, shaderName, cb);
		} else {
			cc.loader.loadRes(vert, function (err, shaderStr){
				if (err) {
					console.error('load %s fail, error: ', err);
					return;
				}
				this.shaderMap[vert] = shaderStr;
				this.onLoadShader(sprite, shaderName, cb);
			}.bind(this));
		}

		// frag shader
		var frag = cc.js.formatStr("shader/%s.frag", shaderName);
		if (this.shaderMap[frag]) {
			this.onLoadShader(frag, shaderName, cb);
		} else {
			cc.loader.loadRes(frag, function (err, shaderStr){
				if (err) {
					console.error('load %s fail, error: ', err);
					return;
				}
				this.shaderMap[frag] = shaderStr;
				this.onLoadShader(sprite, shaderName, cb);
			}.bind(this));
		}
	},

	onLoadShader: function(sprite, shaderName, cb) {
		var vert = cc.js.formatStr("shader/%s.vert", shaderName);
		var frag = cc.js.formatStr("shader/%s.frag", shaderName);
		if (this.shaderMap[vert] && this.shaderMap[frag]) {
			var glProgram = this.getGlPropgram(this.shaderMap[vert], this.shaderMap[frag]);
			this.shaderPrograms[shaderName] = glProgram;
			sprite._sgNode.setShaderProgram(glProgram);
			if (cb) {
				cb(glProgram);
			}
		}
	}
};

module.exports = ShaderUtils;

使用跟之前的一致,不过如果要获得创建的 glProgram, 在 update 更新数据,需要使用穿入一个函数,gl将作为函数参数返回,例如:

    onLoad: function() {
	    this.startTime = Date.now();
        this.atkAreaInfo = {
            color: {x: 1, y: 0, z: 0},
            minR: 0.05,
            maxR: 0.3,
            dura: 1,
        };
        ShaderUtils.usePropram(this.sp, "atk_circle", function(glPropgram){
			this.aaShaderPro= glPropgram;
        }.bind(this));
    },
    update: function(dt) {
		this.aaShaderPro.use();
		this.time = (Date.now() - this.startTime) / 1000;
        if (cc.sys.isNative) {
            var state = cc.GLProgramState.getOrCreateWithGLProgram(this.aaShaderPro);
            state.setUniformFloat('saturation', this.atkAreaInfo.dura);
            state.setUniformFloat('maxRadius', this.atkAreaInfo.maxR);
            state.setUniformFloat('minRadius', this.atkAreaInfo.minR);
            state.setUniformVec3('areaColor', this.atkAreaInfo.color);
            glState.setUniformFloat("time", this.time);
        } else {
            var saturation = this.aaShaderPro.getUniformLocationForName('saturation');
            var maxRadius = this.aaShaderPro.getUniformLocationForName('maxRadius');
            var minRadius = this.aaShaderPro.getUniformLocationForName('minRadius');
            var areaColor = this.aaShaderPro.getUniformLocationForName('areaColor');
            var time = this.program.getUniformLocationForName("time");
            this.aaShaderPro.setUniformLocationWith1f(saturation, this.atkAreaInfo.dura);
            this.aaShaderPro.setUniformLocationWith1f(maxRadius, this.atkAreaInfo.maxR);
            this.aaShaderPro.setUniformLocationWith1f(minRadius, this.atkAreaInfo.minR);
            this.aaShaderPro.setUniformLocationWith3f(areaColor, this.atkAreaInfo.color.x, this.atkAreaInfo.color.y, this.atkAreaInfo.color.z);
            this.program.setUniformLocationWith1f(time, this.time);
        }
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值