Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPU - Duplicate shadow when logarithmicDepthBuffer is true - Shadow appears below and above object (WebGPU build 167) #29200

Closed
PoseidonEnergy opened this issue Aug 21, 2024 · 3 comments · Fixed by #29447

Comments

@PoseidonEnergy
Copy link
Contributor

PoseidonEnergy commented Aug 21, 2024

Description

Note: I'm using the WebGPU build 167 of three.js for this report.

See screenshot, JSFiddle, and provided code. I have a simple sphere between two "floors", with a DirectionalLight pointing straight down from above. Notice how the sphere casts a shadow on the top "floor". This top shadow appears when logarithmicDepthBuffer is true. How can I remove the shadow from the top floor? I don't want to disable logarithmicDepthBuffer, because I need it elsewhere in my scene. I also don't want to disable the top floor's ability to receive shadows from other objects, so I can't set receiveShadow = false on the top floor mesh.

JSFiddle: https://jsfiddle.net/0fwkyvu6

discourse thread: https://discourse.threejs.org/t/duplicate-shadow-shadow-appears-below-and-above-object-webgpu-build-167/69481

StackOverflow question: https://stackoverflow.com/questions/78893677/three-js-duplicate-shadow-issue-bug-shadow-appears-below-and-above-object

Update 1: setting logarithmicDepthBuffer: false appears to fix the issue somewhat, however the logarithmic depth buffer is a requirement for my scene, so I'd like to get the shadow working with it.

Update 2: the top shadow is not visible in the standard WebGLRenderer build of three.js, so there must be some issue specifically with the WebGPU build.

image

Reproduction steps

Please use the provided code to reproduce the issue.

Code

<!doctype html>
<html>
<head>
<title>WebGPU 167 Shadow Bug<title>
<style>
* {
  margin: 0;
  border: 0;
  padding: 0;
}

html {
  height: 100%;
  overflow: hidden;
}

body {
  height: 100%;
  overflow: hidden;
  font-family: sans-serif;
  background-color:#808080;
}
</style>
</head>
<body>
<script>
(async function() {
  const THREE = await (async function() {
    const url = "https://unpkg.com/three@0.167.1/build/three.webgpu.min.js";
    const code = await (await fetch(url)).text();
    return import("data:text/javascript;base64," + btoa(code));
  })();
  const renderer = new THREE.WebGPURenderer({
    antialias: false,
    logarithmicDepthBuffer: true,
    powerPreference: "high-performance"
  });
  if (renderer.backend.isWebGPUBackend !== true) {
    const div = document.createElement("div");
    div.style.cssText = "position:absolute;inset:0;z-index:10;background-color:#ffffff;padding:20px;text-align:center;";
    div.innerHTML = "Alert: This demo must be run with WebGPU. Please use a browser that supports WebGPU.";
    document.body.appendChild(div);
    return;
  }
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.BasicShadowMap;
  renderer.outputColorSpace = THREE.SRGBColorSpace;
  document.body.appendChild(renderer.domElement);
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(0, 8, 40);
  camera.rotation.set(-0.37, 0, 0);
  const light = new THREE.DirectionalLight(0xffffff, Math.PI);
  light.position.set(0, 15, 0);
  light.target.position.set(0, 0, 0);
  const shadowCam = light.shadow.camera;
  light.castShadow = true;
  light.shadow.mapSize.width = 8192;
  light.shadow.mapSize.height = 8192;
  shadowCam.near = 5;
  shadowCam.far = 40;
  const d = 16;
  shadowCam.left = -d;
  shadowCam.right = d;
  shadowCam.top = d;
  shadowCam.bottom = -d;
  shadowCam.updateProjectionMatrix();
  const box1Geometry = new THREE.BoxGeometry(40, 1, 20, 1, 1, 1);
  const box2Geometry = new THREE.BoxGeometry(40, 1, 20, 1, 1, 1);
  const sphereGeometry = new THREE.SphereGeometry(6, 32, 32);
  const box1Mesh = new THREE.Mesh(box1Geometry, new THREE.MeshLambertMaterial({
    color: new THREE.Color(0xffffff)
  }));
  const box2Mesh = new THREE.Mesh(box1Geometry, new THREE.MeshLambertMaterial({
    color: new THREE.Color(0x3ff57b)
  }));
  const sphereMesh = new THREE.Mesh(sphereGeometry, new THREE.MeshLambertMaterial({
    color: new THREE.Color(0x3e9df4)
  }));
  sphereMesh.position.set(0, -10, 0);
  sphereMesh.castShadow = true;
  box2Mesh.position.set(0, -20, 0);
  box1Mesh.receiveShadow = true;
  box2Mesh.receiveShadow = true;
  scene.add(light);
  scene.add(light.target);
  scene.add(box1Mesh);
  scene.add(box2Mesh);
  scene.add(sphereMesh);
  scene.add(new THREE.CameraHelper(shadowCam));
  await renderer.renderAsync(scene, camera);
  setInterval(function() {
    camera.aspect = document.body.offsetWidth / document.body.offsetHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(document.body.offsetWidth, document.body.offsetHeight);
    renderer.render(scene, camera);
  }, 100);
})();
</script>
</body>
</html>

Version

167

Device

Desktop

Browser

Chrome, Edge

OS

Windows

@PoseidonEnergy
Copy link
Contributor Author

PoseidonEnergy commented Aug 21, 2024

I've been playing around with the AnalyticLightNode.setupShadow() function in three.webgpu.js (version 168dev) in the hope that I'll stumble upon a fix, but I've had no luck. The code in the AnalyticLightNode class looks interesting, and may be where the bug is, but I'm not sure...the fix may lie elsewhere.

Link to version 168: https://unpkg.com/browse/three@0.168.0/build/three.webgpu.js

if ( renderer.coordinateSystem === WebGPUCoordinateSystem ) {
    coordZ = coordZ.mul( 2 ).sub( 1 ); // WebGPU: Convertion [ 0, 1 ] to [ - 1, 1 ]
}

@PoseidonEnergy
Copy link
Contributor Author

PoseidonEnergy commented Sep 15, 2024

It looks like this particular issue has come up several times in the past with WebGL and was eventually fixed. Here are the relevant historic issues and a pull request for WebGL:

Issue: #7815
Issue: #9108
Issue: #12126
Issue: #17525
Pull Request: #17442

Hopefully the fix for WebGPU is the same?

I also found this pull request for WebGPU that appears to be related: #27243

I've tinkered around with the depth calculation nodes and was able to get the shadow working (somewhat) by changing this code in three.webgpu.js (excerpt is from the setupDepth() function in the NodeMaterial class):

// three.webgpu.js v168 (line 32506)
// https://unpkg.com/browse/three@0.168.0/build/three.webgpu.js
const fragDepth = modelViewProjection().w.add(1);
depthNode = fragDepth.log2().mul(cameraLogDepth).mul(0.5);

to this:

depthNode = viewZToOrthographicDepth(positionView.z, cameraNear, cameraFar);

Can someone tell me what's going on here?

@PoseidonEnergy
Copy link
Contributor Author

PoseidonEnergy commented Sep 19, 2024

Fixed in this pull request I just submitted: #29447

PoseidonEnergy added a commit to PoseidonEnergy/three.js that referenced this issue Sep 21, 2024
1) Disabled logarithmic depth buffer for orthographic cameras.
2) Removed "cameraLogDepth" node from Camera.js as it is no longer used in the perspective-to-logarithmic depth conversion.
3) Ensured that modelViewProjection.w is used for perspective-to-logarithmic depth conversion, and that positionView.z is always used for orthographic cameras.
4) Adapted Outerra's logarithmic depth buffer formula, re-added "C" constant for increased resolution close to the camera, per Outerra's findings.
(issue mrdoob#29200, PR mrdoob#29447)
PoseidonEnergy added a commit to PoseidonEnergy/three.js that referenced this issue Sep 23, 2024
1) Removed "cameraLogDepth" node from Camera.js as it is no longer used in the perspective-to-logarithmic depth conversion.
2) Removed "cameraIsPerspective" and "cameraIsOrthographic" nodes that were added in a previous commit
3) Ensured that modelViewProjection.w is used for perspective-to-logarithmic depth conversion, and that positionView.z is always used for orthographic cameras.
4) Adapted a modified version of Outerra's logarithmic depth buffer without requiring a "C" constant
(issue mrdoob#29200, PR mrdoob#29447)
PoseidonEnergy added a commit to PoseidonEnergy/three.js that referenced this issue Sep 23, 2024
1) fixed bug where the shadowmap's "coordZ" was not getting set when not using WebGPU
2) fixed typos in comments
(issue mrdoob#29200, PR mrdoob#29447)
sunag pushed a commit that referenced this issue Sep 28, 2024
…icDepthBuffer` (#29447)

* Fixed shadows not rendering correctly when logarithmicDepthBuffer = true (issue #29200)

* Further improvements to the logarithmic depth buffer shadows bugfix:
1) Disabled logarithmic depth buffer for orthographic cameras.
2) Removed "cameraLogDepth" node from Camera.js as it is no longer used in the perspective-to-logarithmic depth conversion.
3) Ensured that modelViewProjection.w is used for perspective-to-logarithmic depth conversion, and that positionView.z is always used for orthographic cameras.
4) Adapted Outerra's logarithmic depth buffer formula, re-added "C" constant for increased resolution close to the camera, per Outerra's findings.
(issue #29200, PR #29447)

* Further improvements to the logarithmic depth buffer shadows bugfix:
1) Removed "cameraLogDepth" node from Camera.js as it is no longer used in the perspective-to-logarithmic depth conversion.
2) Removed "cameraIsPerspective" and "cameraIsOrthographic" nodes that were added in a previous commit
3) Ensured that modelViewProjection.w is used for perspective-to-logarithmic depth conversion, and that positionView.z is always used for orthographic cameras.
4) Adapted a modified version of Outerra's logarithmic depth buffer without requiring a "C" constant
(issue #29200, PR #29447)

* Further improvements to the logarithmic depth buffer shadows bugfix:
1) fixed bug where the shadowmap's "coordZ" was not getting set when not using WebGPU
2) fixed typos in comments
(issue #29200, PR #29447)

* cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants