3D Objects appearing as flat in three.js - 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.

Related

Three.js: renderer not working with ArrayCamera

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.

Create cone geometry with flat base in three.js?

Trying to figure out how to create something that looks like the image below... Tried searching around but couldn't really figure it out...
Try it with CylinderGeometry. This class allows to define different radii for the top and bottom.
let mesh;
let camera, scene, renderer;
init();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 2;
scene = new THREE.Scene();
const geometry = new THREE.CylinderGeometry(0.2, 0.5, 1, 16);
const material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animation);
document.body.appendChild(renderer.domElement);
}
function animation(time) {
mesh.rotation.x = time / 2000;
mesh.rotation.y = time / 1000;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.133.1/build/three.min.js"></script>

How to create tubes efficiently in three.js

I'm struggling to make tubes in THREE.js and there are very few tutorials on them so I decided to ask here. How can I create a tube without using this difficult piece of code taken from the official docs?
class CustomSinCurve extends THREE.Curve {
constructor( scale = 1 ) {
super();
this.scale = scale;
}
getPoint( t, optionalTarget = new THREE.Vector3() ) {
const tx = t * 3 - 1.5;
const ty = Math.sin( 2 * Math.PI * t );
const tz = 0;
return optionalTarget.set( tx, ty, tz ).multiplyScalar( this.scale );
}
}
Preferably with the use of bezier curves or something intuitive.
(To be clear, I'm using React-three-fiber to create these models but I know how to convert vanilla THREE.js to it.)
Try it like so:
let mesh;
let camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 4;
scene = new THREE.Scene();
const curve = new THREE.QuadraticBezierCurve3(new THREE.Vector3(-1, -1, 0), new THREE.Vector3(-1, 1, 0), new THREE.Vector3(1, 1, 0))
const geometry = new THREE.TubeGeometry(curve);
const material = new THREE.MeshNormalMaterial({
side: THREE.DoubleSide
});
mesh = new THREE.Mesh(geometry, material);
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);
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.133.1/build/three.min.js"></script>
The example code uses a simple quadratic bezier curve in order to generate the tube's shape.

Adding DeviceOrientationControls to a GLTF GLB object

I am using Three.JS and have gotten a .glb file to display beautifully with some mouse movement.
I'm now trying to add DeviceOrientationControls to my GLtf/GLB scene, so I can move around the scene when moving the mobile phone around, however I can't quite work it out.
No matter what I try I always get one error or another.
I have tried adapting the code from this example: https://threejs.org/examples/misc_controls_deviceorientation.html
This is my current code without any DeviceOrientationControls code added:
HTML
<!-- three.min.js r87 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<!-- GLTFLoader.js -->
<script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js#r92/examples/js/loaders/GLTFLoader.js"></script>
<!-- Orientation Controls -->
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/DeviceOrientationControls.js"></script>
<div id="overlay">
<div>
<button id="startButton">Start Demo</button>
<p>Using device orientation might require a user interaction.</p>
</div>
</div>
<div class="single-object">
<div id="container"></div>
JS
window.addEventListener("load", loadGltf, false);
window.addEventListener("resize", onWindowResize, false);
const progressBar = document.querySelector("progress");
const gltfStore = {};
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// Attach to container
container = document.getElementById( 'container' );
//Setup camera
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight,0.1, 1000 );
camera.position.set(0, 0, 0);
camera.lookAt(0, 0, 5);
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
//Re-establish camera view on window resize
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//Lighting
const lightFill = new THREE.PointLight(0xffffff, 1, 500);
lightFill.position.set(0, 0, 5);
scene.add(lightFill);
const lightKey = new THREE.PointLight(0xffffff, 1, 500);
lightKey.position.set(20, 0, 20);
scene.add(lightKey);
const loader = new THREE.GLTFLoader();
// Device Orientation
// Load the GLB file
function loadGltf() {
loader.load(
"<?php echo get_template_directory_uri();?>/objects/SM_LookDev_TextureTest_FromFBX.glb",
function(gltf) {
scene.add(gltf.scene);
mesh = gltf.scene;
gltf.scene.children[0];
gltfStore.scene = gltf.scene;
// Set listener
document.addEventListener("mousemove", onMouseMove);
}
);
container.appendChild(renderer.domElement);
// Mouse movement
function onMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 30;
}
function animate() {
requestAnimationFrame(animate);
// Adjust mouse and scene position
camera.position.x += (mouseX - camera.position.x) * .0005;
camera.position.y += (-mouseY - camera.position.y) * .0003;
camera.lookAt(0,0,10);
renderer.render(scene, camera);
}
animate();
}
I have gathered from the examples that I need to add these lines (I think):
controls = new DeviceOrientationControls( camera );
controls.update();
and the below for the start button...
var startButton = document.getElementById( 'startButton' );
startButton.addEventListener( 'click', function () {
init();
animate();
}, false );
EDIT
Below is an example of where I have tried to add the lines, but have not worked successfully.
window.addEventListener("load", loadGltf, false);
window.addEventListener("resize", onWindowResize, false);
const progressBar = document.querySelector("progress");
// Add Start Button
var startButton = document.getElementById( 'startButton' );
startButton.addEventListener( 'click', function () {
init();
animate();
}, false );
const gltfStore = {};
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// Attach to container
container = document.getElementById( 'container' );
//Setup camera
const camera = new THREE.PerspectiveCamera(
75, window.innerWidth / window.innerHeight,0.1, 1000 );
camera.position.set(0, 0, 0);
camera.lookAt(0, 0, 5);
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
mouseX = 0,
mouseY = 0;
//Re-establish camera view on window resize
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
//Lighting
const lightFill = new THREE.PointLight(0xffffff, 1, 500);
lightFill.position.set(0, 0, 5);
scene.add(lightFill);
const lightKey = new THREE.PointLight(0xffffff, 1, 500);
lightKey.position.set(20, 0, 20);
scene.add(lightKey);
const loader = new THREE.GLTFLoader();
// Added Device Orientation Controls
controls = new DeviceOrientationControls( camera );
// Load the GLB file
function loadGltf() {
loader.load(
"<?php echo get_template_directory_uri();?>/objects/SM_LookDev_TextureTest_FromFBX.glb",
function(gltf) {
scene.add(gltf.scene);
mesh = gltf.scene;
gltf.scene.children[0];
gltfStore.scene = gltf.scene;
// Set listener
document.addEventListener("mousemove", onMouseMove);
}
);
container.appendChild(renderer.domElement);
// Mouse movement
function onMouseMove(event) {
mouseX = (event.clientX - windowHalfX) / 10;
mouseY = (event.clientY - windowHalfY) / 30;
}
function animate() {
requestAnimationFrame(animate);
// Adjust mouse and scene position
camera.position.x += (mouseX - camera.position.x) * .0005;
camera.position.y += (-mouseY - camera.position.y) * .0003;
camera.lookAt(0,0,10);
// Add Controls Update
controls.update();
renderer.render(scene, camera);
}
animate();
}
When adding the lines like this, I get these 2 error messages:
Uncaught TypeError: Cannot read property 'addEventListener' of null
and
Uncaught ReferenceError: Cannot access 'loader' before initialization at loadGltf
So, I am unsure how to actually add these lines into my specific code to get it to work?
Any assistance would be appreciated
It's hard to tell exactly the reason why it's not working, especially since your question includes so much extraneous code. You should try to ask your questions with a minimal reproducible example.
First, you're importing your <scripts> from varying sources, and varying versions. The Three.js and GLTFLoader you're loading is r92, but the DeviceOrientationControls script doesn't specify a version, so it's loading the latest at r112, which might no longer be compatible with ThreeJS r92.
Secondly, you're using:
controls = new DeviceOrientationControls( camera );, when it should be:
controls = new THREE.DeviceOrientationControls( camera );
Finally, make sure you organize your code, and call your functions in a sequential manner. Is loadGltf() being called before you initiate const loader = new THREE.GLTFLoader()? Does startButton exist, and is it defined before you call startButton.addEventListener();?
Here is a minimal example implementing the orientation controls: Notice that both scripts are being imported from the same source with r92 to ensure compatibility.
window.addEventListener("resize", resize);
const renderer = new THREE.WebGLRenderer({canvas: document.querySelector("canvas")});
const camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
const controls = new THREE.DeviceOrientationControls( camera );
// Make a scene with geometry
const scene = new THREE.Scene();
const geometry = new THREE.DodecahedronBufferGeometry(100,1);
const material = new THREE.MeshBasicMaterial({
color: 0xff9900,
wireframe: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
function resize() {
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
function animate(time) {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
resize();
animate(0);
body { margin: 0; }
<canvas></canvas>
<!-- three.min.js r92 -->
<script src="https://cdn.jsdelivr.net/npm/three#0.92/build/three.min.js"></script>
<!-- Orientation Controls r92 -->
<script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js#r92/examples/js/controls/DeviceOrientationControls.js"></script>
Note:
Apple disabled gyroscope access by default since iOS 12.2, so you'll have to enable it in settings when testing. See here for a discussion on the matter.

Basic THREE.js scene setup

my question is simple but for the life of me i cannot figure out what is going on. I am trying to set up a basic three.js scene and add a simple cube with a BaiscMaterial however the cube is not showing up in my scene.
"use strict";
var renderer, scene, camera;
var light;
function init() {
var canvasWidth = 850;
var canvasHeight = 450;
var canvasRatio = canvasWidth / canvasHeight;
camera = new THREE.PerspectiveCamera(45, canvasRatio, 0.9, 1000);
camera.position.set(0, 200, -550);
camera.lookAt(0, 0, 0);
light = new THREE.AmbientLight(0xFFFFFF, 1);
light.position.set(-800, 900, 300);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(canvasWidth, canvasHeight);
renderer.setClearColorHex(0x000000, 1.0); //canvas color
renderer.gammaInput = true;
renderer.gammaOutput = true;
}
function cube() {
var cubeGeo = new THREE.CubeGeometry(1000, 1000, 1000);
var cubeMaterial = new THREE.MeshBasicMaterial();
var cube1 = new THREE.Mesh(cubeGeo, cubeMaterial);
cube1.position.set(0, 0, 0);
return cube1;
}
function fillScene() {
scene = new THREE.Scene();
scene.add(light);
var cube = cube();
scene.add(cube);
}
function addToDOM() {
var container = document.getElementById('container');
var canvas = container.getElementsByTagName('canvas');
if (canvas.length > 0) {
container.removeChild(canvas[0]);
}
container.appendChild(renderer.domElement);
}
function render() {
fillScene();
renderer.render(scene, camera);
}
try {
init();
fillScene();
addToDOM();
render();
} catch (e) {
var errorReport = "Your Program encountered an ERROR, cannot draw on canvas. Error was:<br/><br/>";
$('#container').append(errorReport + e);
}
First of all, Cube is updated to BoxGeometry now. And I see lots of problem on your code and improper function definition and usage.
Here's a very simple example from mr.doob's Github.
var scene, camera, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
geometry = new THREE.BoxGeometry( 200, 200, 200 );
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render( scene, camera );
}
See the demo here
P.S Three.js Docs is the best place for resources, it makes work alot easier with tons of example code.

Resources