Raycaster ignores displacement maps and their effct on geometries
See original GitHub issueDescribe the bug
I’m not sure if this is a bug or the expected behavior, but according to the Three.js manual, “a displacement map affects the position of the mesh’s vertices” and “the displaced vertices can cast shadows, block other objects, and otherwise act as real geometry”, so based on that, they should affect the results produced by a Raycaster. However, they do not.
To Reproduce
Steps to reproduce the behavior:
- Create a basic Three.js setup
- Rotate the camera around an object that is using a displacement map
- Use Raycaster to cast a ray towards the object
- Notice that the logged distance to the intersect point with the object doesn’t change
Code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {margin: 0;}
</style>
</head>
<body>
<script type="importmap">
{
"imports":
{
"three": "http://localhost:8080/three.module.js",
"ArcballControls": "http://localhost:8080/ArcballControls.js"
}
}
</script>
<script type="module">
import * as THREE from "three";
import {ArcballControls} from "ArcballControls";
var scene, camera, light, renderer, earth, controls, reqid, paused = false;
function createcontrols()
{
controls = new ArcballControls(camera, renderer.domElement, scene);
controls.addEventListener("change", function(e) {renderer.render(scene, camera); controls.update();});
};
function create()
{
renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 1.75);
light = new THREE.AmbientLight("rgb(255, 255, 255)");
light.position.copy(camera.position);
var geometry = new THREE.SphereGeometry(1, 360, 180);
var material = new THREE.MeshPhongMaterial();
material.map = new THREE.TextureLoader().load("http://localhost:8080/Earth.png");
material.displacementMap = new THREE.TextureLoader().load("http://localhost:8080/Earth - Height.png");
material.displacementScale = 0.002 * 99;
earth = new THREE.Mesh(geometry, material);
scene = new THREE.Scene();
scene.add(camera);
scene.add(light);
scene.add(earth);
createcontrols();
document.addEventListener("keyup", function(e)
{
switch (e.code)
{
case "KeyP": console.log("P"); paused = !paused; if (!paused) {animate();} else {stagnate();}; break;
};
}, {passive: false});
};
function raycast()
{
var cameradirection = new THREE.Vector3();
camera.getWorldDirection(cameradirection);
cameradirection.normalize();
var cameraorigin = new THREE.Vector3();
camera.getWorldPosition(cameraorigin);
var raycaster = new THREE.Raycaster(cameraorigin, cameradirection);
var intersects = raycaster.intersectObjects([earth], false);
if (intersects.length) {console.log(intersects[0].distance);};
};
function animate()
{
if (paused) {return;};
requestAnimationFrame(animate);
earth.rotation.y += 0.01;
renderer.render(scene, camera);
raycast();
};
function stagnate()
{
if (!paused) {return;};
cancelAnimationFrame(reqid);
};
create();
animate();
</script>
</body>
</html>
Live example
None
Expected behavior
I expect Raycaster to react to the effects that the displacement map “should” have on the geometry of the object (and not only on its material, according to the manual), and log different distances to the intersection point with the object, based on the “height of the terrain” being displaced. I intentionally set a considerable displacement scale in the code above to make it even more obvious. My actual usage scenario is preventing the camera to approach closer than the minimum distance to an object, but according to the altitudes / terrain on the object’s surface. Let me know if Raycaster ignoring displacement maps is a bug or it’s something expected to happen due to the displacement maps being just as “fake” as the bump maps (i.e. not affecting the real geometry of the object).
Screenshots
None
Platform:
- Device: Desktop
- OS: Windows 10
- Browser: Chrome, Edge WebView2 via the WebView plugin for Rainmeter
- Three.js version: r.140, r.143
Issue Analytics
- State:
- Created a year ago
- Comments:16 (4 by maintainers)
Top GitHub Comments
Maybe you shouldn’t use the displacement map but instead just change the geometry based on the map data?
You’re right -
.fromBufferAttribute()
does indeed simplify things, I will use that instead, thanks! Avoiding creating reusable entities is a good advice as well, unfortunately it’s something that I personally only think of once the job of figuring out how to do something (i.e. the “hard” part) is done, since the former almost always needs the latter in order to get something out of it.