I'm learning three.js's frustum by modifying the example in Scene Graph below.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
Settings:
A sphere at the origin with radius = 1.
A camera positioned at y = 50, looking towards the origin, with the UP direction as (0,0,1).
Its frustum has fov = 40, aspect = 2, near = 0.1 and far = 1000.
I changed two parameters:
near: 0.1 → 48
far: 1000 → 52.
I'm expecting the sphere being contained inside the frustum, but why is the sphere's top part truncated?
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 40;
const aspect = 2; // the canvas default
const near = 48;
const far = 52;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
Thanks to WestLangley's comment, I've found out the answer: the property Object3D.scale is a 3D vector that scales the object locally. Mesh is a subclass of Object3D. In my modified example above, the "sphere" (a hexagon in fact) is scaled to 5×. I commented out this line, so that there's no scaling (default factor 1×), and I observed that the "sphere" is contained inside the frustum.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 10;
const aspect = 2; // the canvas default
const near = 49;
const far = 51;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
//sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
To check my understanding, I reduced the distance between near and the center of the "sphere" by 0.3, and I observed a hole as expected.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 10;
const aspect = 2; // the canvas default
const near = 49.3;
const far = 50.7;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// an array of objects who's rotation to update
const objects = [];
const radius = 1;
const widthSegments = 6;
const heightSegments = 6;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
//sunMesh.scale.set(5, 5, 5);
scene.add(sunMesh);
objects.push(sunMesh);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
height: 100%;
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.144.0/three.min.js"></script>
Remark: I'm answering my own question in order to clear it from the unanswered queue.
Related
In THREE.js, I want to add an orbitControls with controls.enableDamping=true to add a kind of inertia to make them feel less stiff. Here is what I did:
import * as THREE from 'https://threejs.org/build/three.module.js';
import {OrbitControls} from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.target.set(0, 0, 0);
controls.update();
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
function makeInstance(geometry, color, x) {
const material = new THREE.MeshPhongMaterial({color});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
return cube;
}
makeInstance(geometry, 0x44aa88, 0);
makeInstance(geometry, 0x8844aa, -2);
makeInstance(geometry, 0xaa8844, 2);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
// controls.update();
renderer.render(scene, camera);
}
render();
function requestRender() {
requestAnimationFrame(render);
}
controls.addEventListener('change', requestRender);
window.addEventListener('resize', requestRender);
}
main();
of course it didn't have any inertia at all.When I uncommented the controls.update() in the render function, it just got a little better, with just a little inertia. But when I use a Render Flag to control the render, it works much better:
import * as THREE from 'https://threejs.org/build/three.module.js';
import {OrbitControls} from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
controls.target.set(0, 0, 0);
controls.update();
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
function makeInstance(geometry, color, x) {
const material = new THREE.MeshPhongMaterial({color});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
return cube;
}
makeInstance(geometry, 0x44aa88, 0);
makeInstance(geometry, 0x8844aa, -2);
makeInstance(geometry, 0xaa8844, 2);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
let renderRequested = false;
function render() {
renderRequested = undefined;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
controls.update();
renderer.render(scene, camera);
}
render();
function requestRenderIfNotRequested() {
if (!renderRequested) {
renderRequested = true;
requestAnimationFrame(render);
}
}
controls.addEventListener('change', requestRenderIfNotRequested);
window.addEventListener('resize', requestRenderIfNotRequested);
}
main();
The code above is from this manual of THREE.js, the problem is WHY should I use the renderRequested flag to make it work?
You are using the renderRequested boolean flag to avoid an uncontrolled/infinite loop.
In this line of your code:
controls.addEventListener('change', requestRenderIfNotRequested);
... you're causing the requestRenderIfNotRequested method to be called every time OrbitControls emits a change event, and you're calling requestAnimationFrame inside your requestRenderIfNotRequested method, which calls your render() function, which calls the controls.update() method, which emits another change event, which circles back to the beginning of this process causing an uncontrolled/infinite loop.
By "flip-flopping" the renderRequested boolean flag, you're making sure that you won't have a request to render a new frame in case there's already one in place being executed.
I understand that might seem a little confusing, but I hope that helps you to think through your whole render cycle.
Best regards!
I am using Three.js to develop a cube that translates and rotate in 3D space using data from accelerometer and gyroscope data.
So far I have one canvas that shows the accelerometer movement. Now I need to have another canvas that shows the gyroscope data on a separate canvas, I prefer to have two JS code for each canvas, so they are independent of each other. I wasn't able to make two separate canvases, and I don't know if it is even possible to use two different javascript codes in the html.
Below is how my HTML is structured:
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="canvas" width="500" height="800" style="border:1px solid #eb1c1c;"></canvas>
<div id="info">
<div>t = <span id="time">0</span> s</div>
<div>accX = <span id="accX">0</span></div>
<div>accY = <span id="accY">0</span></div>
<div>accZ = <span id="accZ">0</span></div>
</div>
</body>
</html>
and this is the javascript:
import * as THREE from "three";
import data from "../data/data.json"
import "./style.css"
var width = window.innerWidth;
var height = window.innerHeight;
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize (width, height);
var planeSize = 100000
const fov = 70;
const aspect = 2; // the canvas default
const near = 20;
const far = 500;
const camera = new THREE.PerspectiveCamera(70, width/height, 1, 10000);
camera.position.y = 3;
camera.position.z = 30;
camera.lookAt (new THREE.Vector3(0,0,0));
// camera.position.set(0, 40, 1.5);
// camera.up.set(0, 10, 1);
// camera.lookAt(0, 10, 0);
const scene = new THREE.Scene();
{
const color = 0x00afaf;
const intensity = 10;
const size = 10;
const divisions = 10;
const gridHelper = new THREE.GridHelper( planeSize, 5000 );
gridHelper.setColors( new THREE.Color(0xff0000), new THREE.Color(0xffffff) );
scene.add(gridHelper);
const light = new THREE.PointLight(color, intensity);
scene.add(light);
// scene.add( gridHelper );
}
// // label the axis
// var textGeo = new THREE.TextGeometry('Y', {
// size: 5,
// height: 2,
// curveSegments: 6,
// font: "helvetiker",
// style: "normal"
// });
// var color = new THREE.Color();
// color.setRGB(255, 250, 250);
// var textMaterial = new THREE.MeshBasicMaterial({ color: color });
// var text = new THREE.Mesh(textGeo , textMaterial);
// text.position.x = axis.geometry.vertices[1].x;
// text.position.y = axis.geometry.vertices[1].y;
// text.position.z = axis.geometry.vertices[1].z;
// text.rotation = camera.rotation;
// scene.add(text);
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({ color: "green", wireframe: false });
const object = new THREE.Mesh(boxGeometry, boxMaterial);
var cubeAxis = new THREE.AxesHelper(3);
object.add(cubeAxis);
object.scale.set(5, 5, 5)
scene.add(object);
scene.background = new THREE.Color(0.22, 0.23, 0.22);
let currentIndex = 0
let time = data[currentIndex].time
let velocity = new THREE.Vector3()
requestAnimationFrame(render);
function render(dt) {
dt *= 0.0001 // in seconds
time += dt
document.querySelector("#time").textContent = time.toFixed(2)
// Find datapoint matching current time
while (data[currentIndex].time < time) {
currentIndex++
if (currentIndex >= data.length) return
}
const { rotX, rotY, rotZ, accX, accY, accZ } = data[currentIndex]
document.querySelector("#accX").textContent = accX* 10;
document.querySelector("#accY").textContent = accY* 10;
document.querySelector("#accZ").textContent = accZ* 10;
const acceleration = new THREE.Vector3()
// object.rotation.set( rotX, rotY, rotZ)
object.position.x = accX * 30;
// object.position.add(velocity.clone().multiplyScalar(dt)).add(acceleration.clone().multiplyScalar(50 * dt ** 2))
// object.position.add(accZ)
// velocity.add(acceleration.clone().multiplyScalar(dt))
var relativeCameraOffset = new THREE.Vector3 (0,10,10);
var cameraOffset = relativeCameraOffset.applyMatrix4( object.matrixWorld );
camera.position.x = cameraOffset.x;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt( object.position );
resizeToClient();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
function resizeToClient() {
const needResize = resizeRendererToDisplaySize()
if (needResize) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
}
function resizeRendererToDisplaySize() {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
this is how I want the canvas to look like:
Good news is it is possible, you just have to put all your code(at least animations) in separate functions :
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const scene1 = new THREE.Scene();
const camera1 = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
scene.background = new THREE.Color( 0xf0000f);
scene1.background = new THREE.Color( 0x0000ff );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
const renderer1 = new THREE.WebGLRenderer();
renderer1.setSize( window.innerWidth, window.innerHeight );
renderer1.domElement.width=500;
renderer1.domElement.height=300;
renderer1.domElement.style.position="absolute";
renderer1.domElement.style.top=0;
renderer1.domElement.style.height=150+"px";
renderer1.domElement.style.width=200+"px";
renderer.domElement.width=500;
renderer.domElement.height=300;
renderer.domElement.style.position="absolute";
renderer.domElement.style.top=0;
document.body.appendChild( renderer.domElement );
document.body.appendChild( renderer1.domElement );
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
const geometry1 = new THREE.BoxGeometry(3,3,3);
const material1 = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube1 = new THREE.Mesh( geometry1, material1 );
scene1.add( cube1 );
camera.position.z = 5;
camera1.position.z = 5;
const animate = function () {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
};
const animate1 = function () {
requestAnimationFrame( animate1 );
cube1.rotation.x += 0.01;
cube1.rotation.y += 0.01;
renderer1.render( scene1, camera1 );
};
animate();
animate1();
<script src="http://threejs.org/build/three.min.js"></script>
I want to create a grid of geometries (ROWS * COLUMNS) and wanna rotate each individual mesh around it's own center.
I generate the grid through two for loops, translate the single elements to there correct position and push them into an array to use them again in the animation function. In the animation function I iterate again over all the elements and rotate every single one.
the problem is that even though I address each element individually and I have translated each element to the right place, each element still revolves around the center of the page and not its own center (see the screenshot)
Here is my current attempt:
const main = () => {
const ROWS = 4;
const COLUMNS = 4;
const ITEMS = [];
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(1, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#e5e5e5');
camera.position.z = 500;
// GEOMETRY
for (let x = 0; x < COLUMNS; x++) {
for (let y = 0; y < ROWS; y++) {
const geometry = new THREE.BoxGeometry(1, 1, 0.5);
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x1940EB });
const edgesMesh = new THREE.LineSegments(edges, edgesMaterial);
ITEMS.push(edgesMesh);
edges.translate(x*1.5, y*1.5, 0);
scene.add(edgesMesh);
}
}
scene.position.set(-COLUMNS/2, -ROWS/2, 0);
const animation = () => {
requestAnimationFrame(animation);
ITEMS.map(item => {
item.rotation.x += 0.01;
item.rotation.y += 0.01;
})
camera.updateMatrixWorld();
renderer.render(scene, camera);
}
const onWindowResize = () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
animation();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
};
window.addEventListener('load', main, false);
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<canvas id="canvas"></canvas>
Can someone explain to me why the elements individually addressed in the animation function still do not revolve around themselves?
Thanks for your help
Because the code is moving the geometry itself so that it's center is no longer in the middle of the box.
change
edges.translate(x*1.5, y*1.5, 0);
to
edgesMesh.position.set(x*1.5, y*1.5, 0);
This will move the Object3D in the scenegraph instead of the vertices of the geometry data.
const main = () => {
const ROWS = 4;
const COLUMNS = 4;
const ITEMS = [];
const canvas = document.querySelector('#canvas');
const renderer = new THREE.WebGLRenderer({canvas, antialias: true});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(1, window.innerWidth/window.innerHeight, 0.1, 1000);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#e5e5e5');
camera.position.z = 500;
// GEOMETRY
for (let x = 0; x < COLUMNS; x++) {
for (let y = 0; y < ROWS; y++) {
const geometry = new THREE.BoxGeometry(1, 1, 0.5);
const edges = new THREE.EdgesGeometry(geometry);
const edgesMaterial = new THREE.LineBasicMaterial({ color: 0x1940EB });
const edgesMesh = new THREE.LineSegments(edges, edgesMaterial);
ITEMS.push(edgesMesh);
edgesMesh.position.set(x*1.5, y*1.5, 0);
scene.add(edgesMesh);
}
}
scene.position.set(-COLUMNS/2, -ROWS/2, 0);
const animation = () => {
requestAnimationFrame(animation);
ITEMS.map(item => {
item.rotation.x += 0.01;
item.rotation.y += 0.01;
})
camera.updateMatrixWorld();
renderer.render(scene, camera);
}
const onWindowResize = () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
}
animation();
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
};
window.addEventListener('load', main, false);
body {
padding: 0;
margin: 0;
height: 100vh;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.js"></script>
<canvas id="canvas"></canvas>
You might find this article useful
Im trying to understand an issue i am facing with ThreeJS. I have a simple cube on a page and i have a PNG of a white question mark, with the rest of the image transparent. When i add the texture to the cube;
cubeGeometry = new THREE.BoxGeometry( 5, 5, 5 );
cubeMaterial = new THREE.MeshStandardMaterial( { color: this.cubeColor, flatShading: true, map:new THREE.TextureLoader().load( require("./question-mark.png") ) } );
cubeMesh = new THREE.Mesh( cubeGeometry, cubeMaterial );
The transparent pixels in the PNG appear black no matter what i do. Whats even stranger is the white pixels of the PNG appear the color of this.cubeColor - which is set to orange.
I would expect the cube color to be orange with the transparent pixels of the PNG taking that color and the white pixels in the PNG to remain white.
Anyone shed some light ?
what I see
the texture
You can apply 2 or more materials like this
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur.com/iFom4eT.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
Example, note it uses BoxBufferGeometry not BoxGeometry
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('purple');
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur.com/hrzoDMj.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.min.js"></script>
<canvas id="c"></canvas>
note that if you put a color and a texture (map) in a single material the 2 colors, (1) the color you specified and (2) the color from the texture, are multiplied with each other. So, wherever the texture has zeros, RGBA = 0,0,0,0 then the result is 0,0,0,0 as anything multiplied by zero is zero. This is why you got the results you got originally where outside the question mark is transparent and inside is orange.
Knowing this you can use your white question mark and change its color by setting the color on the material that is using the texture.
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('purple');
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth);
// prepare geometry to use 2 materials
geometry.clearGroups();
geometry.addGroup( 0, Infinity, 0 );
geometry.addGroup( 0, Infinity, 1 );
const loader = new THREE.TextureLoader();
// create 2 materials
const material0 = new THREE.MeshBasicMaterial({
color: 'orange',
});
const material1 = new THREE.MeshBasicMaterial({
map: loader.load('https://i.imgur.com/hrzoDMj.png'),
transparent: true,
});
// apply 2 materials to geometry
const materials = [material0, material1];
const cube = new THREE.Mesh(geometry, materials);
scene.add(cube);
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
material1.color.setHSL(time, 1, 0.5);
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
main();
body { margin: 0; }
#c { width: 100vw; height: 100vh; display: block; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.min.js"></script>
<canvas id="c"></canvas>
i m following this guide for the light Light in Threejs
and i already add some light in my scene.
Now i m try to add a light on the character of my game, but still dont work.
i use the same code of the guide changing just the position.set
const color = 0xFFFFFF;
const intensity = 1;
const light2 = new THREE.SpotLight(color, intensity,0,Math.PI/3);
light2.position.set(100,-5000,1000);
light2.target = lightTarget;
light2.castShadow = true;
const helper2 = new THREE.SpotLightHelper(light2);
and after i add in this way to my character
self.flame.add( helper2 );
self.flame.add(lightTarget);
self.flame.add(light2);
I added a helper too, but if I use just the helper in the scene , so if comment
self.flame.add(light2)
I see the position of the light in perfect way, when add the light the helper disappear (in other light don't happened) and the light go as her want.
Someone can help me?
The helpers have to be parented to the scene (or at least the SpotLightHelper does). You may or may not want to parent the target to the scene.
You also need to call helper.update for each helper
'use strict';
/* global THREE, dat */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 5, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
{
const planeSize = 40;
const loader = new THREE.TextureLoader();
const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/checker.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.NearestFilter;
const repeats = planeSize / 2;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(planeGeo, planeMat);
mesh.rotation.x = Math.PI * -.5;
scene.add(mesh);
}
const cubes = [];
let parent = scene;
{
const cubeSize = 1;
const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
const cubeMat = new THREE.MeshPhongMaterial({
color: '#8AC',
emissive: '#333',
});
for (let i = 0; i < 6; ++i) {
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
mesh.position.set(1, 0, 0);
parent.add(mesh);
cubes.push(mesh);
parent = mesh;
}
}
cubes[0].position.set(-3, 7, 0);
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.SpotLight(color, intensity);
light.position.set(0, 0, 0);
light.target.position.set(0, -1, 0);
parent.add(light);
parent.add(light.target);
//scene.add(light.target);
const helper = new THREE.SpotLightHelper(light);
scene.add(helper);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
for (const cube of cubes) {
cube.rotation.z = Math.sin(time) * .4;
}
light.angle = THREE.Math.lerp(
THREE.Math.degToRad(20),
THREE.Math.degToRad(80),
Math.sin(time * 0.77) * 0.5 + 0.5);
helper.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/js/controls/OrbitControls.js"></script>