Three.js - THREE.Points上透明画布纹理贴图的depthWrite与depthTest

时间:2016-06-05 23:41:13

标签: javascript three.js webgl

问题

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

原始问题

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

透明度并没有完全发挥作用,有些圆圈重叠得很好,但其他圆圈的表现就好像是坚固的。

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

我在哪里

我有一个代码示例(嵌入在底部),显示重叠点错误,可以使用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>

1 个答案:

答案 0 :(得分:21)

深度测试关闭意味着一起关闭深度测试。 (阅读/测试和写作)

深度注销意味着防止深度缓冲区被写入。

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

如果你试图在没有深度测试的情况下做到这一点,你只会在一半的时间内获得所需的效果:如果远处的物体在较近的物体之前被绘制,没有问题,与现实生活相同;但如果在距离物体之前画出更近的物体,哦 - 哦,远处的物体应该是可见的。有问题的。

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

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

希望现在很清楚,深度测试有两个方面:深度值的比较和深度值写入深度缓冲区。 DepthTest和depthWrite可以很好地控制如何存档期望的效果。

一起关闭深度测试会比深度编写更快。但是,有时您只想阻止新像素写入深度缓冲区,但仍然启用了深度测试。例如,在您的演示中,如果您要在中心绘制一个完全不透明的立方体;你仍然希望像素的深度远远超过隐藏不透明立方体的像素(深度测试方面),但也希望防止透明圆圈中的像素相互阻挡(写入方面)。您看到的常见绘图配置是在深度测试中绘制所有不透明对象,转动深度注释,然后按照从前到后的顺序绘制透明对象。