Three.js: renderer not working with ArrayCamera - three.js

I have following in html:
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.121.1/build/three.module.js";
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three#0.121.1/examples/jsm/controls/OrbitControls.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const cameraR = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const cameraL = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper );
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cameraR.position.x = 0.5;
cameraR.position.z = 5;
cameraL.position.x = -0.5;
cameraL.position.z = 5;
var cameras = [];
cameras.push(cameraL)
cameras.push(cameraR)
var cameraVR = new THREE.ArrayCamera( cameras );
cameraVR.position.set( 0, 0, 5 );
const controls = new OrbitControls(cameraVR,
renderer.domElement);
controls.update();
(function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
controls.update();
renderer.render(scene, cameraVR);
})();
It gives me error:
What am I missing in this code?
Edit: Edited earlier code as camera assigned to controls was wrong earlier. But issue still persists.

Your code fails since you are missing to add a viewport definition for each sub camera. The documentation states:
An instance of ArrayCamera always has an array of sub cameras. It's mandatory to define for each sub camera the viewport property which determines the part of the viewport that is rendered with this camera.
I've updated your code accordingly:
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const cameraR = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
cameraR.viewport = new THREE.Vector4( window.innerWidth / 2, 0, window.innerWidth / 2, window.innerHeight );
const cameraL = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
cameraL.viewport = new THREE.Vector4( 0, 0, window.innerWidth / 2, window.innerHeight );
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cameraR.position.x = 0.5;
cameraR.position.z = 5;
cameraR.lookAt( 0, 0, 0 );
cameraL.position.x = -0.5;
cameraL.position.z = 5;
cameraL.lookAt( 0, 0, 0 );
var cameras = [];
cameras.push(cameraL)
cameras.push(cameraR)
var cameraVR = new THREE.ArrayCamera(cameras);
cameraVR.position.set(0, 0, 5);
(function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, cameraVR);
})();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.121/build/three.js"></script>
The official array camera demo webgl_camera_array also demonstrates the usage of the class.

Related

Threejs / Raycast doesn't compute intersection with my cube

Here is my js files. It works. When I click on the cube, it goes inside raycast function, but doesn't enter the for loop and console.log( intersects[ 0 ] ) gives undefined
let camera, scene, renderer;
let mesh, mesh_green;
let raycaster, mouse = { x : 0, y : 0 };
init();
function init() {
camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 40;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: "red" } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
mesh.position.set( 0, 10, 0 );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
raycaster = new THREE.Raycaster();
renderer.domElement.addEventListener( 'click', raycast, false );
function raycast ( e ) {
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
console.log( "raycast" , e.clientX, mouse.x, window.innerWidth);
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects( scene.children );
for ( let i = 0; i < intersects.length; i++ ) {
console.log( intersects[ i ] );
}
}
It seems your code works by using a latest version of three.js. I've just refactored/simplified it a bit.
let camera, scene, renderer;
let mesh;
let raycaster, pointer = new THREE.Vector2();
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 40;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({
color: "red"
});
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 10, 0);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
raycaster = new THREE.Raycaster();
renderer.domElement.addEventListener('pointerdown', raycast);
}
function raycast(e) {
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObject(scene);
for (let i = 0; i < intersects.length; i++) {
console.log(intersects[i]);
}
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.140.2/build/three.min.js"></script>

Uncaught TypeError: Cannot read property 'map' of undefined in Three.js

I am trying to create a wave effect on an image using Three.js, I am following this tutorial on youtube to do so : -
Tutorial That I Followed
After completing the code, I am getting Uncaught TypeError: Cannot read property 'map' of undefined error in my console and the wireframe is also not visible.
Maybe this is due to the new Three.js update that introduced BufferGeometry instead of normal one.
My Js Code is as follows :-
const section = document.querySelector('#wrapper');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
renderer.setSize( window.innerWidth, window.innerHeight );
section.appendChild( renderer.domElement );
const texture = new THREE.TextureLoader().load( 'Wrapper2 Central.jpg' );
const geometry = new THREE.PlaneGeometry(7.5, 4 , 50, 30);
const material = new THREE.MeshBasicMaterial({
map: texture
});
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
cube.rotation.set(0, 0, 0)
camera.position.z = 5;
const clock = new THREE.Clock()
function animate() {
const t = clock.getElapsedTime()
cube.geometry.vertices.map( v => {
const wavex1 = 0.5 * Math.sin(v.x * 2 * t)
const wavex2 = 0.25 * Math.sin(v.x * 3 * t * 2)
const wavex3 = 0.5 * Math.sin(v.y * t)
v.z = wavex1 + wavex2 + wavex3
})
cube.geometry.verticesNeedUpdate()
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
There is no geometry.vertices anymore, as THREE.Geometry() was deprecated since r125, and how all geometry constructors return THREE.BufferGeometry, where vertices are stored in geometry.attributes.position:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.125.2/build/three.module.js";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const texture = new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/uv_grid_opengl.jpg' );
const geometry = new THREE.PlaneGeometry(7.5, 4 , 50, 30);
const material = new THREE.MeshBasicMaterial({
map: texture
});
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
cube.rotation.set(0, 0, 0)
camera.position.z = 5;
const clock = new THREE.Clock()
let v = new THREE.Vector3();
let pos = cube.geometry.attributes.position;
function animate() {
const t = clock.getElapsedTime()
for (let i = 0; i < pos.count; i++){
v.fromBufferAttribute(pos, i);
const wavex1 = 0.5 * Math.sin(v.x * 2 * t)
const wavex2 = 0.25 * Math.sin(v.x * 3 * t * 2)
const wavex3 = 0.5 * Math.sin(v.y * t)
pos.setZ(i, wavex1 + wavex2 + wavex3)
}
pos.needsUpdate = true;
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
</script>

Rendered object looks flat even with shadows

I'm a three.js newb. I've been playing around with it for a couple of days and haven't been able to figure out how to make my objects look more realistic. I suspect there's no simple answer for this question, but is there anything I can do to improve my rendering quality without going into the depth of "rendering science"? Maybe I'm missing some configs. Thank you for any advice!
Here's the relevant code used in rendering a kitchen cabinet frame.
this.renderer = new THREE.WebGLRenderer({ antialias: true })
this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight)
this.renderer.sortObjects = false
this.renderer.setClearColor(0xf0f0f0)
this.renderer.gammaFactor = 2.2
this.renderer.gammaOutput = true
this.renderer.setPixelRatio(window.devicePixelRatio)
this.renderer.shadowMap.enabled = true
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
const light = new THREE.AmbientLight(0xffffff, 0.8)
const light2 = new THREE.PointLight(0xffffff, 0.3)
light2.position.set(400, 400, 400)
light2.shadow.camera.near = 10
light2.shadow.camera.far = 10000
light2.shadow.mapSize.width = 2048
light2.shadow.mapSize.height = 2048
light2.castShadow = true
this.scene.add(light2)
this.scene.add(light)
const material = new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 0, roughness: 0 })
let scene, camera, controls, ambient, point, loader, renderer, container, stats;
const targetRotation = 0;
const targetRotationOnMouseDown = 0;
const mouseX = 0;
const mouseXOnMouseDown = 0;
const windowHalfX = window.innerWidth / 2;
const windowHalfY = window.innerHeight / 2;
init();
animate();
var box, b1, b2, b3;
function init() {
// Create a scene which will hold all our meshes to be rendered
scene = new THREE.Scene();
// Create and position a camera
camera = new THREE.PerspectiveCamera(
60, // Field of view
window.innerWidth / window.innerHeight, // Aspect ratio
/*window.innerWidth / -8,
window.innerWidth / 8,
window.innerHeight / 8,
window.innerHeight / -8,
*/
0.1, // Near clipping pane
1000 // Far clipping pane
);
scene.add(camera)
// Reposition the camera
camera.position.set(0, 5, 10);
// Point the camera at a given coordinate
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Add orbit control
controls = new THREE.OrbitControls(camera);
controls.target.set(0, -0.5, 0);
controls.update();
// Add an ambient lights
ambient = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambient);
// Add a point light that will cast shadows
point = new THREE.PointLight(0xffffff, 1);
point.position.set(25, 50, 25);
point.castShadow = true;
point.shadow.mapSize.width = 1024;
point.shadow.mapSize.height = 1024;
scene.add(point);
group = new THREE.Group();
group.position.y = 0;
scene.add(group);
rotationAnchor = new THREE.Object3D()
group.add(rotationAnchor);
box = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({
color: 'grey'
}))
b1 = box.clone();
b2 = box.clone();
b3 = box.clone();
b3.material = b3.material.clone()
b3.material.color.set('red')
group.add(box);
group.add(b1);
b1.position.y += 1
group.add(b2);
b2.position.z += 1
rotationAnchor.add(b3);
rotationAnchor.position.set(0.5, 0.5, 1.5)
b3.position.set(-.5, -.5, -.5)
// Create a renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
// Set size
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Set color
renderer.setClearColor(0xf8a5c2);
renderer.gammaOutput = true;
// Enable shadow mapping
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Append to the document
container = document.createElement("div");
document.body.appendChild(container);
document.body.appendChild(renderer.domElement);
// Add resize listener
window.addEventListener("resize", onWindowResize, false);
// Enable FPS stats
stats = new Stats();
container.appendChild(stats.dom);
var gui = new dat.GUI({
height: 5 * 32 - 1
});
let params = {
'test': 4,
'bevelThickness': 1,
'bevelSize': 1.5,
'bevelSegments': 3
}
gui.add(params, 'test', 0, 10).onChange(val => {
test = val
})
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
rotationAnchor.rotation.z = (Math.cos(performance.now() * 0.001) * Math.PI * 0.25) + (Math.PI * 1.25)
requestAnimationFrame(animate);
// Re-render scene
renderer.render(scene, camera);
// Update stats
stats.update();
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.min.js"></script>

3D Objects appearing as flat in three.js

I just setup a very basic "hello world" for three.js…
But apparently my 3d cube is appearing as a 2d square…
The code to the js file is here
const t = THREE;
const scene = new t.Scene();
const camera = new t.PerspectiveCamera(75, window.innerWidth, window.innerHeight, 0.1, 1000);
const renderer = new t.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// create a shape
const geometry = new t.BoxGeometry(1, 1, 1);
// create a material
const material = new t.MeshBasicMaterial({color: 0xFFFFFF, wireframe: true});
const cube = new t.Mesh(geometry, material);
// cube.position.y = 1;
scene.add(cube);
// camera.position.x = 300;
camera.position.z = 5;
// logic
const update = function () {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
};
// draw scene
const render = function () {
renderer.render(scene, camera);
};
// run render loop (update-render-repeat)
const RenderLoop = function () {
window.requestAnimationFrame(RenderLoop);
update();
render();
};
RenderLoop();
And the result of the code is here
Any help is appreciated!
EDIT:
Sorry for not including the code
You're building your PerspectiveCamera wrong:
Your way:
PerspectiveCamera(75, window.innerWidth, window.innerHeight, 0.1, 1000);
Correct way:
PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
You need to pass the ratio w/h, not two separate w, h arguments.

Assigning displacement/normal map to a single side of a cylinder

I have a cylinder created like this:
var geometry = new THREE.CylinderGeometry( 50, 50, 2, 128 );
It is a flat cylinder, like a coin. When I ad a displacementMap & normalMap, I end up with textures on both side of the cylinder, but I only want the maps to be on one side.
How can I do this?
For a THREE.CylinderGeometry a separated material for the shaft, the top plane and the bottom plane can be set:
var material1 = new THREE.MeshBasicMaterial({color:'#ff0000'});
var material2 = new THREE.MeshBasicMaterial({color:'#00ff00'});
var material3 = new THREE.MeshBasicMaterial({color:'#0000ff'});
var materialList = [material1, material2, material3];
var geometry = new THREE.CylinderGeometry( 50, 50, 2, 128 );
var mesh = new THREE.Mesh(geometry, materialList);
See the code snippet:
(function onLoad() {
var container, loader, camera, scene, renderer, controls, mesh;
init();
animate();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, -100, -100);
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = resize;
scene.add(camera);
window.onresize = resize;
var material1 = new THREE.MeshBasicMaterial({color:'#ff0000'});
var material2 = new THREE.MeshBasicMaterial({color:'#00ff00'});
var material3 = new THREE.MeshBasicMaterial({color:'#0000ff'});
var materialList = [material1, material2, material3];
var geometry = new THREE.CylinderGeometry( 50, 50, 2, 128 );
mesh = new THREE.Mesh(geometry, materialList);
scene.add(mesh);
controls = new THREE.OrbitControls(camera, renderer.domElement);
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function animate() {
mesh.rotation.x += 0.01;
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
})();
<script src="https://threejs.org/build/three.min.js"></script>
<!--script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js"></script-->
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
Use the thetaLength option of CylinderGeometry to separate it into two pieces.
// Create two half-cylinders.
var part1 = new THREE.CylinderGeometry( 50, 50, 2, 128, 1, false, 0, Math.PI );
var part2 = new THREE.CylinderGeometry( 50, 50, 2, 128, 1, false, Math.PI, Math.PI );
// Add the half-cylinders to a group, with different materials on each.
var group = new THREE.Group();
group.add( new THREE.Mesh( part1, material1 ) );
group.add( new THREE.Mesh( part2, material2 );
If you need more custom control than this, you probably want to use a modeling program like Blender to set up UVs and texture the cylinder.

Resources