my three.js scene is not rendering. not sure why - javascript

my main.js file contains :-
import './style.css';
import * as THREE from 'three';
//create scene
const scene = new THREE.Scene();
//arguments - field of view, aspect ratio, last 2 are view frustrum(controls which objects are visible relative to the camera)
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth/window.innerHeight,
0.1,
1000,
);
const renderer = new THREE.WebGLRenderer({
//which DOM element to use
canvas: document.querySelector('.canvas'),
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth / window.innerHeight);
camera.position.setZ(100);
//arguments - radius, widthsegments, heightsegements
const geometry = new THREE.SphereGeometry(15, 32, 16);
//wireframe true to get a better look at its geometry
const material = new THREE.MeshBasicMaterial({color: 0xffff00, wireframe: true});
//torus is creating the mesh with geometry and material //mesh = geometry + material
const globe = new THREE.Mesh(geometry, material);
scene.add(globe);
function animate(){
requestAnimationFrame(animate); //optimized rendering
//rotation
globe.rotateOnAxis += 0.01;
renderer.render( scene, camera );
}
animate();
renderer.render(scene, camera);
and my index.html :-
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Gautam</title>
</head>
<body>
<canvas id="globe">This is the canvas
</canvas>
<script type="module" src="./main.js"></script>
</body>
</html>
All that appears on my screen is :-
[what displays on my browser][1]
[1]: https://i.stack.imgur.com/PQmJu.png
I checked and my main.js file is definitely executing, but nothing is rendering on the screen

You have a few issues.
document.querySelector('.canvas')
This would select elements with the class canvas, but your DOM has no such element. It does have an element with the type canvas and the ID globe, so that line should be one of the following:
document.querySelector('canvas')
document.querySelector('#globe')
document.getElementById('globe')
Next,
renderer.setSize(window.innerWidth / window.innerHeight);
As #Mugen87 points out, this should be
renderer.setSize(window.innerWidth, window.innerHeight);
As you've written it, the renderer's width is being set to the quotient, and its height (the missing second parameter) is being set to undefined, a situation that three.js has no handling for.
And,
globe.rotateOnAxis += 0.01;
rotateOnAxis is a function that takes a vector (the axis of rotation) and a scalar (the rotation angle) as its parameters. By assigning +0.01 to it, you're simply replacing the function itself, not calling the function. If, for example, you want to rotate around the globe's y-axis, you could use
globe.rotateOnAxis(new THREE.Vector3(0, 1, 0), 0.01);
Finally, the second call to renderer.render(...) (the one outside the animate() function) is unnecessary.

renderer.setSize(window.innerWidth / window.innerHeight);
Call the method like so instead:
renderer.setSize(window.innerWidth, window.innerHeight);
//create scene
const scene = new THREE.Scene();
//arguments - field of view, aspect ratio, last 2 are view frustrum(controls which objects are visible relative to the camera)
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000,
);
const renderer = new THREE.WebGLRenderer({
//which DOM element to use
canvas: document.querySelector('#canvas')
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
camera.position.setZ(100);
//arguments - radius, widthsegments, heightsegements
const geometry = new THREE.SphereGeometry(15, 32, 16);
//wireframe true to get a better look at its geometry
const material = new THREE.MeshBasicMaterial({
color: 0xffff00,
wireframe: true
});
//torus is creating the mesh with geometry and material //mesh = geometry + material
const globe = new THREE.Mesh(geometry, material);
scene.add(globe);
function animate() {
requestAnimationFrame(animate); //optimized rendering
//rotation
globe.rotateOnAxis += 0.01;
renderer.render(scene, camera);
}
animate();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.140.2/build/three.min.js"></script>
<canvas id="canvas"></canvas>

Related

three.js - how to render scene.background before a mesh?

I am a novice to Javascript and ThreeJS. I have a 3D rotating cube that appears on top of a static background, but one frustrating property is that the cube typically appears first and then the background image appears. How do I ensure the background is rendered first? Specifically, I always want the background image to appear before the cube.
My code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="js/three.js"></script>
<script>
function resize() {
var aspect = window.innerWidth / window.innerHeight;
let texAspect = bgWidth / bgHeight;
let relAspect = aspect / texAspect;
bgTexture.repeat = new THREE.Vector2( Math.max(relAspect, 1), Math.max(1/relAspect,1) );
bgTexture.offset = new THREE.Vector2( -Math.max(relAspect-1, 0)/2, -Math.max(1/relAspect-1, 0)/2 );
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
const scene = new THREE.Scene();
// Arguments:
// 1) Field of Value (degrees)
// 2) Aspect ratio
// 3) Near clipping plane
// 4) Far clipping plane
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
// Need to set size of renderer. For performance, may want to reduce size.
// Can also reduce resolution by passing false as third arg to .setSize
renderer.setSize( window.innerWidth, window.innerHeight );
// Add the rendered to the HTML
document.body.appendChild( renderer.domElement );
// A BoxGeometry is an object that contains all points (vertices) and fill (faces)
// of the cube
const geometry = new THREE.BoxGeometry();
// Determines surface color (maybe texture?)
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
// Mesh takes a geometry and applies the material to it
const cube = new THREE.Mesh( geometry, material );
// Add background image
const loader = new THREE.TextureLoader();
bgTexture = loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' ,
function(texture) {
// Resize image to fit in window
// Code from https://stackoverflow.com/a/48126806/4570472
var img = texture.image;
var bgWidth = img.width;
var bgHeight = img.height;
resize();
});
scene.background = bgTexture;
// By default, whatever we add to the scene will be at coordinates (0, 0, 0)
scene.add( cube );
camera.position.z = 5;
// This somehow creates a loop that causes the rendered to draw the scene
// every time the screen is refreshed (typically 60fps)
function animate() {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
}
animate();
</script>
</body>
</html>
This is happening because the texture is taking longer to load than it takes for Three.js to set up the rest of the scene. You already have a handler for the onLoad callback of TextureLoader.load(), so we can use that to adjust the behavior.
Before scene.add( cube );, add a new line:
cube.visible = false;
Now the cube will still be added to the scene, but it won't be visible. Now after the resize() call at the end of function(texture), add
cube.visible = true;
While testing the problem and solution locally, I ran into a few other, less significant issues with your code. You can see all of the changes I had to make to get it running properly at this Gist.

Setting the position of a text geometry?

I have looked through stack overflow and google and I have found how to CENTER a text geometry but that is not what I want to do.
I have a scene that just has a block of text that says "Buy Here!". Using the documentation in the three.js website and examples here I was able to do that after some struggling. I had some trouble finding out how to refer to that mesh since I had created the geometry inside a function, and it took hours for me to know about setting a name for it as a string so it can be accessible from different parent/child levels.
What I am NOT able to do now is to offset the text by some arbitrary number of units. I tried shifting it down by 5 units. No matter how I try to do it it isn't working. I either manage to make the text geometry disappear OR my whole scene is black.
Here is my code...
I have the basic scene setup working properly and I'll include it here but feel free to skip since I'm pretty sure this has nothing to do with the issue...
import './style.css'
import * as THREE from 'three';
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three#0.117.0/examples/jsm/controls/OrbitControls.js';
import TWEEN from 'https://cdn.jsdelivr.net/npm/#tweenjs/tween.js#18.5.0/dist/tween.esm.js';
//BASIC SCENE SETUP
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
//LIGHTS (POINT AND AMBIENT)
const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(5, 5, 5);
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
scene.add(pointLight, ambientLight);
//RESIZE WINDOW
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}, false);
//ORBIT CONTROLS
const controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 5;
controls.maxDistance = 70;
controls.enablePan = false;
controls.enableRotate = false;
controls.enableZoom = false;
controls.target.set(0,0,-1);
camera.position.setZ(25);
window.addEventListener("click", (event) => {
onClick(event);
})
window.addEventListener("mousemove", onMouseMove);
var animate = function() {
requestAnimationFrame(animate);
controls.update();
render();
TWEEN.update();
};
function render() {
renderer.render(scene, camera);
}
animate();
and here is my code for the text object....
var loaderF = new THREE.FontLoader();
loaderF.load( 'https://threejs.org/examples/fonts/optimer_regular.typeface.json', function ( font ) {
var geometry = new THREE.TextGeometry( 'Buy Here!', {
font: font,
size: 2.3,
height: 0.1,
curveSegments: 15,
bevelEnabled: true,
bevelThickness: 0.5,
bevelSize: 0.31,
bevelSegments: 7
} );
geometry.center();
var material = new THREE.MeshLambertMaterial({color: 0x686868});
var mesh = new THREE.Mesh( geometry, material );
mesh.name = "bhText"
scene.add( mesh );
mesh.userData = { URL: "http://google.com"};
} );
Here's what I have tried.....
under "var geometry ({...});" I typed....
geometry.position.setX(-5);
but the text object disappears completely so I tried
geometry.position.setX = -5;
but there was no difference so i tried taking out
geometry.center();
but it had the same results.
So then I tried using
mesh.position.x = -5;
with AND without
geometry.center();
but again, they all just make my text object disappear.
So now I tried to set the position from outside the function by typing the following code OUTSIDE of everything that is contained in
loaderF.load ('https.....', function (font){var geometry = .....})
using the reference I learned....
scene.getObjectByName("bhText").position.x(-5);
but this makes my entire scene go blank (black). So I tried variations of like
scene.getObjectByName("bhText").position.x = -5;
scene.getObjectByName("bhText").position.setX(-5);
scene.getObjectByName("bhText").position.setX = -5;
mesh.position.setX = -5;// I was pretty sure this wasn't going to work since I wasn't
//using the mesh name specifically for when it's inside something
//I can't reach because of parent-child relations
and again trying each of those with AND without
geometry.center();
but they all made my scene go black.
I just wanna move it down a couple of units. Sheesh.
Could anyone be kind enough to tell me WHERE in my code I can set the position of the text geometry? Thank you please.
I just wanna move it down a couple of units.
In this case use mesh.position.y = - 5;. Changing the x coordinate will move the mesh to the left or right. Here is a complete live example based on your code:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set( 0, 0, 10 );
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(5, 5, 5);
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
scene.add(pointLight, ambientLight);
const loader = new THREE.FontLoader();
loader.load('https://threejs.org/examples/fonts/optimer_regular.typeface.json', function(font) {
const geometry = new THREE.TextGeometry('Buy Here!', {
font: font,
size: 2,
height: 0.5
});
geometry.center();
const material = new THREE.MeshLambertMaterial({
color: 0x686868
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = - 1; // FIX
mesh.name = "bhText"
scene.add(mesh);
renderer.render(scene, camera);
});
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.min.js"></script>

How to set vertical angle of spherebuffergeometry? Why Orbit controls don't work properly when mesh is rotated?

I am having issues trying to set the default view of a panoramic image inside spherebuffergeometry. I have a mesh that makes use of this geometry and the material is an equirectangle panorama image. During runtime, the default view of the image is somewhere towards the right. I want the initial angle to be at the bottom of the camera. I tried changing the Phi angle and theta angle parameters of spherebuffergeometry. While I am able to move the default horizontal angle by changing Phi angle, the panoramic image looks weird when I change the theta angle.
I took the first two snapshots when phi angle was 0 and 100 respectively and theta angle was 0. I took the last snapshot when phi angle was 100 and theta angle was 1.
Any help would be appreciated.
Thanks
sphereBufferGeometry = new THREE.SphereGeometry(100,100,100,0,Math.PI*2,0,Math.PI); sphereBufferGeometry.scale( 1, 1, -1 );
var material = new THREE.MeshBasicMaterial( {
map: new THREE.TextureLoader().load([image] )
});
mesh = new THREE.Mesh( sphereBufferGeometry, material );
I have tried this as well.
init();
var scene,renderer,camera;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
var light = new THREE.PointLight(0xffffff, 0.8);
var light2 = new THREE.DirectionalLight( 0xffddcc, 1 );
light.position.set( 1, 0.75, 0.5 );
scene.add( light2 );
var sphereBufferGeometry = new THREE.SphereGeometry(100, 100, 100, 0, Math.PI * 2, 0, Math.PI);
sphereBufferGeometry.scale(1, 1, -1);
var material = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load('https://threejs.org/examples/textures/2294472375_24a3b8ef46_o.jpg')
});
var mesh = new THREE.Mesh(sphereBufferGeometry, material);
mesh.rotation.x=1.6;
scene.add(camera);
scene.add(light);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
var orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
camera.position.set(0, 0, 0.001);
orbitControls.update();
var container = document.getElementById('container');
var canvas = renderer.domElement;
canvas.setAttribute("class", "frame");
container.appendChild(canvas);
document.body.appendChild(container);
animate();
}
function animate()
{
requestAnimationFrame(animate);
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
. As you can see, I've rotated the mesh so that the default view is looking at the floor.. if we click and drag the mouse upwards such that the window in the view is horizontal and then click and drag left or right, orbit controls don't seem to work properly in that it doesn't give a proper panoramic experience.. I would like to know how can this be fixed

ThreeJS not rendering Cube with setAnimationLoop

So I'm working on a ThreeJS WebVR Page. (I'm quite new to three.js)
So I tired to make a basic scene to test some stuff. But when I load the page with renderer.setAnimationLoop(render) I get my green cube for 1 Frame and then it disappears.
(I got it working with requestAnimationFrame() but this will not work with WebVR)
This is my code for my test sandbox:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.vr.enabled = true;
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
function render() {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
function animate() {
renderer.setAnimationLoop(render);
}
animate();
//Button vor start VR Session
document.body.appendChild(WEBVR.createButton(renderer));
Not the problem with setAnimationLoop
The problem is you cannot set camera position when vr is enabled
check this stack overflow question
Unable to change camera position when using VRControls
Check this CodePen Link where i fixed your code by changing position of cube instead of camera
var cube = new THREE.Mesh(geometry, material);
cube.position.z = -5; //added this instead of camera.position.z = 5;
if you want to move the camera you need make it as child of another object and set the position to that object

Three.js ply colorless (all black)

I am trying to load my ply file using Three.js. It has worked but I almost don't see any colors. There are some glimpses here and there, but it's mostly black (the first image below). The image opens properly (with colors) in MeshLab (the second image below). I have tried vertexColors: THREE.VertexColors (as suggested in Three.js - ply files - why colorless?) and vertexColors: THREE.FaceColors but nothing seemed to help. I am a three.js newbie, so please tell me what am I doing wrong.
and my code:
<!doctype html>
<html lang="en">
<head>
<title>Icon 7</title>
<meta charset="utf-8">
</head>
<body style="margin: 0;">
<script src="js/three.min.js"></script>
<script src="js/PLYLoader.js"></script>
<script src="js/OrbitControls.js"></script>
<script>
// Set up the scene, camera, and renderer as global variables.
var scene, camera, renderer;
init();
animate();
// Sets up the scene.
function init() {
// Create the scene and set the scene size.
scene = new THREE.Scene();
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
// Create a renderer and add it to the DOM.
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(WIDTH, HEIGHT);
document.body.appendChild(renderer.domElement);
// Create a camera, zoom it out from the model a bit, and add it to the scene.
camera = new THREE.PerspectiveCamera(35, WIDTH / HEIGHT, 0.1, 100);
camera.position.set(0,0.15,3);
camera.lookAt(new THREE.Vector3(0,0,0));
scene.add(camera);
// Create an event listener that resizes the renderer with the browser window.
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
// Set the background color of the scene.
renderer.setClearColor(0xd3d3d3, 1);
// Create a light, set its position, and add it to the scene.
var light = new THREE.PointLight(0xffffff);
light.position.set(0,200,100);
scene.add(light);
// Load in the mesh and add it to the scene.
var loader = new THREE.PLYLoader();
loader.load( './models/foot.ply', function ( geometry ) {
geometry.computeVertexNormals();
geometry.center();
var material = new THREE.MeshStandardMaterial ({ shininess: 1000,vertexColors: THREE.FaceColors } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = 0;
mesh.castShadow = false;
mesh.receiveShadow = false;
scene.add( mesh );
} );
// Add OrbitControls so that we can pan around with the mouse.
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.userPanSpeed = 0.05;
}
// Renders the scene and updates the render as needed.
function animate() {
// Read more about requestAnimationFrame at http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
requestAnimationFrame(animate);
// Render the scene.
renderer.render(scene, camera);
controls.update();
}
</script>
</body>
</html>
Edit: as I don't have separate textures, this is not a duplicate question.
Instead of using MeshStandardMaterial use MeshBasicMaterial

Categories

Resources