I am trying to implement my own sphere based on a geospatial dataset.
I import a GeoJSON file that describes a grid which covers the entire earth. The coordinates for each grid cell (a grid cell has more than 3 verticies!) are given as latitude (-90 90) and longitude (-180 180). These polar coordinates are transformed to cartesian coordinates x,y,z and send to the below function:
function triangulate_grid(vertices, color) {
//http://blog.cjgammon.com/threejs-geometry
var holes = [];
var triangles, mesh;
var geometry = new THREE.Geometry();
var color = new THREE.Color(color[0], color[1], color[2]);
//http://jsfiddle.net/RnFqz/22/
var material = new THREE.MeshPhongMaterial({
color: color,
// FlatShading: THREE.FlatShading,
// side: THREE.DoubleSide,
// side: THREE.BackSide,
side: THREE.FrontSide,
wireframe: false,
transparent: false,
vertexColors: THREE.FaceColors, // CHANGED
// overdraw: true
});
var materialIndex = 0; //optional
geometry.vertices = vertices;
triangles = THREE.ShapeUtils.triangulateShape(vertices, holes);
for (var i = 0; i < triangles.length; i++) {
face = new THREE.Face3(triangles[i][0], triangles[i][1], triangles[i][2])
face.color = color;
geometry.faces.push(face);
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
mesh = new THREE.Mesh(geometry, material);
//https://stackoverflow.com/questions/28848863/threejs-how-to-rotate-around-objects-own-center-instead-of-world-center
mesh.rotation.x = -90;
scene.add(mesh)
}
The sphere that is drawn looks like this:
It would be easier to see if you could rotate it yourself. But what is happening is, that I see the surface of the faces of the northern hemisphere as an astronaut would see it (from above), while the faces of the southern hemisphere can only be seen by looking through the earth. It looks like the equator is 'flipping' the faces with latitude coordinates 0 to -90 to only show the backside.
I am using this function to make the coordinate transformation.
//https://bl.ocks.org/mbostock/2b85250396c17a79155302f91ec21224
function vertex(point) {
var lambda = (point[0]) * Math.PI / 180, // lon
phi = (point[1]) * Math.PI / 180, //lat
cosPhi = Math.cos(phi);
var radius = 1;
vector = new THREE.Vector3(
radius * cosPhi * Math.cos(lambda),
radius * cosPhi * Math.sin(lambda),
radius * Math.sin(phi)
);
// vector.applyAxisAngle( 0, 90 );
return vector
}
I have used THREE.DoubleSide, but that solves the problem only partially. First, I think it is not necessary and only takes ressources and second, there are still artifacts at the equator. See the upper left corner of the image.
Question:
How to draw all faces on the nothern and southern hemisphere, so I can see the surface and not look through?
Update:
Added a cube to make it more clear.
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 have a particle cloud I'm making in threejs with buffer geometry, and I am connecting those particles via THREE.LineSegments with a THREE.LineBasicMaterial:
As you can (kind of) see, some of the lines are black or gray - I want to make it so the lines are transparent shades of white.
Here is, I believe, the relevant lines of code:
var material = new THREE.LineBasicMaterial({
vertexColors: THREE.VertexColors,
blending: THREE.AdditiveBlending,
color: 0xffffff,
transparent: true,
});
var colors = new Float32Array(segments * 3);
geometry.addAttribute(
"color",
new THREE.BufferAttribute(colors, 3).setDynamic(true)
); //will allow us to set the color of the lines between particles in buffer geometry
linesMesh = new THREE.LineSegments(geometry, material);
...
animate(){
for (var i = 0; i < particleCount; i++) { //loop through particles to make line connections
for (var j = i + 1; j < particleCount; j++) { //check collision
var dist = Math.sqrt(dx * dx + dy * dy + dz * dz); //getting particle positions to neighbors
if (dist < effectController.minDistance) { //make a connection
var alpha = 1.0 - dist / effectController.minDistance;
colors[colorpos++] = alpha;
}
}
}
}
Simple Answer
The default three.js shader implements vertex colors as a vec3 (RGB), so there is no transparency component.
Search for USE_COLOR in https://github.com/mrdoob/three.js/blob/r118/src/renderers/webgl/WebGLProgram.js (r118) to see this definition.
Advanced Answer
You could write your own shader to accept a color attribute as a vec4 instead, adding a transparency component to each color. You will need to look further into how a line-type material uses colors, and how it blends vertex colors along a line. Or, because it's your shader, make it do the blending however you want.
I am working on a project that want to move small objects and show them in a 360 image using ThreeJS library. So I am using Spherical coordinate system in a sphere with some radius to move the objects. User starts seeing the app in the center of the sphere. I want to show some grid helper lines on the sphere ( very similar to longitude and latitude lines). I found the following code from here in the library:
var radius = 10;
var radials = 16;
var circles = 8;
var divisions = 64;
var helper = new THREE.PolarGridHelper( radius, radials, circles, divisions );
scene.add( helper );
But it only adds a polar plate with some circles not a sphere shape grid helper to the scene.
PolarGridHelper is a flat circle. If you want a spherical geometry just use SphereBufferGeometry and give it a wireframe look:
var radius = 10;
var latSegments = 18; // 10° increments
var longSegments = 36; // 10° increments
var geometry = new THREE.SphereBufferGeometry( radius, longSegments, latSegments);
var material = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: true
});
var sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
I'm aiming to map a rectangular texture onto a curved surface. The curved section of my custom, extruded, shape, geometry consists of 599 Faces (Face4) out of a total of 1800 faces. I'm trying to split my texture across these 599 curved faces.
Image 1: The black face of my custom geometry is the curved section I'm aiming to map a texture onto
Image 2: A rough example of what I'm trying to do
Image 3: Each face is a vertical standing rectangle: faces[ i ].color.setHex( Math.random() * 0xffffff );
Trying to get the UV coordinates of the selected faces (each face is demonstrated by image 3) and aiming to map my texture, by splitting it up, across these curved faces. Here is what I got so far:
var image = 'resources/customRectangle.png';
var texture = THREE.ImageUtils.loadTexture( image );
texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;;
var settings = {
amount: 14.5,
steps : 1,
bevelEnabled: false,
curveSegments: 300,
material: 0,
extrudeMaterial: 1
};
var shape = new THREE.Shape();
shape.moveTo( 90, 120 );
shape.absarc( -30, -30, 200, 0, Math.PI * 0.27, false );
shape.moveTo( 160, -40 );
shape.moveTo( 90, 120 );
var geometry = new THREE.ExtrudeGeometry( shape, settings );
geometry.computeBoundingBox();
var max = geometry.boundingBox.max;
var min = geometry.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;
geometry.faceVertexUvs[0] = [];
for ( var i = 0; i < faces.length; i ++ ) {
var face4 = faces[i].d;
if( face4 > 0 && i < 1800 && i > 1200 ) { // Selects curved faces
faces[ i ].color.setHex( 0x555555 );
var v1 = geometry.vertices[faces[i].a];
var v2 = geometry.vertices[faces[i].b];
var v3 = geometry.vertices[faces[i].c];
geometry.faceVertexUvs[0].push([
new THREE.Vector2((v1.x + offset.x)/range.x,(v1.y + offset.y)/range.y),
new THREE.Vector2((v2.x + offset.x)/range.x,(v2.y +offset.y)/range.y),
new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y)
]);
};
};
geometry.uvsNeedUpdate = true;
var material = new THREE.MeshLambertMaterial( { map: texture } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = x - 46;
mesh.position.y = y;
mesh.position.z = z + 20;
mesh.rotation.set( 1.5, 0.045, 0.59);
object3D.add(mesh); `
It's my first time working with UV coordinates so im not sure im creating them correctly, as well not knowing how to use the UV coordinates to map a texture, across multiple faces. Is there anyway to split a texture into multiple divisions? (shown in image 3). To formulate a result like image 2?.
I have been stuck on this for a while, searched the internet deeply and had to resort to professional assistance. I greatly appreciate any help in advance! :) Kind regards, Leon.
I am trying to draw a circle very similar to the orbital patterns on this website. I would like to use Three.js instead of pure WebGL.
Three.js r50 added CircleGeometry. It can be seen (albeit with a face) in the WebGL Geometries example.
The first vertex in the geometry is created at the center of the circle (in r84, see CircleGeometry.js line 71, in r65, see CircleGeometry.js line 18), which is nifty if you are going for that "full Pac-Man" or "uninformative pie chart" look. Oh, and it appears to be necessary if you are going to use any material aside from LineBasicMaterial / LineDashedMaterial.
I've verified that the following code works in both r60 & r65:
var radius = 100,
segments = 64,
material = new THREE.LineBasicMaterial( { color: 0x0000ff } ),
geometry = new THREE.CircleGeometry( radius, segments );
// Remove center vertex
geometry.vertices.shift();
// Non closed circle with one open segment:
scene.add( new THREE.Line( geometry, material ) );
// To get a closed circle use LineLoop instead (see also #jackrugile his comment):
scene.add( new THREE.LineLoop( geometry, material ) );
PS: The "docs" now include a nice CircleGeometry interactive example: https://threejs.org/docs/#api/geometries/CircleGeometry
The API changed slightly in newer versions of threejs.
var segmentCount = 32,
radius = 100,
geometry = new THREE.Geometry(),
material = new THREE.LineBasicMaterial({ color: 0xFFFFFF });
for (var i = 0; i <= segmentCount; i++) {
var theta = (i / segmentCount) * Math.PI * 2;
geometry.vertices.push(
new THREE.Vector3(
Math.cos(theta) * radius,
Math.sin(theta) * radius,
0));
}
scene.add(new THREE.Line(geometry, material));
Modify segmentCount to make the circle smoother or more jagged as needed by your scene. 32 segments will be quite smooth for small circles. For orbits such as those on the site you link you, you may want to have a few hundred.
Modify the order of the three components within the Vector3 constructor to choose the orientation of the circle. As given here, the circle will be aligned to the x/y plane.
I used code that Mr.doob references in this github post.
var resolution = 100;
var amplitude = 100;
var size = 360 / resolution;
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial( { color: 0xFFFFFF, opacity: 1.0} );
for(var i = 0; i <= resolution; i++) {
var segment = ( i * size ) * Math.PI / 180;
geometry.vertices.push( new THREE.Vertex( new THREE.Vector3( Math.cos( segment ) * amplitude, 0, Math.sin( segment ) * amplitude ) ) );
}
var line = new THREE.Line( geometry, material );
scene.add(line);
This example is in the Three.js documentation:
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
var radius = 5;
var segments = 32; //<-- Increase or decrease for more resolution I guess
var circleGeometry = new THREE.CircleGeometry( radius, segments );
var circle = new THREE.Mesh( circleGeometry, material );
scene.add( circle );
I had to do this lol:
function createCircle() {
let circleGeometry = new THREE.CircleGeometry(1.0, 30.0);
circleGeometry.vertices.splice(0, 1); //<= This.
return new THREE.LineLoop(circleGeometry,
new THREE.LineBasicMaterial({ color: 'blue' }));
}
let circle = createCircle();
Reason: Otherwise, it doesn't draw a "pure" circle, there's a line coming from the center to the rim of the circle, even if you use LineLoop instead of Line. Splicing (removing) the first vertex from the array is a hack but seems to do the trick. :)
(Note that apparently, according to mrienstra's answer, "Oh, and it appears to be necessary if you are going to use any material aside from LineBasicMaterial / LineDashedMaterial.")
If you want thickness, though, you're screwed ("Due to limitations of the OpenGL Core Profile with the WebGL renderer on most platforms linewidth will always be 1 regardless of the set value.")... Unless you use: https://github.com/spite/THREE.MeshLine
Code example for that is here: https://stackoverflow.com/a/61312721/1599699
Well, I dunno when they added it - but TorusGeometry should do the job...
THREE TorusGeometry
const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const torus = new THREE.Mesh( geometry, material );
scene.add( torus );
Dunno, but I think it shouldn't be (much) more expensive than the line thingy and it's a buffer geometry and you may adjust size and material etc...
See the three.js sample http://mrdoob.github.com/three.js/examples/webgl_lines_colors.html to see how to draw colored lines.
A circle like the ones you cite is drawn as a large # of little straight segments. (Actually, the ones you show may be ellipses)
var getStuffDashCircle2 = function () {
var segment = 100, radius = 100;
var lineGeometry = new THREE.Geometry();
var vertArray = lineGeometry.vertices;
var angle = 2 * Math.PI / segment;
for (var i = 0; i < segment; i++) {
var x = radius * Math.cos(angle * i);
var y = radius * Math.sin(angle * i);
vertArray.push(new THREE.Vector3(x, y, 0));
}
lineGeometry.computeLineDistances();
var lineMaterial = new THREE.LineDashedMaterial({ color: 0x00cc00, dashSize: 4, gapSize: 2 });
var circle = new THREE.Line(lineGeometry, lineMaterial);
circle.rotation.x = Math.PI / 2;
circle.position.y = cylinderParam.trackHeight+20;
return circle;
}
I had some issues getting the other answers to work here -- in particular, CircleGeometry had an extra point at the center of the circle, and I didn't like the hack of trying to remove that vertex.
EllipseCurve does what I wanted (verified in r135):
const curve = new THREE.EllipseCurve(
0.0, 0.0, // Center x, y
10.0, 10.0, // x radius, y radius
0.0, 2.0 * Math.PI, // Start angle, stop angle
);
const pts = curve.getSpacedPoints(256);
const geo = new THREE.BufferGeometry().setFromPoints(pts);
const mat = new THREE.LineBasicMaterial({ color: 0xFF00FF });
const circle = new THREE.LineLoop(geo, mat);
scene.add(circle);