3
votes

I am making my first steps coding with javascript. I made some courses and now I am exploring the webgl world using Three.js

I am experimenting with this function very used in all the examples of threes.org:

        function onWindowResize() {

          camera.aspect = window.innerWidth / window.innerHeight;
          camera.updateProjectionMatrix();

          renderer.setSize(window.innerWidth, window.innerHeight);

        }

With this function I can center my geometry when I reduce or increase the width of my browser's window and I can make my geometry smaller or bigger when I reduce or increase the height of my browser's window.

But now the thing is that when I reduce the height of my window I can see my geometry because it's centered and smaller. But when I reduce the width of my window I can't see all my geometry because it's centered but not smaller.

In this example: http://codepen.io/gnazoa/pen/BjoBZz I tried to avoid this problem using object.scale.devideScalar(1.5); in my onWindowResize() function, but it doesn't makes a change.

I am searching a solution for this because I consider it really important for a correct experience, because for the moment, I see this geometry in my desktop screen:

enter image description here

And this incomplete geometry in my iphone:

enter image description here

Do you have some suggestion? Is this problem something possible to solve?

1

1 Answers

3
votes

Welcome to the world of js/Three.js! And thanks for putting together a fiddle. Here's my suggested solution.

1) You have an unfortunate typo on line 44

object.scale.devideScalar(1.5);

should be

object.scale.divideScalar(1.5);

When working with javascript in the browser, open up your dev tools (chrome, firefox, or your browser of choice). It will throw something like:

Uncaught TypeError: object.scale.devideScalar is not a function

2) Fixing that, you still have a problem. The line as you've written it mutates (changes) the three.js cube by reducing the scale by 66% every call to onWindowResize(). As you'll imagine, 0.66^many becomes zero very quickly and the cube vanishes. Bummer.

I think what you want is the scale of object to match the scale of the screen. So a cube always takes up roughly 50% (or whatever) of your div, no matter the starting or resized screen.

First, a brief explanation as to why your scene seems to resize on the vertical and not the horizontal. The three.js perspective camera fov (field of view, ie the angle of viewing the camera collects into your render) depends on the vertical space. As you update the camera's available space, the camera adjusts to keep the scene in the same fov. You can see that in you 'tall' phone; the apparent height of the cube is the same in vertical space, but the sides are cut off.

This leads to a problem. Messing with the object's scale can be done like you commented out:

object.scale.set(factor, factor, factor);

This can kind of get things in line, but becomes unwieldy when you have more than one cube or you start transforming your objects (scales are all over the place). The renderer can't be stretched in the way you suggest without distorting your 3d projection. Think about it this way: if you have a cube, can that cube fill any screen with being distorted? A rectangular screen will either have: empty space, cut off cube, or distort the scale.

I'm also assuming the end goal isn't just a rectangle (which you could emulate with any number of 2d javascript or css solutions with less cpu and headache time).

So, my radical solution is don't have your canvas resize to completely fill the container. The modified example puts the content in a containing div that partially fill the window. Change the css to your liking. One way to fill the window would be:

width: 100%;
height: 100vh;

In any case, resize the preview window and my code with adjust the canvas. I set this ratio at 16:9 (common tv), but you can change that as you like too. The container itself is purple, the render canvas is light blue, and the cube is blue. The real new code is:

var targetAspectRatio = 16 / 9; //cinematic!
function aspectSize(availableWidth, availableHeight) {
  var currentRatio = availableWidth / availableHeight;
  if (currentRatio > targetAspectRatio) {
    //then the height is the limiting factor
    return {
      width: availableHeight * targetAspectRatio,
      height: availableHeight
    };
  } else {
    // the width is the limiting factor
    return {
      width: availableWidth,
      height: availableWidth / targetAspectRatio
    };
  }
}

Essentially, a rectangle of any size over 0 will either be not wide enough or not tall enough. Take up as much space in the limited dimension than only use what will keep the aspect ratio in the other.

There are also some hacked lines (68:69) that center the renderer in the container. If the container is the window, the renderer will be centered in that.

I hope that helps and good luck with your work.