5
votes

I'm trying to use the Logarithmic Depth Buffer with an Orthographic camera, and encountering some interesting results. I've constructed a sample here: http://jsfiddle.net/TheJim01/05up96m0/

The renderer on the left (the red knot) uses a standard linear depth buffer (1 to 1000). Selecting Perspective or Orthographic will re-render the scene with the selected camera type, and both appear to work as expected.

The renderer on the right (the green knot) uses a logarithmic depth buffer (1e-6 to 1e27). As with the left sample, selecting a camera will re-render the scene with that camera. But in this case, only the Perspective camera works as expected. When Orthographic is selected, the knot "unties". It looks almost like it's not considering the depth buffer at all.

Am I using it wrong, or is something else going on here?

var WIDTH = 250,
  HEIGHT = 250,
  BACKGROUND = 0xcccccc;

var gl1 = new THREE.WebGLRenderer({
    antialias: true,
    logarithmicDepthBuffer: false
  }),
  gl2 = new THREE.WebGLRenderer({
    antialias: true,
    logarithmicDepthBuffer: true
  }),
  scene1 = new THREE.Scene(),
  scene2 = new THREE.Scene(),
  s1pCam = new THREE.PerspectiveCamera(
    28,
    WIDTH / HEIGHT,
    1,
    1000
  ),
  s1oCam = new THREE.OrthographicCamera(-1 * (WIDTH / HEIGHT),
    1 * (WIDTH / HEIGHT),
    1, -1,
    1,
    1000),
  aLight1 = new THREE.AmbientLight(0x333333),
  dLight1 = new THREE.DirectionalLight(0xffffff, 0.75);

s1pCam.position.set(0, 0, 100);
s1pCam.lookAt(scene1.position);
s1oCam.position.set(0, 0, 100);
s1oCam.lookAt(scene1.position);
dLight1.position.set(0, 0, 100);
dLight1.lookAt(new THREE.Vector3(0, 0, -1));

// calculate ortho frustum
var modelCenter = new THREE.Vector3(),
  tmpCamPosition = s1pCam.position.clone(),
  camTarget = new THREE.Vector3(),
  radFOV = (Math.PI / 180.) * s1pCam.fov;
modelCenter.sub(camTarget);
tmpCamPosition.sub(camTarget);
var projectedLocation = modelCenter.projectOnVector(tmpCamPosition);
var distance = tmpCamPosition.distanceTo(projectedLocation);
var halfHeight = Math.tan(radFOV / 2.) * distance;
var halfWidth = halfHeight * s1pCam.aspect;
s1oCam.left = -halfWidth;
s1oCam.right = halfWidth;
s1oCam.top = halfHeight;
s1oCam.bottom = -halfHeight;
s1oCam.zoom = s1pCam.zoom;
s1oCam.updateProjectionMatrix();

var s2pCam = s1pCam.clone(),
  s2oCam = s1oCam.clone(),
  aLight2 = aLight1.clone(),
  dLight2 = dLight1.clone();

s2pCam.near = 1e-6;
s2pCam.far = 1e27;
s2oCam.near = 1e-6;
s2oCam.far = 1e27;

scene1.add(s1pCam);
scene1.add(s1oCam);
scene1.add(aLight1);
scene1.add(dLight1);
scene1.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
  color: 'red'
})));

scene2.add(s2pCam);
scene2.add(s2oCam);
scene2.add(aLight2);
scene2.add(dLight2);
scene2.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
  color: 'green'
})));

document.getElementById("view1").appendChild(gl1.domElement);
document.getElementById("view2").appendChild(gl2.domElement);

gl1.setSize(WIDTH, HEIGHT);
gl1.setClearColor(BACKGROUND);

gl2.setSize(WIDTH, HEIGHT);
gl2.setClearColor(BACKGROUND);

gl1.render(scene1, s1pCam);
gl2.render(scene2, s2pCam);

function handleCameraChanges(e) {
  debugger;
  if (gl1 && e.target.id.indexOf("1") !== -1) {
    gl1.render(scene1, (e.target.id.indexOf("p") !== -1) ? s1pCam : s1oCam);
  } else if (gl2) {
    gl2.render(scene2, (e.target.id.indexOf("p") !== -1) ? s2pCam : s2oCam);
  }
}

document.getElementById("v1p").addEventListener("click", handleCameraChanges);
document.getElementById("v1o").addEventListener("click", handleCameraChanges);
document.getElementById("v2p").addEventListener("click", handleCameraChanges);
document.getElementById("v2o").addEventListener("click", handleCameraChanges);
.view {
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js"></script>

<fieldset id="view1" class="view">
  <legend>Standard Depth Buffer</legend>
  <label><input id="v1p" type="radio" name="cameraMode1" checked />Perspective</label>
  <label><input id="v1o" type="radio" name="cameraMode1" />Orthographic</label><br>
</fieldset>

<fieldset id="view2" class="view">
  <legend>Logarithmic Depth Buffer</legend>
  <label><input id="v2p" type="radio" name="cameraMode2" checked />Perspective</label>
  <label><input id="v2o" type="radio" name="cameraMode2" />Orthographic</label><br></fieldset>
1
Just a note: This occurs in Chrome and Firefox, but not IE, because IE doesn't support the EXT_frag_depth extension. - TheJim01
Your geometry may reside in just a sliver of depth space. You may not be getting sufficient depth precision. Experiment with the related three.js example(s). Sorry, I do not have sufficient time to pursue this. File a bug report if you think your have found a bug. - WestLangley
It is indeed a small model compared to the depth range. But the issue still exists, even if I set the depth range for all cameras to 75-125 (which fits the model much closer). No worries about not having time--we all have lives outside this stuff, and I appreciate your help on this and all the other assistance you've given across a multitude of topics. I'll proceed to submit this as a potential bug. Thanks! - TheJim01
Thanks. As usual, considerable thought, and nice example, on your part. - WestLangley

1 Answers

1
votes

This has been resolved in PR 17442, set for release in r109.

r109 was released on 30 September, 2019.

Here is the exact same example as above, using r109. Thanks to everyone involved in the fix!

var WIDTH = 250,
  HEIGHT = 250,
  BACKGROUND = 0xcccccc;

var gl1 = new THREE.WebGLRenderer({
    antialias: true,
    logarithmicDepthBuffer: false
  }),
  gl2 = new THREE.WebGLRenderer({
    antialias: true,
    logarithmicDepthBuffer: true
  }),
  scene1 = new THREE.Scene(),
  scene2 = new THREE.Scene(),
  s1pCam = new THREE.PerspectiveCamera(
    28,
    WIDTH / HEIGHT,
    1,
    1000
  ),
  s1oCam = new THREE.OrthographicCamera(-1 * (WIDTH / HEIGHT),
    1 * (WIDTH / HEIGHT),
    1, -1,
    1,
    1000),
  aLight1 = new THREE.AmbientLight(0x333333),
  dLight1 = new THREE.DirectionalLight(0xffffff, 0.75);

s1pCam.position.set(0, 0, 100);
s1pCam.lookAt(scene1.position);
s1oCam.position.set(0, 0, 100);
s1oCam.lookAt(scene1.position);
dLight1.position.set(0, 0, 100);
dLight1.lookAt(new THREE.Vector3(0, 0, -1));

// calculate ortho frustum
var modelCenter = new THREE.Vector3(),
  tmpCamPosition = s1pCam.position.clone(),
  camTarget = new THREE.Vector3(),
  radFOV = (Math.PI / 180.) * s1pCam.fov;
modelCenter.sub(camTarget);
tmpCamPosition.sub(camTarget);
var projectedLocation = modelCenter.projectOnVector(tmpCamPosition);
var distance = tmpCamPosition.distanceTo(projectedLocation);
var halfHeight = Math.tan(radFOV / 2.) * distance;
var halfWidth = halfHeight * s1pCam.aspect;
s1oCam.left = -halfWidth;
s1oCam.right = halfWidth;
s1oCam.top = halfHeight;
s1oCam.bottom = -halfHeight;
s1oCam.zoom = s1pCam.zoom;
s1oCam.updateProjectionMatrix();

var s2pCam = s1pCam.clone(),
  s2oCam = s1oCam.clone(),
  aLight2 = aLight1.clone(),
  dLight2 = dLight1.clone();

s2pCam.near = 1e-6;
s2pCam.far = 1e27;
s2oCam.near = 1e-6;
s2oCam.far = 1e27;

scene1.add(s1pCam);
scene1.add(s1oCam);
scene1.add(aLight1);
scene1.add(dLight1);
scene1.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
  color: 'red'
})));

scene2.add(s2pCam);
scene2.add(s2oCam);
scene2.add(aLight2);
scene2.add(dLight2);
scene2.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
  color: 'green'
})));

document.getElementById("view1").appendChild(gl1.domElement);
document.getElementById("view2").appendChild(gl2.domElement);

gl1.setSize(WIDTH, HEIGHT);
gl1.setClearColor(BACKGROUND);

gl2.setSize(WIDTH, HEIGHT);
gl2.setClearColor(BACKGROUND);

gl1.render(scene1, s1pCam);
gl2.render(scene2, s2pCam);

function handleCameraChanges(e) {
  debugger;
  if (gl1 && e.target.id.indexOf("1") !== -1) {
    gl1.render(scene1, (e.target.id.indexOf("p") !== -1) ? s1pCam : s1oCam);
  } else if (gl2) {
    gl2.render(scene2, (e.target.id.indexOf("p") !== -1) ? s2pCam : s2oCam);
  }
}

document.getElementById("v1p").addEventListener("click", handleCameraChanges);
document.getElementById("v1o").addEventListener("click", handleCameraChanges);
document.getElementById("v2p").addEventListener("click", handleCameraChanges);
document.getElementById("v2o").addEventListener("click", handleCameraChanges);
.view {
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>

<fieldset id="view1" class="view">
  <legend>Standard Depth Buffer</legend>
  <label><input id="v1p" type="radio" name="cameraMode1" checked />Perspective</label>
  <label><input id="v1o" type="radio" name="cameraMode1" />Orthographic</label><br>
</fieldset>

<fieldset id="view2" class="view">
  <legend>Logarithmic Depth Buffer</legend>
  <label><input id="v2p" type="radio" name="cameraMode2" checked />Perspective</label>
  <label><input id="v2o" type="radio" name="cameraMode2" />Orthographic</label><br></fieldset>