I'm just starting to get my bearings with threejs and I'm having an issue using Displacement Maps.
http://codepen.io/jpschwinghamer/pen/BWPebJ
I have a simple BoxGeometry that I'm trying to apply textures to a phong material. All seem to work correctly except for the displacement map. I made sure to add segments to the BoxGeometry instantiation. Is there some bit of magic that I'm missing to make my displacement map work correctly?
Consider this code:
var animate, camera, displacement, geometry, light, light1, map, material, mesh, normal, reflection, renderer, roughness, textureLoader;
renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('canvas'),
antialiased: true
});
renderer.setClearColor(0xfff000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 3000);
window.scene = new THREE.Scene();
light = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(light);
light1 = new THREE.PointLight(0xffffff, 0.6);
scene.add(light1);
textureLoader = new THREE.TextureLoader();
textureLoader.setCrossOrigin("anonymous");
map = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_COL_2K.jpg");
normal = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_NRM_2K.jpg");
roughness = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_GLOSS_2K.jpg");
reflection = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_REFL_2K.jpg");
displacement = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_DISP_2K.jpg");
geometry = new THREE.BoxGeometry(100, 100, 100, 10, 10, 10);
material = new THREE.MeshPhongMaterial({
map: map,
normalMap: normal,
normalScale: new THREE.Vector2(30, -1),
roughnessMap: roughness,
reflectionMap: reflection,
displacementMap: displacement,
displacementScale: 1,
displacementBias: 0
});
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, -700);
scene.add(mesh);
animate = function() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
requestAnimationFrame(animate);
return renderer.render(scene, camera);
};
animate();
The problem with your displacement map is that there is very little variation in the shades of grey (see it here).
The lowest points should be black and the highest should be white (or the other way around, I forget).
You would also need to have enough verticies for this to affect.
I think though that this map should be assigned to the bump map.
But... you probably don't want to use a bump map and normal map. it's usually one or the other.
But, having a look at the normal map you are loading, it is having no effect because it is all the same color. (see it here), so therefore, it's only the bump map that you need.
Also, MeshPhongMaterial does not seem to have roughnessMap or reflectionMap uniforms/properties, so these are doing nothing.
So basically, you only need to load you map and your displacement map, but put the displacement map on the bump map instead.
Maybe also your roughness image putting it on your specularMap.
EDIT.
Embedding code here does not seem to run because of CORS on the images.
See a FIDDLE HERE
textureLoader = new THREE.TextureLoader();
textureLoader.setCrossOrigin("anonymous");
map = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_COL_2K.jpg");
//normal = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_NRM_2K.jpg");
roughness = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_GLOSS_2K.jpg");
//reflection = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_REFL_2K.jpg");
displacement = textureLoader.load("https://s3-us-west-2.amazonaws.com/s.cdpn.io/65874/WoodFlooring044_DISP_2K.jpg");
// Create material
material = new THREE.MeshPhongMaterial({
map: map,
//normalMap: normal,
//normalScale: new THREE.Vector2(30, -1),
//roughnessMap: roughness,
//reflectionMap: reflection,
bumpMap: displacement,
bumpScale: 100,
//displacementBias: 0
specularMap: roughness
});
I believe the problem is that the BoxGeometry only contains 4 vertices per side. When you apply a displacement map, you actually move the positions of the vertices where a normal map would "fake" this on a fragment level.
To solve your problem, I would try with a geometry with a higher number of vertices. A sphere works fine for testing. Then import/generate a box geometry with more vertices.
Hope this helps!
Related
I am using multiple scenes as a workaround for selective lighting. Now, I meet a difficulty in using transparent objects.
For simplity, I created a jsfiddle illustration:
[1]: https://jsfiddle.net/curisiro/w9ke75ma/2/
I have two transparent squares which are in different scenes. The problem is I can see the blue square behind the red square (figure 1) but I can NOT see the red square behind the blue square (figure 2).
With material, by using other effects, depthTest and depthWrite must be set to true as default.
Do you have any solution to solve this problem?
Edit: If you insist on using two scenes, you can fix this problem by clearing the depth between the renders:
function render() {
requestAnimationFrame(render);
this.renderer.clear();
renderer.render(scene, camera);
renderer.clearDepth(); // <--- Like this
renderer.render(scene1, camera);
}
However, this is limiting if you plan to add more complexity to the scene and need depth testing to take place between them. Alternatively, just render to the same scene:
let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.MeshStandardMaterial({color: 0x0000ff, transparent: true, opacity: 0.4});
let mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
let geometry1 = new THREE.BoxGeometry(1, 1, 1);
let material1 = new THREE.MeshStandardMaterial({color: 0xff0000, transparent: true, opacity: 0.4});
let mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.position.z = 2;
scene.add(mesh1);
(see forked fiddle). In this case, you would handle selective lighting some other way (layers, or custom materials perhaps, depending on what you need).
I have two sphere objects on the scene. Both of them been made with default position (scene center). There is no problem when objects are in the middle, however when I want to move one to the right and second to the left, strange deformation has a place. When spheres moving away from the center on X axis they seems to be more squeezed on Y axis. It is kind of "FishEye" lens effect. Is it possible that some default cameras value is interfering to make such a result? FOV value does not bring solution for that, and I did not find information about camera lens properties. What is wrong with that?
I have tried to play with Vector3 as a position provider and spheres has the same result.
I have tried object.position.set(vector3) with no result.
Also object.position.copy(vector3) gave the same result.
Trying translate position without animation gave the same result.
Playing with FOV parameter in camera object also did not solve the problem.
// init
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1,10000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//creating sphere
var geometry = new THREE.SphereGeometry(6, 16, 16);
var material = new THREE.MeshBasicMaterial({color: 0xffffff, wireframe: true});
var sphere = new THREE.Mesh(geometry, material);
//earth
var geometry = new THREE.SphereGeometry(3, 16, 16);
var material = new THREE.MeshBasicMaterial({color: 0x0000ff, wireframe: true});
var earth = new THREE.Mesh(geometry, material);
scene.add(sphere, earth);
camera.position.z = 10;
var animation = function(){
requestAnimationFrame(animation);
update();
renderer.render(scene,camera);
}
var update = function(){
earth.rotation.y +=0.001;
sphere.rotation.y -=0.001;
sphere.rotation.x -=0.001;
sphere.position.x +=0.001;
}
I expect to move sphere.postion.x -=1; and earth.position.x+=1; without squeezing and deformation of spheres on Y axis.
Welcome to Stack Overflow. Thank you for taking the time to take the tour, and for including your code.
The fish-eye effect is likely being caused by your camera defintion:
var camera = new THREE.PerspectiveCamera(100, window.innerWidth/window.innerHeight, 0.1,10000);
The 100 is the FOV (Field of View) for your camera, and is quite wide. In a PerspectiveCamera, the wider your FOV, the more distortion you'll see for objects closer to the edges. Try setting it lower to get a more natural effect.
Do some searches for "Perspective Distortion" and you should find a host of articles on why it happens and how to mitigate it. For starters, here's the Wikipedia page: https://en.wikipedia.org/wiki/Perspective_distortion_(photography) which has a nice animation of changing the FOV for an image of a house.
In three.js, I'm trying to draw a tetrahedron using THREE.TetrahedronGeometry where each face is a different color. When I use MeshNormalMaterial, each vertex has a different color but the faces are color gradients between the vertexes. This works for a BoxGeometry, but not for TetrahedronGeometry.
I tried using PhongMaterial with shading: THREE.FlatShading but that just gives me black or white faces.
I tried writing my own ShaderMaterial and in the fragment material, I color using the normal vector, but that also gets the gradient affect.
I'm sure I'm missing something obvious, but can't see it...
For versions of three.js prior to r125*, this is how you do it:
var geo = new THREE.TetrahedronGeometry(sphereRadius, 0);
for ( var i = 0; i < geo.faces.length; i ++ ) {
geo.faces[ i ].color.setHex( Math.random() * 0xffffff );
}
var material = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
shading: THREE.FlatShading,
vertexColors: THREE.VertexColors
})
var mesh = new THREE.Mesh( geo, material );
So you need THREE.FlatShader, THREE.VertexColors, and then you need to assign the face colors.
For later versions, see how to render a tetrahedron with different texture on each face (using three.js)?.
* THREE.Geometry will be removed from core with r125
I have an instance of THREE.PlaneBufferGeometry that I apply an image texture to like this:
var camera, scene, renderer;
var geometry, material, mesh, light, floor;
scene = new THREE.Scene();
THREE.ImageUtils.loadTexture( "someImage.png", undefined, handleLoaded, handleError );
function handleLoaded(texture) {
var geometry = new THREE.PlaneBufferGeometry(
texture.image.naturalWidth,
texture.image.naturalHeight,
1,
1
);
var material = new THREE.MeshBasicMaterial({
map: texture,
overdraw: true
});
floor = new THREE.Mesh( geometry, material );
floor.material.side = THREE.DoubleSide;
scene.add( floor );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, texture.image.naturalHeight * A_BUNCH );
camera.position.z = texture.image.naturalWidth * 0.5;
camera.position.y = SOME_INT;
camera.lookAt(floor.position);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
appendToDom();
animate();
}
function handleError() {
console.log(arguments);
}
function appendToDom() {
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene,camera);
}
Here's the code pen: http://codepen.io/anon/pen/qELxvj?editors=001
( Note: ThreeJs "pollutes" the global scope, to use a harsh term, and then decorates THREE using a decorator pattern--relying on scripts loading in the correct order without using a module loader system. So, for brevity's sake, I simply copy-pasted the source code of a few required decorators into the code pen to ensure they load in the right order. You'll have to scroll down several thousand lines to the bottom of the code pen to play with the code that instantiates the plane, paints it and moves the camera. )
In the code pen, I simply lay the plane flat against the x-y axis, looking straight up the z-axis, as it were. Then, I slowly pan the camera down along the y-axis, continuously pointing it at the plane.
As you can see in the code pen, as the camera moves along the y-axis in the negative direction, the texture on the plane appears to develop a kink in it around West Texas.
Why? How can I prevent this from happening?
I've seen similar behaviour, not in three.js, not in a browser with webGL but with directX and vvvv; still, i think you'll just have to set widthSegments/heightSegments of your PlaneBufferGeometry to a higher level (>4) and you're set!
We give color while initializing a material. We also specify a color while initializing ambient and directional light sources. How is the final color of the mesh is determined.
I see no change in the final color of mesh when i change the color of the material. However the rendered color of the mesh is changing while i change the color of light sources (ambient or directional).
So
1) what is the use of specifying a color, while initializing a material ?, and
2) How is the final color of the mesh is determined
darkMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
darkMaterialL = new THREE.MeshLambertMaterial( { color: 0xffff00 } );
darkMaterialP = new THREE.MeshPhongMaterial( { color: 0xffff00 } );
var ambientLight = new THREE.AmbientLight(0x00ff00);
var light = new THREE.PointLight(0x000000);
light.position.set(0,150,100);
scene.add(ambientLight);
scene.add(light);
The above are the lights and materials i used.
I wrote a jsfiddle for you to take a look at: http://jsfiddle.net/fnR4E/
var camera, scene, renderer;
var geometry = new Array();
var material = new Array();
var mesh = new Array();
var light;
var angle = 0.1;
init();
render();
function init() {
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 5;
camera.position.y = 5;
scene = new THREE.Scene();
geometry[0] = new THREE.SphereGeometry(1, 8, 6, 0, Math.PI * 2, 0, Math.PI);
geometry[1] = new THREE.SphereGeometry(1, 8, 6, 0, Math.PI * 2, 0, Math.PI);
geometry[2] = new THREE.SphereGeometry(1, 8, 6, 0, Math.PI * 2, 0, Math.PI);
material[0] = new THREE.MeshBasicMaterial({ color: 0xff0000 });
material[1] = new THREE.MeshLambertMaterial({ ambient: 0xffffff, color: 0x00FF00 });
material[2] = new THREE.MeshPhongMaterial({ ambient: 0xffffff, color: 0xdddddd, specular: 0xFFFFFF, shininess: 15 });
mesh[0] = new THREE.Mesh(geometry[0], material[0]);
mesh[1] = new THREE.Mesh(geometry[1], material[1]);
mesh[2] = new THREE.Mesh(geometry[2], material[2]);
var ambientLight = new THREE.AmbientLight(0x007700);
var light = new THREE.PointLight(0xFFFFFF);
light.position.set(0, 2, 0);
scene.add(ambientLight);
scene.add(light);
mesh[0].position.set(-2, 0, 0);
mesh[2].position.set(2, 0, 0);
scene.add(mesh[0]);
scene.add(mesh[1]);
scene.add(mesh[2]);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function render() {
requestAnimationFrame(render);
camera.position.x = 5 * Math.cos(angle);
camera.position.z = 5 * Math.sin(angle);
camera.lookAt(new THREE.Vector3(0, 0, 0));
angle += 0.01;
renderer.render(scene, camera);
}
The first mesh is using MeshBasicMaterial which essentially means it is lit by material color alone, for proof you can change the values of ambientLight and light to whatever you want and it won't effect the rendered color of this mesh.
The following two meshes (the first is MeshLambertMaterial and the second is MeshPhongMaterial) use both lights. For additional reading on the theory behind each of the shading models (Lambertian and Phong) check out these excellent wikipedia articles:
http://en.wikipedia.org/wiki/Lambertian_reflectance
http://en.wikipedia.org/wiki/Phong_reflection_model
Here is a more "practical" explanation of what is going on (but you'll probably at least want to refer to the wiki articles for the equations that are discussed below):
The ambientLight is multiplied by the material 'ambient' value to produce the mesh ambient color. This color only gets used up to the amount specified by the diffuse color of the material. For example, if material ambient value is 0xFFFFFF and AmbientLight is 0x00FF00 then the mesh has a fully green ambient light - but, if the diffuse color of the material ('color') contains NO green color channel (e.g. 0xFF00FF) then there is no ambient light applied to the mesh. Alternatively, if there is a diffuse color of 0x007700 (half of the full green channel) then you will see ambient light on the object of the color 0x007700.
The diffuse color is denoted by the material 'color' value. This is the perceived color of the mesh. In both the Lambert and BlinnPhong shading models this color is multiplied by the dot product of the vertex or fragment normal with the light vector. In essence, this means that the more directly lit a vertex or fragment is - the closer to the full diffuse color it will be. A vertex or fragment that is not directly lit by a light source at all is black. AmbientLight sources are not included in this dot product calculation.
NOTE: Occluding meshes are not accounted for in this dot product calculation. Only the angle between the light source and the vertex or fragment is considered.
Finally, the MeshPhongMaterial uses an additional property called specular. This is the reflective light that produces the "shiny" spot on a mesh. This comes from calculating the angle of reflection against the normal from the light source. The material property 'specular' determines the color of this reflection spot. Once again, AmbientLight sources are not included in this lighting calculation.
NOTE: Once again, occluding meshes are not accounted for in this calculation.
Fixed the problem.