【问题标题】:Three.js - depthWrite vs depthTest for transparent canvas texture map on THREE.PointsThree.js - depthWrite vs depthTest 用于 THREE.Points 上的透明画布纹理贴图
【发布时间】:2021-09-01 19:40:56
【问题描述】:

问题

depthWrite: falsedepthTest: false 之间有显着差异吗?使用depthTest 是否提供性能优势?选择其中一种功能是否有任何牺牲?

原来的问题

我想渲染一个带有半透明圆圈的THREE.Points 对象作为每个点。我使用从canvas 元素加载的THREE.Texture 并将其传递给THREE.PointsMaterial 上的map 属性。

透明度没有完全起作用,一些圆圈重叠得很好,但其他的表现得好像它们是实心的。

我在了解THREE.PointsMaterial 上的depthWrite: falsedepthTest: false 后修复了它。

我在哪里

我有一个显示重叠点错误的代码示例(嵌入在底部),可以使用depthTestdepthWrite 来修复它:

var points = new THREE.Points(
    new THREE.Geometry(),
    new THREE.PointsMaterial({
        //depthTest: false,
        //depthWrite: false,
        map: circleTexture,
        size: circleDiameter,
        transparent: true
    })
);

我对这一切都很陌生,但我尝试阅读该主题,并且据我所知(如果我错了,请纠正我)深度缓冲区用于确定哪些片段被遮挡并且不需要渲染。关闭depthWritedepthTest 将使对象免于此过程。它们的不同之处在于:

  • depthWrite: false 仍然计算深度,但无论如何都会渲染整个对象

  • depthTest: false 甚至不计算深度

所以听起来我会通过关闭 depthTest 而不是 depthWrite 来失去一些对象质量,但可能通过完全跳过计算来提高性能?但是,我会失去什么品质?实际上是否存在性能差异?我的无知在这里闪耀。

// Sizes
var sceneWidth = 200;
var sceneHeight = 200;
var lineLength = 50;
var circleRadius = 32;
var circleDiameter = circleRadius * 2;

// Renderer
var renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
});
renderer.setSize(sceneWidth, sceneHeight);
document.body.appendChild(renderer.domElement);

// Scene
var scene = new THREE.Scene();

// Camera
var d = 100;
var aspect = sceneWidth / sceneHeight;
var camera = new THREE.OrthographicCamera(
    -d * aspect,
    d * aspect,
    d,
    -d,
    1,
    12000
);
camera.position.set(140, 140, 140);
scene.add(camera);

// Controls
var controls = new THREE.OrthographicTrackballControls(
    camera,
    renderer.domElement
);
controls.rotateSpeed = 0.2;
controls.addEventListener('change', function () {
    renderer.render(scene, camera);
});
window.addEventListener('resize', function() {
    controls.handleResize();
});

// Circle texture
var canvasEl = document.createElement('canvas');
var context = canvasEl.getContext('2d');
canvasEl.width = circleDiameter;
canvasEl.height = circleDiameter;
context.fillStyle = 'rgba(255, 255, 255, 0.5)';
context.beginPath();
context.arc(circleRadius, circleRadius, circleRadius, 0, Math.PI * 2);
context.fill();
var circleTexture = new THREE.Texture(canvasEl);
circleTexture.needsUpdate = true;

// Points
var points = new THREE.Points(
    new THREE.Geometry(),
    new THREE.PointsMaterial({
        //depthTest: false,
        //depthWrite: false,
        map: circleTexture,
        size: circleDiameter,
        transparent: true
    })
);
points.geometry.vertices.push(new THREE.Vector3(0, 0, 0));
points.geometry.vertices.push(new THREE.Vector3(0, lineLength, 0));
points.geometry.vertices.push(new THREE.Vector3(0, lineLength, lineLength));
points.geometry.vertices.push(new THREE.Vector3(0, 0, lineLength));
scene.add(points);

// Lines
var lines = new THREE.Line(
    new THREE.Geometry(),
    new THREE.LineBasicMaterial({
        linewidth: 1.2,
        color: 0xffffff,
        transparent: true,
        opacity: 0.25
    })
);
lines.geometry.vertices.push(new THREE.Vector3(0, 0, 0));
lines.geometry.vertices.push(new THREE.Vector3(0, lineLength, 0));
lines.geometry.vertices.push(new THREE.Vector3(0, lineLength, 0));
lines.geometry.vertices.push(new THREE.Vector3(0, lineLength, lineLength));
lines.geometry.vertices.push(new THREE.Vector3(0, lineLength, lineLength));
lines.geometry.vertices.push(new THREE.Vector3(0, 0, lineLength));
lines.geometry.vertices.push(new THREE.Vector3(0, 0, lineLength));
lines.geometry.vertices.push(new THREE.Vector3(0, 0, 0));
scene.add(lines);

// Render
function render() {
    window.requestAnimationFrame(render);
    renderer.render(scene, camera);
    controls.update();
}

render();
* { margin: 0; padding: 0; }
body { background-color: #333; }
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Document</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r76/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrthographicTrackballControls.js"></script>
</body>
</html>

【问题讨论】:

    标签: javascript three.js


    【解决方案1】:

    深度测试关闭意味着将深度测试全部关闭。 (阅读/测试和写作)

    深度注销是指防止深度缓冲区被写入。

    首先,什么是深度测试?假设您要在您面前直接绘制 2 个相同的形状,但与您的距离不同。在现实生活中,您希望只看到离您更近的形状,对吗?

    如果你在没有深度测试的情况下尝试这样做,你只会得到一半的效果:如果远处的物体在近处的物体之前绘制,没问题,和现实生活一样;但是如果较近的物体在远距离物体之前绘制,哦,哦,远处的物体在不应该看到的时候是可见的。有问题的。

    深度测试是一种内置于当今 GPU 中的工具,允许获得所需的绘制输出无论对象的绘制顺序如何。这通常非常有用,但也有一个严重的弱点:深度和混合(透明度)不能一起工作。为什么会这样?那么深度测试所做的是,对于绘制的每个像素,将该像素到相机的距离(深度)与存储在该像素中的深度值进行比较。如果距离小于存储的深度值,则绘制像素,否则丢弃该像素。

    这解释了为什么您有时会在演示中看到黑色四边形。当首先绘制这些四边形时,它们的深度值被写入深度缓冲区。然后,当绘制更远的四边形时,它们的深度值大于缓冲区中的深度,因此这些像素被丢弃。在其他视角下,恰好先绘制较远的四边形,然后绘制较近的四边形,因此不会因深度测试而丢弃任何像素。

    希望现在已经清楚了,深度测试有两个方面:深度值的比较和将深度值写入深度缓冲区。 DepthTest 和 depthWrite 让您可以很好地控制如何归档想要的效果。

    一起关闭深度测试会比仅仅深度写入更快。但是,有时您只想阻止新像素写入深度缓冲区,但仍启用深度测试。例如,在您的演示中,如果您要在中心绘制一个完全不透明的立方体;您仍然希望隐藏比表示不透明立方体的像素更深的像素(深度测试方面),但还希望防止透明圆圈中的像素相互阻挡(书写方面)。你看到的一个常见的绘制配置是在深度测试开启的情况下绘制所有不透明对象,关闭深度注销,然后以从后到前的顺序绘制透明对象。

    【讨论】:

    • 啊,好的,谢谢你的帮助!因此,如果我将一个实心立方体放入我的示例代码(例如这里的jsbin.com/bigikijogi/edit?js,output)并使用depthTest: false,那么当在实心几何体后面时,圆圈将不会被正确遮挡。但如果我使用depthWrite: false,它们会被正确阻止。
    • 优秀的答案。它帮助我在这里解决了一个类似的问题:stackoverflow.com/questions/42871582/…
    猜你喜欢
    • 2015-06-29
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 2019-10-12
    相关资源
    最近更新 更多