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 this
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.
niao
April 17, 2020, 8:11am
#4
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.
niao
April 18, 2020, 2:30am
#6
I have modified some code.
Regards
niao
April 20, 2020, 7:46am
#7
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.
niao
April 21, 2020, 2:57am
#10
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);
1 Like
Box3()
there is an error in your Box 3 code return min and max variable but you are using varaible ( x , y)
var z = self.camera.position.z-self.obj.position.z-(size.z/2); error => size.z.
const w = size.x * ratio; => size.x error
const h = size.y * ratio; => size.y error
and const width = height * (renderer.domElement.width/renderer.domElement.height);
width not used .
https://threejs.org/docs/#api/en/math/Box3
.min : Vector3
Vector3 representing the lower (x, y, z) boundary of the box.
Default is ( + Infinity, + Infinity, + Infinity ).
.max : Vector3
Vector3 representing the upper (x, y, z) boundary of the box.