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

CocosCreator2.1.0渲染流程与shader相关推荐

  1. 使用opengl编程实现一个三维渲染实体_Unity Shader学习随记_01_渲染流水线

    什么是Shader?它和Material(材质)的关系 Shader,中文翻译:着色器,是可编程图形管线的算法片段 Shader实际上就是一小段程序,它负责将输入的顶点数据以指定的方式和输入的贴图或者 ...

  2. 【逆向】UE4 渲染流程分析

    UE4作为当今商业引擎界的大佬,渲染和图形质量一直是首屈一指的水准,但是相对于unity来说UE4基本上是一套完整方案提供,不通过源码修改对渲染进行定制的可能性比较小,而且同时UE4这方面的文档很少, ...

  3. cocos2d-x游戏引擎核心(3.x)----启动渲染流程

    (1) 首先,这里以win32平台下为例子.win32下游戏的启动都是从win32目录下main文件开始的,即是游戏的入口函数,如下: #include "main.h" #inc ...

  4. 【Android 应用开发】Paint 渲染 之 BitmapShader 位图渲染 ( 渲染流程 | CLAMP 拉伸最后像素 | REPEAT 重复绘制图片 | MIRROR 绘制反向图片 )

    文章目录 1. 位图渲染 BitmapShader 简介 ( 1 ) 位图渲染综述 ( ① 三种方式 : Shader.TileMode.CLAMP | Shader.TileMode.REPEAT ...

  5. [转贴]Cocos2d-x3.2与OpenGL渲染总结(一)Cocos2d-x3.2的渲染流程

    看了opengles有一段时间了,算是了解了一下下.然后,就在基本要决定还是回归cocos2dx 3.2的,看了这篇好文章,欣喜转之~ 推荐看原帖: Cocos2d-x3.2与OpenGL渲染总结(一 ...

  6. 第一回 开篇 D3D渲染流程简介

    第一回 开篇 D3D渲染流程简介 http://developer.178.com/201004/65293187592.html 开发这个3D engine已经两年半了,从06年8月刚开始统计的4万 ...

  7. Cocos2d-x3.2与OpenGL渲染总结(一)Cocos2d-x3.2的渲染流程

    转载自:https://www.2cto.com/kf/201409/336234.html 最近几天,我都在学习如何在Cocos2d-x3.2中使用OpenGL来实现对图形的渲染.在网上也看到了很多 ...

  8. WebGL树形结构的模型渲染流程

    今天和大家分享的是webgl渲染树形结构的流程.用过threejs,babylonjs的同学都知道,一个大模型都是由n个子模型拼装而成的,那么如何依次渲染子模型,以及渲染每个子模型在原生webgl中的 ...

  9. 庄懂老师TA学习笔记 - 渲染流程

    视频链接 庄懂的技术美术入门课(美术向)-直播录屏-第1课_哔哩哔哩_bilibili 首先我们用一张图来看一下基础的渲染流程 第一步:读取模型的结构 大家都知道,在建模软件中,我们是通过创建一个一个 ...

最新文章

  1. xCode BuildSetting 设置
  2. 观念什么意思_俗语“女怕午时生,男怕子夜临”是啥意思?古人的忌讳有道理吗?...
  3. 【OpenCV】图像的通道分离
  4. Paxos Made Simple 中文翻译
  5. 锐起无盘服务器安装教程,锐起无盘安装方法 图文教程
  6. 那些不开源的公司,后来怎么样了?
  7. java8的新特性详解-----------Lamda表达式
  8. Object-C---gt;Swift之(十一)属性观察者
  9. bootstrapTable导出excel无响应问题
  10. 基于matlab的谐波处理及无功功率补偿源码,谐波抑制和无功功率补偿(第3版) pdf epub mobi txt 下载...
  11. 谷歌Android flash,取消支持Flash 谷歌TV升级安卓4.2.2
  12. 我们盘点了7家财税SaaS平台,深入解读财税SaaS AB面
  13. 利用U盘制作虚拟软驱加载raid驱动
  14. 软件开发通识之一:什么是计算机语言?
  15. Windows 10(Win10) 怎么删除设备和驱动里的CD驱动器
  16. python将日期分隔成单独的年月日时分列
  17. 水电站下泄生态流量监控解决方案-智能监测生态流量遥测终端-水电站流量监测站
  18. 进程、线程及python的多线程编程
  19. Java开发环境简介及下载
  20. 三国论(6-10章)

热门文章

  1. PageLayoutControl的基本操作
  2. 悖论:早期互联网项目,是否需要技术含量?
  3. win32 api 文件操作!
  4. 捕捉Entity framework 6的详细异常提示
  5. 获取套接字相关联信息
  6. 使用IE WebControls中的TabStrip控件和MultiPage控件实现选项卡式风格页面(转载)
  7. Vue项目代码改进(五)—— 将侧边栏菜单改造为动态后,如何按需显示不同图标
  8. Python多线程豆瓣影评API接口爬虫
  9. LINUX REDHAT第十单元文档
  10. mybatis中的#{value}和${value}的区别