No cubemap ("material.envMap") reflection on polygon faces with orthographic cam - three.js

Why won't a MeshPhongMaterial's envMap property work on polygonal faces when viewed through an orthographic camera?
It works on spheres but not an IcosahedronGeometry, for example. If I set the detail parameter of the IcosahedronGeometry to 2+ (more faces), the envMap begins to show. But if I switch to perspective cam, the envMap is fully visible even with detail of 0.
This is what it looks like with perspective cam, note the cubemap reflection of some clouds:
This is what it looks like with orthogonal cam and detail is 0, note the lack of cubemap reflection (please ignore the warping of the image):
Orthogonal cam, detail is 1; cubemap reflection is back:
The only difference between these two versions of the script is the camera.
Here's the code I'm using to create this object:
import uvGridImg from './img/grid.png';
import nxImg from './img/nx_50.png';
import pxImg from './img/px_50.png';
import nyImg from './img/ny_50.png';
import pyImg from './img/py_50.png';
import nzImg from './img/nz_50.png';
import pzImg from './img/pz_50.png';
const envTexture = new THREE.CubeTextureLoader().load([
pxImg, //right
nxImg, //left
pyImg, //top
nyImg, //bottom
pzImg, //back
nzImg, //front
])
envTexture.mapping = THREE.CubeReflectionMapping
const texture = new THREE.TextureLoader().load(uvGridImg)
const icosahedronGeometry = new THREE.IcosahedronGeometry(1, 0)
const material = new THREE.MeshPhongMaterial()
material.map = texture;
material.envMap = envTexture;
///An attempt to explicitly set every potentially relevant property...
material.envMapIntensity = 0.0;
material.transparent = false;
material.opacity = 1.0;
material.depthTest = true;
material.depthWrite = true;
material.alphaTest = 0.0;
material.visible = true;
material.side = THREE.FrontSide;
material.flatShading=true;
material.roughness = 0.0;
material.color.setHex(0xffffff);
material.emissive.setHex(0x0);
material.specular.setHex(0xffffff);
material.shininess = 30.0;
material.wireframe = false;
material.flatShading = false;
material.combine = THREE.MultiplyOperation;
material.reflectivity = 1.0;
material.refractionRatio = 1.0;
const icosahedron = new THREE.Mesh(icosahedronGeometry, material)
icosahedron.position.x = 0
scene.add(icosahedron);
For an MVCE, please see the example from this tutorial (you will have to add your own orthographic cam to compare with the given perspective cam). Here are image files for the textures.
UPDATE It seems like all non-spherical geometries cannot render a cubemap reflection correctly thru an orthographic cam. The plane, cylinder, box geometries all fail to render a environment map reflection beyond painting the entire face one uniform reflective color. The sphere, lathe, *hedron geometries (at high levels of detail) will render cubemap reflections.
Is there any way around this? This seems like a huge limitation while working with orthographic cameras.

This is the expected behavior.
With perspective cameras, the reflective "rays" separate as they get further away from the camera, reflecting a wider angle of the envMap.
With an ortho camera these reflective "rays" do not separate because they're parallel. So the reflection on a flat face is a very narrow angle of the envMap.
See this demo I quickly put together to demonstrate what you're seeing:
It seems to work on spheres because when the parallel orthographic "rays" bounce off a rounded surface, these rays grow wider apart. They are no longer parallel (as is the case with a Perspective camera).
You can see the reflections still work on your demo because the faces alternate between light and dark as you rotate them. You're just looking at a much narrower segment of the envMap:

Related

Map Texture to Plane without distortion

In Three.js, how can I change the way in which a texture gets mapped onto a plane?
Let's assume we have a 1x1 plane and a 16:9 image. How can I control the way in which that image gets mapped onto the plane?
By default, the image gets "squished". I would like it to maintain its aspect ratio and have any overlap get "cut off". Is there a way to configure the material or texture to do this, or would I use a shader? If so, what would it need to look like?
const planeMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1),
new THREE.MeshBasicMaterial({
map: texture,
})
);
PS: In future, I would also like to be able to zoom into and out of the image on mouse hover without affecting the size of the plane, so would think a shader might be better?
A Texture already has several properties built-in that can do what you're looking for.
const texture = textureLoader.load("whatever.png");
const planeMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1),
new THREE.MeshBasicMaterial({
map: texture,
})
);
// Sets the pivot point to the center of the texture
texture.center.set(0.5, 0.5);
// Make the texture repeat 0.5625 times in the x-axis to match 16:9 ratio
let ratio = 9 / 16;
texture.repeat.set(ratio, 1);
// Scale texture up to "zoom" into it
let zoom = 0.5;
texture.repeat.set(ratio * zoom, 1 * zoom);
You can read more about the .repeat .center and even .rotation properties in the Texture docs. Just keep in mind that repeating a texture is a bit counter-intuitive because you're doing the inverse of scaling a texture. So to scale a texture by 2, you have to tell it to repeat 1/2 times.

ThreeJS: how add PointLight to scene with PNG textures?

I have a scene with one mesh with PNG textures. I taken PointLight code from ThreeJS example and added to my project:
var intensity = 15;
var pointLight = new THREE.PointLight( color, intensity, 20 );
pointLight.castShadow = true;
pointLight.shadow.camera.near = 1;
pointLight.shadow.camera.far = 60;
pointLight.shadow.bias = - 0.005;
But I not see light and shadows on my mesh:
I created a codepen for reproduce this case
How I can resolve this issue?
There were multiple problems with your pen:
You have to tell the renderer to globally enable shadow maps like so:
renderer.shadowMap.enabled = true
You have to tell the extruded shape to receive shadows:
mesh.receiveShadow = true;
The extruded shaped used MeshBasicMaterial in your pen. This is an unlit material which means it does not react on lights. The codepen below now uses MeshPhongMaterial. You might want to consider to add an ambient or hemisphere light so all parts of your mesh are lit.
Codepen: https://codepen.io/anon/pen/vPPJxW?editors=1010
three.js R102

set transparent (opacity: 0) to some faces of a sphere geometry

I want to make half the faces of a sphere transparent and the other half colored.
How can I do that?
I've tried to set a transparent color, but it seems it doesn't work that way.
geometry = new THREE.SphereGeometry(1.0, 17, 17);
for (var i = 0; i < geometry.faces.length; i++) {
let x = Math.random();
//Here I'm trying to set a transparent color to half the faces of the sphere.
let color = 0
if (x < 0.5) {
color = '0x000000';
} else {
color = '0xffffff';
}
geometry.faces[i].color.setHex(color);
}
var material = new THREE.MeshPhongMaterial({ vertexColors: THREE.VertexColors });
sphere = new THREE.Mesh(geometry, material);
All the faces of the sphere are colored if I do in the way above.
I want half the faces to be randomly selected and to be transparent so that it will make the light inside the sphere scatter its rays like god rays effect, which is something like the one in the video below.
https://www.youtube.com/watch?v=suqFV7VGsL4
Looking at the GLSL shaders in three.js, three.js does not support alpha on vertex colors. It only uses Red, Green, and Blue, not the alpha.
To use vertex colors to make something transparent you'd need to write a custom shader or modify three.js's shaders

Seams on cube edges when using texture atlas with three.js

I have seams between horizontal faces of the cube when use texture atlas in three.js.
This is demo: http://jsfiddle.net/rnix/gtxcj3qh/7/ or http://jsfiddle.net/gtxcj3qh/8/ (from comments)
Screenshot of the problem:
Here I use repeat and offset:
var materials = [];
var t = [];
var imgData = document.getElementById("texture_atlas").src;
for ( var i = 0; i < 6; i ++ ) {
t[i] = THREE.ImageUtils.loadTexture( imgData ); //2048x256
t[i].repeat.x = 1 / 8;
t[i].offset.x = i / 8;
//t[i].magFilter = THREE.NearestFilter;
t[i].minFilter = THREE.NearestFilter;
t[i].generateMipmaps = false;
materials.push( new THREE.MeshBasicMaterial( { map: t[i], overdraw: 0.5 } ) );
}
var skyBox = new THREE.Mesh( new THREE.CubeGeometry( 1024, 1024, 1024), new THREE.MeshFaceMaterial(materials) );
skyBox.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, -1 ) );
scene.add( skyBox );
The atlas has size 2048x256 (power of two). I also tried manual UV-mapping instead of repeat, but the result is the same. I use 8 tiles instead of 6 because I have thought precision of division 1/6 causes the problem, but not.
Pixels on this line are from next tile in atlas. I tried completly white atlas and there was not any artefacts. This explains why there are not seams on vertical borders of Z-faces. I have played with filters, wrapT, wrapS and mipmaps but it does not help. Increasing resolution does not help. There is 8192x1024 atlas http://s.getid.org/jsfiddle/atlas.png I tried another atlas, the result is the same.
I know that I can split atlas into separate files and it works perfectly but it is not convenient.
Whats wrong?
I think the issue is the filtering problem with texture sheets. On image borders in a texture sheet, the gpu may pick the texel from either the correct image or the neighbor image due to limited precision. Because the colors are usually very different, this results in the visible seams. In regular textures, this is solved with CLAMP_TO_EDGE.
If you must use texture alias, then you need to fake CLAMP_TO_EDGE behavior by padding the image borders. See this answer https://gamedev.stackexchange.com/questions/61796/sprite-sheet-textures-picking-up-edges-of-adjacent-texture. It should look something like this: (exaggerated borders for clarity)
Otherwise, the simpler solution is to use a different texture for each face. Webgl supports the cube texture and that is usually used the majority of the time to implement skyboxes.
Hack the uv, replace all value 1.0 with 0.999, replace all value 0 with 0.001 will fakely resolve part of this problem.

Three.js terrain shadowing

I've done a terrain using planegeometry object. I set a vertexes' y coordinate to bump my terrain. Then I added a directional light to my scene and see that there is no shadowing of the "hills" etc.
I added also a sphere and noticed that there is also no shadow on this terrain.
var light = new THREE.DirectionalLight(0xffffff, 1);
light.castShadow = true;
light.shadowCameraVisible = true;
light.position.set(-300, 120, -200); // CHANGED
scene.add(light);
scene.add( new THREE.DirectionalLightHelper(light, 0.2) );
Directional light with sphere above the terrain
But when I replaced directional light by a spotlight I've seen shadowing on a terrain that I want to have.
var slight = new THREE.SpotLight(0xffffff,1);
slight.position.set(-100,60,100);
slight.shadowCameraVisible = true;
scene.add(slight);
scene.add(new THREE.SpotLightHelper(slight, 0.5));
Spotlight with terrain
So the questions are:
how can I do a light that looks like sunshine so the terrain will be not plane color but depend on light? (in future it will be a part of a real city)
what should I do to see the objects' shadows on the terrain? (from fiddle example: sphere's shadow)
Thanks
You are modifying the vertices of your terrain. When you do so, you have to also modify the vertex normals. One way to do that is like so:
geometry.computeVertexNormals();
To create shadows you must enable them.
renderer.shadowMap.enabled = true;
three.js r.130

Resources