51
votes

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 pixellated

Following are some screenshots:

Before resize:

enter image description here

After resize:

enter image description here

How it should be after resize:

enter image description here

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

6
AFAIK setSize changes the canvas width/height (render resolution) for you and uses the new size for glViewport. Can you double check instance.dom.clientWidth/Height are actually changing? It looks like the canvas is changing size and not its internal resolution.jozxyqk

6 Answers

122
votes

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 );

}
13
votes

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 :)

12
votes

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.

3
votes

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)
0
votes

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>
-2
votes

try antialias property to resolve pixelation problem by 50 - 70%

e.g :var renderer = new THREE.WebGLRenderer({antialias:true});