I have a main scene with a sphere and another subwindow (in right bottom) where I have drawn the (x,y,z) axis of main scene.
In this subwindow, I would like to draw the labels "X" "Y" and "Z" on each axis (more precisely located on the end of each AxisHelper). I know how to use TextGeometry but the issue is that I can't get to make rotate these labels in order to make them appear always in face on the user.
You can see the problem on the [following link][1] : label "X" is fixed relatively to axis and is rotating with camera, so it is not always in face of user.
From these two links link1 and link2, I tried to add (in my example, I tried with only "X" label) :
function addLabelAxes() {
// Axes label
var loader = new THREE.FontLoader();
loader.load( 'js/helvetiker_regular.typeface.js', function ( font ) {
var textGeo1 = new THREE.TextGeometry( 'X', {
font: font,
size: 5,
height: 0.1,
bevelThickness: 0.1,
bevelSize: 0.1,
bevelEnabled: true,
} );
var color = new THREE.Color();
color.setRGB(255, 255, 255);
textMaterial = new THREE.MeshBasicMaterial({ color: color });
var meshText1 = new THREE.Mesh(textGeo1, textMaterial);
// Position of axes extremities
var positionEndAxes = axes2.geometry.attributes.position;
var label1X = positionEndAxes.getX(0);
meshText1.position.x = label1X + axisLength;
meshText1.position.y = 0;
meshText1.position.z = 0;
// Rotation of "X" label
//meshText1.rotation = zoomCamera.rotation;
meshText1.lookAt(zoomCamera.position);
// Add meshText to zoomScene
zoomScene.add(meshText1);
});
}
zoomCamera represents a PerspectiveCamera which is the camera of subwindow (i.e zoomScene) ;I add TextGeometry to zoomScene by doing :
zoomScene.add(meshText1);
What might be wrong in my code ? I wonder if I can make rotate the "X" label on itself, i.e the "X" label is rotating like axis but a self (local) orientation is applied as a function of rotation theta angle, so the label is always kept in face of user during camera rotation ?
You are probably looking for THREE.SPRITE. From the docs:
Object3D -> Sprite: A sprite is a plane in an 3d scene which faces always towards the camera.
Here's a simple example of how to use it:
var map = new THREE.TextureLoader().load( "sprite.png" );
var material = new THREE.SpriteMaterial( { map: map, color: 0xffffff, fog: true } );
var sprite = new THREE.Sprite( material );
scene.add( sprite );
Here's a working example of a similar scenario (3 scaled sprites with different positioning). You can find the code on github.
Related
I am currently trying to project an image onto the inside of a halfsphere in a three.js project. The half sphere is created via
const geometry = new THREE.SphereGeometry(Component.radius, this.resolution, this.resolution,
Math.PI, Math.PI, 0, Math.PI);
this.material = new THREE.MeshStandardMaterial({color: 0xffffff});
this.material.side = THREE.DoubleSide;
this.sphere = new THREE.Mesh(geometry, this.material);
// The image of the texture is set later dynamically via
// this.material.map = textureLoader.load(...);
With radius and resolution being constants. This works fine, except for one issue: The image becomes distorted around the "top" and "bottom" of the sphere, like this:
Simple example of distorted texture:
The texture originally had the value "0,0" in the bottom left and "0,1" in the bottom right, and with the camera facing down from the center of the demisphere the bottom left and bottom right corner are squished onto the "bottom" point of the sphere.
I want to change this behavior so the texture corners are instead on where they would be if you place the square texture into a sphere, with the corners touching the circle, then stretching the lines between the corners to match the circle. Simple mock of what i mean:
I have tried playing with the mapping Attribute of my texture, but that doesn't change the behaviour from my understanding.
After changing the UV coordinates, my half sphere texture is't stretching on border as well:
this.sphereGeometry = new THREE.SphereGeometry(
10,
32,
24,
0,
Math.PI,
0,
Math.PI
);
const {
uv: uvAttribute,
normal
} = this.sphereGeometry.attributes;
for (let i = 0; i < normal.count; i += 1) {
let u = normal.getX(i);
let v = normal.getY(i);
u = u / 2 + 0.5;
v = v / 2 + 0.5;
uvAttribute.setXY(i, u, v);
}
const texture = new THREE.TextureLoader().load(
'https://i.imgur.com/CslEXIS.jpg'
);
texture.flipY = false;
texture.mapping = THREE.CubeUVRefractionMapping;
texture.needsUpdate = true;
const basicMaterial = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide,
});
this.sphere = new THREE.Mesh(this.sphereGeometry, basicMaterial);
this.scene.add(this.sphere);
I am learning three.js library in JS. I used the Line objects to create my own coordinate axes. I am trying to move them. The problem is when I update the vertices, two of the lines work fine, but the third line disappears completely. I tried printing its vertices on console but they are within visibility range. So, what is the reason I am facing this problem? My code:
// create and axes to the scene
//// X axis
var material = new THREE.LineBasicMaterial({
color: 0x00ff00
})
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(1500,0,0), new THREE.Vector3(-1500,0,0));
Xaxis = new THREE.Line(geometry,material);
scene.add(Xaxis);
//// Y axis
var material = new THREE.LineBasicMaterial({
color: 0xff0000
})
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0,1500,0), new THREE.Vector3(0,-1500,0));
Yaxis = new THREE.Line(geometry,material);
scene.add(Yaxis);
//// Z axis
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
})
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0,0,1500), new THREE.Vector3(0,0,-1500));
Zaxis = new THREE.Line(geometry,material);
scene.add(Zaxis);
function changePosition(newVal)
{
if(!transformCo_ordinate) {
sphere.position.set((vx/100)*newVal,(vy/100)*newVal,(vz/100)*newVal);
}
else{
Xaxis.geometry.vertices[0].set(1500,-(vy/100)*newVal,-(vz/100)*newVal);
Xaxis.geometry.vertices[1].set(-1500,-(vy/100)*newVal,-(vz/100)*newVal);
Yaxis.geometry.vertices[0].set(-(vx/100)*newVal,1500,-(vz/100)*newVal);
Yaxis.geometry.vertices[1].set(-(vx/100)*newVal,-1500,-(vz/100)*newVal);
Zaxis.geometry.vertices[0].set(-(vx/100)*newVal,-(vy/100)*newVal,1500);
Zaxis.geometry.vertices[0].set(-(vx/100)*newVal,-(vy/100)*newVal,-1500);
Xaxis.geometry.verticesNeedUpdate = true;
Yaxis.geometry.verticesNeedUpdate = true;
Zaxis.geometry.verticesNeedUpdate = true;
console.log(Xaxis.geometry.vertices[1]);
}
}
THe function changePosition is being called by a slider which passes its current value as newVal within range 0-100. vx = 100, vy = 100, vz = -300
Xaxis is the line that disappears. transformCo_ordinate is a boolean controlled by a checkbox. I am new to three.js so please understand if I am doing some stupid mistake.
Zaxis.geometry.vertices[0].set(-(vx/100)*newVal,-(vy/100)*newVal,1500);
Zaxis.geometry.vertices[0].set(-(vx/100)*newVal,-(vy/100)*newVal,-1500);
It seems you are updating the same vertex of the z axis twice. I've ported a simplified version of your code into the following fiddle. After fixing this minor bug, everything seems to work fine.
https://jsfiddle.net/gcL7dwbp/
The axis are automatically updated two seconds after application start.
three.js R104
I have a ribbon that displays a few thumbnails. Just to give a background, the thumbnail images are painted on a canvas, which is then added to Texture.
var texture = new THREE.Texture(textureCanvas);
The mesh is created as follows
loader.load('mesh_blender.js', function(geom) {
var mesh = new THREE.Mesh(geom, new THREE.MeshLambertMaterial({
color: 0xffffffff,
opacity: 0.7,
overdraw: 0.21,
side: THREE.DoubleSide,
transparent: true,
//shading: THREE.SmoothShading,
map: texture
}));
Everything till here is fine. The ribbon is created as follows
I have no complaints with this. But I need this additional effect you see in the image below. As it can be seen, the thumbnail that is in the centre (focus) needs to have a darker effect to show it is being highlighted/selectable. All the remaining thumbnails have a transparent effect depicting they are not selectable.
I am trying to wrap my head around this using Lights in Threejs but not very successful. I thought of using an AmbientLight to throw light on the entire ribbon and then an additional SpotLight only on the centre image (with a darker color maybe) to achieve the desired effect. But that didn't work. I have got something like this
But the centre focused image has no effect. As you can see in the image, I have used a helper to show the Light direction but I can't really see any light on the image. This is the code I use to develop that SpotLight
var spotLight = new THREE.SpotLight( 0xffeedd );
spotLight.position.set( 0, -50, 50 );
spotLight.castShadow = true;
//spotLight.penumbra = 0.2;
spotLight.decay = 2;
spotLight.distance = 300;
spotLight.angle = 0.5;
var helper = new THREE.SpotLightHelper( spotLight, 2.5 );
scene.add(helper);
scene.add( spotLight );
I am very new to Threejs and 3d graphics. Any help will be appreciated. Thanks.I am open to any other suggestion as well, if Lights are not to be used to achieve the end result.
You have given the opacity of the material as 0.7 so adding another light will not exactly give you the expected result. I would suggest using a raycaster to identify the object in the center and making the opacity of that object as 1 and the rest as 0.7.
var intersects = raycaster.intersectObjects( objects );
use this to get the objects that are intersecting in an array. And in the renderer function set the opacity of the first element in the array that is the object in the middle to 1. This only works if the thumbnails are all separate objects.
//set as the center of you vision
var mouse = new THREE.Vector2();
mouse.x = 0;
mouse.y = 0;
function highlightObject() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
//sets the object in the center opacity = 0.2
if(intersects.length > 0) {
intersects[0].object.material.opacity = 0.2;
}
//sets the opacity of the rest of the objects in scene to opacity = 1.0
for (var i = scene.children.length - 1; i >= 0; i--) {
var obj = scene.children[i];
obj.material.opacity = 1.0;
}
}
I've already searched, but didn't find anything that helps:
I got an vector and a CylinderGeometry Mesh. I want to acchieve, that the cylinder is facing the point where the vector is showing. As input I got a position (c) and a direction (m) (like a line equation: y = mx + c):
function draw (m,c, _color) {
//... create the geometry and mesh
// set the position
line.position.x = c.x;
line.position.y = c.y;
line.position.z = c.z;
// i've tried something like this:
line.lookAt(c.add(m));
//.. and add to scene
}
But it looks like the direction is the direct opposite of what I want to acchieve.
I've also tried stuff like translation:
geometry.applyMatrix( new THREE.Matrix4().makeTranslation(0, length/2, 0));
and tried to get the rotation manually like line.rotation.x = direction.angleTo(vec3(1,0,0))* 180 / Math.PI;. But none of them worked like I needed.
This works for me:
// Make the geometry (of "distance" length)
var geometry = new THREE.CylinderGeometry( 0.6, 0.6, distance, 8, 1, true );
// shift it so one end rests on the origin
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, distance / 2, 0 ) );
// rotate it the right way for lookAt to work
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( THREE.Math.degToRad( 90 ) ) );
// Make a mesh with the geometry
var mesh = new THREE.Mesh( geometry, material );
// Position it where we want
mesh.position.copy( from.sceneObject.position );
// And make it point to where we want
mesh.lookAt( to.sceneObject.position );
Is it possible to load multiple textures on a sphere?
I mean to say is there any way in Three.js to split a sphere into n pieces , texture them separately and render those pieces once again as a whole sphere?
I do not want to load the entire texture on the sphere, instead, only those parts are to be rendered which the user will first see on the screen and as the user rotates the sphere the rest part of the texture must be loaded.
Moreover, when I use a single image on a sphere it seems to converge at poles making it worse.
This should help: https://open.bekk.no/procedural-planet-in-webgl-and-three-js
Instead of using a sphere try using a cube and expanding it into a sphere. Cube logic on the cube sphere will save you a good amount of time.
var geometry = new THREE.BoxGeometry( 1, 1, 1, 8, 8, 8 );
for ( var i in geometry.vertices ) {
var vertex = geometry.vertices[ i ];
vertex.normalize().multiplyScalar(radius);
}
var materialArray = [];
var faceMaterial = new THREE.MeshLambertMaterial({
color: sphereColor,
transparent: true,
opacity: 0.4
});
for (var i = 0; i < 6; i++) {
materialArray.push(faceMaterial);
}
var material = new THREE.MeshFaceMaterial(materialArray);
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );