CocosCreator2.1.0渲染流程与shader

CocosCreator2.1.0版本正式支持导入3D模型

对于2.5D游戏的开发来说有着重要意义

自己此前在写捕鱼游戏时了解过自定义shader

并实现了4种不同的水波效果

但经过CocosCreator版本的不断升级

尤其是1.10和2.0两个版本

旧的渲染器被抛弃了

因此老的shader特效也全都不能用了

直到最近正好有时间,花了几天把原先写的特效升级到了最新的2.1.0版本

下面记录一下自定义shader实现方法的改变

以及新的渲染器的理解

 

过往自定义shader的实现依赖

cc.gl

cc.GLProgram

cc.GLProgramState

CCSprite._sgNode

CCTexture2D.setTexParameters

这些统统都不能用了!

取而代之的是新的渲染结构

显然又是多层封装咯

最上层的material关联到sprite组件

最底层的pass关联到具体的vert和frag着色器代码,也就是Shader层

其实Shader层才应该是最底层的

从底向上一层层来看

 

Shader

系统默认的shader是通过以下方法保存在lib对象中的

ProgramLib.prototype.define = function define (name, vert, frag, defines) {

而lib对象所在的位置比较奇怪,可能往后的版本会变更

cc.renderer._forward._programLib

着色器代码也需要稍作修改

以往的CC_Texture0等固定变量都不存在了

 

Pass

Pass的构造函数传入的name就是与着色器同名的name

所以Pass是直接关联shader的

另外Pass还包含了混合参数、深度测试参数、模板测试参数等等

Base.prototype._draw = function _draw (item) {var this$1 = this;var device = this._device;var programLib = this._programLib;var node = item.node;var ia = item.ia;var effect = item.effect;var technique = item.technique;var defines = item.defines;// reset the pool// NOTE: we can use drawCounter optimize this// TODO: should be configurable
  _float2_pool.reset();_float3_pool.reset();_float4_pool.reset();_float9_pool.reset();_float16_pool.reset();_float64_pool.reset();_int2_pool.reset();_int3_pool.reset();_int4_pool.reset();_int64_pool.reset();// set common uniforms// TODO: try commit this depends on effect// {node.getWorldMatrix(_m4_tmp$2);device.setUniform('model', mat4.array(_float16_pool.add(), _m4_tmp$2));var inverse = mat3.invert(_m3_tmp$1, mat3.fromMat4(_m3_tmp$1, _m4_tmp$2));if (inverse) {mat3.transpose(_m3_tmp$1, inverse);device.setUniform('normalMatrix', mat3.array(_float9_pool.add(), _m3_tmp$1));}// }// set technique uniformsfor (var i = 0; i < technique._parameters.length; ++i) {
// 这里遍历technique._parameters
// 再从effect找到参数的值
// 因此参数必须在technique中声明并同时在effect中定义
// 若不在technique中声明,则不会遍历不会走到device.setUniform这一步
var prop = technique._parameters[i];var param = effect.getProperty(prop.name);// 若未在effect中赋值,则从technique中找默认if (param === undefined) {param = prop.val;}// 默认也找不到,就给个该类型的default值if (param === undefined) {param = this$1._type2defaultValue[prop.type];}if (param === undefined) {console.warn(("Failed to set technique property " + (prop.name) + ", value not found."));continue;}if (prop.type === enums.PARAM_TEXTURE_2D ||prop.type === enums.PARAM_TEXTURE_CUBE) {if (prop.size !== undefined) {if (prop.size !== param.length) {console.error(("The length of texture array (" + (param.length) + ") is not corrent(expect " + (prop.size) + ")."));continue;}var slots = _int64_pool.add();for (var index = 0; index < param.length; ++index) {slots[index] = this$1._allocTextuerUnit();}device.setTextureArray(prop.name, param, slots);} else {device.setTexture(prop.name, param, this$1._allocTextuerUnit());}} else {var convertedValue = (void 0);if (param instanceof Float32Array || param instanceof Int32Array) {convertedValue = param;}else if (prop.size !== undefined) {var convertArray = _type2uniformArrayValue[prop.type];if (convertArray.func === undefined) {console.error('Uniform array of color3/int3/float3/mat3 can not be supportted!');continue;}if (prop.size * convertArray.size > 64) {console.error('Uniform array is too long!');continue;}convertedValue = convertArray.func(param);} else {var convertFn = _type2uniformValue[prop.type];convertedValue = convertFn(param);}device.setUniform(prop.name, convertedValue);}}// for each passfor (var i$1 = 0; i$1 < technique._passes.length; ++i$1) {var pass = technique._passes[i$1];var count = ia.getPrimitiveCount();// set vertex bufferdevice.setVertexBuffer(0, ia._vertexBuffer);// set index bufferif (ia._indexBuffer) {device.setIndexBuffer(ia._indexBuffer);}// set primitive type device.setPrimitiveType(ia._primitiveType);// set program (通过pass里保存的program名字找到着色器program!)var program = programLib.getProgram(pass._programName, defines);device.setProgram(program);// cull mode device.setCullMode(pass._cullMode);// blendif (pass._blend) {device.enableBlend();device.setBlendFuncSep(pass._blendSrc,pass._blendDst,pass._blendSrcAlpha,pass._blendDstAlpha);device.setBlendEqSep(pass._blendEq,pass._blendAlphaEq);device.setBlendColor32(pass._blendColor);}// depth test & writeif (pass._depthTest) {device.enableDepthTest();device.setDepthFunc(pass._depthFunc);}if (pass._depthWrite) {device.enableDepthWrite();}// stencilif (pass._stencilTest) {device.enableStencilTest();// front device.setStencilFuncFront(pass._stencilFuncFront,pass._stencilRefFront,pass._stencilMaskFront);device.setStencilOpFront(pass._stencilFailOpFront,pass._stencilZFailOpFront,pass._stencilZPassOpFront,pass._stencilWriteMaskFront);// back device.setStencilFuncBack(pass._stencilFuncBack,pass._stencilRefBack,pass._stencilMaskBack);device.setStencilOpBack(pass._stencilFailOpBack,pass._stencilZFailOpBack,pass._stencilZPassOpBack,pass._stencilWriteMaskBack);}// draw pass device.draw(ia._start, count);this$1._resetTextuerUnit();} };

 

Technique

Technique的构造函数如下

var Technique = function Technique(stages, parameters, passes, layer) {

stages不太了解

parameters声明了注入shader代码中的参数名和类型

未声明的参数即使写在shader里面也是无法使用的

passes可以指定多个,是否意味着多次渲染

以下是默认的SpriteMaterial中的Technique

var mainTech = new renderer.Technique(['transparent'],[{ name: 'texture', type: renderer.PARAM_TEXTURE_2D },{ name: 'color', type: renderer.PARAM_COLOR4 } ],[pass]
);

可以看到只设置了两个参数

因此在着色器中可以使用texture纹理采样

同时使用节点颜色color

 

Effect

Effect的构造函数如下

var Effect = function Effect(techniques, properties, defines) {

以下是默认的SpriteMaterial中的Effect

this._effect = new renderer.Effect([mainTech ],{'color': this._color},[{ name: 'useTexture', value: true },{ name: 'useModel', value: false },{ name: 'alphaTest', value: false },{ name: 'useColor', value: true } ]
);

在自定义材质中properties直接传空对象{}即可

如果是不变的uniform参数可以在technique中赋值默认val

如果是变化的uniform参数,如time、衰减因子、点击位置等等

通过以下方法来更新变量的值即可

Effect.prototype.setProperty = function setProperty (name, value) {

 

Material

自定义材质可以继承自默认材质

也可以类比SpriteMaterial来写

但我觉得那样太麻烦了,直接继承Material把几个有用的参数填进去就行了

而材质与sprite的绑定也简化为两行代码

原本CCSprite._activateMaterial统统省去

当纹理和顶点信息不改变的情况下

我认为以下两句是可以省略的

this.markForUpdateRenderData(true);

this.markForRender(true);

并且在h5、微信、安卓原生平台均验证有效
class CustomMaterial extends cc.renderer.renderEngine.Material{constructor(name , vert , frag , uniforms = [] , defines = []){super(false);this.name = namelet lib = cc.renderer._forward._programLib;!lib._templates[name] && lib.define(name, vert, frag, defines);this.init(name , uniforms);}use(sprite){// cc.dynamicAtlasManager.enabled = false;// 设置基本纹理和颜色let texture = sprite.spriteFrame.getTexture();let color = sprite.node.colorthis.setTexture(texture);this.setUniform('color' , { r: color.r / 255, g: color.g / 255, b: color.b / 255, a: sprite.node.opacity / 255 })this.updateHash();// 指定sprite的材质sprite._material = this;sprite._renderData._material = this;sprite._state = cc.Sprite.State.CUSTOM;}init(name , uniforms) {let renderer = cc.renderer.renderEngine.renderer;let gfx = cc.renderer.renderEngine.gfx;let pass = new renderer.Pass(name);pass.setDepth(false, false);pass.setCullMode(gfx.CULL_NONE);pass.setBlend(gfx.BLEND_FUNC_ADD,gfx.BLEND_SRC_ALPHA, gfx.BLEND_ONE_MINUS_SRC_ALPHA,gfx.BLEND_FUNC_ADD,gfx.BLEND_SRC_ALPHA, gfx.BLEND_ONE_MINUS_SRC_ALPHA);let mainTech = new renderer.Technique(['transparent'],[...uniforms,{ name: 'texture', type: renderer.PARAM_TEXTURE_2D /*, val : '默认值'*/},{ name: 'color', type: renderer.PARAM_COLOR4 /*, val : '默认值'*/},],[pass]);this._texture = null;this._effect = this.effect = new renderer.Effect([mainTech], {}, []);this._mainTech = mainTech;}
};

 

Render

可渲染节点如包含CCSprite组件的node

渲染组件CCSprite继承自RenderComponent

渲染组件onEnable时会为node赋值渲染组件的索引

this.node._renderComponent = this;

CCDirector.mainLoop中发起渲染命令

RenderComponentWalker.visit遍历场景节点

RenderFlow._children方法中会过滤点!active和全透明的节点

if (!c._activeInHierarchy || c._opacity === 0) continue;

RenderComponentWalker._commitComp中比较material的hash值

这也是updateHash的意义所在(若不调用updateHash很可能会报错,比如当节点是首个渲染节点时)

若hash值相同会使用上一个材质(流水线操作)

    _commitComp (comp, assembler, cullingMask) {if (this.material._hash !== comp._material._hash || this.cullingMask !== cullingMask) {this._flush();this.node = assembler.useModel ? comp.node : this._dummyNode;this.material = comp._material;this.cullingMask = cullingMask;}assembler.fillBuffers(comp, this);},

RenderComponentWalker._flush

Scene.prototype.addModel添加至渲染模型数组

Base.prototype._render会遍历模型数组

显然model中是包含material等全部渲染信息的

再由Base.prototype._draw渲染每一个显示模型

最后由Device.prototype.draw调用opengl命令完成绘制~

 

贴一张微信小游戏的水波点击效果图(H5、安卓原生效果一致)

参考文献

https://forum.cocos.com/t/cocos-creator-2-x-shader/69098

转载于:https://www.cnblogs.com/billyrun/articles/10383935.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/264803.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Linux多线程服务端编程——使用muduo C++网络库》读书笔记

第一章 线程安全的对象生命期管理 第二章 线程同步精要 第三章 多线程服务器的适用场合与常用编程模型 第四章 C多线程系统编程精要 1.&#xff08;P84&#xff09;11个常用的最基本Pthreads函数&#xff1a; 2个&#xff1a;线程的创建和等待结束&#xff08;join&#xff09;…

MVC之前的那点事儿系列(4):Http Pipeline详细分析(上)

文章内容 继续上一章节的内容&#xff0c;通过HttpApplicationFactory的GetApplicationInstance静态方法获取实例&#xff0c;然后执行该实例的BeginProcessRequest方法进行执行余下的Http Pipeline 操作&#xff0c;代码如下&#xff1a; // Get application instance IHttpH…

Java开发人员的十大戒律

对Java开发者来说&#xff0c;有许多的标准和最佳实践。本文列举了每一个开发人员必须遵从的十大基本法则&#xff1b;如果有了可以遵从的规则而不遵从&#xff0c;那么将导致的是十分悲惨的结局。1&#xff0e; 在你的代码里加入注释每个人都知道这点&#xff0c;但不知何故…

c++学习书籍推荐《Advanced C++》下载

百度云及其他网盘下载地址&#xff1a;点我 作者简介 James Coplien先在威斯康星大学获得电气与计算机工程学士学位&#xff0c;后又在该大学获得计算机科学硕士学位。他在贝尔实验室的软件产品研发部门工作&#xff0c;在这个部门从一开始就使用C程序设计语言。近年来致力于大…

Centos 编译安装nodejsexpress框架

一. 下载nodejs 版本 wget http://nodejs.org/dist/v0.10.28/node-v0.10.28.tar.gz 二. 编译安装 cp node-v0.10.28.tar.gz /usr/src/ cd /usr/src tar zxvf node-v0.10.28.tar.gz cd node-v0.10.28/ ./configuration --prefix/usr/local/node make&make install 设置环境变…

Spring Boot 2 实践记录之 条件装配

实验项目是想要使用多种数据库访问方式&#xff0c;比如 JPA 和 MyBatis。 项目的 Service 层业务逻辑相同&#xff0c;只是具体实现代码不同&#xff0c;自然是一组接口&#xff0c;两组实现类的架构比较合理。 不过这种模式却有一个问题&#xff0c;如果 Bean 是按实现类装配…

通过简易的前台代码实现无限二级域名转向(来自无忧 biyuan老矣)

只要空间支持泛域名解析&#xff0c;无须服务端脚本语言和DNS&#xff0c;通过前台代码便可实现无限二级域名的构造&#xff1a; <html> <head> <title>碧原网络免费二级域名</title> <head> </head> <body> <scrīp…

MySQL入门-3:安装与客户端工具

大纲1、安装 MySQL2、检索数据3、数据过滤一、安装环境CentOS-6.5-i386mysql 5.1.73为了方便&#xff0c;这里采用yum方式安装&#xff0c;对于学习实验环境完全没问题&#xff0c;注意下面的操作都以root身份操作。除非对MySQL需要定制化或者安装多个实例&#xff0c;建议使用…

实验吧-web-天下武功唯快不破(Python中byte和str的转换)

题目&#xff1a;看看响应头 打开网站&#xff0c;既然已经提示我们看响应头了&#xff0c;那我们就看看呗(习惯bp&#xff0c;也可直接F12查看) 可以看到&#xff0c;响应头部分有个FLAG&#xff0c;而且有提示&#xff1a;please post what you find with parameter:key 所以…

一个弹出页面的徐徐升起的js效果

<HTML><HEAD><title>提醒信息</title><base target"_blank"><SCRIPT language"JavaScript"> window.resizeTo(220,210); var windowW220 // wide var windowH210 // high var Yoffset0 // in pixels, ne…

我国域名***日均58起 高安全DNS防御系统建设刻不容缓

日前&#xff0c;国家互联网应急中心发布的《2013年中国互联网网络安全报告》显示&#xff0c;作为互联网运行的关键基础设施&#xff0c;我国域名系统面临安全漏洞和拒绝服务***等多种威胁&#xff0c;是影响网络稳定运行的薄弱环节。中心监测报告显示&#xff0c;2013年针对我…

P4592 [TJOI2018]异或 (可持久化Trie)

[题目链接] https://www.luogu.org/problemnew/show/P4592 题目描述 现在有一颗以\(1\)为根节点的由\(n\)个节点组成的树&#xff0c;树上每个节点上都有一个权值\(v_i\)。现在有\(Q\)次操作&#xff0c;操作如下&#xff1a; \(1\;x\;y&#xff1a;\)查询节点xx的子树中与\(y\…

xml与数据库略解

1.0 简介 本论文简要的探讨了XML和数据库之间的关系&#xff0c;同时列出一些可以使用数据库处理XML文档的软件。虽然这里不打算详尽地介绍这些软件&#xff0c;但是笔者希望它能够描述使用数据库处理XML文档中的主要部分。这里有点偏向与关系数据库&#xff0c;…

魔方阵

①问题描述 魔方阵是一个古老的智力问题&#xff0c;它要求在一个mm的矩阵中填入1&#xff5e;m2的数字&#xff08;m为奇数&#xff09;&#xff0c;使得每一行、每一列、每条对角线的累加和都相等&#xff0c;如图1所示。 15 8 1 24 17 16 14 7 5 23 22 20 13 6…

专题训练5总结

这个专题学习了两种算法 1.稳定婚姻匹配问题 2.最大团问题 稳定婚姻匹配问题&#xff1a; 1.Stable Match 关于信号站匹配 多了一个容量的权值 如果距离相同看容量大小 数据处理较麻烦&#xff01; 2.marriage 稳定婚姻匹配问题入门题 3.The Stable Marriage problem 和入门题…

CodeSmith实用技巧(八):生成的代码输出到文件中

在CodeSmith中&#xff0c;要把生成的代码文件输出到文件中&#xff0c;你需要在自己的模版中继承OutputFileCodeTemplate类。<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" /><% CodeTemplate Language"C#"TargetL…

python 3的33个保留字列表_python 33个保留字是什么意思

and 用于表达式运算&#xff0c;逻辑与操作as 用于类型转换assert 断言&#xff0c;用于判断变量或条件表达式的值是否为真break 中断循环语句的执行class 用于定义类continue 继续执行下一次循环def 用于定义函数或方法del 删除变量或者序列的值elif 条件语句 与if else 结合使…

SSH整合jar包下载

2019独角兽企业重金招聘Python工程师标准>>> http://blog.sina.com.cn/s/blog_8a3d83320100zhmp.html svn使用 spring 下载 http://maven.springframework.org/release/org/springframework/spring/4.0.5.RELEASE/ http://repo.spring.io/libs-release-local/org/sp…

面试之什么是java虚拟机

java虚拟机体系结构 方法区 堆 java虚拟机栈 本地方法栈方法区 java虚拟机编译的class文件中二进制数据类型解析数据存在方法区中 是所有线程共享和存在数据的线程安全问题 当二个线程使用同一类并且类还被加载 则让一个线程加载 另一个线程等待java虚拟机栈 是线程私有的 既决…

高质量c/c++编程(9)

第9章 类的构造函数、析构函数与赋值函数构造函数、析构函数与赋值函数是每个类最基本的函数。它们太普通以致让人容易麻痹大意&#xff0c;其实这些貌似简单的函数就象没有顶盖的下水道那样危险。每个类只有一个析构函数和一个赋值函数&#xff0c;但可以有多个构造函数&#…