I'm trying to implement the ScreenToWorldPoint from Unity3D in threeJS.
Basically it transform a screen point into a world point at a given distance.
https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html
In unity, given a perspective Camera with these settings.
//fov,ratio,near,far
const camera = new THREE.PerspectiveCamera(60, 411 / 731, 0.3, 1000)
And these parameters
Camera.main.ScreenToWorldPoint(new Vector3(0, 0, -16))
In unity3D I get this vector which is expected (5.2, 9.2, -32.0)
But in my implementation I'm not managing to get the same vector.
This is my implementation.
function screenToWorldPoint(screenSpaceCoord, target = new THREE.Vector3()) {
const ndc = new THREE.Vector2();
ndc.x = (2 * screenSpaceCoord.x) / 411 - 1;
ndc.y = 1 - (2 * screenSpaceCoord.y) / 731;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(ndc, camera);
return raycaster.ray.at(screenSpaceCoord.z, new THREE.Vector3());
}
Related
//SCENE and CAMERA
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 10);
//GEOMETRIES
const boxGeo=new THREE.BoxGeometry(2,2)
const boxMat=new THREE.MeshStandardMaterial()
const box1=new THREE.Mesh(boxGeo,boxMat)
const box2=new THREE.Mesh(boxGeo,boxMat)
const box3=new THREE.Mesh(boxGeo,boxMat)
scene.add(box1)
scene.add(box2)
scene.add(box3)
box1.position.set(-7,0,0)
box3.position.set(7,0,0)
let objectsToTest=[box1,box2,box3]
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor('#333333')
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
let clock=new THREE.Clock()
let tick = () => {
controls.update();
let elapsedTime=clock.getElapsedTime()
const raycaster = new THREE.Raycaster();
const rayOrigin = new THREE.Vector3(-8, 0, 0);
const rayDirection = new THREE.Vector3(10, 0, 0);
rayDirection.normalize();
raycaster.set(rayOrigin, rayDirection);
let intersects = raycaster.intersectObjects(objectsToTest);
for (let obj of objectsToTest){
obj.material.color.set('blue')
}
for (let obj of intersects) {
obj.object.material.color.set('pink')
}
box1.position.y = (Math.sin(elapsedTime/2)) * 5;
box2.position.y = (Math.sin(elapsedTime/4)) * 5;
box3.position.y = (Math.sin(-elapsedTime/3)) * 5;
renderer.render(scene, camera);
window.requestAnimationFrame(tick);
};
tick();
Initially they're all pink which I get is cuz they all start at the center.
Then all 3 of them turn pink even if one of them touches the ray, even if the rest of the boxes are far away from the ray.
How can I make only the one that is touching the ray pink?
Please ignore the below text
(Initially they're all pink which I get is cuz they all start at the center.
Then all 3 of them turn pink even if one of them touches the ray, even if the rest of the boxes are far away from the ray.
How can I make only the one that is touching the ray pink?)
You first initialize the box material with a color(a default one).
Then you make 3 boxes with the same material.
When you change the color of the material all 3 boxes change color because they all use the same material instance.
if you initialize your boxes like this it should work:
const box1=new THREE.Mesh(boxGeo,new THREE.MeshStandardMaterial())
Because then every box gets their own instance of material and you can change them independently from each other.
Hello i'm trying to make a wave pattern on the surface of a cylinder. The waves should rotate with the rotation of the surface. and in a way the sine period is moving in circles, and the amplitudes are long mounds on the surface. Here's some pictures to better explain what i mean.
This is what i'm trying to get the top down view of the cylinder to look similar to:
this is the top view of my cylinder. I'd like the wave to change direction with the rotation of the circle, so it looks the same from all directions.
I feel like i'm very close, i'm just not sure what quaternion or angle to multiply against the vector:
var geometry = this.threeDHandler.threeD_meshes[0].geometry;
var vec3 = new THREE.Vector3(); // temp vector
for (let i = 0; i < geometry.vertices.length; i++) {
vec3.copy(geometry.vertices[i]); // copy current vertex to the temp vector
vec3.setX(0);
vec3.normalize(); // normalize
//part i'm confsude about
const quaternion = new THREE.Quaternion();
const xPos = geometry.vertices[i].x;
//trying to twist the sine around the circle
const twistAmount = 100;
const upVec = new THREE.Vector3(0, 0, 1);
quaternion.setFromAxisAngle(
upVec,
(Math.PI / 180) * (xPos / twistAmount)
);
vec3.multiplyScalar(Math.sin((geometry.vertices[i].x* Math.PI) * period) * amplitude) // multiply with sin function
geometry.vertices[i].add(vec3); // add the temp vector to the current vertex
geometry.vertices[i].applyQuaternion(quaternion);
}
geometry.verticesNeedUpdate = true;
geometry.computeVertexNormals();
You can use absolute value of the sin function of the angle, that a vertex belongs to.
In this case you can use THREE.Spherical() object that allows to get spherical coordinates of a vector:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 6);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var cylinderGeom = new THREE.CylinderGeometry(1, 1, 4, 128, 40, true);
var vec3 = new THREE.Vector3(); // temp vector
var vec3_2 = new THREE.Vector3(); // temp vector 2
var spherical = new THREE.Spherical();
cylinderGeom.vertices.forEach(v => {
vec3.copy(v); // copy current vertex to the temp vector
vec3.setY(0); // leave x and z (thus the vector is parallel to XZ plane)
vec3.normalize(); // normalize
vec3.multiplyScalar(Math.sin(v.y * Math.PI) * 0.25) // multiply with sin function
// radial wave
vec3_2.copy(v).setY(0).normalize();
spherical.setFromVector3(vec3_2);
vec3_2.setLength(Math.abs(Math.sin((spherical.theta * 4) + v.y * 2) * 0.25));
v.add(vec3).add(vec3_2); // add the temp vectors to the current vertex
})
cylinderGeom.computeVertexNormals();
var cylinder = new THREE.Mesh(cylinderGeom, new THREE.MeshNormalMaterial({
side: THREE.DoubleSide,
wireframe: false
}));
scene.add(cylinder);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.124.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.124.0/examples/js/controls/OrbitControls.js"></script>
I am looking at creating a helical shape and would like some guidance on the approach. Imagine you wrapped a flat ribbon around a cylindrical tube (with a space between each turn), that Is the shape I am trying to create.
my initial thought was to use the tube geometry, but that seems to only be able to accept a circular profile. I would like a flat/straight profile
Since I will eventually also want a shape at the end of the helix, I thought about creating a couple of arrays of points, one for the outer helix, the other for the inner helix then use the three shape, but I believe that can only do 2D shapes, which this wont be.
Whilst defining a cylinder would be very confinient, just putting a texture on it wont give me the control I require
The maths behind a helix aren't difficult, I just don't know the best approach in ThreeeJS to actually create it? Any guidance would be very much appreciated.
Incase anyone is interested I did get a decent repsonse with a good approach from the three discourse page..
https://discourse.threejs.org/t/creating-a-helical-sweep-with-a-flat-profile/3163/2
All credit to "prisoner849" from that forum, heres a direct link to a code pen with his answer which involved bending a thin box
https://jsfiddle.net/5Lycd4rm/7/
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(2, 3, 5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x101000);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(100);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
var rbnWidth = .5;
var rbnThickness = 0.1;
var rbnSteps = 1;
var rbnStepLength = 1.5;
var rbnSegsPerStep = 50;
var rbnRadius = 1;
var rbnGeom = new THREE.BoxGeometry(rbnSteps * Math.PI * 2, rbnWidth, rbnThickness, rbnSteps * rbnSegsPerStep, 1, 1);
rbnGeom.computeBoundingBox();
var size = new THREE.Vector3();
rbnGeom.boundingBox.getSize(size);
rbnGeom.translate(size.x * 0.5, size.y * 0.5, size.z * 0.5);
// bend it!
rbnGeom.vertices.forEach(v => {
let angle = -v.x;
let radius = rbnRadius + v.z;
let shift = (v.x / (Math.PI * 2)) * rbnStepLength + v.y;
v.x = Math.cos(angle) * radius;
v.y = shift;
v.z = Math.sin(angle) * radius;
});
rbnGeom.computeFaceNormals();
rbnGeom.computeVertexNormals();
rbnGeom.center();
var ribbon = new THREE.Mesh(rbnGeom, new THREE.MeshStandardMaterial({color: 0x0099ff}));
scene.add(ribbon);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
I have a project, where I have to show a globe on home page, where visitor can select location(major known cities) & then make a search. I google & find some best examples like :
http://paperplanes.world &
http://news-lab-trends-experiment.appspot.com/
If there is any raw code available so that I can make changes as per requirements. I look around some js https://threejs.org/ & http://www.webglearth.org, how these can be helpful.
If you just want some abstract representation of the earth, it doesn't make much sense to go for webglearth and the likes, as you a) will not need that complexity they implement and b) can't easily adjust the appearance of the globe towards something as simple as the examples.
The good news is that all that isn't really as complicated as it might sound at first. For a simplified 3d-model, there are some of them out there. Just have a look at these search-results. I believe this is the one that was used for the paperplanes project.
Positioning stuff on a spherical shape isn't that hard either, you just need to make yourself familiar with spherical coordinates (the math-version of latitude/longitude) and the THREE.Spherical class. A simple example for this below (for simplicity using a unit-sphere as earth, but it would be mostly the same if you would load a complex-model instead, as long as it's roughly spherical):
const textureLoader = new THREE.TextureLoader();
function setup(scene) {
// add some helpers
scene.add(new THREE.GridHelper(50, 100, 0x444444, 0x222222));
scene.add(new THREE.AxisHelper(2));
// add a textured sphere representing the earth
const texture = textureLoader.load(
'https://raw.githubusercontent.com/' +
'jiwonkim/d3gl/master/img/earth-blank.png'
);
const earth = new THREE.Mesh(
new THREE.SphereGeometry(1, 36, 18),
new THREE.MeshStandardMaterial({
map: texture,
metalness: 0,
roughness: 1
})
);
scene.add(earth);
const marker = new THREE.Mesh(
new THREE.BoxGeometry(0.05, 0.2, 0.05),
new THREE.MeshStandardMaterial({color: 0xff5500})
);
const lat = 52.5;
const lng = 10;
// compute position (adjust theta/phi conversion to the
// orientation of your model)
const spherical = new THREE.Spherical(
1, // radius
(90 - lat) / 180 * Math.PI, // latitude -> phi
(90 + lng) / 180 * Math.PI // longitude -> theta
);
marker.position.setFromSpherical(spherical);
earth.add(marker);
// compute orientation
const v3 = new THREE.Vector3();
v3.copy(marker.position).normalize();
marker.quaternion.setFromUnitVectors(marker.up, v3);
}
// ---- boilerplate-code
// .... setup renderer
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
// .... setup scene
const scene = (window.scene = new THREE.Scene());
// .... setup camera and controls
const camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
.01,
100
);
const controls = new THREE.OrbitControls(camera);
camera.position.set(-3, 3, 4);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// .... setup some lighting
const dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
dirLight.position.set(1, 0, 0.5);
scene.add(dirLight, new THREE.AmbientLight(0x666666));
// .... setup and run demo-code
setup(scene);
requestAnimationFrame(function loop(time) {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(loop);
});
// .... bind events
window.addEventListener("resize", ev => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});
document.body.appendChild(renderer.domElement);
body {margin: 0; background: black;}
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
I'm developing a VR app and I'm attempting to store the current camera viewing angle with
THREE.Utils = {
cameraLookDir: function(camera) {
var vector = new THREE.Vector3(0, 0, -1);
vector.applyEuler(camera.rotation, camera.eulerOrder);
return vector;
}
};
var dir = THREE.Utils.cameraLookDir(camera);
var defaults = {
x:dir.x,
y:dir.y,
z:dir.z,
fov: camera.fov
};
I'm then attempting to apply this to a dolly which I use to pre-set the camera viewing angle for using with VRControls
The dolly is constructed as
dolly = new THREE.PerspectiveCamera();
dolly.position.set( 0, 0, 0 );
scene.add( dolly );
dolly.add( camera );
and I try and apply the angle so the camera is facing the same way
dolly.rotation.x = defaults.x;
dolly.rotation.y = defaults.y;
dolly.rotation.z = defaults.z;
This isn't' really working, and I'm not really sure how to go about getting the camera to face the same way as it's saved values.
Any clues on how to go about this?