[LayaAir 2.0]Laya2自动同步cullingMask方案以及灯光裁剪补丁

本文解决了两个问题,代码在文末
1、Laya的U3D导出工具无法导出摄相机和Light的cullingMask,每次都要手工设置,容易出现编辑器与代码不同步的情况
2、Laya的Light渲染时,没有cullingMask标记,所有光源会对所有对象生效。
最近在将之前做的一个2D项目,换成3D版本。看到Laya2.0说,几乎可以做到U3D和Laya中效果一致,果断就入坑了。毕竟U3D编辑器的稳定性没得说。
一、我的需求如下:
1、一个相机(透视投影)用于渲染桌面
2、一个相机(正交投影)用于渲染手牌。
3、灯光也就需要单独调整。
当我编辑好效果后,第一次加载场景时就傻眼了,所有东西在LAYA中都显示出来了。 我就感觉应该是cullingMask没有同步导致的。
查阅文档后发现,可以通过代码来自行添加。https://ldc2.layabox.com/doc/?nav=zh-ts-4-5-10
编码测试后发现功能是OK的,于是我想了一个办法,为Camera建了一个子对象,通过子对象的名字来标记这个Camera的cullingMask(一开始是想用名字的,但用名字来标记的话,对象通过名字获取就困难了。)
比如,我们的cullingMask是 除了UI层,都需要。 那么子对象名称就是 culling_mask=all|!ui
如果我们的cullingMask是,只要ui和fx层,那么子对象名称就是 culling_mask='ui|fx'
如下图所示,我为摄相机和灯光都添加了culling_mask子对象,这个子对象会在代码中被删除,避免性能损耗。
[img]​[img]
接下来就发现灯光不对的。 在U3D中,我的手牌单独用了一个灯光,桌面用了一个点光源和一个方向光。
明白是怎么回事后,我就去找Laya.SpotLight,Laya.DirectionLight,Laya.PointLight的cullingMask。不出所料,果然没有这个东西。
于是就着手做这个事情,阅读代码后发现,渲染和相机裁剪都在laya.d3.js这个文件中。
看完灯光相关的代码后,发现LAYA在单光源和多光源管理上做了许多处理,特别是多光源做了Cluster优化。介于对源码不熟悉,怕出错。所以我放弃了光源对物体进行裁剪的想法,转而做了一个折衷的方案。
光源cullingMask方案大概描述如下。
1、摄相机的cullingMask会裁剪掉不属于当前摄相机的物体
2、我们假设当前相机渲染的所有物体都受光
3、根据摄相机的cullingMask进行光源的控制。
这样虽然达不到精细的控制,但总的来说,可以用摄相机对光源分类了。
 
图1为U3D中的效果,图2为LAYA中的效果。
[img]​[img]
[img]​[img]
完整代码如下:ExtCullingMask.ts
[pre]
/**
* 函数:LAYA 灯光Layer裁剪函数
* 作者 麒麟子
* 用法说明
1、在laya.d3.js中找到Camera.render函数
2、render函数中,this._prepareCameraToRender();这一行后面添加如下代码
if(this._scene.lightCullingMask && this._scene.lightCullingMask instanceof Function){this._scene.lightCullingMask(scene,this);}
3、对想要开启灯光裁剪的场景调用 LightCullingMask.enableLightCullingMask(scene3d)即可

//注意
ExtCullingMask并不是针对对象进行,而是对摄相机进行。因此务必保持与摄相机相同。
**/

export default class ExtCullingMask {
public static enableLightCullingMask(scene: Laya.Scene3D) {
//var proto = Laya.Scene3D.prototype as any;
(scene as any).lightCullingMask = function (scene, camera) {
//灯光处理开始 麒麟子添加
var allLights = [];
allLights = allLights.concat(scene._directionLights._elements);
allLights = allLights.concat(scene._spotLights._elements);
allLights = allLights.concat(scene._pointLights._elements);
for (let i = 0; i < allLights.length; ++i) {
let light = allLights[i];
for (let layer = 0; layer < 32; ++layer) {
let layerMask = Math.pow(2,layer);
light.active = (camera.cullingMask & layerMask) && (light.cullingMask & layerMask);
if (light.active) {
break;
}
}
}
scene._prepareSceneToRender();
//灯光处理结束 麒麟子添加
}
}

public static disable(scene: Laya.Scene3D) {
delete (scene as any).lightCullingMask;
}

private static _layerMap = {'ui':5}

public static assignCullingMask(target){
let layerNodeName = '';
let prefix = 'culling_mask=';
for(let i = 0; i < target.numChildren; ++i){
let child = target.getChildAt(i);
if(child.name.indexOf(prefix) == 0){
layerNodeName = child.name.replace(prefix,'');
child.destroy();
break;
}
}
if(!layerNodeName){
return;
}

let arr = layerNodeName.split('|');
this.removeAllLayers(target);
for(let i = 0; i < arr.length; ++i){
let layerStr = arr[i] as string;
if(layerStr == 'all'){
this.addAllLayers(target);
}
else{
let bRemove = layerStr.indexOf('!') == 0;
if(bRemove){
layerStr = layerStr.substr(1);
}
let layer = this._layerMap[layerStr];
if(layer === null || layer === undefined){
console.log(layerNodeName + ' don not match the rule of layer node.');
return;
}
if(bRemove){
this.removeLayer(target,layer);
}
else{
this.addLayer(target,layer);
}
}
}
}

public static removeAllLayers(target) {
target.cullingMask = 0;
}

public static addAllLayers(target) {
target.cullingMask = 0x7FFFFFF;
}

public static addLayer(target, layer) {
let mask = target.cullingMask || 0;
target.cullingMask = mask | Math.pow(2,layer);
}
public static removeLayer(target, layer) {
let mask = target.cullingMask || 0;
target.cullingMask = mask & (~Math.pow(2,layer));
}
}
[/pre][img]
已邀请:

麒麟子

赞同来自:

发出来竟然没图了,代码格式也不对。
关于cullingMask的导出以及灯光的cullingMask,希望团队尽快支持。做成会员功能也不介意。

要回复问题请先

商务合作
商务合作