javascript – Three.js – depthWrite vs depthTest for THREE.Points上的透明画布纹理贴图

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

原来的问题

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

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

我在THREE.PointsMaterial上了解了depthWrite:false和depthTest:false后修复了它.

我在哪里

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

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

我对这一切都很陌生,但是我试着阅读这个主题,并且从我所知道的(如果我错了,请纠正我)深度缓冲区用于确定哪些片段被遮挡而不需要渲染.关闭depthWrite或depthTest将使对象免于此过程.他们的不同之处在于:

> 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>

解决方法:

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

深度写入意味着防止深度缓冲区被写入.

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

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

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

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

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

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

上一篇:javascript – 是否有一个网站,我可以看到在不同的浏览器中支持哪些HTML 5标签?和WebGL?


下一篇:在JavaScript / HTML5中显示3D模型