Fitting a 3D object (Collada file) within the Three.JS canvas on boot

I have a field (collada file) that loads into a three.js canvas. I can interact with him, as expected. However, the window size changes as users can resize .

When I load it into a canvas with a size of 500px by 500 pixels, if the drawer is large, users must zoom in before viewing it, and if it is small, it is tiny, and users need to zoom in. The size varies depending on the variables that are transmitted.

How would I add an object (collada file) to the canvas when loading, and then allow users to enlarge? Here is the code that loads when clicked to show the 3D object in three.js canvas:

$scope.generate3D = function () { // 3D OBJECT - Variables var texture0 = baseBlobURL + 'Texture_0.png'; var boxDAE = baseBlobURL + 'Box.dae'; var scene; var camera; var renderer; var box; var controls; var newtexture; // Update texture newtexture = THREE.ImageUtils.loadTexture(texture0); // Initial call to render scene, from this point, Orbit Controls render the scene per the event listener THREE.DefaultLoadingManager.onProgress = function (item, loaded, total) { // console.log( item, loaded, total ); // debug if (loaded === total) render(); }; //Instantiate a Collada loader var loader = new THREE.ColladaLoader(); loader.options.convertUpAxis = true; loader.load(boxDAE, function (collada) { box = collada.scene; box.traverse(function (child) { if (child instanceof THREE.SkinnedMesh) { var animation = new THREE.Animation(child, child.geometry.animation); animation.play(); } }); box.scale.x = box.scale.y = box.scale.z = .2; box.updateMatrix(); init(); }); function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(100, window.innerWidth / window.innerHeight, 0.1, 1000); renderer = new THREE.WebGLRenderer(); renderer.setClearColor(0xdddddd); //renderer.setSize(window.innerWidth, window.innerHeight); renderer.setSize(500, 500); // Load the box file scene.add(box); // Lighting var light = new THREE.AmbientLight(); scene.add(light); // Camera camera.position.x = 40; camera.position.y = 40; camera.position.z = 40; camera.lookAt(scene.position); // Rotation Controls controls = new THREE.OrbitControls(camera, renderer.domElement); controls.addEventListener('change', render); controls.rotateSpeed = 5.0; controls.zoomSpeed = 5; controls.noZoom = false; controls.noPan = false; // Add 3D rendering to HTML5 DOM element var myEl = angular.element(document.querySelector('#webGL-container')); myEl.append(renderer.domElement); } // Render scene function render() { renderer.render(scene, camera); console.log('loaded'); } } // Initial 3D Preview Load $scope.generate3D(); 

Update: I appreciated the solution presented here: How to put the camera in an object , but I'm not sure how to determine the distance for my collada file, since it can be different depending on what sizes the user enters. The collada file is generated by users sending variables to a third-party provider, which returns the collada file, which is subsequently loaded into the three.js file.

Update 2: Thanks to @ Blindman67, I'm closer to understanding how this happens. When I manually pick up camera.position x, y, z objects, the object is on the screen. The challenge I have is how to determine what the correct x, y, z values ​​will be as each box is dynamically changed, and I have literally more than 280 million options. I know that @ Blindman67 already gave me the logical answer, but I just need the last push to find out how to get the right position for objects that change every time, so I can set the correct x, y, z.

+5
source share
3 answers

This code will "auto-center" the camera around this object.

To “scale” the user, also consider examples of three.js / js / controls / TrackballControls .js, which move the camera position (and change the view and up). If you decide to use this, I remember that you need to initialize the controls before moving the camera position using centerCam . In this case, centerCam receives the entire object in preparation for user interaction, and then TrackballControls listeners.

 <body onload="initPage()"> <canvas id="cadCanvas" width="400" height="300"></canvas> <button onclick="loadFile()">Load Collada File</button> </body> function initPage(){ var domCanvas = document.getElementById('cadCanvas'); scene = new THREE.Scene(); var fovy = 75; camera = new THREE.PerspectiveCamera( fovy, domCanvas.width/domCanvas.height, 0.1, 1000 ); renderer = new THREE.WebGLRenderer({canvas:domCanvas}); renderer.setSize( domCanvas.width, domCanvas.height ); } function loadFile(){ new THREE.ColladaLoader().load( url, function(obj3D) { scene.add(obj3D); centerCam(obj3D); renderer.render(scene, camera); }); } function centerCam(aroundObject3D){ //calc cam pos from Bounding Box var BB = new THREE.Box3().setFromObject(aroundObject3D); var centerpoint = BB.center(); var size = BB.size(); var backup = (size.y / 2) / Math.sin( (camera.fov/2)*(Math.PI/180) ); var camZpos = BB.max.z + backup + camera.near ; //move cam camera.position.set(centerpoint.x, centerpoint.y, camZpos); camera.far = camera.near + 10*size.z; camera.updateProjectionMatrix(); } 
+2
source

Set 3D Object to View

There are several ways to place a 3D object to view the camera.

  • Move the camera back or forward.
  • Increase or decrease the focal length (this also affects the FOV field (of View field))
  • Change the scale of world space or the scale of the local space of an object to make the object larger or smaller.

You can undo the last option, since in most cases this is impractical. (although I notice that you are doing this in the specified code, this answer has enough information to determine how to scale the object to fit. But I do not recommend you to do this)

Thus, you just have to move the camera or keep it motionless and scale. This is the same as with a real camera, you are approaching or approaching.

Both methods have their pros and cons.

Transfer

Moving the camera (trolley) is best suited for most situations, since it keeps the perspective the same (how quickly the lines converge to the vanishing point), and thus does not distort the object in the field of view. In 3D, there are 3 problems with this method.

  • The review node (the volume in which the scene is displayed) has (the maximum distance at which the object will be displayed) and the font (the closest object can be displayed). Moving the camera to a very large or very small object can cause the object to be outside the front or back planes and be cropped partially or completely.
  • Moving planes in accordance with an object may also have undesirable results. Moving both the front and back planes to hold the object can bring objects closer or further in the scene to be cropped.
  • An extension of the total distance between the rear and front plane can also cause Z-buffer smoothing artifacts. But these problems apply only to very large or very small objects and scenes.

Click to enlarge

Zoom means changing the focal length of the camera. In the library that you use, this is done by adjusting the FOV (Field of View), this is the angle between the left and right sides of the view, indicated in degrees. Reducing FOV effectively increases focal length and zoom (3D graphics do not have a focal length, such as a camera). Increases FOV increase. There are problems with this method.

  • As the FOV decreases, the perspective (parallax) decreases, making the scenes appear less and less 3D. As FOV increases perspective, it increases distorting objects and makes objects at a distance very small.
  • Since the camera does not move, the front and rear planes remain in place, but an increase or decrease in objects near the rear or front plane can still cause an aliasing z-buffer artifact.

Which method to use ip for you, you can use one or the other or combine both.

How it's done

To a question. We need to know how large the object will be displayed on the stage and use this information to change the camera's setting to the desired effect (namely, set the object on the display).

A diagram showing the camera and various properties necessary to create a view Figure 1. Camera, object, and view.

So there are some meanings that are needed. See Fig. 1 for a visual explanation.

  • FOV. I will convert it to Radians.
  • Object distance from camera
  • The radius of the limited boundary of the sphere.
  • Frontal plane
  • Back plane
  • Screen pixel size

You will need to calculate the bounding sphere of the object. Or use a different value that approximates the bounding sphere. I will leave it to you.

Code

 var oL,cL; // for the math to make it readable var FOV = 45 * (Math.PI / 180); // convert to radians var objectLocation = oL = {x : 0, y : 0, z : 400}; var objectRadius = 50; var cameraLocation = cL = {x : 0, y : 0, z : 0}; var farPlane = 1000; var nearPlane = 200; var displayWidth = 1600; var displayHeight = 1000; 

To determine how large a bounding ball appears in a view, it is simply a trigger.

 // Get the distance from camera to object var distToObject = Math.sqrt(Math.pow(oL.x - cL.x, 2) + Math.pow(oL.y - cL.y, 2) + Math.pow(oL.z - cL.z, 2));Figure 1 

When we use the right triangle (see Fig. 1), we multiply the result by 2 to get the total angular size

 // trig inverse tan of opposite over adjacent. var objectAngularSize = Math.atan( (objectRadius) / distToObject ) * 2; 

Get the FOV share that the object occupies.

 var objectView = objectAngularSize / FOV; 

And finally, you get the pixel size of the object.

 var objectPixelSize = objectView * displayWidth; 

That’s all you need to know to do what you ask. This will help you understand if you are using the above code and math to try to reorder the calculations so that you can occupy the required pixel size by moving the camera or FOV. Copying the code does not reproach you very much, using the above information, applying it, it will be configured at your discretion and will do more other processes that you will need for 3D in the future.

This means that copying code is a quick fix and is what all frameworks and libraries are. You don’t need to know how you should study.

Zoom in to size.

It is the easiest and gets the size of the angular object and adjusts the FOV to fit. (NOTE: I use radians. Three.js uses degrees for FOV that you need to convert)

 var requieredObjectPixelSize = 900; var distToObject = Math.sqrt(Math.pow(oL.x - cL.x, 2) + Math.pow(oL.y - cL.y, 2) + Math.pow(oL.z - cL.z, 2)); var objectAngularSize = Math.atan( (objectRadius) / distToObject ) * 2; // get the amount the FOV must be expanded by var scaling = displayWidth / requieredObjectPixelSize; // change the FOV to set the objects size FOV = objectAngularSize * scaling; 

Convert FOV to degrees and use it to create a camera.

Translate to

Move the camera to fit the subject. This is a bit more, but this is the best method.

 // Approx size in pixels you want the object to occupy var requieredObjectPixelSize = 900; // camera distance to object var distToObject = Math.sqrt(Math.pow(oL.x - cL.x, 2) + Math.pow(oL.y - cL.y, 2) + Math.pow(oL.z - cL.z, 2)); // get the object angular size. var objectAngularSize = Math.atan( (objectRadius) / distToObject ) * 2; // get the fraction of the FOV the object must occupy to be 900 pixels var scaling = requieredObjectPixelSize / displayWidth; // get the angular size the object has to be var objectAngularSize = FOV * scaling; // use half the angular size to get the distance the camera must be from the object distToObject = objectRadius / Math.tan(objectAngularSize / 2); 

Now move the camera. It should move along the vector between the object and the camera.

 // Get the vector from the object to the camera var toCam = { x : cL.x - oL.x, y : cL.y - oL.y, z : cL.z - oL.z, } 

Normalize the vector. This means that the length of the vector is 1 and is performed by dividing each component (x, y, z) by the length of the vector.

 // First length var len = Math.sqrt(Math.pow(toCam.x, 2) + Math.pow(toCam.y, 2) + Math.pow(toCam.z, 2)); // Then divide to normalise (you may want to test for divide by zero) toCam.x /= len; toCam.y /= len; toCam.z /= len; 

Now you can scale the vector so that it is equal to the distance that the camera should be from the object.

 toCam.x *= distToObject; toCam.y *= distToObject; toCam.z *= distToObject; 

Then it’s just a matter of adding a vector to the location of the object and placing it at the location of the camera.

 cL.x = oL.x + toCam.x; cL.y = oL.y + toCam.y; cL.z = oL.z + toCam.z; 

cl now contains the location of the camera.

Last thing. You need to check if the object is inside the view.

 if (distToObject - objectRadius < nearPlane) { nearPlane = (distToObject - objectRadius) * 0.8; // move the near plane towards the camera // by 20% of the distance between the front of the object and the camera } if (distToObject + objectRadius > farPlane) { farPlane = distToObject + objectRadius * 1.2; // move the far plane away from the camera // by 1.2 time the object radius } 

There is one more problem. The subject, if very small, can be so close that there is a camera in front of the subject. If this happens, you will need to use the zoom method and move the camera back. This will only be for very selective cases and can be ignored for the most part.

I did not provide information on how to integrate this with Three.js, but this is for a general answer that applies to all 3D packages. You will need to consult the three.js documentation on how to change various camera settings. It is simple and applicable to a perspective camera.

Well, a great answer, and I need to forget it a little, since I do not see typos and errors without a break. I will come back and fix it later that day.

Hope this helps

+6
source

Thanks to Blindmann67, I was able to better understand all these difficulties and come up with a solution three times. There is one particular line in which the scale bar was manually set:

 box.scale.x = box.scale.y = box.scale.z = .2; 

Since I know the width of my object (dynamic) and the size of the canvas (500 pixels), I was able to get the correct scale ratio using the following:

 var initialZoomScale = 500 / canvasWidthProdPixels; // canvasWidthProdPixels is my large object (ex. 25,000 px wide) dividing these gives me a small fraction such as .02 which scales the object perfectly) box.scale.x = box.scale.y = box.scale.z = initialZoomScale; 
0
source

Source: https://habr.com/ru/post/1237434/


All Articles