Visibility of Vector3 - 3D to 2D projection - javascript

I already asked this question over there:
https://github.com/mrdoob/three.js/issues/2070#issuecomment-6372113
But I did not really get it right now.
Here is my problem again:
i am using this function to calculate the screen position (x,y) of my Vector3.
this.toScreenXY = function(position, camera, canvas)
{
var pos3D = position.clone();
var pos2D = projector.projectVector( pos3D, camera );
return {
x : ((pos2D.x + 1) * canvas.domElement.width / 2 + canvas.domElement.offsetLeft),
y : ((-pos2D.y + 1) * canvas.domElement.height / 2 + canvas.domElement.offsetTop)
};
};
But when I got the correct screen position, turn the camera by 180 degree (about the y-axis), I got the same x,y values even if the real Vector3 is behind the Cameras view.
Any idea how to check wether the Vector3 is in Cameras view or not?
Thank you for your help!

Related

Problem with click corner of fabricjs using threejs raycast

i'm trying to recreate this app, and it is currently working now. But i can't click the corner of the text perfectly, it always need to be offset.
https://jsfiddle.net/naonvl/ecdxfkbm/3/
right now, it's hard to scale the text. i think the getRealPosition is not correct so the mouse X and Y also not precise.
Does anyone know how to fix this?
The problem is i set getIntersects like this
var getIntersects = function (point, objects) {
mouse.set(point.x * 2 - 0.97, -(point.y * 2) + 0.97);
raycaster.setFromCamera(mouse, camera);
return raycaster.intersectObjects(objects);
};
compare to three.js example here https://threejs.org/examples/#webgl_raycast_texture
function getIntersects( point, objects ) {
mouse.set( ( point.x * 2 ) - 1, - ( point.y * 2 ) + 1 );
raycaster.setFromCamera( mouse, camera );
return raycaster.intersectObjects( objects, false );
}
i miss calculate the mouse.set there, i change the calculation and tweak X and Y position of mouse by adding 3 to each of them, and it raycasted perfectly in the center of mouse pointer

Three.js reverse raycasting

I'm setting up a world map on a curved plane that the user can rotate and highlight countries. When a country is clicked, I determine what exactly was clicked and then show a tooltip with some information about the selected country, like on the screenshot below:
The map itself is generated in three, but the tooltip is a regular old div. The thing is, I'd like the tooltip to stay glued to that particular point of the texture, even if the plane is rotated. The original positioning of the tooltip is based on the information from the raycaster. As such, I'd want the app to return the current position of that precise point, relative to the container (or window) - based on uv coordinate for instance. How can I achieve that?
Basing on the link provided by #prisoner849, I've implemented the following solution:
a) on mouseup event I set a new tooltip 3D origin point (only if there was no significant delta in mouse position, to discern between clicking and dragging the camera)
b) every frame, the following function is fired, which converts the 3D coords to 2D and calculates the position of the tooltip:
function setTooltipOrigin( camera, renderer, tooltip, lastClickPoint ) {
var canvas = renderer.domElement,
point = new THREE.Vector3();
point.x = lastClickPoint.x,
point.y = lastClickPoint.y,
point.z = lastClickPoint.z,
point.project( camera );
point.x = Math.round(( 0.5 + point.x / 2 ) * ( canvas.width / window.devicePixelRatio ));
point.y = Math.round(( 0.5 - point.y / 2 ) * ( canvas.height / window.devicePixelRatio ));
point.x -= 14; //small adjustment to the position, so the line points to the click point
point.y -= (tooltip.scrollHeight * 0.7); //same for y
tooltip.style.transform = 'translate(' + point.x + 'px, ' + point.y + 'px)';
}

Three JS convert 3D dimensions to 2D

I would like to take the world space coordinates of a sphere to screenspace coordinates in order to get the screen space bounds, which I can then use to overlay a div.
Ideally I would like to extend this function to return the height and width of the object, as well as the x & y :
toScreenPosition : function (obj, camera)
{
var vector = new THREE.Vector3();
var widthHalf = 0.5*world.renderer.context.canvas.width;
var heightHalf = 0.5*world.renderer.context.canvas.height;
obj.updateMatrixWorld();
vector.setFromMatrixPosition(obj.matrixWorld);
vector.project(camera);
vector.x = ( vector.x * widthHalf ) + widthHalf;
vector.y = - ( vector.y * heightHalf ) + heightHalf;
return {
x: vector.x,
y: vector.y
};
},
You can create few THREE.Object3D and locate them in the scene in position of the border of the main object you want to project to the screen.
then you can use the method you used on the main object on the other empty objects and get the pixels position on the screen of the border of the main object.
If for example you want to know the screen coordinates of the border of a sphere that has a radius of 5:
var first = new THREE.Object3D();
var second = new THREE.Object3D();
var third = new THREE.Object3D();
var fourth = new THREE.Object3D();
first.position.set(sphere.x,sphere.y+5,sphere.z);
second.position.set(sphere.x,sphere.y-5,sphere.z);
then you can apply the function you wrote, but instead of:
obj.updateMatrixWorld();
etc...
you will do:
first.updateMatrixWorld();
second.updateMatrixWorld();
etc...
then you will have the x,y coordinates of those two objects (that are on border of the main object) on screen and you can check the height by subtracting.
I would write a function that takes as input a 3D point and returns the 2D screenpoint, like the one presented there. Then if you deal with a sphere, that would be easy: get the center of the sphere in 3D, compute the 2D point, that will be the center of the overlay div, get any 3D point on the surface of the sphere, compute the 2D point, you will know the required radius of the overlay div, from there you can easily compute a rectangular area.

Trigonometry Issue causing distortion when drawing floor textures in raycaster

I'm creating a game with raycasted 3D graphics like Wolfenstein 3D but using line segments instead of a grid of blocks for walls. Everything is fine when drawing the floors until rotating the player view.
the floor should be aligned against the walls
Here is the view in 2D, with each pixel on the floor on the screen rendered as a blue point:
In the top image is when the player's rotation is Math.PI. In the bottom image it is rotated slightly.
A significant feature of this is the beginning of the cone of points is aligned along the y axis. It should look like a frustrum.
Here is the code I am using to find the x and y coordinates of each point where a texture is drawn on the floor. This code is run for each x value on the screen.
The variable "projPlane" is the projection plane, which is the size of the screen.
projDistance is the distance from the player to the projection plane so that it fits within the field of view, or (projPlane.width/2)/Math.tan(VectorMath.toRadians(fov/2))
pHeight is the players height.
The variable "x" is the x value of the row being rendered on the screen.
//FLOOR TEXTURE
var floorSize = Math.floor((projPlane.height-wallSize)/2); //draw the floor from under the wall
var floorTextureIndex = 1;
//for texture y
if(floorSize > 0){ // values need to be positive
//find the point on the floor
var textureWidth = textures[floorTextureIndex].textureImage.width;
var textureHeight = textures[floorTextureIndex].textureImage.height;
//console.log(coordX);
for (var ty = 0; ty < floorSize; ty++){
//angle is tan
var yAngle = projPlane.distance / (ty + wallSize/2); //ty + wallSize/2 is the point on the projection plane
var yDistance = yAngle * pHeight; //pHeight is player height
var worldY = player.y + Math.sin(player.vector)*yDistance;
var coordY = Math.floor(worldY % (textureHeight));
var xAngle = Math.atan((projPlane.width/2 - x)/projPlane.distance);
/*if(x < projPlane.width/2){//tangent of the angle in the projectionPlane
xAngle = (x) / projPlane.distance;
}
else{
xAngle = (x-projPlane.width) / projPlane.distance;
}*/
var xDistance = yDistance/Math.cos(xAngle);
var worldX = player.x + Math.cos(player.vector - xAngle)*xDistance;
//console.log(xDistance);
var coordX = Math.floor(worldX % (textureWidth));//disable until I can get y
floorPoints.push(new Point(worldX,worldY));
var tempTexture = textures[floorTextureIndex];
if(tempTexture.textureData[coordX] != undefined){
// a different function drawns these to the screen in descending order
floorTextureColors.push(tempTexture.textureData[coordX][coordY]);
}
};
}
It doesn't seem to be an issue with the y value since the y coordinates of the floor texture seem to appear where they should.(EDIT: it actually was to do with the y value. Adding the xAngle to the player.vector when finding the y position returns a correct y position but there is still a "curved" distortion. I hope one of you can propose a more concrete solution.)
What I do to find the X coordinate is form a triangle with the distance from the player to the floor point as the opposite side the angle that the point makes with the player. The hypotenuse should be the magnitude of the distance to the point's x coordinate.
Then I multiply the cosine of the angle by the magnitude to get the x value.
It works whenever the character isn't pointing west and east. What is causing all the first points to have the same y value? I think that's the biggest clue on the distortion occurring here.

ThreeJS: Screen position to camera position

I've seen a lot of posts people want to have the camera position to screen position. My question is how to do the contrary.
What I currently want to achieve is set the "door" position to a % of the screen, this calculation is ready, and I do have the final screen X, Y (px) position. The current Z offset = 250 of the camera.
I've found this code to convert camera position to screen position:
var vector = projector.projectVector(door.position.clone(), camera);
vector.x = (vector.x + 1) / 2 * window.innerWidth;
vector.y = -(vector.y - 1) / 2 * window.innerHeight;
To do the reversie I tried this, but does not give the result I expect:
var mouseX = ((perc.x) / window.innerWidth) * 2 - 1,
mouseY = -((perc.y) / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3(mouseX, mouseY, 1);
projector.unprojectVector(vector, camera);
return vector.sub(camera.position).normalize()
I have tried several ways and tried to Google, but didn't found an answer.
Q: How to convert screen position to camera position?
Well, as posted by Gero3, what you want to do is to create a plane that will cover all of your visible camera area (something like new THREE.PlaneGeometry(5000,5000); and then use raycaster to locate screen coordinated on that plane.
Here is an example from another similar question:
http://jsfiddle.net/PwWbT/
Hope that helps.
Use a large invisble plane that defines in which direction the door can expand and then use a raycaster on the plane to search for the position to where the door object should extend.

Categories

Resources