THREE.js fit Orthographic camera to scene - javascript

I've searched through many related questions on stack overflow, but I couldn't quite find the answer to this problem.
I have a big, dynamic group of Object3D's in my scene, with an orthographic camera looking at them head-on.
I want to have a simple function which, when called, will simply match the top/left/bottom/right/zoom properties of the Orthographic camera to properly fit the Object3D group.
I've tried all kinds of things, but none of my code is worth posting. I need to look at this from a whole new angle (pun intended). I have found various other answers which discuss changing the fov of the camera, once you know the distance from the face of bounding box of the group to the camera, but I don't know how to implement that with an orthographic camera, since (as far as I've tried) the fov property doesn't work with it (maybe it actually does, I just don't know).
So I don't particularly like asking for code, but nevertheless I would like a function which would automatically adjsut the appropriate properties of the Orthographic camera to fit the object passed to it as a parameter, for example:
function fitOrthographicCameraToObject3DGroup(group) {
//implement here (my question)
}

Calculate the bounding box of your mesh and apply this code.
This works for me
var camera = new THREE.OrthographicCamera(container.offsetWidth / -2, container.offsetWidth / 2, container.offsetHeight / 2, container.offsetHeight / -2, 100, 100000);
//For centering the meshGroup
var box = new THREE.Box3().setFromObject(meshGroup);
box.center(meshGroup.position);
meshGroup.localToWorld(box);
meshGroup.position.multiplyScalar(-1);
//For fitting the object to the screen when using orthographic camera
Camera.zoom = Math.min(container.offsetWidth / (box.max.x - box.min.x),
container.offsetHeight / (box.max.y - box.min.y)) * 0.4;
Camera.updateProjectionMatrix();
Camera.updateMatrix();

Setting the top/left/bottom/right of the orthographic camera once you have the bounding box should not be a problem. Just take the half lengths of the bounding box.
The zoom is the issue and for that you can confine your scene in a unit cube by scaling up or down your scene by the appropriate amount depending on your bounding box size. Then you dont have to worry about the zoom and the above top/left/bottom/right values become 0.5/0.5/-0.5/-0.5.

Related

Fit object group to page centre without breaking OrbitControls

What I have is an OrthographicCamera set up so that is has an isometric view of the scene and OrbitConrols added to allow for panning around and zooming but not for rotation.
What I’d like to have is a button that will centre the objects in a scene and zoom the OrthographicCamera so that the objects fit within the canvas area while keeping the isometric view, i.e. the angle between the camera.position and the camera.lookAt (control.target) point.
What I’ve tried is to set the controls.target at the centre of the bounding box of the objects in the scene.
I have 2 problems with the code at the moment.
The First is I couldn’t work out how to calculate the zoom level needed to make sure the objects in the scene are all in view. I’ve hard coded a value for just now.
The Second is that with the current code, if the camera is panned so that the objects appear nearly off the screen, either up or down, then when centred the angle of the camera changes. This was happening when the camera was panned far left or right but setting the max and min Azimuth Angle seems to prevent this.
camera rotates after centring
The image above shows the scene when loaded then after centering when the camera was panned so the objects were going off the top of the screen.
I have tried a number of ways to do this after looking at answers to similar questions as this but am still having problems getting it to work.
function fitDrawingToPage(){
// Variables Bbox etc are set outside the function
Bbox = new THREE.Box3();
for (const object of sceneObjects) Bbox.expandByObject(object);
newTarget = Bbox.getCenter(new THREE.Vector3());
controls.target.set( newTarget.x, newTarget.y, newTarget.z );
controls.update();
camera.zoom = 0.5;
camera.updateProjectionMatrix();
camera.updateMatrix();
render();
}
current example of code in jsfiddle

three.js CameraHelper rotates faster than PerspectiveCamera

I have a THREE.PerspectiveCamera to which I add a THREE.CameraHelper.
cameraLocal = new THREE.PerspectiveCamera(70, 1, 20, 120);
scene.add(cameraLocal);
cameraLocalHelper = new THREE.CameraHelper(cameraLocal);
cameraLocal.add(cameraLocalHelper);
However when I rotate the camera,
cameraLocal.rotateX(0.1);
the CameraHelper rotates by a larger amount than the camera.
I've made a
demo that shows this.
Initially, cameraLocal can't see the help lines drawn by the CameraHelper. However, if cameraLocal is rotated either way about the x-axis, the help lines come into view, I'm supposing on account of the CameraHelper rotating by a different amount.
Could anyone point out what I'm doing wrong here?
I'm using the build of three.js from 5-Aug-2019.
CameraHelper needs to be added directly to the scene.
Do not try to add it as a child of the camera itself.
three.js r.107

Resize canvas without enlarge the elements WEBGL

I m new in WebGl and i wish resize the canvas without enlarge all the elements.
i try to edit in the HTML file this line of code:
<canvas id="gl-canvas" width="512"" height="512">
with the new one
<canvas id="gl-canvas" width="1024"" height="1024">
but the figure inside the html will be more big and i dont want. I want just resize the space that contain the elements.
i tried to do this one too:
In the JS file i tried to edit
gl.viewport( 0, 0, canvas.width, canvas.height );
with
gl.viewport( 0, 0, 0.5*canvas.width, 0.5*canvas.height );
the figure will be half smaller, but the space remain the same. I need more space for the animation because now at some point the animation exits the screen even if I have a lot of space still available (which is white).
I want upload some photos for explain better my point
My Canvas
Details of the Web Inspector
thanks.
WebGL requires you to draw the items yourself to match the size. WebGL takes normalized coordinates in (called clip space). They always go from -1 to +1 across the canvas and -1 to +1 up the canvas regardless of size. To draw anything you supply math via JavaScript and/or GLSL shaders that take whatever data you supply and convert that data to clip space. That means if you want the items to stay the same size you need to adjust the math you're using to draw the things you are drawing. Without seeing your math we can't really know what to suggest.
The most common way to draw things in WebGL is to multiply 2D or 3D data by 1 or more matrices. You can read about that here. You may need to read the articles before that one to understand that one. You may also wish to read the articles after that one as they continue from 2D to 3D
So, then there is no easy answer except to say it's up to you to decide on a solution to fix your math. Without knowing what math you're using we can't suggest a way to keep the size the same.
If you were using clip space coordinates directly then you could just pass in an X and Y scale. if the canvas gets twice as large then scale by half as much and the objects would stay the same size.
gl_Position = someClipspaceCoordinates * vec4(scaleX, scaleY, 1, 1);
If you were using an orthographic projection then you'd choose some number of units per pixel and adjust the values you pass into your orthographic projection matrix making function
const pixelsPerUnit = ???;
const unitsAcross = gl.canvas.clientWidth / pixelsPerUnit;
const unitsDown = gl.canvas.clientHeight / pixelsPerUnit;
// assuming you want 0,0 at the center
const left = -unitsAcross / 2;
const right = unitsAcross / 2;
const bottom = -unitsDown / 2;
const top = unitsDown / 2;
const near = ???;
const far = ???;
const projectionMatrix = someOrthographicProjectionMatrixFunction(
left, right, bottom, top, near, far);
If you were drawing in 3D using a perspective projection then you'd have to either calculate a field of view based on the width or height of the canvas to keep objects the same size, OR, move the camera closer to or further away from the objects.
To adjust by field of view you'd do something like
const fovPerPixel = 0.1 * Math.PI / 180;
const fov = gl.canvas.clientHeight * fovPerPixel;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const near = 0.5;
const far = 1000;
const projectionMatrix = somePerspectiveProjectionMatrixFunction(
fov, aspect, near, far);
Note that this method has the issue that as the canvas gets larger the things drawn toward the edges of the canvas will get more and more distorted as the field of view gets wider and wider to keep everything the same size. When the field of view hits 180 degrees or wider the perspective math will break and things won't display.
To adjust by moving the camera see this answer as a starting point.

how to remove clipping plane in THREE js?

I want to remove clipping planes in THREE js but I can't seem to find any information on how to do this. What I found is that orthographic camera can have negative value for near clipping plane.
If I put negative value in near clipping plane of perspective camera, it doesn't throw an error but doesn't show any objects.
I draw relatively huge objects and the near clipping plane is very frustrating when I try to explore them as they disapear completely if they are behind the camera even if most of the object should still be visible, is there a way to remove it so my objects always gets drawn?
here is the camera values I use:
var camera = new THREE.PerspectiveCamera(90, size.x/size.y, 0.1, 1000);
When I move, I move the camera instead of all the objects relative to a fixed camera, I guessed it would be more performent, but I don't think it matters for my problem.
All of the objects have position (0, 0, 0) but can have part extending up to 10-15 units away from their position

Camera arguments in Three.js

This is how a camera is instantiated:
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR
);
What do these values mean?
I was wondering the same thing so I looked it up, it is a view "frustum".
I'll paste here a code comment I wrote in a recent project because it sums it up nicely IMHO.
// "What the f*** is a frustum?" you ask. Well I did.
// Think about it as a truncated pyramid. The tip is the camera. From a certain
// point "down" the pyramid (the "near plane"), stuff can be seen on-screen.
// After the "base" of the pyramid (the "far plane"), stuff is too far to be
// seen. Stuff outside the pyramid is off-screen too. There is also the "aspect
// ratio" (the rectangle that makes up the pyramid's base, so this value is
// width/height) and the "field of view" (think of it as a lens or something,
// it distorts the pyramid so there's more objects on screen - it is set in
// degrees and 45° is more-or-less a "natural" perspective. The bigger the
// number, the more "perspective" there is).
I found this tutorial very useful for understanding all the camera parameters, and the difference between PerspectiveCamera and OrthographicCamera.
PerspectiveCamera
Fov (Field of view) - This is part of scene that can be seen from the position of the camera. As you probably know, we, humans, have almost 180-degree field of view, while some birds might even have a complete 360-degree field of view. However, for computers, we usually use the field of view between 60 and 90 degrees.
Aspect - The aspect ratio is ratio between the horizontal and vertical size of the area where we render the output. As we usually use the entire window, we will just use that ratio. The aspect ratio determines the difference between the horizontal field of view and the vertical field of view as you can see in the figure on the following page. Ordinary value is window.innerWidth / window.innerHeight.
Near - This property defines a min distance from the camera the Three.js renders the scene. Usually, this is a very small value, e.g. 0.1.
Far - This property defines a max distance we see the scene from the position of the camera. If we set this as too low, a part of our scene might not be rendered; if we set it as too high, in some cases, it might affect the rendering performance. Normal value is between 500 and 2000.
OrthographicCamera
Left (Camera frustum left plane) - You should see this as what is the left border of what will be rendered. If we set this value to -100, you won’t see any objects that are farther to the left.
Right (Camera frustum right plane) - Anything farther to the right won't be rendered.
Top (Camera frustum top plane) - The maximum top position to be rendered.
Bottom (Camera frustum bottom plane) The bottom position to be rendered.
Near (Camera frustum near plane) - From this point on, based on the position of the camera, the scene will be rendered.
Far (Camera frustum far plane) - The furthest point, based on the position of the camera, to which the scene will be rendered.
The following picture should be very illustrative:
The main difference between the two camera modes is that in the OrthographicCamera distance plays no role, so all the elements are of the same size, as you can see in the case of the red and yellow ball.
Finally here is some code you can use to switch from one camera to the other:
this.switchCamera = function(SCENE_WIDTH, SCENE_HEIGHT) {
if (camera instanceof THREE.PerspectiveCamera) {
camera = new THREE.OrthographicCamera( SCENE_WIDTH / - 2, SCENE_WIDTH / 2, SCENE_HEIGHT / 2, SCENE_HEIGHT / - 2, 0.1, 1000 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = -1;
camera.lookAt(scene.position);
this.perspective = "Orthographic";
} else {
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = -1;
camera.lookAt(scene.position);
this.perspective = "Perspective";
}
};
Notes:
The function camera.lookAt(scene.position) orients the camera to where the scene is located, so it is visible.
Units in three.js are SI units, so the values of left,right,top,bottom should not assumed to be pixels.
The aspect ratio of the camera's frustum should normally match the canvas' aspect ratio.
SCENE_WIDTH, SCENE_HEIGHT, can be determined through the geometries that are added in the scene. The orthographic frustum could be much larger than the scene but it wouldn't be very parsimonious.
Useful links:
http://www.glprogramming.com/red/chapter03.html
Three.js - Orthographic camera
The first param is FOV means field of view, imagine a camera on a tripod, if you change lens to wide angle you get a higher FOV. Try to imagine a cone coming out from the camera, it can only see objects in that area.
ASPECT means aspect ratio, a widescreen TV is 16/9 and old ones were 4/3, usually just give it the screen width/height or the dims of a DIV you would like three.js to use.
fov: Camera frustum vertical field of view.
aspect: Camera frustum aspect ratio.
near: Camera frustum near plane.
far: Camera frustum far plane.
On these pages there some image for the FOV, NEAR plane, anmd FAR plane.
https://threejsfundamentals.org/threejs/lessons/resources/frustum-3d.svg
https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/ViewFrustum.svg/440px-ViewFrustum.svg.png
https://threejsfundamentals.org/threejs/lessons/threejs-cameras.html
https://en.wikipedia.org/wiki/Viewing_frustum
This is the aspect ratio.
https://en.wikipedia.org/wiki/Aspect_ratio_(image)
This is the official docs.
https://threejs.org/docs/#api/en/cameras/PerspectiveCamera

Categories

Resources