Egret的shader支持glsl语言进行编写,原因应该在于webgl是基于opengl的基础上进行开发的,所以我们在学习的时候完全可以惨遭opengl的glsl语言进行学习。本篇文章不在于讲述glsl语言的介绍,而是着重于在Egret中如何通过glsl来实现shader,并且探讨一些在Egret中需要注意的事项。所以此篇文章针对于略有glsl语言基础的读者。
以下提供两篇着色器的介绍以及glsl常用的函数以及其他api
https://blog.csdn.net/u014291990/article/details/103112914
https://blog.csdn.net/dengfuxing3570/article/details/101250127
由于Egret将WebGLRenderingContext封装在了其底层渲染,所以正常情况下我们不需要直接采用WebGLRenderingContext来进行shader渲染。而是通过Egret定义的egret.CustomFilter来执行自定义的shader。
本文将介绍如何在Egret中实现简单Shader,以及如何让两张纹理进行混合
简单用法是:
let _shader:egret.CustomFilter = new egret.CustomFilter(vertexSrc,fragmentSrc);
let _bmp:egret.Bitmap = new egret.Bitmap( RES.getRes(“bg_png”));
this.addChild(_bmp);
_bmp.filters=[shader];
即我们必须通过设置滤镜的方式在Egret中来实现Shader。接下来我们参照官方demo修改一段比较简单的shader来分析分析,注意此处的字符串使用的是``来表示,这样就避免了增加换行符,同时阅读起来也方便,当然也可以在vscode里面添加glsl插件进行编写。
let vertexSrc = `
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
attribute vec2 aColor;
uniform vec2 projectionVector;
const vec2 center = vec2(-1.0, 1.0);
varying vec2 vTextureCoord;
varying vec4 vColor;
void main(){
gl_Position=vec4((aVertexPosition/projectionVector) + center,0.0,1.0);
vTextureCoord = aTextureCoord;
vColor = vec4(aColor.x,aColor.x,aColor.x,aColor.x);
}
`
let fragmentSrc = `
precision lowp float;
varying vec2 vTextureCoord;
varying vec4 vColor;
uniform sampler2D uSampler;
void main(){
gl_FragColor = texture2D( uSampler,vTextureCoord) * ( vTextureCoord.x > 0.5 ? 1.0 : 0.5 );
}
`
废话少说,看上面的代码编译后得到
可以看到我们实现了左半边部分为半透明,右半边部分为非透明的效果。上面的代码意思是我们能够遍历整张图片的所有像素点,并判断如果x坐标小于整张图的一半,那么设置0.5的透明度,否则颜色值不变。
不过此时我们可能会发现有几个变量貌似根本没有赋值就能直接用,难道说webgl的底层会给我们自动赋值吗,看网上的其他教程貌似都没讲到自动赋值这个。如果能有这个想法,说明你并没有依葫芦画瓢。证明你还是有探索精神的。下面我们看看这是哪几个变量
顶点着色器中有
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
attribute vec2 aColor;
uniform vec2 projectionVector;
片段着色器中有
uniform sampler2D uSampler;
这几个变量之所有能够在Egret中直接使用,是因为Egret的webgl渲染底层在执行渲染时自动给这几个变量进行了值绑定,即赋值。所以我们能够直接使用。
而他们分别代表的的是
aVertexPosition顶点位置
aTextureCoord纹理坐标
aColor 顶点颜色值
projectionVector画布的尺寸大小
uSampler 2d纹理在webgl渲染通道里面的索引值默认为0
除此之外还有其他Egret自定义并有默认值的变量
至此一个简单在Egret中的实现Shader的demo就算完成了。
那么如果我们要在Egret中实现两张或者多张纹理混合怎么办?
通过查看源码发现Egret始终只是激活了TEXTURE0通道,而且默认情况下如上图uniforms[key].setValue(0),这里就是将通过0的纹理绑定在uSampler上,而下面的uSamplerAlphaMask 则对应了通道1。可能有人就觉得我们是否可以利用这个来实现?其实一开始我也这么想的,不过发现根本行不通。其实解决办法也简单,只要我们能够拿到webgl原生渲染上下文对象即可(WebGLRenderingContext)所以这个时候我们又得查看源码。
果然功夫不负有心人我们可以通过以下代码来获得(5.3以上直接egret.sys.WebGLRenderContext[“getInstance”]())
let _gl = egret.web["WebGLRenderContext"]["getInstance"]();
let _webgl:WebGLRenderingContext = _gl.context;
所以编写两个纹理混合可有以下代码,修改片段着色器
fragmentSrc = `
precision lowp float;
varying vec2 vTextureCoord;
varying vec4 vColor;
uniform sampler2D uSampler;
uniform sampler2D uSampler1;
void main(){
gl_FragColor = mix( texture2D( uSampler,vTextureCoord),texture2D( uSampler1,vTextureCoord),0.4 );
}
`
代码如下:
let _texture:egret.Texture = RES.getRes("egret_icon_png");
let _gl = egret.web["WebGLRenderContext"]["getInstance"]();
let _render:Function = ( _tex:egret.Texture)=>{
let _context:WebGLRenderingContext = _gl.context;
_context.activeTexture(_context.TEXTURE2);
_context.bindTexture(_context.TEXTURE_2D, _gl.getWebGLTexture(_tex.bitmapData ));
};
let _bm: egret.Bitmap = new egret.Bitmap(RES.getRes("bg_jpg"));
this.addChild(_bm);
let filter = new egret.CustomFilter(vertexSrc, fragmentSrc,{uSampler1:2});
_bm.filters = [filter];
//此处需要延迟才能实现效果,或者在帧循环里面调用
egret.setTimeout(()=>{
_render(_texture);
},this,1);
效果图如下:
至此一个Egret中的入门shader已经完成。
值得注意的是由于glsl采用的是 类c语法,所以我们在书写时需要严格注意每一行结束后需添加冒号,以及浮点数的小数位。