Textures and RingGeometry / CylinderGeometry - three.js

UPDATE:
I made jsfiddle example - jsfiddle.net/NEXny/1/
[ignore this - just including a code block so stackoverflow will
let me post the above JSFiddle link. Yeah, seriously.]
I'm having trouble with applying texture to RingGeometry and CylinderGeometry, hope this image will explain my issue.
It is possible to apply texture by one of this ways ?
Currently i'm getting very unexpected results...

You have to modify the geometry vertex UVs to your liking.
Instead, why not just use CircleGeometry for your cylinder end-caps. That is, construct the end-caps yourself?
// cylinder
geometry = new THREE.CylinderGeometry( 192, 192, 40, 64, 1, true ); // open-ended
geometry1 = new THREE.CircleGeometry(192, 64);
// end-cap material
material1 = new THREE.MeshBasicMaterial({
map: textures.circle,
overdraw: 0.5 // for canvas renderer only
});
// cylinder material
material = new THREE.MeshBasicMaterial({
map: textures.line,
overdraw: 0.5 // for canvas renderer only
});
object = new THREE.Object3D();
scene.add(object);
// end-caps
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.rotation.x = - Math.PI / 2;
mesh1.position.y = 20
object.add(mesh1);
var mesh2 = new THREE.Mesh(geometry1, material1);
mesh2.rotation.x = Math.PI / 2;
mesh2.position.y = -20
object.add(mesh2);
// cylinder
var mesh = new THREE.Mesh(geometry, material);
object.add(mesh);
fiddle: http://jsfiddle.net/NEXny/2/
three.js r.61

Related

lights do not eliminate shadow from another light source

I was just experimenting with some lightning in three.js and came across a problem which I seem to be the only on having.
The setup is simple, two PointLight, one PlaneGeometry and one BoxGeometry.
"use strict";
var scale = 0.8;
var w = parseInt('' + Math.floor(innerWidth * scale));
var h = parseInt('' + Math.floor(innerHeight * scale));
var camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 1000);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
var scene = new THREE.Scene();
// init
{
scene.background = new THREE.Color(0x404040);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);
}
// plane
{
let geometry = new THREE.PlaneGeometry(40, 40, 10, 10);
let material = new THREE.MeshLambertMaterial({
color: 0x70B009,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(geometry, material);
plane.lookAt(new THREE.Vector3());
plane.rotateX(90 / 180 * Math.PI);
plane.receiveShadow = true;
scene.add(plane);
}
// box
{
let geometry = new THREE.BoxGeometry(1, 1, 1);
let material = new THREE.MeshLambertMaterial({
color: 0xFF6C00
});
var orangeCube = new THREE.Mesh(geometry, material);
orangeCube.castShadow = true;
scene.add(orangeCube);
}
// pointlights
{
var mapSize = 2 << 10;
var pointLight1 = new THREE.PointLight(0xFFFFFF, 0.6, 100);
pointLight1.castShadow = true;
pointLight1.shadow.mapSize.set(mapSize, mapSize);
scene.add(pointLight1);
var pointLight2 = new THREE.PointLight(0xFFFFFF, 0.6, 100);
pointLight2.castShadow = true;
pointLight2.shadow.mapSize.set(mapSize, mapSize);
scene.add(pointLight2);
}
// position camera, lights and box
{
pointLight1.position.set(0, 15, -15);
pointLight2.position.set(0, 15, 15);
orangeCube.position.set(0, 5, 0);
camera.position.set(10, 10, 0);
camera.lookAt(new THREE.Vector3());
}
// render once
{
renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
Which works quite well, but one problem. The lights do not eliminate the shadow projected by the other PointLight.
Does someone know how to fix this?
Thank you for your help.
As explained in this SO answer, shadows in MeshLambertMaterial are an approximation. Try MeshPhongMaterial, for example.
In MeshPhongMaterial and MeshStandardMaterial, shadows are the absence of light. If there is light from two light sources, shadow intensity can vary where the shadows overlap. See this three.js example.
three.js r.91

ThreeJS raycasting is different in R81 than R71

I have a plane with a mesh on it. My code draws a ball when the user double clicks on the mesh. This works just fine in R71 but as soon as I switched to R81 raycaster doesn't return an intersect. Here's the code:
In init():
// Plane
plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 1000, 1000, 3, 3 ),
new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: .5, transparent: true } )
);
plane.visible = false;
scene.add( plane );
planes.push(plane);
In doubleClickEvent():
event.preventDefault();
var mouse = new THREE.Vector2((event.clientX / window.innerWidth ) * 2 - 1, -(((event.clientY / window.innerHeight ) * 2 - 1)));
var directionVector = new THREE.Vector3();
directionVector.set(mouse.x, mouse.y, 0.1);
directionVector.unproject(camera);
directionVector.sub(camera.position);
directionVector.normalize();
raycaster.set(camera.position, directionVector);
intersects = raycaster.intersectObjects(planes);
if (intersects.length) {
var sphereParent = new THREE.Object3D();
var sphereGeometry = new THREE.SphereGeometry(.1, 16, 8);
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphereParent.add(sphere);
sphereParent.position.set(intersects[0].point.x, intersects[0].point.y, 0.0);
scene.add(sphereParent);
objects.push(sphereParent);
}
If I change
intersects = raycaster.intersectObjects(planes);
to
intersects = raycaster.intersectObjects(scene.children);
the ball gets drawn but it gets drawn on the wrong position.
Any ideas?
I found the answer. The reason why the raycast isn't working is because the plane's visibility is false. The solution is to change the visibility of the material visibility rather the plane.

Three.js self transparency, object should not be see through

Codepen demonstrating the problem
https://codepen.io/anon/pen/GjJpYw?editors=0010
I have 2 meshes, one which contains 2 cubes, and the other which is 1 cube. The mesh with 2 cubes sandwiches the mesh with one cube (so the single cube is in the center). When I set all cubes to transparent but set the opacity of the center cube to 1, I would not expect to be able to see the back cube when looking through the front cube but I can.
I was wondering is there any easy way to fix this? This is a very simplified version of the problem I'm facing so I can't easily split the geometries. I also cannot just set transparent to false since ideally I would like to be able to have the middle cube partially transparent as well. Any suggestions?
var width = window.innerWidth;
var height = window.innerHeight;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var cubeGeometry = new THREE.CubeGeometry(100, 100, 100);
var cube = new THREE.Mesh(cubeGeometry);
cube.position.set(0, 25, -200);
var cube2 = new THREE.Mesh(cubeGeometry);
cube2.position.set(0, -25, 200);
cube.updateMatrix();
cube2.updateMatrix();
var singleGeometry = new THREE.Geometry();
singleGeometry.merge(cube.geometry, cube.matrix);
singleGeometry.merge(cube2.geometry, cube2.matrix);
var combinedMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5, transparent: true});
var mesh = new THREE.Mesh(singleGeometry, combinedMaterial);
var cubeGeometry = new THREE.CubeGeometry(200, 200, 200);
var cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff, opacity: 0.8, transparent: true});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
scene.add(cube);
scene.add(mesh);
var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.z = 500;
var controls = new THREE.OrbitControls(camera);
controls.addEventListener('change', render);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(0, 300, 200);
scene.add(pointLight);
render();
animate();
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}

ThreeJS Texture is pixelated when seen from distance

I was playing with webGL and ThreeJS, then I've got the following issue:
Textures with large images gets pixelated when seen from distance.
Check the example: http://jsfiddle.net/4qTR3/1/
Below is the code:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 10, 7000);
var light = new THREE.PointLight(0xffffff);
light.position.set(0, 150, 100);
scene.add(light);
var light2 = new THREE.AmbientLight(0x444444);
scene.add(light2);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.PlaneGeometry(500, 500, 10, 10);
//I use different textures in my project
var texture = new THREE.ImageUtils.loadTexture(TEST_IMAGE);
var textureBack = new THREE.ImageUtils.loadTexture(TEST_IMAGE);
textureBack.anisotropy = renderer.getMaxAnisotropy();
texture.anisotropy = renderer.getMaxAnisotropy();
//Filters
texture.magFilter = THREE.NearestFilter;
texture.minFilter = THREE.LinearMipMapLinearFilter;
textureBack.magFilter = THREE.NearestFilter;
textureBack.minFilter = THREE.LinearMipMapLinearFilter;
var materials = [
new THREE.MeshLambertMaterial({
transparent: true,
map: texture,
side: THREE.FrontSide
}),
new THREE.MeshLambertMaterial({
transparent: true,
map: textureBack,
side: THREE.BackSide
})];
for (var i = 0, len = geometry.faces.length; i < len; i++) {
var face = geometry.faces[i].clone();
face.materialIndex = 1;
geometry.faces.push(face);
geometry.faceVertexUvs[0].push(geometry.faceVertexUvs[0][i].slice(0));
}
planeObject = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
planeObject.overdraw = true;
planeObject.position.z = -5000;
scene.add(planeObject);
camera.position.z = 1000;
(function render() {
requestAnimationFrame(render);
planeObject.rotation.y += 0.02;
renderer.render(scene, camera);
})();
If the image of the texture has got text in it, the text becomes very pixelated with poor quality.
How can I fix this?
In order to not get pixelated you need to use mips but WebGL can't generate mips for non-power-of-2 textures. Your texture is 800x533, neither of those is a power of 2.
a couple of options
1) Scale the picture offline to powers of 2 like 512x512 or 1024x512
2) Scale the picture at runtime before making a texture.
Load the image yourself, once loaded make a canvas that is power-of-2. call drawImage(img, 0, 0, canvas.width, canvas.height) to scale the image into the canvas. Then load the canvas into a texture.
You also probably want to change your mag filtering from NearestFilter to LinearFilter.
Note: (1) is the better option. (2) takes time on the user's machine, uses more memory, and you have no guarantee what the quality of the scaling will be.
Example here.

How to change width of CubeGeometry with Three.js?

I have a cube geometry and a mesh, and i don't know how to change the width (or height... i can change x, y and z though).
Here's a snippet of what i have right now:
geometry = new THREE.CubeGeometry( 200, 200, 200 );
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } );
mesh = new THREE.Mesh( geometry, material );
// WebGL renderer here
function render(){
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render( scene, camera );
}
function changeStuff(){
mesh.geometry.width = 500; //Doesn't work.
mesh.width = 500; // Doesn't work.
geometry.width = 500; //Doesn't work.
mesh.position.x = 500// Works!!
render();
}
Thanks!
EDIT
Found a solution:
mesh.scale.x = 500;
Just to complete comment and solution from question (and have an answer present with example code):
// create a cube, 1 unit for width, height, depth
var geometry = new THREE.CubeGeometry(1,1,1);
// each cube side gets another color
var cubeMaterials = [
new THREE.MeshBasicMaterial({color:0x33AA55, transparent:true, opacity:0.8}),
new THREE.MeshBasicMaterial({color:0x55CC00, transparent:true, opacity:0.8}),
new THREE.MeshBasicMaterial({color:0x000000, transparent:true, opacity:0.8}),
new THREE.MeshBasicMaterial({color:0x000000, transparent:true, opacity:0.8}),
new THREE.MeshBasicMaterial({color:0x0000FF, transparent:true, opacity:0.8}),
new THREE.MeshBasicMaterial({color:0x5555AA, transparent:true, opacity:0.8}),
];
// create a MeshFaceMaterial, allows cube to have different materials on each face
var cubeMaterial = new THREE.MeshFaceMaterial(cubeMaterials);
var cube = new THREE.Mesh(geometry, cubeMaterial);
cube.position.set(0,0,0);
scene.add( cube );
cube.scale.x = 2.5; // SCALE
cube.scale.y = 2.5; // SCALE
cube.scale.z = 2.5; // SCALE
A slightly advanced, dynamic example (still the same scaling) implemented here:
You can dispose the geometry of cube and affect the new one like this :
let new_geometry = new THREE.CubeGeometry(500,200,200);
geometry.dispose();
cube.geometry = new_geometry;
Scale properties can be used to for changing width, height and and depth of cube.
//creating a cube
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial({color:"white"});
var cube = new THREE.Mesh(geometry, material);
//changing size of cube which is created.
cube.scale.x = 30;
cube.scale.y = 30;
cube.scale.z = 30;

Resources