本次LayaAir 2.11.0beta版是鼠年的最后一个版本,同时也是2021年的第一个版本。所以本次的更新内容也是干货满满,再次大幅提升了3D渲染能力。本文将逐个详细介绍。
提示: 1、本文中涉及的代码,均会省略大量代码,仅为介绍功能使用的核心代码, 2、代码的API使用方式为基于引擎源码的使用方式,与开放下载版本的Laya.xxx的写法不同, 3、各不同语言引擎版本API的使用方式、以及完整的示例代码请前往Layabox官网示例栏目查看。
Blinnphong支持光透射功能
从LayaAir2.11 beta版本开始,Blinnphong材质支持了光线透射功能,也就是当光线射入半透明材质,会产生光线透射的效果。例如下图的效果:
(来自官网BlinnPhong_Transmission示例截图)
或者查看视频动态效果:
(视频画质已被压缩,仅用于效果预览)
当我们想使用Blinnphong的光线透射效果时,将Blinnphong的 enableTransmission 属性设置为true即可。
示例代码为:
//打开兔子材质的次表面散射功能
this.rabbitMaterial.enableTransmission = true;
//打开猴子材质的次表面散射功能
this.monkeyMaterial.enableTransmission = true;
(点击代码,左右滑动查看全文)
下面我们继续介绍一下Blinnphong材质光线透射功能相关的其它属性,
厚度图属性thinknessTexture用于加载一张描述物体厚度信息的贴图,这是一张会描述物体各个地方透光比例的材质贴图。例如兔子的耳朵比较薄,透光会强一些,兔子的身体比较厚,透光会弱一些。
厚度图并非普通的贴图,需要找3D美术同学生成
有无厚度图的对比效果如下图所示:
示例代码为:
this.rabbitMaterial.enableTransmission = true;
//为兔子材质添加厚度贴图
this.rabbitMaterial.thinknessTexture = Loader.getRes("xxx/xxx.jpg");
(点击代码,左右滑动查看全文)
由于入射光分为透射部分和反射部分,透射率属性transmissionRate用于描述透射光占总光量的比例,该属性同时也会影响反射光部分的强度。
透射率属性值分别为0、0.5、1的对比效果如下图所示:
示例代码为:
this.rabbitMaterial.enableTransmission = true;
this.rabbitMaterial.thinknessTexture = Loader.getRes("xxx/xxx.jpg");
//设置兔子材质的透射率为0.1
this.rabbitMaterial.transmissionRata = 0.1;
(点击代码,左右滑动查看全文)
backDiffuse属性用于设置透射扩散范围系数,系数的范围是1-10,值越大范围越小,1是最大扩散范围值,10是最小扩散范围值。效果如下图所示:
示例代码为:
this.rabbitMaterial.enableTransmission = true;
this.rabbitMaterial.thinknessTexture = Loader.getRes("xxx/xxx.jpg");
this.rabbitMaterial.transmissionRata = 0.1;
//设置透射扩散范围系数为3.88
this.rabbitMaterial.backDiffuse = 3.88;
(点击代码,左右滑动查看全文)
transMissionColor是透射颜色属性,用于调整透光的颜色,下图分别是
Vector4(1,1,1,1)与Vector4(1,0,0,1) 的对比效果:
示例代码为:
this.rabbitMaterial.enableTransmission = true;
this.rabbitMaterial.thinknessTexture = Loader.getRes("xxx/xxx.jpg");
this.rabbitMaterial.transmissionRata = 0.1;
this.rabbitMaterial.backDiffuse = 3.88;
//设置透射光的颜色
this.rabbitMaterial.transmissionColor = new Vector4(1,1,1,1);
(点击代码,左右滑动查看全文)
backScale属性用于描述光透射后的强度,值越大,透射的光越强。效果如下图所示:
示例代码为:
this.rabbitMaterial.enableTransmission = true;
this.rabbitMaterial.thinknessTexture = Loader.getRes("xxx/xxx.jpg");
this.rabbitMaterial.transmissionRata = 0.1;
this.rabbitMaterial.backDiffuse = 3.88;
this.rabbitMaterial.transmissionColor = new Vector4(1,1,1,1);
//设置透射光的强度
this.rabbitMaterial.backScale = 1.2;
(点击代码,左右滑动查看全文)
除了引擎上的光线透射功能支持外,我们还为LayaAir的Unity插件也新增了对应的功能使用和导出。当开发者使用了LayaAir提供的Blinnphong材质,并为其设置了贴图后,直接在Unity中勾选Enable Transmission,调整相关的参数,导出即可直接使用。
渲染指令drawMeshInstance
LayaAir2.11版本另一个重磅功能是CommandBuffer增加了渲染指令drawMeshInstance,以及增加了DrawMeshInstancedCMD渲染命令类。
增加渲染指令DrawMeshInstance后,开发者可以用来渲染自定义instance属性,进行instance渲染。这种渲染相比普通的渲染会较大的提高渲染性能。
例如官网示例中的CommandBuffer_DrawCustomInstance就是自定义instance的优化示例,示例中自定义了小球的颜色,并且在满帧的情况下,实现了用一个渲染批次画出900个颜色不同的小球效果,体现了渲染优化的能力。
(通过drawMeshInstance渲染自定义instance示例效果的视频)
那我们应该如何使用drawMeshInstance渲染命令呢?
由于drawMeshInstance渲染命令在CommandBuffer中集成,所以当开发者实例化创建好CommandBuffer后,直接调用CommandBuffer的drawMeshInstance()方法在渲染命令流中添加DrawMeshInstanceCMD渲染指令。
示例代码为:
private instanceCMD:DrawMeshInstancedCMD;
private materialBlock:MaterialInstancePropertyBlock;
createCommandBuffer(camera:Camera){
//创建渲染命令流
let buf:CommandBuffer = new CommandBuffer();
this.createMatrixArray();
this.materialBlock= new MaterialInstancePropertyBlock();
this.materialBlock.setVectorArray("a_InstanceColor",this.colors1,InstanceLocation.CUSTOME0);
//创建渲染命令,渲染900个小球
this.instanceCMD = buf.drawMeshInstance(PrimitiveMesh.createSphere(0.5),0,this.matrixs1,this.mat,0,this.materialBlock,900);
camera.addCommandBuffer(CameraEventFlags.BeforeTransparent,buf);
return;
}
(点击代码,左右滑动查看全文)
在上面的示例代码中,除了drawMeshInstance的基础使用流程外,还有一个要点(MaterialInstancePropertyBlock类)。这个类是用来描述instance自定义属性,比如给每一个Mesh分配一个不同的颜色。当然也可以是金属度、粗糙等任何材质描述属性。
开发者在更新instance自定义属性数据的时候,需要用对应的方法来更新对应类型的数据。方法与类型的对应列表如下所示:
在上面的示例中,渲染每一个Mesh的时候需要传入一个不同的颜色,而每一个颜色其实就是一个Vector4类型的属性。所以,我们要用setVectorArray()方法,传入一个Vector4的数组,或者是Vector4转换好的Float32Array,来设置shader中的CUSTOME0槽位名字为a_InstanceColor的instance属性。
示例代码就是这一段:
//改变900小球的颜色
this.materialBlock.setVectorArray("a_InstanceColor",this.currentColor,InstanceLocation.CUSTOME0);
(点击代码,左右滑动查看全文)
要注意的是,每一个槽位和属性名字要一一对应,属性名字和槽位也要对应声明shader中的顶点槽位,
例如下图中的shader顶点Attribute声明:
然后,我们再通过另一个截图看一下在Shader中的应用:
既然讲到shader相关,那我们再提一下,drawMeshInstance命令流设置对应的世界矩阵Matrix数组和自定义材质在shaderVS中的应用。
当大量渲染同一个Mesh的时候,需要用世界矩阵数组来描述每个Mesh的位置,引擎会更新对应的世界矩阵buffer,来保证每一个实例都能得到正确的worldMatrix。
在自定义材质中,声明attributeMap中需要写死a_WorldMat:VertexMesh.Mesh_WORLDMATRIX_ROW0,这样在shader中就可以接收到参数传入的矩阵。如下图所示。
按上面的步骤完成后,在顶点shaderVS中就可以对应使用了,使用的代码如下图所示。
drawMeshInstance功能知识点的最后,再介绍一下如何高性能的修改渲染个数drawNums、位置,以及修改自定义Instance属性。
首先,我们要更新修改位置,就需要调用DrawMeshInstanceCMD类的setWorldMatrix(worldMatrixArray:Matrix4x4[]))
方法,然后传入最新的矩阵数组。
如果要更新修改渲染个数,我们还需要调用DrawMeshInstanceCMD类的setDrawNums(drawNums:number)
方法,传入需要渲染的个数。
但需要注意的是,渲染个数(drawNums)不可以超过最大的instance数(DrawMeshInstancedCMD.maxInstanceCount)。
maxInstanceCount默认值为1024,开发者可以动态的修改。官网示例中同样使用到了drawMeshInstance的草地示例GrassDemo,就将这个值改为了1000000,草地示例的效果非常炫,欢迎大家前往官网示例中查看代码和效果,这里就不过多介绍了。
(用LayaAir引擎实现草地效果的视频)
除了修改渲染个数与位置外,如果我们要动态修改自定义Instance属性,我们需要调用MaterialInstancePropertyBlock中的方法setVectorArray、setVector3Array、setVector2Array、setNumberArray来设置更新类型为Vector4,Vector3,Vector2,number的自定义属性的数据,但是name和attributeLocation必须和之前设置的自定义属性匹配。
其它重要的3D功能更新
除了上面介绍的两个重要新特性之外,还有一些3D实用功能的更新。
首先是增加了打印Webgl指令的功能。
在以前的版本中,由于webgl报错并不能定位到准确位置,所在在本次版本中,我们提供了一种可以定位到哪个Webgl指令报错的方法。
开发者只需要将Config类中的静态属性printWebglOrder设置为true,就可以将webgl指令替换为LayaAir引擎内部指令方法,当webgl报错时,会直接暂停到报错的具体指令,更加方便的分析出现错误原因。
使用代码如下所示:
//在引擎init前设置
Config.printWebglOrder = true;
我们还在WebGLContext中增加_maxUniformFragMentVectors,用于描述uniform最大Vector数。可以在WebGLContext类中查到此设备的传入Shader的最大Vector数,传入的数据超过这个值会报webgl错误。
为Effect unlit材质增加U_ViewProjection,调整支持Instance的时候的计算方案,优化了效率。在这些材质的shader中传入Uniform U_ViewProjection,用来计算projectionPosition。
最后是优化了显存,删除InstanceMVPMatrix所需要的顶点buffer。
在原本的Instance方案中,我们会在CPU端,将所有的渲染实例MVP矩阵算好后,使用一个公用的VertexBuffer传入Shader中,修改后的方案将省去这个计算,减少了CPU端的压力,将计算分给GPU。
END