I have been working on a 3D project where we show 3D object in the web browser using Three.js Library.
The problem is:
1st the model is displayed in a small dom element or when the browser window itself is small.
Then when the window (or the dom element is resized) the model become pixelated
Following are some screenshots:
Before resize:
After resize:
How it should be after resize:
Here is the part of code that is setting the the model dimensions (height and width), and this function gets called when the resize event if fired:
console.log("domChanged fired")
instance.domBoundingBox = instance.dom.getBoundingClientRect();
instance.domCenterPos.x = instance.domBoundingBox.width / 2 + instance.domBoundingBox.left;
instance.domCenterPos.y = instance.domBoundingBox.height / 2 + instance.domBoundingBox.top;
var width = instance.dom.clientWidth, height = instance.dom.clientHeight;
instance.domWidthHalf = width / 2, instance.domHeightHalf = height / 2;
// TODO: fire event to expose it to site developers
// here we should update several values regarding width,height,position
if(instance.cameraReal) {
instance.cameraReal.aspect = instance.dom.clientWidth / instance.dom.clientHeight;
instance.cameraReal.updateProjectionMatrix();
}
if(instance.renderer3D)
instance.renderer3D.setSize(instance.dom.clientWidth, instance.dom.clientHeight);
Can anybody give me a hint? I've been working on that a couple of days already but no clue so far
So the canvas element can be resized like every other element. What you want to do is tell the render and camera to resize the contents of your canvas as well.
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
Finally the problem were solved the actually problem was coming from because the application is using the THREE.EffectComposer object, in the class constructor a composer object were created like following:
this.composer = new THREE.EffectComposer(this.renderer3D);
So as for the renderer, the composer needed to have the size updated after the event handler function like following:
if(instance.renderer3D)
instance.renderer3D.setSize(instance.dom.clientWidth, instance.dom.clientHeight);
if(instance.composer)
instance.composer.setSize(instance.dom.clientWidth, instance.dom.clientHeight);
And this fixed the issue perfectly :)
I applied Shawn Whinnery's answer to my needs with React JS:
Start listener onMount:
componentDidMount() {
window.addEventListener('resize', this.handleResize, false)
}
Remove listener onUnmount (because we like garbage collection):
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize, false)
}
Install handler function:
handleResize = () => {
this.camera.aspect = window.innerWidth / window.innerHeight
this.camera.updateProjectionMatrix()
this.renderer.setSize(window.innerWidth, window.innerHeight)
}
Fat arrow syntax is used to avoid binding this. The code will not work if you have this: handleResize() { ... }, but of course, it would work if you added this to your constructor:
this.handleResize = this.handleResize.bind(this)
Final note: confirm that your camera is this.camera and your renderer is this.renderer. Besides that, you should be able to paste it all in.
For those googling, if you want to start off quick with a helper util that does the resizing for you, check out this github: https://github.com/jeromeetienne/threex.windowresize
The only thing you have to do is install it via bower and initialise it:
new THREEx.WindowResize(renderer, camera)
It is very simple. this three.js code will not change even if resized the browser window.
Code:
//Simple Cube Example
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: "rgb(255, 0, 0)" } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
var edges = new THREE.EdgesHelper( cube, "rgb(0, 0, 0)" );
edges.material.linewidth = 5;
scene.add( edges );
function animate(){
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
}
animate();
function rot(){
requestAnimationFrame( rot );
edges.rotation.x += 0.01;
edges.rotation.y += 0.01;
}
rot();
//this will not change it's size when browser is resized, but use css and correct it.
//this is the code. it does not work use a div and use the javascript
.code{
float: left;
width: 100vw; /*fullscreen*/
height: 100vh;
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="keywords" content="">
<meta name="robots" content="index, follow" />
<link rel="canonical" href="http://" />
<meta name="author" content="">
<meta name="google-site-verification" content="">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Hammersmith+One|Lato" rel="stylesheet">
<title></title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="https://threejs.org/build/three.js"></script>
</body>
</html>
try antialias property to resolve pixelation problem by 50 - 70%
e.g :var renderer = new THREE.WebGLRenderer({antialias:true});
Related
I'm trying to import a 3D module into three.js and I was reading here and here. But it's all black. I've tried moving the Camera's z position to 5, but still black. I've just started with Three.js. I loaded the model in online Three.js viewers and it loads fine. Here's my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Import 3D Model into Three JS</title>
<link rel="stylesheet" href="../common.css">
</head>
<body>
<div id="container">
</div>
</body>
<script src="../three.js-master/build/three.js"></script>
<script type="module">
// import { THREE } from '../three.js-master/build/three.js';
import { GLTFLoader } from '../three.js-master/examples/jsm/loaders/GLTFLoader.js';
const container = document.querySelector('div#container');
const path_to_model = './Mini-Game Variety Pack/Models/gltf/tree_forest.gltf.glb';
const loader = new GLTFLoader();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
loader.load(path_to_model, function (gltf)
{
console.log('Adding glTF model to scene...');
scene.add(gltf.scene);
console.log('Model added.');
console.log('Moving camera 5z...');
camera.position.z = 5;
console.log('Camera moved.');
}, undefined, function (error)
{
console.error(error);
});
container.appendChild(renderer.domElement);
function animate()
{
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
</script>
</html>
My folders are like this:
Three JS/
common.css
three.js-master/
ImportModel/
index.html
Mini-Game Variety Pack/
Models/
gltf/
tree_forest.glb
tree_forest.gltf.glb
(Not all files are shown, only necessary ones)
I downloaded all the models from here, but using the tree_forest one.
When I load the page, all I see is:
I'm on a Mac and I'm using Five Server on VSCode.
<script src="../three.js-master/build/three.js"></script>
<script type="module">
// import { THREE } from '../three.js-master/build/three.js';
import { GLTFLoader } from '../three.js-master/examples/jsm/loaders/GLTFLoader.js';
Mixing global scripts with ES6 modules is no good idea and quickly leads to undefined behavior. I suggest you organize imports like so until the app works:
import * as THREE from 'https://cdn.skypack.dev/three#0.129.0/build/three.module.js';
import { OrbitControls } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/loaders/GLTFLoader.js';
You should also add some lights to your scene otherwise you just see a black screen. Try it with:
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 10, 0 );
scene.add( hemiLight );
const dirLight = new THREE.DirectionalLight( 0xffffff );
dirLight.position.set( 0, 0, 10 );
scene.add( dirLight );
So I'm trying to load a GLTF file and I am receiving this error:
I don't know why it can't locate and open the file. Do I have to set up a local server?
I looked up other examples online and this one includes a DRACOLoader. Admittedly, I don't know what this is for and was wondering if I need to implement this in order for it to load.
Here is my code:
<html>
<head>
<meta charset="utf-8">
<title>Website first attempt</title>
<style>
body { margin: 0;
background: linear-gradient(to bottom, #33ccff 0%, #ffffff 20%);}
canvas { display: block; }
</style>
</head>
<body>
<!-- <script type = "module" src="build/myScript.js"></script>-->
<script type = "module">
import * as THREE from '../build/three.module.js';
import { GLTFLoader } from '../build/GLTFLoader.js';
let scene, camera, renderer, hlight;
function init () {
//scene
scene = new THREE.Scene();
//camera
camera = new THREE.PerspectiveCamera(40, window.innerWidth/ window.innerHeight, 1, 5000);
//light
hlight = new THREE.AmbientLight (0x404040, 100);
scene.add(hlight);
//render
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setClearColor( 0x000000, 0 );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//3d
let loader = new GLTFLoader();
loader.load('assets/londonmap.gltf', function(gltf){
scene.add(gltf.scene);
})
}
function onWindowResize () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize, false);
function animate () {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render( scene, camera );
}
init ();
animate();
</script>
</body>
</html>
DracoLoader is only necessary if the glTF asset was compressed with the same-named compression algorithm.
A HTTP 404 means that it was not possible to load the file (in your case assets/londonmap.gltf) from the given path. So you have to make sure that the asset is actually present in the respective directory.
And yes, it's highly recommended to work with a local web server. Especially to avoid any security related browser issues. The project actually provides a small guide about this topic: How to run things locally.
I am looking to create a simple 3d Model preview based on a '.stp' file.
While searching I found the Three JS libairy. This libairy allows you to render 3D files like this example: https://threejs.org/examples/#webgl_loader_3mf
I would love to implement this into my own site except with a .stp file (The file: https://www.eleq.com/binaries/downloads/ELEQ%20LS-94%203D%20solid.stp).
I have tried to load this file with the THREE.ObjectLoader without a success. The loader expects a JSON format.
// Create a scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Load the object
var loader = new THREE.ObjectLoader();
loader.load(
// resource URL
'https://www.eleq.com/binaries/downloads/ELEQ%20LS-94%203D%20solid.stp',
// called when resource is loaded
function ( gltf ) {
scene.add( gltf.scene );
},
// called when loading is in progresses
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened' );
}
);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
</body>
</html>
Does someone have expirence with 3D model rendering that can help me?
I might have discovered a Chrome Bug related to three.js/WebGL.
I have set up a simple project demonstrating the issue here. It simply initialises a fullscreen three.js canvas, resizes the renderer on resize events and prints window.innerWidth/Height to the console. (The code was created by one of the three.js contributors and should be the standard way of implementing auto-resizing. Related three.js issue)
Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body style="margin: 0;">
<script src="three.js"></script>
<script>
var scene, camera, renderer, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
camera.position.z = 1;
var geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
var material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setPixelRatio( window.devicePixelRatio );
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', resize );
console.log( 'width: ' + window.innerWidth, 'height: ' + window.innerHeight );
}
function resize() {
console.log( 'width: ' + window.innerWidth, 'height: ' + window.innerHeight );
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render( scene, camera );
}
</script>
</body>
</html>
How to reproduce the issue
>>> Link to repository
Open test.html in Chrome
Fire up Devtools
Toggle Device Toolbar
Select a device (for this example Nexus 6P)
Switch from landscape to portrait mode and observe console output
My console output:
width: 412 height: 732 # portrait mode
width: 732 height: 412 # landscape mode
width: 732 height: 1301 # portrait mode
width: 732 height: 412 # landscape mode
The first time I switch from landscape to portrait mode the values of window.innerHeight/Width aren't correct anymore. It should be 412x732 instead of 732x1301.
Excluding the line renderer.setSize(window.innerWidth, window.innerHeight) solves the problem.
Related Devices
I was able to reproduce this bug on 2 different MacBooks (2016, 2013) [Chrome v. 63.0.3239.108] and on my Android Phone (Redmi 4X, Android 7.1.2) [Chrome v. 63.0.3239.111]
It'd be nice if somebody could confirm this behaviour.
Ran into the same error today. I tried using document.documentElement.client[Width or Height] and it gave me the right dimensions. I didn't implement my solution yet, but something that could be good would be to check if the last time you checked your dimensions it looked like the dimensions from innerWidth like :
if(lastDims.height !== nowDims.width) { /*Or something similar, you get the idea*/
//The dimensions are ok assuming lastDims was ok too
}
// or
const width = Math.min(document.documentElement.clientWidth, window.innerWidth);
const height = Math.min(document.documentElement.clientHeight, window.innerHeight);
Also, I'm using fabric.js (canvas) and screenfull.js (fullscreen API), not three.js. I am using Chrome though.
(As I was writing this, I installed Firefox) I just tested my app and it works fine in Firefox, so that might be a problem in Chrome itself when handling the window.inner[stuff] after the fullscreen API has done its thing... I might be wrong but, hey, I got the right dimension in the end!
Edit: Soooo thought about it, you might already found your solution since it's 2018 now...
I swear, last edit: This is a Chrome(Chromium) bug: https://bugs.chromium.org/p/chromium/issues/detail?id=833440&can=2&start=0&num=100&q=window.innerWidth&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified&groupby=&sort=
I need to work on project that uses three.js. So I have tried to learn the basic and O wrote this simple code just to see something. But it doesn't work at all and when I click to view it on web browser nothing is shown.
This is my code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script src="Three.js"></script>
</head>
<body>
<script type="text/javascript">4
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(document.body.clientWidth,
document.body.clientHeight);
document.body.appendChild(renderer.domElement);
renderer.setClearColorHex(0xEEEEEE, 1.0);
renderer.clear();
// new THREE.PerspectiveCamera( FOV, viewAspectRatio, zNear, zFar );
var camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
camera.position.z = 300;
var scene = new THREE.Scene();
var cube = new THREE.Mesh(new THREE.CubeGeometry(50, 50, 50),
new THREE.MeshBasicMaterial({ color: 0x000000 }));
scene.add(cube);
renderer.render(scene, camera);
</script>
</body>
</html>
Why doesn't it work? What did I do wrong ?
Good day, could you tell us what version of Three.js your using (it shows up in the console once it loads if your not sure), and what browser your using?
A few notes, renderer.setClearColorHex is deprecated now, this should be just renderer.setClearColor.
Also you set the Camera with width / height but I dont see them defined anywhere?
Next is the camera.position.z = 300; may be too far away to see a little cube at (0, 0, 0)
Try those items and let us know if it works.
Just see this example.
http://mrdoob.github.io/three.js/examples/#webgl_geometry_cube
You are missing a main loop for cosecutive frame rendering. Also as Darryl said, what is width and height?