Screenshot a object to its height and width

Hello,

I want to take a screenshot of the object as it is loaded on to the screen with its height and width properties, suppose for an object in a scene like the below image

and the screenshot image should look like thisScreen Shot 2020-04-14 at 5.37.54 AM

I’m trying to update the camera values to the world position of the object with respect to the scene. I’m having difficulties in getting exact positions of these corners of boxhelpers. I’m thinking this is the way to achieve the desired screenshot. But any other solution or any help is appreciated.

Thanks in advance.

I think a better option would be to project the box onto the camera’s clip space and get the bound of the rectangle in ndc and then use them to crop out the screenshot of the renderer using canvas2d. The image resolution will be affected by depth tho.

May be it is helpful.

    var box = new THREE.Box3().setFromObject( obj );
    var size = box.getSize();
    console.log( size );

    // Calc distance of obj from camera
    var distance = camera.position.distanceTo( obj.position );
    console.log('distance: ', distance);
    distance = camera.position.z-obj.position.z-(size.z/2);
    
    var aspect  = renderer.domElement.width / renderer.domElement.height;
    // Calc height and width
    var vFOV = THREE.Math.degToRad( camera.fov ); // convert vertical fov to radians
    var height = 2 * Math.tan( vFOV / 2 ) * distance; // visible height
    var width = height * aspect;//camera.aspect;           // visible width

    var ratio = distance/height; // height per 1 of z-depth
    console.log('depath ratio: ', ratio);
    
    width = size.x/ratio * aspect;
    height = size.y/ratio * aspect;

    // Calc position
    var vector = new THREE.Vector3();
    var viewProjectionMatrix = new THREE.Matrix4();
    var viewMatrix = new THREE.Matrix4();
    viewMatrix.copy( camera.matrixWorldInverse );
    viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, viewMatrix );
    var widthHalf = 0.5*renderer.domElement.width;
    var heightHalf = 0.5*renderer.domElement.height;
    obj.updateMatrixWorld();
    vector.setFromMatrixPosition(obj.matrixWorld);
    //vector.project(camera);
    vector.applyMatrix4( viewProjectionMatrix );

    vector.x = ( vector.x * widthHalf ) + widthHalf;
    vector.y = - ( vector.y * heightHalf ) + heightHalf;/*
    vector.x = 2 * (vector.x / renderer.domElement.width) - 1;
    vector.y = 1 - 2 * ( vector.y / renderer.domElement.height );*/
    var x = vector.x;
    var y = vector.y;

    var result = {
        x: x-width/2,
        y: y-height/2,
        w: width,
        h: height
    };
    console.log(result);

   // screen shot as image

var oldCanvas = canvas; // renderer.domElement
    var newCanvas = document.createElement('canvas');
    newCanvas.width = w;
    newCanvas.height = h;
    var newContext = newCanvas.getContext('2d');
    newContext.drawImage(oldCanvas, x, y, w, h, 0, 0, w, h);

    var fileName = 'test.png';
    var strMime = "image/png";
    var strDownloadMime = "image/octet-stream";
    var imgData = newCanvas.toDataURL(strMime);
    var base64str = imgData.replace(strMime, strDownloadMime);

    var link = document.createElement('a');
    if (typeof link.download === 'string') {
        document.body.appendChild(link); //Firefox requires the link to be in the body
        link.download = fileName;
        link.href = base64str;
        link.click();
        document.body.removeChild(link); //remove the link when done
    } else {
        window.location.replace(uri);
    }
2 Likes

Hey,

Thanks for giving the reply. I was wondering the height you used for the variable ‘w’ is the height of object or height of the canvas? and also ‘self’ .

Help me with some more insight.

I have modified some code.
Regards

Hi
I have updated my code using

new THREE.Box3().setFromObject( obj )

Hope to help you!

Hey,

Thanks for taking time and sending your valuable answers. I have been following your code and something seems to be wrong while drawing newCanvas. Could you please check the variables used in there?

Thanks.

And also x, y values are starting from the middle of the object on newCanvas. Also, is there a way to know size of the object/model wrt canvas?

The current width and height values are too low for the canvas to draw image.

Thanks.

Please use the advanced code:

function screenshotObject(obj, camera, renderer) {
    this.obj = obj;
    this.camera = camera;
    this.renderer = renderer;
    this.box = new THREE.Box3().setFromObject( obj );
    this.size = {w: 0, h: 0};
    this.pos = {x: 0, y: 0};

    var distance = this.distance();
    this.size = this.getSizeInPixel(distance);
    this.pos = this.getPositionInPixel();
    this.getImage(this.size.w, this.size.h, this.pos.x, this.pos.y);
}

screenshotObject.prototype.distance = function() {
    var self = this;
    var size = self.box.getSize();
    var z = self.camera.position.z-self.obj.position.z-(size.z/2);
    // or use self.camera.position.distanceTo( self.obj.position );
    return z;
};
screenshotObject.prototype.getSizeInPixel = function(distance) {
    var self = this;
    var size = self.box.getSize();

    // Calc visible height and width
    var vFOV = THREE.Math.degToRad( self.camera.fov ); // convert vertical fov to radians
    var height = 2 * Math.tan( vFOV / 2 ) * Math.abs(distance); // visible height
    var width = height * (self.renderer.domElement.width/self.renderer.domElement.height);           // visible width
    // Calc ratio between pixel and visible z-unit of threejs
    var ratio = self.renderer.domElement.height/height;
    
    var width = size.x * ratio;
    var height = size.y * ratio;
    return {w: width, h: height};
};
screenshotObject.prototype.getPositionInPixel = function() {
    var self = this;
    var vector = new THREE.Vector3();
    var viewProjectionMatrix = new THREE.Matrix4();
    var viewMatrix = new THREE.Matrix4();
    viewMatrix.copy( self.camera.matrixWorldInverse );
    viewProjectionMatrix.multiplyMatrices( self.camera.projectionMatrix, viewMatrix );
    var widthHalf = 0.5*self.renderer.domElement.width;
    var heightHalf = 0.5*self.renderer.domElement.height;
    self.obj.updateMatrixWorld();
    vector.setFromMatrixPosition(self.obj.matrixWorld);
    //vector.project(camera);
    vector.applyMatrix4( viewProjectionMatrix );

    vector.x = ( vector.x * widthHalf ) + widthHalf;
    vector.y = - ( vector.y * heightHalf ) + heightHalf;

    var x = vector.x - self.size.w/2;
    var y = vector.y - self.size.h/2;
    return {x: x, y: y};
};
screenshotObject.prototype.getImage = function(w, h, x, y) {
    var self = this;
    var oldCanvas = self.renderer.domElement;
    var newCanvas = document.createElement('canvas');
    newCanvas.width = w;
    newCanvas.height = h;
    var newContext = newCanvas.getContext('2d');
    newContext.drawImage(oldCanvas, x, y, w, h, 0, 0, w, h);

    var fileName = 'test.png';
    var strMime = "image/png";
    var strDownloadMime = "image/octet-stream";
    var imgData = newCanvas.toDataURL(strMime);
    var base64str = imgData.replace(strMime, strDownloadMime);

    var link = document.createElement('a');
    if (typeof link.download === 'string') {
        document.body.appendChild(link); //Firefox requires the link to be in the body
        link.download = fileName;
        link.href = base64str;
        link.click();
        document.body.removeChild(link); //remove the link when done
    } else {
        window.location.replace(uri);
    }
};

// Run
new screenshotObject(obj, camera, renderer);