Drawing a tetrahedron with triangles - three.js

Trying to create a tetrahedron with three triangles. I could'nt orient faces. Here is the code :
HTML
<div id="myid"></div>
JAVASCRIPT
const divid = document.getElementById("myid");
let facesVectors = [ // triangles vertices
[0, 0, 0],
[-1, 0, 0],
[0, 1, 0],
]
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
1,
1000
);
let renderer = new THREE.WebGLRenderer({ alpha: true });
divid.appendChild(renderer.domElement);
camera.position.z = 2.5;
// equilateral triangle template
const geometry = new THREE.Geometry()
geometry.vertices.push(new THREE.Vector3(-0.48, 0, 0))
geometry.vertices.push(new THREE.Vector3(0.48, 0, 0))
geometry.vertices.push(new THREE.Vector3(0, 0.72, 0))
geometry.faces.push(new THREE.Face3(0, 1, 2))
let material, face, i
let faces = []
for (i = 0; i < facesVectors.length; i++) {
material = new THREE.MeshBasicMaterial({ color: 0x00FF00, side: THREE.DoubleSide })
face = new THREE.Mesh(geometry, material)
face.position.set(facesVectors[i][0], facesVectors[i][1], facesVectors[i][2])
if (i === 1) {
face.rotation.x = -(Math.PI / 2)
} else if (i === 2) {
face.rotation.x = -(Math.PI / 2)
}
faces.push(face)
scene.add(face)
}
let animate = function() {
requestAnimationFrame(animate);
faces.forEach((face)=>{
face.rotation.x += 0.005;
face.rotation.y += 0.01;
})
renderer.render(scene, camera);
};
animate();
demo : https://codepen.io/jeffprod/pen/GRJGYoQ
How to rotate triangles 1 and 2 so that i get a tetrahedron ? Is there a simplest way to do ?
Of course i don't want to use the TetrahedronBufferGeomety because i will need to manage faces colors and a lot of other triangles oriented differently.

Based on this forum topic. Just commented out two lines for faces and uvs:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, -1, 2);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
// tetrahedron
// ---------------------------------------------------------------------------------------
var pts = [ // https://en.wikipedia.org/wiki/Tetrahedron#Coordinates_for_a_regular_tetrahedron
new THREE.Vector3(Math.sqrt(8 / 9), 0, -(1 / 3)),
new THREE.Vector3(-Math.sqrt(2 / 9), Math.sqrt(2 / 3), -(1 / 3)),
new THREE.Vector3(-Math.sqrt(2 / 9), -Math.sqrt(2 / 3), -(1 / 3)),
new THREE.Vector3(0, 0, 1)
];
var faces = [ // triangle soup
//pts[0].clone(), pts[2].clone(), pts[1].clone(),
pts[0].clone(), pts[1].clone(), pts[3].clone(),
pts[1].clone(), pts[2].clone(), pts[3].clone(),
pts[2].clone(), pts[0].clone(), pts[3].clone()
];
var geom = new THREE.BufferGeometry().setFromPoints(faces);
geom.rotateX(-Math.PI * 0.5);
geom.computeVertexNormals();
geom.setAttribute("uv", new THREE.Float32BufferAttribute([ // UVs
//0.5, 1, 0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001,
0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001, 0.5, 1,
0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001, 0.5, 1,
0.06698729810778059, 0.2500000000000001, 0.9330127018922194, 0.2500000000000001, 0.5, 1
], 2));
// ---------------------------------------------------------------------------------------
var tetrahedron = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg"),
side: THREE.DoubleSide
}));
scene.add(tetrahedron);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Related

How to project a texture to curved surface?

Screen capture
I tried to make a 3/4 cylinder surface, and I made it from extruting by a ellipe path,
but when I tried to load a texture to the surface, It does not as I exprected: uniformly painted to the surface, it's stretched
I know it's about texture projection, But I dont know how to set options.
class EllipseCurve3 extends THREE.Curve {
ellipse = null
constructor (ellipse) {
super()
this.ellipse = ellipse
}
getPoint(t, optionalTarget = new THREE.Vector3()) {
const point = this.ellipse.getPoint(t, optionalTarget)
return new THREE.Vector3(
point.x,
point.y,
0
)
}
}
// Scene
const scene = new THREE.Scene();
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.moveTo(0, 1);
shape.lineTo(50, 1);
shape.moveTo(50, 0);
shape.lineTo(0, 0);
// var curve = new THREE.CatmullRomCurve3([
// new THREE.Vector3(0, 0, 50),
// new THREE.Vector3(-50, 0, 0),
// new THREE.Vector3(0, 0, -50)
// ]);
const arc = new THREE.EllipseCurve(
0,
0, // ax, aY
100,
100, // xRadius, yRadius
0,
1.5 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
),
path = new EllipseCurve3(arc)
geometry = new THREE.ExtrudeGeometry(shape, {
bevelEnabled: false,
extrudePath: path,
steps: 50,
depth: 5,
amount: 20,
material: 0,
extrudeMaterial: 1
});
// Set up lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(100, 200, 100); // x, y, z
scene.add(directionalLight);
// Camera
const width = 200;
const height = width * (window.innerHeight / window.innerWidth);
const camera = new THREE.OrthographicCamera(
width / -2, // left
width / 2, // right
height / 2, // top
height / -2, // bottom
0.1, // near
1000 // far
);
camera.position.set(400, 400, 400);
camera.lookAt(0, 0, 0);
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
renderer.render(scene, camera);
// Add it to HTML
document.body.appendChild(renderer.domElement);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;
const picture = 'https://threejs.org/examples/textures/uv_grid_opengl.jpg'
textureLoader.load(picture, function(texture) {
// repeat pattern
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
// zoom in on pattern
texture.repeat.set(.01, .01);
// assign texture via MeshBasicMaterial
var material = new THREE.MeshBasicMaterial({
map: texture,
needsUpdate: true,
// transparent: true,
// premultipliedAlpha: true,
// side: THREE.DoubleSide,
// blending: THREE.AdditiveBlending
});
// var material = new THREE.MeshPhongMaterial({ color: 0x0048ff });
var mesh = new THREE.Mesh(geometry, material)
mesh.rotation.x = Math.PI / 2
// mesh.rotation.z = Math.PI / 2
scene.add(mesh)
scene.add(cube)
})
function render() {
renderer.render(scene, camera);
// Rotate out group
// svgGroup.rotation.y -= 0.005
controls.update();
requestAnimationFrame(render);
}
render();
Code here
https://codepen.io/mike-xu/pen/RwQeEXJ
"I know it's about texture projection" - It's about computing UV, based on vertices coordinates.
CylinderGeometry also may help to achieve the result you described. With less code, and more convenient and predictable way.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
console.clear();
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.AxesHelper(10));
let g = new THREE.CylinderGeometry(5, 5, 5, 100, 20, true, 0, Math.PI * 1.5);
let m = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
map: new THREE.TextureLoader().load(
"https://threejs.org/examples/textures/uv_grid_opengl.jpg",
tex => {
tex.wrapS = tex.wrapT = THREE.MirroredRepeatWrapping;
tex.repeat.set(3, 1);
}
)
});
let c = new THREE.Mesh(g, m);
scene.add(c);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>

Three.js assign array of materials to an indexed BufferGeometry

I have a BufferGeometry triangular prism; where I have specified the 6 vertices and then added indexes to create the 8 faces (2 triangle faces per square plane).
I have then setup 5 groups, so that each side of the prism is a group.
And it works here when I have a single material assigned to the whole object: https://jsfiddle.net/30ez17dw/1/
However I want to be able to assign an array of materials, so that each side (group) has it's own materials. But when I do this the object disappears:
https://jsfiddle.net/30ez17dw/2/
How can I fix my code to make the array of materials work?
You have not specified your groups correctly. Try it like so:
var camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 5;
camera.position.y = 2;
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
var geometry = new THREE.BufferGeometry();
const y = 0.866025404;
const y2 = 0.5;
const h = 1;
const vertices = new Float32Array([
-y2, 0, 0,
y2, 0, 0,
0, 0, y,
-y2, h, 0,
y2, h, 0,
0, h, y,
]);
const indices = [
0, 1, 2, // Top
5, 4, 3, // Bottom
3, 1, 0, // Back
1, 3, 4, // Back
0, 2, 3, // Left
5, 3, 2, // Left
4, 2, 1, // Right
2, 4, 5, // Right
];
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(indices);
geometry.computeVertexNormals();
geometry.clearGroups();
geometry.addGroup(0, 3, 0);
geometry.addGroup(3, 6, 1);
geometry.addGroup(6, 12, 2);
geometry.addGroup(12, 18, 3);
geometry.addGroup(18, 24, 4);
var material = [
new THREE.MeshBasicMaterial({
color: 0x00ff00
}),
new THREE.MeshBasicMaterial({
color: 0xff0000
}),
new THREE.MeshBasicMaterial({
color: 0x0000ff,
}),
new THREE.MeshBasicMaterial({
color: 0xffff00
}),
new THREE.MeshBasicMaterial({
color: 0x00ffff
})
];
var mesh = new THREE.Mesh(geometry, material); // , side: THREE.DoubleSide
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.132.0/build/three.min.js"></script>
The indices you pass to addGroup() are meant to be consecutive.

Create object with different colors on faces

Drawing a Tetrahedron, I would like to set one color per face, for example, red, green, blue and orange.
Here is the code :
const divid = document.getElementById("myid");
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
1,
1000
);
let renderer = new THREE.WebGLRenderer({ alpha: true });
divid.appendChild(renderer.domElement);
camera.position.z = 3;
let triangle = new THREE.Mesh(
new THREE.TetrahedronBufferGeometry(),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(triangle);
let animate = function() {
requestAnimationFrame(animate);
triangle.rotation.x += 0.005;
triangle.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
Can't we pass an array of 4 colors to set on each face ?
Code available here too : https://codepen.io/jeffprod/pen/JjdLdjO
Use .vertexColors: true with the material and add color attribute to the geometry, setting the same colour to each vertex of a face:
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(
70,
innerWidth / innerHeight,
1,
1000
);
let renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 3;
let geom = new THREE.TetrahedronBufferGeometry();
geom.setAttribute("color", new THREE.Float32BufferAttribute([
1, 0, 0, //red
1, 0, 0,
1, 0, 0,
0, 1, 0, //green
0, 1, 0,
0, 1, 0,
0, 0, 1, //blue
0, 0, 1,
0, 0, 1,
1, 0.75, 0.25, //orange (sort of)
1, 0.75, 0.25,
1, 0.75, 0.25
], 3));
let triangle = new THREE.Mesh(
geom,
new THREE.MeshBasicMaterial({
vertexColors: true
})
);
scene.add(triangle);
let animate = function() {
requestAnimationFrame(animate);
triangle.rotation.x += 0.005;
triangle.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>

Why cubes do not always animate with three.js

In the code below, cubes sometimes move with animation and sometimes without. How can I fix it ?
Also, how should I set rotation speed ?
new Vue({
el: '#app',
data () {
return {
camera: null,
scene: null,
renderer: null,
cube: null,
angle: null
}
},
methods: {
init: function () {
this.camera = new THREE.PerspectiveCamera(1, 1);
this.camera.position.z = 200;
// Make a scene
this.scene = new THREE.Scene();
this.clock = new THREE.Clock();
//
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
let container = document.getElementById('container')
this.renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild(this.renderer.domElement);
let cube2 = this.createCube()
cube2.name = "cube2"
// cube2.position = new THREE.Vector3(1, 0)
this.scene.add(cube2);
},
createCube: function () {
// GEOMETRY
// 1. Start with empty geometry
let geometry = new THREE.Geometry();
// 2. Add vertices to geometry
geometry.vertices.push(
// verts [0-3] are in in +z
new THREE.Vector3(-1, 1, 1),
new THREE.Vector3(-1, -1, 1),
new THREE.Vector3(1, -1, 1),
new THREE.Vector3(1, 1, 1),
// verts [4-7] in -z
new THREE.Vector3(-1, 1, -1),
new THREE.Vector3(-1, -1, -1),
new THREE.Vector3(1, -1, -1),
new THREE.Vector3(1, 1, -1),
);
// 3. Connect vertices in desired order to make faces
let b = 0x1db0ec
let y = 0xffef3a
let r = 0xea353d
let w = 0xffffff
// Set half faces
geometry.faces.push(new THREE.Face3(0, 1, 2)); // blue
geometry.faces.push(new THREE.Face3(0, 2, 3)); // yellow
geometry.faces.push(new THREE.Face3(5, 4, 6)); // white
geometry.faces.push(new THREE.Face3(6, 4, 7)); // red
// Set whole faces
geometry.faces.push(new THREE.Face3(1, 0, 5)); // blue
geometry.faces.push(new THREE.Face3(5, 0, 4));
geometry.faces.push(new THREE.Face3(1, 5, 2)); // white
geometry.faces.push(new THREE.Face3(5, 6, 2));
geometry.faces.push(new THREE.Face3(2, 6, 3)); // red
geometry.faces.push(new THREE.Face3(3, 6, 7));
geometry.faces.push(new THREE.Face3(0, 3, 4)); // yellow
geometry.faces.push(new THREE.Face3(3, 7, 4));
// Set faces colors
geometry.faces[0].color.setHex(b); // Half face
geometry.faces[1].color.setHex(y);
geometry.faces[2].color.setHex(w);
geometry.faces[3].color.setHex(r);
geometry.faces[4].color.setHex(b); // Whole face
geometry.faces[5].color.setHex(b);
geometry.faces[6].color.setHex(w);
geometry.faces[7].color.setHex(w);
geometry.faces[8].color.setHex(r);
geometry.faces[9].color.setHex(r);
geometry.faces[10].color.setHex(y);
geometry.faces[11].color.setHex(y);
// MATERIAL
// Make a material
let material = new THREE.MeshBasicMaterial({
// color: 0x00FF00,
vertexColors: THREE.FaceColors,
wireframe: false,
});
// MESH
let cube = new THREE.Mesh(geometry, material);
return cube
},
rotateTo: function (face) {
if (face == 'yellow')
this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 2, Math.PI / 2));
else if (face == 'red')
this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, Math.PI / 2));
else if (face == 'blue')
this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, - Math.PI / 2));
else if (face == 'white')
this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(- Math.PI / 2, Math.PI / 2, 0));
else if (face == 'yb')
this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
else if (face == 'rw')
this.angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI, 0, 0));
this.animate()
},
animate: function () {
let id = requestAnimationFrame(this.animate);
let delta = this.clock.getDelta();
this.scene.children[0].quaternion.rotateTowards(this.angle, Math.PI * delta);
this.renderer.render(this.scene, this.camera);
if (this.scene.children[0].quaternion.equals(this.angle)) {
cancelAnimationFrame(id)
}
}
},
mounted () {
this.init();
this.renderer.render(this.scene, this.camera);
}
})
#container {
background-color: #aaa;
width: 20em;
height: 20em;
}
<script src="https://unpkg.com/vue"></script>
<script src="https://threejs.org/build/three.min.js"></script>
<div id="app">
<div>
<button v-on:click="rotateTo('yellow')">yellow</button>
<button v-on:click="rotateTo('red')">red</button>
<button v-on:click="rotateTo('blue')">blue</button>
<button v-on:click="rotateTo('white')">white</button>
<button v-on:click="rotateTo('yb')">yellow/blue</button>
<button v-on:click="rotateTo('rw')">red/white</button>
</div>
<div id="container"></div>
</div>
UPDATE
Code isolated to only the threejs portion.
var camera = null;
var scene = null;
var renderer = null;
var cube = null;
var angle = null;
init();
renderer.render(scene, camera);
function init() {
camera = new THREE.PerspectiveCamera(1, 1);
camera.position.z = 200;
// Make a scene
scene = new THREE.Scene();
clock = new THREE.Clock();
//
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
let container = document.getElementById('container');
renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild(renderer.domElement);
let cube2 = createCube();
cube2.name = "cube2";
// cube2.position = new THREE.Vector3(1, 0)
scene.add(cube2);
}
function createCube() {
// GEOMETRY
// 1. Start with empty geometry
let geometry = new THREE.Geometry();
// 2. Add vertices to geometry
geometry.vertices.push(
// verts [0-3] are in in +z
new THREE.Vector3(-1, 1, 1),
new THREE.Vector3(-1, -1, 1),
new THREE.Vector3(1, -1, 1),
new THREE.Vector3(1, 1, 1),
// verts [4-7] in -z
new THREE.Vector3(-1, 1, -1),
new THREE.Vector3(-1, -1, -1),
new THREE.Vector3(1, -1, -1),
new THREE.Vector3(1, 1, -1),
);
// 3. Connect vertices in desired order to make faces
let b = 0x1db0ec;
let y = 0xffef3a;
let r = 0xea353d;
let w = 0xffffff;
// Set half faces
geometry.faces.push(new THREE.Face3(0, 1, 2)); // blue
geometry.faces.push(new THREE.Face3(0, 2, 3)); // yellow
geometry.faces.push(new THREE.Face3(5, 4, 6)); // white
geometry.faces.push(new THREE.Face3(6, 4, 7)); // red
// Set whole faces
geometry.faces.push(new THREE.Face3(1, 0, 5)); // blue
geometry.faces.push(new THREE.Face3(5, 0, 4));
geometry.faces.push(new THREE.Face3(1, 5, 2)); // white
geometry.faces.push(new THREE.Face3(5, 6, 2));
geometry.faces.push(new THREE.Face3(2, 6, 3)); // red
geometry.faces.push(new THREE.Face3(3, 6, 7));
geometry.faces.push(new THREE.Face3(0, 3, 4)); // yellow
geometry.faces.push(new THREE.Face3(3, 7, 4));
// Set faces colors
geometry.faces[0].color.setHex(b); // Half face
geometry.faces[1].color.setHex(y);
geometry.faces[2].color.setHex(w);
geometry.faces[3].color.setHex(r);
geometry.faces[4].color.setHex(b); // Whole face
geometry.faces[5].color.setHex(b);
geometry.faces[6].color.setHex(w);
geometry.faces[7].color.setHex(w);
geometry.faces[8].color.setHex(r);
geometry.faces[9].color.setHex(r);
geometry.faces[10].color.setHex(y);
geometry.faces[11].color.setHex(y);
// MATERIAL
// Make a material
let material = new THREE.MeshBasicMaterial({
// color: 0x00FF00,
vertexColors: THREE.FaceColors,
wireframe: false,
});
// MESH
let cube = new THREE.Mesh(geometry, material);
return cube;
}
function rotateTo(face) {
if (face == 'yellow')
angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 2, Math.PI / 2));
else if (face == 'red')
angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, Math.PI / 2));
else if (face == 'blue')
angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 2, 0, -Math.PI / 2));
else if (face == 'white')
angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(-Math.PI / 2, Math.PI / 2, 0));
else if (face == 'yb')
angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
else if (face == 'rw')
angle = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI, 0, 0));
animate();
}
function animate() {
let id = requestAnimationFrame(animate);
let delta = clock.getDelta();
scene.children[0].quaternion.rotateTowards(angle, Math.PI * delta);
renderer.render(scene, camera);
if (scene.children[0].quaternion.equals(angle)) {
cancelAnimationFrame(id);
}
}
#container {
background-color: #aaa;
width: 20em;
height: 20em;
}
<script src="https://threejs.org/build/three.min.js"></script>
<div>
<button onclick="rotateTo('yellow')">yellow</button>
<button onclick="rotateTo('red')">red</button>
<button onclick="rotateTo('blue')">blue</button>
<button onclick="rotateTo('white')">white</button>
<button onclick="rotateTo('yb')">yellow/blue</button>
<button onclick="rotateTo('rw')">red/white</button>
</div>
<div id="container"></div>
The reason for the animation not playing is because when you cancelAnimationFrame the clock is still running, so the next time you call rotateTo the delta is so high that the animation ends immediately.
you can avoid this by stopping the clock with cancelAnimationFrame, and start it again when calling rotateTo as follows:
function rotateTo(face) {
clock.start()
...
}
function animate() {
...
if (scene.children[0].quaternion.equals(angle)) {
cancelAnimationFrame(id);
clock.stop()
}
}
as for rotation speed, the 2nd arg of rotateTowards is step, which determines how fast will it get there. So if you add a modifier var to it, you can control the speed.
let rotSpeedMotifier = 0.2 // the higher the faster
scene.children[0].quaternion.rotateTowards(angle, Math.PI * delta * rotSpeedMotifier );

Custom plane does not cast/ receive shadows in THREE js

I am trying to create a box (a wall) using six planes. I have created planes but shadows are not there.
This is how I create custom planes.
function createWall(vertices) {
var geometry = new THREE.Geometry(), i;
for (i = 0; i < vertices.length; i = i + 1) {
geometry.vertices.push(vertices[i]);
}
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 2, 3));
geometry.computeVertexNormals();
geometry.computeFaceNormals();
var material = new THREE.MeshStandardMaterial({
emissive: 0x708090,
emissiveIntensity: 1,
side: THREE.DoubleSide,
color: 0xD3D3D3
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
Here is the complete code.
var camera, scene, renderer;
function addFloor() {
var material = new THREE.MeshStandardMaterial({
roughness: 0.8,
color: 0x696969,
metalness: 0.2,
bumpScale: 0.0005
});
var geometry = new THREE.PlaneBufferGeometry(2000, 2000);
var mesh = new THREE.Mesh(geometry, material);
mesh.receiveShadow = true;
mesh.rotation.x = -Math.PI / 2.0;
scene.add(mesh);
}
function createWall(vertices) {
var geometry = new THREE.Geometry(), i;
for (i = 0; i < vertices.length; i = i + 1) {
geometry.vertices.push(vertices[i]);
}
geometry.faces.push(new THREE.Face3(0, 1, 2));
geometry.faces.push(new THREE.Face3(0, 2, 3));
geometry.computeVertexNormals();
geometry.computeFaceNormals();
var material = new THREE.MeshStandardMaterial({
emissive: 0x708090,
emissiveIntensity: 1,
side: THREE.DoubleSide,
color: 0xD3D3D3
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
function addBulb(location) {
var geometry = new THREE.SphereGeometry(2, 20, 20);
var light = new THREE.PointLight(0xffffff, 1, 100, 2);
var material = new THREE.MeshStandardMaterial({
emissive: 0xffffee,
emissiveIntensity: 1,
color: 0x000000
});
light.add(new THREE.Mesh(geometry, material));
light.position.set(location.x, location.y, location.z);
light.shadow.camera.near = 0.0001;
light.castShadow = true;
//light.shadow.darkness = 0.5;
//light.shadow.camera.vsible = true;
return light;
}
function addWalls() {
var wall1 = createWall([
new THREE.Vector3(0, 0, 0), //vertex0
new THREE.Vector3(200, 0, 0), //1
new THREE.Vector3(200, 100, 0), //2
new THREE.Vector3(0, 100, 0) //3
]);
var wall2 = createWall([
new THREE.Vector3(0, 0, 5), //vertex0
new THREE.Vector3(200, 0, 5), //1
new THREE.Vector3(200, 100, 5), //2
new THREE.Vector3(0, 100, 5) //3
]);
scene.add(wall1);
scene.add(wall2);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(50, 100, 300);
scene.add(camera);
}
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.renderSingleSided = false;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
addCamera();
addFloor();
addWalls();
scene.add(addBulb({x: 100, y: 50, z: 25}));
var ambientLight = new THREE.AmbientLight(0x999999, 0.6);
scene.add(ambientLight);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I may have done something wrong while creating custom plane. I do not understand what is wrong here.
UPDATED:
renderer.shadowMap.renderSingleSided = false;
It seems shadows are working but lighting has strange square effect.
Here I have tried BoxGeometry which has little depth as suggested in three-js-plane-doesnt-cast-shadow ,
As I noted this occurs only for small values for depth. (depth < 1)
var camera, scene, renderer;
function addFloor() {
var material = new THREE.MeshStandardMaterial({
roughness: 0.9,
color: 0xffffff,
metalness: 0.1,
bumpScale: 0.0005
});
var geometry = new THREE.PlaneBufferGeometry(2000, 2000);
var mesh = new THREE.Mesh(geometry, material);
mesh.receiveShadow = true;
mesh.rotation.x = -Math.PI / 2.0;
scene.add(mesh);
}
function createWall(location) {
var geometry = new THREE.BoxGeometry(200, 100, 0.1);
geometry.translate((location.x1 + location.x2) / 2, 150 / 2, location.z);
var material = new THREE.MeshStandardMaterial({
emissive: 0x708090,
emissiveIntensity: 1,
side: THREE.DoubleSide,
color: 0xD3D3D3,
roughness: 0.9,
metalness: 0.1
});
var mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
function addBulb(options) {
var geometry = new THREE.SphereGeometry(2, 20, 20);
var light = new THREE.PointLight(options.color, 1, 500, 2);
var material = new THREE.MeshStandardMaterial({
emissive: options.color,
emissiveIntensity: 1,
color: options.color
});
light.add(new THREE.Mesh(geometry, material));
light.position.set(options.x, options.y, options.z);
light.shadow.camera.near = 0.0001;
light.castShadow = true;
light.shadow.darkness = 0.5;
light.shadow.camera.vsible = true;
return light;
}
function addWalls() {
var wall1 = createWall({ x1: 0, x2: 200, z: 0});
var wall2 = createWall({ x1: 0, x2: 200, z: 5});
scene.add(wall1);
//scene.add(wall2);
}
function addCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(100, 100, 300);
scene.add(camera);
}
function init() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.renderSingleSided = false;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xffffff, 1);
document.body.appendChild(renderer.domElement);
addCamera();
addFloor();
addWalls();
scene.add(addBulb({x: 0, y: 100, z: 25, color: 0xff0000 }));
scene.add(addBulb({x: 100, y: 100, z: 25, color: 0x00ff00 }));
scene.add(addBulb({x: 200, y: 100, z: 25, color: 0x0000ff }));
var ambientLight = new THREE.AmbientLight(0x999999, 0.6);
scene.add(ambientLight);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
You're facing Z fighting. Since the thickness of the wall is so small, the 2 faces are nearly touching each other, so they cast shadows on each other.
You can either remove the casting/receiving of shadows on the wall or, better, increase the z value in the geometry, like so:
var geometry = new THREE.BoxGeometry(200, 100, 1);
Also, since you changed to a box from a plane, the line side: THREE.DoubleSide, in the wall material is no longer needed. In fact, it's part of the self-shadowing problem. Changing those two lines should solve your problem.
Here's a working Fiddle.

Resources