cguser

计算机图形学应用 | 张盼的博客

0%

threejs半透明渲染问题记

这是我之前在工作中遇到的问题,一开始认为应该进行无序渲染,并且自己也做了一些尝试,后来发现 babylonjs 对这个问题有个深度预渲染选项可以完美解决…

这里是 babylonjs 的 sandbox 里面的渲染效果对比

关闭深度预渲染

可以看到上图明显有很多碎面。

打开深度预渲染

打开深度预渲染(即 depth pre-pass),碎面消失。

我先尝试了 OIT

我使用的算法是:http://casual-effects.blogspot.com/2014/03/weighted-blended-order-independent.html

参考了:https://github.com/mrdoob/three.js/issues/4814

效果并不理想,因为随着相机的远近,这个加权平均的无序渲染算法似乎有点问题,也有可能是我做错了。

近

远

可以看到,近的时候颜色重,远的时候颜色轻,说白了就是因为深度在变化,加权平均后值变化了,但是本来这就是基于深度的加权平均算法,肯定深度对其有影响的,我并没有深入的研究这个算法,感兴趣的可以去看看这个英伟达大佬发表的论文…

英伟达大佬博客:http://casual-effects.blogspot.com/

然后我在 threejs 论坛里和友好的朋友们讨论 babylonjs 的深度预渲染实现原理

最终在一位大佬的回复中解决了问题。

当然我早就找到了 babylonjs 的实现,但是无奈没有想到这个选项仅仅只是需要在渲染之前先渲染一次深度…看了这个回复大佬的代码,一下就明白了。

babylonjs 的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for (subIndex = 0; subIndex < sortedArray.length; subIndex++) {
subMesh = sortedArray[subIndex];

if (transparent) {
let material = subMesh.getMaterial();

if (material && material.needDepthPrePass) {
let engine = material.getScene().getEngine();
// Turn off color writing 关闭颜色写入
engine.setColorWrite(false);
// Turn off Blend 关闭混合
engine.setAlphaMode(Constants.ALPHA_DISABLE);
// Render the mesh with Blend off 以关闭混合的状态渲染
subMesh.render(false);
// Turn on color writing 打开颜色写入
engine.setColorWrite(true);
}
}
// Turn on the Blend rendering mesh again 以打开混合的状态渲染
subMesh.render(transparent);
}

确实就是这里的这样,第一次渲染只渲染深度,然后正常渲染就 ok 了。

错误的半透明

正确的半透明

简单的通过 threejs 实现:

1
2
3
4
5
6
7
8
9
10
11
let depthMaterial = new MeshStandardMaterial({
colorWrite: false,
depthWrite: true,
transparent: true,
});
let _mesh = new Mesh(mesh.geometry, depthMaterial);
_mesh.renderOrder = -1;
mesh.add(_mesh);

material.transparent = true;
material.opacity = alpha;

你可以随便用哪个材质,先渲染一遍就可以了,记住那三个选项…

需要的朋友在这里看更多讨论的细节:https://discourse.threejs.org/t/does-anyone-know-how-the-depth-pre-pass-option-of-babylonjs-is-implemented/20623/11

最后这个朋友提了一个 pr,改到了渲染器里,看看 threejs 到底会不会在后面的版本里使用了

https://github.com/mrdoob/three.js/pull/20673

集成到渲染器里对性能是有好处的…

题外话

不选择深度剥离是性能开销太大了…