Hy!
I'm facing a strange problem in THREE JS(r71) / THREEx (THREEx.LaserBeam). I'm having problems with rotation of Object 3D.
I'm calculating latitude, attitude points into phi,theta like this:
(Or any other variables for 50/-51)
var phi = (90 - 50) * Math.PI / 180;
var theta = (-51) * Math.PI / 180;
After this I drop a sphere on the location with the following Code:
var geometry = new THREE.SphereGeometry( 0.005, 15, 15 );
var material = new THREE.MeshBasicMaterial( {color: 0x0000ff} );
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
sphere.position.x = 0.5 * Math.sin(phi) * Math.cos(theta);
sphere.position.y = 0.5 * Math.cos(phi);
sphere.position.z = 0.5 * Math.sin(phi) * Math.sin(theta);
Then I rotate my ray to the same position with the following code:
laserBeam.rotation.y = -theta
laserBeam.rotation.z = phi
The laserBeam is actually acts as "line", in an Object3D. The Origin of the ray is at (0,0). So I haven't got a faintest idea why it is not going trough the sphere (See screenshot).
Any ideas?
---EDIT---
or here is the example with a simple line
var phi = (90 - 50) * Math.PI / 180;
var theta = (-51) * Math.PI / 180;
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, 0, 0));
geometry.vertices.push(new THREE.Vector3(1 * Math.sin(phi) * Math.cos(theta), 1* Math.cos(phi), 1 * Math.sin(phi) * Math.sin(theta)));
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var line = new THREE.Line(geometry, material);
containerLine = new THREE.Object3D();
containerLine.add( line );
scene.add(containerLine);
You incorrectly calculates a small radius and y-coordinates:
var rm = R * Math.cos(phi); // vs `R * Math.sin(phi)`
sphere.position.x = rm * Math.cos(theta);
sphere.position.y = R * Math.sin(phi); // vs `R * Math.cos(phi)`
sphere.position.z = rm * Math.sin(theta);
http://jsfiddle.net/sxen2kLd/
Ah finaly.... Dunno how and why I'm too tired to undestand now, but posting it
function latLongToVector3(lat, lon, radius, heigth) {
var phi = (lat)*Math.PI/180;
var theta = (lon-180)*Math.PI/180;
var x = -(radius+heigth) * Math.cos(phi) * Math.cos(theta);
var y = (radius+heigth) * Math.sin(phi);
var z = (radius+heigth) * Math.cos(phi) * Math.sin(theta);
return new THREE.Vector3(x,y,z);
}
var helper = latLongToVector3(51.227821,51.3865431,0.5,0);
var geometry = new THREE.SphereGeometry( 0.005, 15, 15 );
var material = new THREE.MeshBasicMaterial( {color: 0x0000ff} );
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
sphere.position.x = helper.x
sphere.position.y = helper.y
sphere.position.z = helper.z
----------------------------------------------------
var helper = latLongToVector3(51.227821,51.3865431,0.5,0);
function rotateAroundWorldAxis(object, axis, radians) {
rotWorldMatrix = new THREE.Matrix4();
rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rotWorldMatrix.multiply(object.matrix);
object.matrix = rotWorldMatrix;
//object.rotation.setEulerFromRotationMatrix(object.matrix);
object.rotation.setFromRotationMatrix(object.matrix);
}
laserBeam.useQuaternion = true;
var origVec = new THREE.Vector3(1, 0, 0);
var targetVec = helper;
targetVec.normalize();
var rotateQuaternion = new THREE.Quaternion();
var axis = new THREE.Vector3(0, 0, 0);
var angle = Math.acos(origVec.dot(targetVec));
axis.cross(origVec, targetVec);
axis.normalize();
rotateAroundWorldAxis(laserBeam,axis,angle);
Related
I am using
var geometry = new THREE.SphereGeometry( 15, 32, 16, 0, 2*Math.PI, 0, x);
with x < 2*PI to create a part of a sphere looking like that :
The thing is I need to have tens of thousands of those, all with the same center and radius but with different rotations and different and very small x values.
I have done research about instancing but can't find a way to use it the way I want to. Any suggestions ?
Use an additional InstancedBufferAttribute to pass phi angles per instance in InstancedMesh, and process those values in vertex shader to form the parts you want, using .onBeforeCompile() method.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0/build/three.module.js";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 5000);
camera.position.set(0, 0, 50);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", (event) => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let control = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
const MAX_COUNT = 10000;
let g = new THREE.SphereGeometry(1, 20, 10, 0, Math.PI * 2, Math.PI * 0.25, Math.PI * 0.5);
let m = new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.vertexShader = `
attribute float instPhi;
// straight from the docs on Vector3.setFromSphericalCoords
vec3 setFromSphericalCoords( float radius, float phi, float theta ) {
float sinPhiRadius = sin( phi ) * radius;
float x = sinPhiRadius * sin( theta );
float y = cos( phi ) * radius;
float z = sinPhiRadius * cos( theta );
return vec3(x, y, z);
}
${shader.vertexShader}
`.replace(
`#include <beginnormal_vertex>`,
`#include <beginnormal_vertex>
vec3 sphPos = setFromSphericalCoords(1., instPhi * (1. - uv.y), PI * 2. * uv.x); // compute position
objectNormal = normalize(sphPos); // normal is just a normalized vector of the computed position
`).replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
transformed = sphPos; // set computed position
`
);
//console.log(shader.vertexShader);
}
});
let im = new THREE.InstancedMesh(g, m, MAX_COUNT);
scene.add(im);
let v3 = new THREE.Vector3();
let c = new THREE.Color();
let instPhi = []; // data for Phi values
let objMats = new Array(MAX_COUNT).fill().map((o, omIdx) => {
let om = new THREE.Object3D();
om.position.random().subScalar(0.5).multiplyScalar(100);
om.rotation.setFromVector3(v3.random().multiplyScalar(Math.PI));
om.updateMatrix();
im.setMatrixAt(omIdx, om.matrix);
im.setColorAt(omIdx, c.set(Math.random() * 0xffffff))
instPhi.push((Math.random() * 0.4 + 0.1) * Math.PI);
return om;
});
g.setAttribute("instPhi", new THREE.InstancedBufferAttribute(new Float32Array(instPhi), 1));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>
PS May be turned into the using of InstancedBufferGeometry. Creativity is up to you :)
I've tried debugging this multiple MULTIPLE times but can't find a solution to this. So basically, my Earth model and Sun model are not being rendered properly. They are appearing as a dull filled colour. Despite having a texture added to the sphere, the texture is not loading onto it.
I'd say to look at lines 104 - 141 as that's where I'm creating the Sun and Earth models.
Also, would love some help on my Animate function.
Current code:
// Standard Variables / To be changed later.
var scene, camera, renderer //, container;
var W, H;
var delta = Math.delta;
W = parseInt(window.innerWidth);
H = parseInt(window.innerHeight);
camera = new THREE.PerspectiveCamera(45, W / H, 1, 1000000);
camera.position.z = 36300;
scene = new THREE.Scene();
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(W, H);
document.body.appendChild(renderer.domElement);
// Adding Stars.
var starsGeometry = new THREE.Geometry();
var starsMaterial = new THREE.ParticleBasicMaterial({
color: 0xbbbbbbb,
opacity: 0.6,
size: 1,
sizeAttenuation: false
});
var stars;
// Adding stars to the Scene.
for (var i = 0; i < 45000; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2 - 1;
vertex.y = Math.random() * 2 - 1;
vertex.z = Math.random() * 2 - 1;
vertex.multiplyScalar(7000);
starsGeometry.vertices.push(vertex);
}
stars = new THREE.ParticleSystem(starsGeometry, starsMaterial);
stars.scale.set(50, 50, 50);
scene.add(stars);
// ------------------------------------------------------------
var starsGeometry2 = new THREE.Geometry();
var starsMaterial2 = new THREE.ParticleBasicMaterial({
color: 0xbbbbbbb,
opacity: 1,
size: 1,
sizeAttenuation: false
});
var stars2;
// Adding stars to the Scene.
for (var i = 0; i < 10000; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2 - 1;
vertex.y = Math.random() * 2 - 1;
vertex.z = Math.random() * 2 - 1;
vertex.multiplyScalar(7000);
starsGeometry2.vertices.push(vertex);
}
stars2 = new THREE.ParticleSystem(starsGeometry2, starsMaterial2);
stars2.scale.set(70, 150, 100);
scene.add(stars2);
// Ambient light to the Scene.
var ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// ------------------------------------------------------------
//Sun
var sun, gun_geom, sun_mat;
sun_geom = new THREE.SphereGeometry(2300, 80, 80);
//texture.anisotropy = 8;
sun_mat = new THREE.MeshPhongMaterial();
sun = new THREE.Mesh(sun_geom, sun_mat);
sun_mat = THREE.ImageUtils.loadTexture('images/sunmap.jpg');
//sun_mat.bumpMap = THREE.ImageUtils.loadTexture('images/sunmap.jpg');
//sun_mat.bumpScale = 0.05;
//var texture = THREE.ImageUtils.loadTexture('images/sunmap.jpg');
scene.add(sun);
var geometry = new THREE.SphereGeometry(2300, 80, 80);
var texture2 = THREE.ImageUtils.loadTexture('images/earthmap1k.jpg');
var material = new THREE.MeshPhongMaterial({
map: texture2,
emissive: 0xffffff
});
var earth = new THREE.Mesh(geometry, material);
//earth_mat = new THREE.MeshNormalMaterial();
//earth = new THREE.Mesh(earth_geom, earth_mat);
scene.add(earth);
var t = 0;
document.addEventListener('mousemove', function(event) {
y = parseInt(event.offsetY);
});
// Call Animate function within load function.
animate();
function animate() {
requestAnimationFrame(animate);
sun.rotation.y += 0.001;
earth.rotation.y += 1 / 16 * delta;
//camera.position.y = y * 5;
camera.lookAt(scene.position);
t += Math.PI / 180 * 2;
renderer.render(scene, camera);
}
// everything now within `onload`
body {
background: whitesmoke;
margin: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
What I mean:
When I run your code I get all these errors
THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.
THREE.ParticleSystem has been renamed to THREE.Points.
THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.
THREE.ParticleSystem has been renamed to THREE.Points.
THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.
THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.
You should fix those errors
Otherwise not sure what this code was supposed to do
sun_mat = new THREE.MeshPhongMaterial();
sun = new THREE.Mesh(sun_geom, sun_mat);
sun_mat = THREE.ImageUtils.loadTexture('images/sunmap.jpg');
It makes a material, passes it to THREE.Mesh then tries to make a texture that is not used and it re-assigns sun_mat to that texture but sun_mat is used by nothing else.
I changed the code the code to this
const loader = new THREE.TextureLoader();
//Sun
var sun, gun_geom, sun_mat;
sun_geom = new THREE.SphereGeometry(2300, 80, 80);
sun_mat = new THREE.MeshPhongMaterial({
emissive: 0xffffff,
emissiveMap: loader.load('https://threejs.org/examples/textures/waterdudv.jpg'),
});
sun = new THREE.Mesh(sun_geom, sun_mat);
scene.add(sun);
var geometry = new THREE.SphereGeometry(2300, 80, 80);
var texture2 = loader.load('https://threejs.org/examples/textures/planets/earth_atmos_2048.jpg');
var material = new THREE.MeshPhongMaterial({
emissiveMap: texture2,
emissive: 0xffffff,
});
var earth = new THREE.Mesh(geometry, material);
scene.add(earth);
You'll also notice above I changed from using map to emissiveMap. You need to add some lights other than the AmbientLight if you want map to work.
Then the code has the earth and sun the same size and stacked on top each other. I moved the earth
earth.position.set(5000, 0, 0);
Then in the render loop there's this code
earth.rotation.y += 1 / 16 * delta;
but delta is defined as
var delta = Math.delta;
There is no such thing as Math.detla so delta is undefined which means earth.rotation.y += 1 / 16 * delta; just becomes NaN which means the math for the earth breaks so it disappears.
I just set delta = 1.
You might find this articles helpful with your three.js learning as they are up to date with version 109 (they aren't using the outdated classes the code you posted referenced)
// Standard Variables / To be changed later.
var scene, camera, renderer //, container;
var W, H;
var delta = 1.;//Math.delta;
W = parseInt(window.innerWidth);
H = parseInt(window.innerHeight);
camera = new THREE.PerspectiveCamera(45, W / H, 1, 1000000);
camera.position.z = 36300;
scene = new THREE.Scene();
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(W, H);
document.body.appendChild(renderer.domElement);
// Adding Stars.
var starsGeometry = new THREE.Geometry();
var starsMaterial = new THREE.PointsMaterial({
color: 0xbbbbbbb,
opacity: 0.6,
size: 1,
sizeAttenuation: false
});
var stars;
// Adding stars to the Scene.
for (var i = 0; i < 45000; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2 - 1;
vertex.y = Math.random() * 2 - 1;
vertex.z = Math.random() * 2 - 1;
vertex.multiplyScalar(7000);
starsGeometry.vertices.push(vertex);
}
stars = new THREE.Points(starsGeometry, starsMaterial);
stars.scale.set(50, 50, 50);
scene.add(stars);
// ------------------------------------------------------------
var starsGeometry2 = new THREE.Geometry();
var starsMaterial2 = new THREE.PointsMaterial({
color: 0xbbbbbbb,
opacity: 1,
size: 1,
sizeAttenuation: false
});
var stars2;
// Adding stars to the Scene.
for (var i = 0; i < 10000; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2 - 1;
vertex.y = Math.random() * 2 - 1;
vertex.z = Math.random() * 2 - 1;
vertex.multiplyScalar(7000);
starsGeometry2.vertices.push(vertex);
}
stars2 = new THREE.Points(starsGeometry2, starsMaterial2);
stars2.scale.set(70, 150, 100);
scene.add(stars2);
// Ambient light to the Scene.
var ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// ------------------------------------------------------------
const loader = new THREE.TextureLoader();
//Sun
var sun, gun_geom, sun_mat;
sun_geom = new THREE.SphereGeometry(2300, 80, 80);
sun_mat = new THREE.MeshPhongMaterial({
emissive: 0xffffff,
emissiveMap: loader.load('https://i.imgur.com/gl8zBLI.jpg'),
});
sun = new THREE.Mesh(sun_geom, sun_mat);
scene.add(sun);
var geometry = new THREE.SphereGeometry(2300, 80, 80);
var texture2 = loader.load('https://i.imgur.com/BpldqPj.jpg');
var material = new THREE.MeshPhongMaterial({
emissiveMap: texture2,
emissive: 0xffffff,
});
var earth = new THREE.Mesh(geometry, material);
earth.position.set(5000, 0, 0);
scene.add(earth);
var t = 0;
document.addEventListener('mousemove', function(event) {
y = parseInt(event.offsetY);
});
// Call Animate function within load function.
animate();
function animate() {
requestAnimationFrame(animate);
sun.rotation.y += 0.001;
earth.rotation.y += 1 / 16 * delta;
//camera.position.y = y * 5;
camera.lookAt(scene.position);
t += Math.PI / 180 * 2;
renderer.render(scene, camera);
}
// everything now within `onload`
body {
background: whitesmoke;
margin: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
I have a situation where I use an OrbitControls to have a limited view in a room/space. The OrbitControls gets min/max values for azimuth and polar angles to control the view.
The OrbitControls' target is close by the camera to achieve the correct view, compare to the "panorama / cube" example (https://threejs.org/examples/?q=cube#webgl_panorama_cube).
In one situation this works fine. In an other situation this does not work. The reason is that because of the placement of objects, the starting azimuth is -179.999 degrees.
I have not found a way to tell the orbitcontrol to have the azimuth between -179.999 +/- 20 degrees.
I have experimented a little with the algorithm of #Αλέκος (Is angle in between two angles) and it looks like I can calculate the correct angles (not fully implemented). For that I would have set an azimuth and a delta in stead of min/max (and change the OrbitControls code).
Any suggestions for an easier solution? Thanks!
Test code:
var scene = new THREE.Scene();
var orbitControls;
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
vertexColors: THREE.FaceColors
});
var geometry = new THREE.BoxGeometry(1, 1, 1);
// colors
red = new THREE.Color(1, 0, 0);
green = new THREE.Color(0, 1, 0);
blue = new THREE.Color(0, 0, 1);
var colors = [red, green, blue];
console.log("FACES", geometry.faces.length)
for (var i = 0; i < 3; i++) {
geometry.faces[4 * i].color = colors[i];
geometry.faces[4 * i + 1].color = colors[i];
geometry.faces[4 * i + 2].color = colors[i];
geometry.faces[4 * i + 3].color = colors[i];
}
geometry.faces[0].color = new THREE.Color(1, 1, 1);
geometry.faces[4].color = new THREE.Color(1, 1, 1);
geometry.faces[8].color = new THREE.Color(1, 1, 1);
var cube = new THREE.Mesh(geometry, material);
cube.position.x = 0;
cube.position.y = 4;
cube.position.z = 44;
console.log("CUBE", cube.position);
var cubeAxis = new THREE.AxesHelper(20);
cube.add(cubeAxis);
scene.add(cube);
camera.position.x = 0.8;
camera.position.y = 4.5;
camera.position.z = 33;
orbitControls = new THREE.OrbitControls(camera, renderer.domElement);
orbitControls.enablePan = false;
orbitControls.enableZoom = false;
orbitControls.minPolarAngle = (90 - 10) * Math.PI / 180;
orbitControls.maxPolarAngle = (90 + 10) * Math.PI / 180;
// These values do not work:
orbitControls.maxAzimuthAngle = 200 * Math.PI / 180;
orbitControls.minAzimuthAngle = 160 * Math.PI / 180;
orbitControls.target.x = 0.8;
orbitControls.target.y = 4.5;
orbitControls.target.z = 33.1;
orbitControls.enabled = true;
var animate = function () {
requestAnimationFrame(animate);
if (orbitControls) {
orbitControls.update();
}
renderer.render(scene, camera);
// console.log("orbitControls", "azi", orbitControls.getAzimuthalAngle() * 180 / Math.PI);
};
animate();
I have a function where I make a star, here we go:
function CreateStar( radius, thickness, isWireframe, starColor) {
// material
var starMaterial = new THREE.MeshLambertMaterial({
color: starColor,
wireframe: isWireframe,
shading: THREE.FlatShading
});
// array for vertices
var vertices = [];
// set "zero" vertex for thickness
vertices.push( new THREE.Vector3(0, 0, thickness) );
// calculate a vertex and a pit for a half of a ray... 5 times for each 72 degrees
var deg = Math.PI / 180; // for me it's easier to work with degrees rather than radians
var maxR, minR;
maxR = radius; // radius for a vertex
var x4Rad = maxR * Math.cos( - 72 * deg );
minR = x4Rad/Math.cos( - 36 * deg ); // radius for a pit
var firstVertex;
for ( var i = 0; i < 5; i++ ){
// vertex
var vertX = maxR * Math.cos( (90 + (72 * i)) * deg );
var vertY = maxR * Math.sin( (90 + (72 * i)) * deg );
if ( i == 0 ) firstVertex = new THREE.Vector3( vertX, vertY, 0 );
vertices.push( new THREE.Vector3( vertX, vertY, 0 ));
// pit
var pitX = minR * Math.cos( (126 + (72 * i)) * deg );
var pitY = minR * Math.sin( (126 + (72 * i)) * deg );
vertices.push( new THREE.Vector3( pitX, pitY, 0 ));
}
vertices.push( firstVertex ); // add the first vertex again to close the contour of the star
var holes = []; // no holes in our contour
var triangles, star;
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
triangles = THREE.Shape.Utils.triangulateShape( vertices, holes ); // triangulation
for ( var j = 0; j < triangles.length; j++ ){
geometry.faces.push( new THREE.Face3( triangles[j][0], triangles[j][1], triangles[j][2] ));
}
star = new THREE.Mesh( geometry, starMaterial );
//star = new THREE.Mesh( new THREE.CubeGeometry(200,200,200), starMaterial);
return star;
}
My problem is when I return a cube (commented in this code) from this function and add to a scene, I get exactly a cube with correct shades which depend on position of a directional light source, but, when I return a star and add it to the scene, I get... hm.. just a black star with no color (it's black), no shades... nothing. So why I can apply a material to the cube, but I can't apply it to the star?
Can anybody explain to me what I'm going wrong?
Three.js r68
Maybe you just need to recalculate your normals.
Try :
geometry.computeFaceNormals();
geometry.computeVertexNormals();
star = new THREE.Mesh( geometry, starMaterial );
I am creating an sphere geometry.
geometry = new THREE.SphereGeometry( 200, 20, 10 );
material = new THREE.MeshLambertMaterial({ shading: THREE.FlatShading, color: 0xff0000 });
sphere = new THREE.Mesh(geometry, material);
scene.add( sphere );
What I want is when I click on this geometry the faces get detach like in the example below.
(Click on the sphere button to see detached faces)
http://www.mrdoob.com/lab/javascript/threejs/css3d/periodictable/
The full code for the example you posted is available here: http://jsfiddle.net/9XGuK/4/
Specifically, this part of the example:
var vector = new THREE.Vector3();
for ( var i = 0, l = objects.length; i < l; i ++ ) {
var phi = Math.acos( -1 + ( 2 * i ) / l );
var theta = Math.sqrt( l * Math.PI ) * phi;
var object = new THREE.Object3D();
object.position.x = 800 * Math.cos( theta ) * Math.sin( phi );
object.position.y = 800 * Math.sin( theta ) * Math.sin( phi );
object.position.z = 800 * Math.cos( phi );
vector.copy( object.position ).multiplyScalar( 2 );
object.lookAt( vector );
targets.sphere.push( object );
}
Perhaps you can recreate this code locally to better understand how it all works and then adapt it to suit your needs.