-前言-

之前也写过一篇关于Laya2D自定义Shader的博客。不过那篇博客局限性太大,是完全独立于Laya框架下独自更新的。不能通过Laya的添加层级关系,设置坐标等。所以这次Shader方案是基于Laya的运行框架下的。

-正文-

Laya框架简述

在开始说具体方案时,还是先来了解下Laya框架的运行规则。
Laya框架下分为3块:

  • 用户逻辑层,这一层逻辑是我们自己写游戏的逻辑层,基于Timer帧更新去循环跑
  • 框架逻辑层,这一层逻辑是确定每个图元的渲染方式,是否重绘等逻辑。是根据用户层所设置的各种数据所确定
  • CPU到GPU的渲染层,这一层逻辑是Laya根据框架逻辑层所设值的Sumit渲染函数依次向显卡提交渲染申请。其中每一次的调用WebGL接口为一次着色器调用请求。
//上面三层都存在于Stage.render函数
updateTimers();//用户逻辑层
//框架逻辑层
if (this.renderingEnabled) {for (var i: number = 0, n: number = this._scene3Ds.length; i < n; i++)//更新3D场景,必须提出来,否则在脚本中移除节点会导致BUGthis._scene3Ds[i]._update();context.clear();super.render(context, x, y);Stat._StatRender.renderNotCanvas(context, x, y);
}
//渲染层
if (this.renderingEnabled) {Stage.clear(this._bgColor);context.flush();VectorGraphManager.instance && VectorGraphManager.getInstance().endDispose();
}

自定义Shader

在明确了Laya的框架之后,我们可以确定,我们的Shader完整流程也是需要从用户逻辑层——>框架逻辑层——>渲染层一步一步的透传的。

实现目标

我们可过下面的伪代码来感受下实现效果

//step0 创建图片对象
let shaderImage:Laya.Image = new Laya.Image(图片路径);
//step1 创建控制Image的Shader对象
let shader:Laya.Shader2X = new Laya.Shader2X(顶点着色器、片元着色器、其余参数)
//step2 连接shader与shaderImage
//关键步骤
//step3 添加图片到Image
Laya.stage.addChild(shaderImage);//以后shaderImage就由shader着色器程序控制

通过上面的伪代码,0,1,3步都是在框架之下执行的,关键步骤就2步,如何连接Image对象与Shader程序。不过在讲如何连接之前,我们首先来讲讲如何创建Shader程序。

创建Shader及自定义Value2D对象

创建Value2D对象

Laya调用WebGL API是通过Value2D对象进行调用,Value2D会根据自己的属性调用不同Shader程序来渲染自己所持有的texture对象。

//以下是Value2D的提交函数upload
upload(): void {var renderstate2d: any = RenderState2D;// 如果有矩阵的话,就设置 WORLDMAT 宏RenderState2D.worldMatrix4 === RenderState2D.TEMPMAT4_ARRAY || this.defines.addInt(ShaderDefines2D.WORLDMAT);this.mmat = renderstate2d.worldMatrix4;if (RenderState2D.matWVP) {this.defines.addInt(ShaderDefines2D.MVP3D);this.u_MvpMatrix = RenderState2D.matWVP.elements;}var sd: Shader2X = Shader.sharders[this.mainID | this.defines._value] || this._ShaderWithCompile();if (sd._shaderValueWidth !== renderstate2d.width || sd._shaderValueHeight !== renderstate2d.height) {this.size[0] = renderstate2d.width;this.size[1] = renderstate2d.height;sd._shaderValueWidth = renderstate2d.width;sd._shaderValueHeight = renderstate2d.height;sd.upload((<ShaderValue>this), null);}else {sd.upload((<ShaderValue>this), sd._params2dQuick2 || sd._make2dQuick2());}
}

//mainID定义于ShaderDefines2D,this.deines._value是subID,是在ShaderDefines2D下进一步区分Shader的标志位。
由此看出我们需要控制一个Image使用就需要修改this.deines._value区别于其他ID。幸运是的每一个Image都有一个唯一ID(GID)来区分不同纹理。因此我们可以使用_GID)来区分不同纹理。因此我们可以使用G​ID)来区分不同纹理。因此我们可以使用_GID来告诉Laya遇到这个ID的纹理就使用我们自定义的Shader。
Value2D只定义的默认的需要传入Shader程序的uniform变量。因此我们需要自定义Shader时需要新建自己的Value2D扩展类。

export default class MyValue2D extends Laya.Value2D{//-----定义自定义的uniform变量----//-----定义自定义的uniform变量----constructor(subID:number=0){super(Laya.ShaderDefines2D.TEXTURE2D,subID);      }/**调用完成后清除数据**/public clear():void{super.clear();}public setValue():void{super.setValue();}/**提交函数**/public upload():void{//渲染之前可用更新自定义的变量super.upload();}
}

上面我们就自定义了一个Value2D对象,值得注意的是,我们在逻辑层是取不到这个对象的,因此需要在逻辑层更新这里的参数要么是从逻辑层的数据传到这里,要么直接暴力的使用静态变量。我认为直接使用静态变量更为简单直接,不过也要看具体应用场景。

注册自定义Value2D

创建好自定义的Value2D对象后,我们需要将这个对象注册到Laya框架之中,以备需要用到的时候可以实例化这个自定义对象。

Laya.Value2D._initone(Laya.ShaderDefines2D.TEXTURE2D | (Image对象的$_GID), MyValue2D);
//ex 示例
let img:Laya.Image = new Laya.Image("xxx.png");
let id:number = img['_bitmap'].$_GID;
Laya.Value2D._initone(Laya.ShaderDefines2D.TEXTURE2D | id, MyValue2D);

修改创建Value2D实例化逻辑

完成上面几步我们还是没法让我们想要的Image使用自定义的Value2D对象,默认情况下Context对象在绘制纹理时创建的Value2D默认是TextureSV对象。我们需要修改Context._inner_drawTexture函数来告诉Laya遇到哪个Image对象要使用自定义Shader

_inner_drawTexture(tex: Texture, imgid: number, x: number, y: number, width: number, height: number, m: Matrix, uv: ArrayLike<number>, alpha: number, lastRender: boolean): boolean {//省略部分代码..//修改之前:this._submits[this._submits._length++] = this._curSubmit = submit = SubmitTexture.create(this, mesh, Value2D.create(ShaderDefines2D.TEXTURE2D, 0));//修改之后:this._submits[this._submits._length++] = this._curSubmit = submit = SubmitTexture.create(this, mesh, Config.customRenderID.indexOf(imgid) != -1 ? Value2D.create(ShaderDefines2D.TEXTURE2D, imgid) : Value2D.create(ShaderDefines2D.TEXTURE2D, 0));
}

这里我将需要使用的自定义纹理ID存储到了Config新建的customRenderID数组中,不过任何可以访问到的对象存储都可以。

完成的创建过程

let img = new Laya.Image("shader/noise.png");
let id: number = img['_bitmap'].$_GID;
Config.customRenderID.push(id);
Laya.Value2D._initone(Laya.ShaderDefines2D.TEXTURE2D | id, CustomShader);
//顶点着色器 片元着色器
this._shader = new Laya.Shader2X(vs,fs,Laya.ShaderDefines2D.TEXTURE2D | id);
this._image.pos(100,100);
this._image.scale(1.2,1.2);
Laya.stage.addChild(this._image);

注意事项

要想使用Laya框架下添加节点,设置坐标,旋转纹理这些都需要在顶点着色器匹配Laya默认的纹理类型的顶点着色器。

precision highp float;
attribute vec4 posuv;
varying vec2 v_texUV;
#ifdef WORLDMAT
uniform mat4 mmat;
#endif
#ifdef MVP3D
uniform mat4 u_MvpMatrix;
#endif
void main(){vec4 pos = vec4(posuv.xy,0.,1.);//其余顶点着色器逻辑//...//其余顶点着色器逻辑//匹配Laya默认情况下的顶点着色器#ifdef WORLDMATpos=mmat*pos;#endifvec4 pos1=vec4((pos.x/size.x-0.5)*2.0,(0.5-pos.y/size.y)*2.0,0.,1.0);#ifdef MVP3Dgl_Position=u_MvpMatrix*pos1;#elsegl_Position=pos1;#endif
}

-结语-

这种使用Shader的方式适用于,我们既想使用Laya控制纹理的方式,又想实现不同的效果的方式。包括个性化的滤镜,高级的图形效果都可以使用该方式。

LayaBox2D使用自定义Shader的方法相关推荐

  1. CocosStudio的节点如何使用自定义shader

    CocosStudio的节点如何使用自定义shader 问题: 我想对CocosStudio 的 某个UI 里的 某个图片(如下图所示的Image类型)使用自定义shader. 但是,我把 对传统的c ...

  2. 在自定义Shader中应用Unity全局光照

    目录 从一个简单的Diffuse Shader开始 接受间接光照--使用内置函数 贡献间接光照--meta pass 结果 上一篇是关于Unity全局光照系统的介绍和使用方法,但是用于测试效果的场景物 ...

  3. unity中使用自定义shader进行光照贴图烘培无法出现透明度的坑爹问题

    最近开发中在对场景进行光照贴图烘焙时发现一个坑爹问题,在使用自定义shader的时候,shader命名中必须包含Transparent路径,否则烘焙的时候不对alpha通道进行计算,烘焙出来都是狗皮膏 ...

  4. dede php 调用自定义字段,在dedecms搜索结果列表页调用自定义字段的方法(绝对可用)...

    关于在dedecms搜索结果列表页调用自定义字段的方法比较多有些教程都写得比较含糊,经过织梦者的测试今天推荐一个比较有效的方法给大家,方法是从网络中搜到的,但是绝对可用 打开 include/exte ...

  5. 绝地求生自定义服务器租用,绝地求生自定义服务器怎么开 自定义服务器设置方法...

    绝地求生自定义服务器怎么开呢?自定义服务器已经开启设置了,玩家们可以已自己的方式享受游戏,但一些玩家还不知道自定义服务器设置方法,那下面就来看下吧. 自定义服务器玩法介绍 当创建自己的自定义游戏时,你 ...

  6. cocos2dx在wp上使用自定义shader

    实践cocos2dx 2.x版本wp上增加自定义shader 根据cocos2dx 的官方文档http://www.cocos2d-x.org/wiki/How_to_update_wp8_shade ...

  7. 织梦php调用字段,织梦dedecms搜索页调用自定义字段的方法

    这篇文章主要为大家详细介绍了织梦dedecms搜索页调用自定义字段的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借鉴. 织梦dedecms搜索页调用自定义字段的 ...

  8. simulink自定义信号源方法matlab数据导入sim

    simulink自定义信号源 方法: https://jingyan.baidu.com/article/e6c8503c7abdb2e54f1a18a0.html https://jingyan.b ...

  9. JS 创建自定义对象的方法

    工厂模式 优点:接受参数,可以无数次的调用这个函数,创建Person对象,而每次他都可以返回一个包含三个属性一个方法的对象. 缺点:虽然解决了创建多个相似对象的问题,但是没有解决对象识别的问题(即怎么 ...

  10. golang 导入自定义包_goLang引入自定义包的方法

    看完golang的基本语法后,为了模块化编程,试用了下golang的包管理,结果真踩了几个坑,总结一下吧. 一. 设置$GOPATH环境变量 golang和C或php不一样,不会自动查找当前路径下的文 ...

最新文章

  1. ORA-12518,TNS:listener could not hand off client connection
  2. XenApp_XenDesktop_7.6实战篇之十四:XenDesktop虚拟桌面的交付
  3. 从零开始学习docker(二十二)容器监控
  4. CSharpGL(36)通用的非托管数组排序方法
  5. 使用 Angular Transfer State 的一个具体例子
  6. Fatal error: Please read “Security“ section of the manual to find out how to run mysqld as root
  7. selenium操作浏览器的前进和后退
  8. 使用若依前后端分离下载需要授权的url文件
  9. 80%的程序员不了解的微服务内幕
  10. java案例代码4-数组的复制
  11. wifi mesh 开关
  12. 点餐推荐系统_麦当劳智慧餐厅的微信小程序终究将取代人工点餐和自助点餐机...
  13. python中exec是什么意思_Python中的exec()
  14. Mybatis教程之Mybatis配置篇
  15. mysql联合索失效_mysql联合索引注意事项,解决为啥联合索引无效的问题
  16. 国家税务总局全国增值税发票查验平台验证码刷不出来显示系统繁忙的解决方法
  17. 给ALV字段添加搜索帮助
  18. 爱康科技拟9.6亿元收购爱康光电 拓展光伏产业链
  19. Cannot create resource output directory
  20. 苹果电脑的CCTV直播软件_我是亲民_新浪博客

热门文章

  1. 完善计算机 实践性教学,计算机基础实践性教学分析论文
  2. 已知坐标增量求坐标方位角_全站仪坐标导线测量及平差方法的比较
  3. 几种防鼠光缆的种类及优缺点分析!
  4. SQL 数据库 学习 004 预备知识
  5. ubuntu安装xp字体
  6. 联想服务器加装显卡无显示,标配11201355主板的启天M4330在 Win8系统加装独立显卡“无显无报警”...
  7. JVM可视化监测工具jconsole 入门说明
  8. resin服务器中间件
  9. PowerDesigner下载地址
  10. Python写一个小小的项目监控