Three.js displacementMap does not affect lighting, only vertices - javascript

I'm using Three.js to apply a displacement map to a simple plane. The displacement applies successfully, but the lighting is wrong, as if all the normals remain unchanged. The result is a surface with the right shape that's lit as if it were flat.
displacement map:
result:
How can I fix this to properly change the lighting?
Here is the relevant bit of code:
// shortened from actual code - please excuse any small typos
var renderer = new THREE.WebGLRenderer();
renderer.setSize(500, 250);
var scene = new THREE.Scene();
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 100, 100);
var ambient = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(light);
scene.add(ambient);
var camera = new THREE.PerspectiveCamera(60, 2, 1, 20000);
var geometry = new THREE.PlaneBufferGeometry(100, 100, 1000, 1000);
geometry.rotateX(-Math.PI / 2);
var material = new THREE.MeshPhongMaterial();
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load('circlemap.png');
material.displacementScale = 20;
material.displacementMap = texture;
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// ... later
renderer.render(scene, camera)
edit: when I set flatShading: true in the material, the normals are updated correctly, but look bad (because of the flat shading):

You're going to have to pass a normalMap along with your displacementMap.
See this demo, when normalScale = 0 it's the equivalent of having no normalMap, and you can see that the reflections don't follow the displacement, only the default topography. However, when normalMap is 1, then the reflections do respect the displacement.
You need to generate a normalMap.

Related

Three.js - How to scale a circle without scaling it's stroke?

I'm using a THREE.Path to create a Circular path and then using a TubeGeometry to use that Path to basically create a circle with transparent fill and a stroke whose thickness I can control. My question is, how can I scale up the Circular path at runtime?
Accessing the vertices through mesh.vertices results in a very weird result, because I'm accessing the Tube's Geometry, not the Path. If I change the path, then I have to create a brand new TubeGeometry and update the Tube with the new geometry by doing mesh.geometry = newTubeGeometry, and that just doesn't work at all. Scaling the Tube itself increases it's radius as well, so that's not the right solution either.
Any ideas?
Thanks
Just changing the geometry property does not work, you have to create a new instance of THREE.Mesh. You should also call geometry.dispose() on your old geometry because the renderer internally caches the corresponding WebGLBuffer objects. More information here:
https://discourse.threejs.org/t/changing-the-geometry-of-a-mesh/306
Just an example of how you can do that trick with THREE.TorusGeometry():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 5, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geometry = new THREE.TorusGeometry(5, 0.5, 8, 32);
geometry.rotateX(Math.PI * -0.5);
geometry.vertices.forEach(v => {
v.initPosition = new THREE.Vector3().copy(v);
});
var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
scene.add(mesh);
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
geometry.vertices.forEach(v => {
v // take a vertex
.setY(0) // make it scalable in two dimensions (x, z)
.normalize() // make a normal from it
.multiplyScalar(Math.sin(time) * 2) // set the distance
.add(v.initPosition); // add the initial position of the vertex
});
geometry.verticesNeedUpdate = true;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

How to create a 3D star view in three.js given the x,y and z coordinates?

I am trying to create a 3d star view, with a movable camera to traverse stars (js objects) using three.js. In searching for something to get me going/started, I found some code (below) and modified it to what seemed 'logically' correct. The code:
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500);
camera.position.set(0, 0, 100);
camera.lookAt(new THREE.Vector3(0, 0, 0));
var scene = new THREE.Scene();
var geometry = new THREE.Geometry();
// Starlist contains 1600+ stars
for (var i=0;i<oldstarlist.length;i++)
{
// Convert RA/DEC to x,y,z coordinates
var s0 = new Star(oldstarlist[i]['starname'], oldstarlist[i]['constellation'], oldstarlist[i]['RA'], oldstarlist[i]['DEC'], oldstarlist[i]['lightyears']);
var star = new THREE.Vector3();
star.x = s0.coordinates.X;
star.y = s0.coordinates.Y;
star.z = s0.coordinates.Z;
console.log(star.x,star.y,star.z);
geometry.vertices.push( star );
}
var starsMaterial = new THREE.PointsMaterial( { color: 0x888888 } );
var starField = new THREE.Points( geometry, starsMaterial );
scene.add( starField );
renderer.render(scene, camera);
I presumed the 'Points' was correct, and I am still not sure where to place the camera. To be sure, I checked around for any examples and tutorials I could find but found nothing similar to what I am looking for.
A working example can be found on my test site area at this address:
http://theangelfallseries.com/stars/sp/test3js.html
Any help is appreciated.

Three.js: WebGL (error) drawing texture on a plane

The problem:
Ok i'm trying draw a few planes with three.js and one of these have a texture but is not drawed when i use WebGLRenderer(), if i change the renderer by CanvasRenderer(), draw the texture, the problem is with WebGL/Three.js.
The error:
Firefox console error: "WebGL: drawElements: bound vertex attribute buffers do not have sufficient size for given indices from the bound element array"
UPDATE
Google chrome error: "[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1"
The Code:
//scene-camera
var scene = new THREE.Scene ();
var camera = new THREE.PerspectiveCamera (90, window.innerWidth/window.innerHeight, 1, 1000);
camera.position.z = 600;
camera.updateMatrixWorld (true);
scene.add (camera);
//renderer
var renderer = new THREE.WebGLRenderer ();
renderer.setSize (window.innerWidth, window.innerHeight);
document.body.appendChild (renderer.domElement);
//geometry
var geometryPlane = new THREE.PlaneGeometry (600, 400);
//textures
var textureWall1 = THREE.ImageUtils.loadTexture ('textures/wall1.jpg');
textureWall1.anisotropy = renderer.getMaxAnisotropy();
//materials
var materialWall1 = new THREE.MeshBasicMaterial ({map: textureWall1});
//objets-plane
var plane = new THREE.Mesh (geometryPlane, materialWall1);
//vectors
var vecX = new THREE.Vector3(1, 0, 0);
var vecY = new THREE.Vector3(0, 1, 0);
var vecZ = new THREE.Vector3(0, 0, 1);
var vec0 = new THREE.Vector3(0, 0, 0);
//add scene-camera position of the objects
scene.add (plane);
var render = function (){
//translate
plane.translateOnAxis (vecZ, 300);
renderer.render (scene, camera);
}
render ();
I think is a problem with de resolutions of the picture and the plane... but i already have try change it, but it still with the same result, if you need, the resolutions are:
Wall1.jpg (598, 398)px...
Thanks for any help!
PD: this isn't all code, the rest is others planes and materials (colors)

THREE.js Triangle is Black

So i was using THREE.JS and created a triangle. But even though i gave it color: 0xFF0000, it appears black on Screen.
Here is my Script:
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
var geometry = new THREE.Geometry(200,200,200);
var v1 = new THREE.Vector3(200,0,0); // Vector3 used to specify position
var v2 = new THREE.Vector3(-100,0,0);
var v3 = new THREE.Vector3(0,50,0); // 2d = all vertices in the same plane.. z = 0
// Push vertices represented by position vectors
geometry.vertices.push(v1);
geometry.vertices.push(v2);
geometry.vertices.push(v3);
// Push face, defined with vertices in counter clock-wise order
geometry.faces.push(new THREE.Face3(0, 2, 1));
// Create a material and combine with geometry to create our mesh
var redMat = new THREE.MeshLambertMaterial({color: 0xFF0000});
var triangle = new THREE.Mesh(geometry, redMat);
scene.add(triangle);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
Hope you can find the mistake i made. I am sure it's just something really stupid.
late to the party...but for all who come after me.
The issue here is the type of material used
change
new THREE.MeshLambertMaterial({color: 0xFF0000});
to
new THREE.MeshBasicMaterial({color: 0xFF0000});
and it will show up red
Even I've struggled from this problem for sometime. The solution for me was to add an ambient light to the scene if you're using Lambert Material.
Just add:
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
Try add computeFaceNormals call upon geometry object:
geometry.computeFaceNormals();
*from https://stackoverflow.com/a/49226898/2154075

Weird behaviour of shadows in THREE.js

I am trying to render a set of photos, positioned in 3-dimensional space, which cast shadows on one another. I am starting with two rectangles, and here is my code
function loadScene() {
var WIDTH = 1200,
HEIGHT = 500,
VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000,
world = document.getElementById('world'),
renderer = new THREE.WebGLRenderer(),
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR),
scene = new THREE.Scene(),
texture = THREE.ImageUtils.loadTexture('image.jpg', {}, function() {
renderer.render(scene, camera);
}),
material = new THREE.MeshLambertMaterial({map: texture}),
solidMaterial = new THREE.MeshPhongMaterial({color: 0xCC0000}),
rectangle = new THREE.PlaneGeometry(200, 120),
mesh = new THREE.Mesh(rectangle, material),
mesh1 = new THREE.Mesh(rectangle, material),
spotLight = new THREE.SpotLight(0xFFFFFF, 1.3);
camera.position.set(0, 0, 200);
mesh.translateX(140);
mesh.translateZ(-70);
mesh.receiveShadow = true;
mesh1.castShadow = true;
spotLight.position.x = -100;
spotLight.position.y = 230;
spotLight.position.z = 230;
spotLight.castShadow = true;
scene.add(spotLight);
scene.add(mesh);
scene.add(mesh1);
renderer.setSize(WIDTH, HEIGHT);
renderer.shadowMapEnabled = true;
renderer.render(scene, camera);
world.appendChild(renderer.domElement);
}
Here solidMaterial is a solid red, and material is a textured material. What I get is the following:
If I use material for both the meshes, the rectangles appear as expected, but without shadows. Same if I use solidMaterial for both.
If I use material for mesh (the one farther away) and solidMaterial for mesh1, I see a red rectangle which casts shadow on a textured one. This is the only case that works as I would expect.
If I use solidMaterial for mesh (the one farther away) and material for mesh1, I see a red rectangle with the shadow on it, but the textured rectangle in front is not drawn at all.
What is the correct use of shadows?
It turns out the shadow does not appear when the two rectangles have the same material. I wonder whether this a bug in THREE.js.
On the other hand, if I use two different material, the shadow appears as expected, even if they have the same texture.

Categories