Making a 3d Earth, made up of particles with Three.js - javascript

I am trying to replicate this effect: http://www.hys-inc.jp/ The only difference is that I want the particles to be positioned in such a way, that they resemble the Earth - a 'textured face' if you will.
Browsing through their code, this is what they use to set up the particles group:
var map = THREE.ImageUtils.loadTexture('/admin/wp-content/themes/hys/assets/img/particle.png');
var m = new THREE.ParticleSystemMaterial({
color: 0x000000,
size: 1.5,
map: map,
//blending: THREE.AdditiveBlending,
depthTest: false,
transparent: true
});
var p = new THREE.ParticleSystem(g, m);
scene.add(p);
This is all great, but how do I position them along a sphere to resemble the planet? I know how to do it in 2d rendering context, using a picture and pixels scanning to get the right coordinates for the particles' position, but I am clueless how to do it in 3d...
Any help is more then welcome

If you have a grid of pixels that represents color values showing the surface of the earth in 2 dimensions as particles, to project it to 3 dimensions requires a sphere projection method. You can take a look at this question for the implementation of mercator projection.
how map 2d grid points (x,y) onto sphere as 3d points (x,y,z)
There are many methods for accomplishing this with a good deal of variation. Stereographic is another common approach : http://en.wikipedia.org/wiki/List_of_map_projections

Related

Threejs - Maintain aspect ratio while adding texture map to threejs object

We want to create a 3d shoe designing tool, where you can design patterns and upload them to the shoe.
I am trying to place an image on a Threejs material. I am able to update the map, but the texture is blurry. I am new to Threejs, so I do not have concepts clear. I don't understand if aspect ratio is the issue or something else.
This how I am loading texture:
var texture_loader = new THREE.TextureLoader();
var texture = texture_loader.load( 'https://ik.imagekit.io/toesmith/pexels-photo-414612_D4wydSedY.jpg', function ( texture ) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.offset.set( 0, 0 );
texture.repeat.set( 1, 1 );
vamp.material = new THREE.MeshPhongMaterial({
map: texture,
color: new THREE.Color('#f2f2f2'),
shininess: 20,
});
});
This is what I am getting
But the expected behavior should be
If anyone could help, that would be great. Thanks
Here is the link to the Codepen code
The problem is that your UVs are occupying a very small area in texture coordinates. As they are now, it looks like your UVs are taking up this much room (see red area):
And that's why it gives the impression that your texture is blurry. What you need to do is make your UVs take up more space, like this:
There are 2 ways to achieve this.
Scale UVs up: Import your model into Blender, and change the UV mapping of the mesh to occupy more of the [0, 1] range.
Scale texture down: You could get creative with the texture.repeat property and use it to scale down your texture to match your existing UVs. Then you'd need to offset it so it's centered correctly. Something like:
texture.repeat = new THREE.Vector2(10, 10);
texture.offset = new THREE.Vector2(xx, yy);

Three.JS: render a large map based on different tilesets (Texture Atlas)

Introduction:
I render an isometric map with Three.JS (v95, WebGL Renderer). The map includes many different graphic tilesets. I get the specific tile via a TextureAtlasLoader and it’s position from a JSON. It looks like this:
The problem is that it performs really slow the more tiles I render (I need to render about 120’000 tiles on one map). I can barely move the camera then. I know there are several better approaches than adding every single tile as sprite to the scene. But I’m stuck somehow.
Current extract from the code to create the tiles (it’s in a loop):
var ts_tile = Map.Imagesets[ims].Map.getTexture((bg_left / tw), (bg_top / th));
var material = new THREE.SpriteMaterial({ map: ts_tile, color: 0xffffff, fog: false });
var sprite = new THREE.Sprite(material);
sprite.position.set(pos_left, -top, 0);
sprite.scale.set(tw, th, 1);
scene.add(sprite)
I also tried to render it as a Mesh, which also works, but the performance is the same (of course):
var material = new THREE.MeshBasicMaterial({ map: ts_tile, color: 0xffffff, transparent: true, depthWrite: false });
var geo = new THREE.PlaneGeometry(1, 1, 1);
var sprite = new THREE.Mesh(new THREE.BufferGeometry().fromGeometry(geo), material);
possible solutions in the web:
I know that I can’t add so many sprites or meshes to a scene and I have tried different things and looked at examples, where it works flawless, but I can’t adapt their approaches to my code. Every tile on my map has a different texture and has it’s own position.
There is an example in the official three.js docs: They work with PointsMaterial and Points. In the end they only add 5 Points to the scene, which includes about 10000 “vertices / Images”. docs: https://threejs.org/examples/#webgl_points_sprites
Another approach can be found here on github: https://github.com/YaleDHLab/pix-plot
They create 5 meshes, every mesh includes around 4096 “tiles”, which they build up with Faces, Vertices, etc.
Final question:
My question is, how can I render my map more performant? I’m simply overchallenged by changing my code into one of the possible solutions.
I think Sergiu Paraschiv is on the right track. Try to split your rendering into chunks. This strategy and others are outlined here: Tilemap Performance. Depending on how dynamic your terrain is, these chunks could be bigger or smaller. This way you only have to re-render chunks that have changed. Assuming your terrain doesn't change, you can render the whole terrain to a texture and then you only have to render a single texture per frame, rather than a huge array of them. Take a look at this tutorial on rendering to a texture, it should give you an idea on where to start with rendering your chunks.

How to use Leaflet flyTo() with unproject() and GeoJSON data on a large raster image?

I'm building a story map with Leaflet using a large image sliced into tiles rather than 'real world' map data. I'm using this plugin: https://commenthol.github.io/leaflet-rastercoords/ and this repo: https://github.com/jackdougherty/leaflet-storymap
Loading my GeoJSON data and unprojecting the coordinates correctly plots them on my image map:
$.getJSON('map.geojson', function(data) {
var geojson = L.geoJson(data, {
// correctly map the geojson coordinates on the image
coordsToLatLng: function (coords) {
return rc.unproject(coords)
},
But when I get to onEachFeature, I hit the wall with map.flyTo(), which is calling geometry.coordinates from my JSON file, but not unprojecting them so flyTo() is interpreting them as geospatial coordinates way off the map:
map.flyTo([feature.geometry.coordinates[1], feature.geometry.coordinates[0] ], feature.properties['zoom']);
I tried passing the unprojected coordinates to variables and then to map.flyTo() and variations on nesting functions, such as map.flyTo.unproject(..., but no luck.
How do I pass my raster coordinates to flyTo()?
I'm not only new to Leaflet, but new to JavaScript. I hacked my way this far, but I'm stumped. I'm sure the solution is obvious. Any help is greatly appreciated.
In your case you would probably just need to use rc.unproject to convert your coordinates into LatLng that you can pass to flyTo:
map.flyTo(
rc.unproject(feature.geometry.coordinates),
feature.properties['zoom']
);
That being said, I must admit I do not exactly see the point of using leaflet-rastercoords plugin, since you can easily do the same by following the Leaflet tutorial "Non-geographical maps".
var yx = L.latLng;
var xy = function(x, y) {
if (L.Util.isArray(x)) { // When doing xy([x, y]);
return yx(x[1], x[0]);
}
return yx(y, x); // When doing xy(x, y);
};
With this, whenever you want to convert your "raster" coordinates into something usable by Leaflet, you would just use xy(x, y) with x being your horizontal value, and y your vertical one.
The added benefit is that many other things will become easily compatible.
Since you use tiles instead of a single image (that is stretched with ImageOverlay in the tutorial in order to fit the coordinates), you should modify the CRS transformation, so that at zoom 0, your tile 0/0/0 fits your entire coordinates. See also Leaflet custom coordinates on image
I.e. in the case of leaflet-rastercoords example:
Original raster image size: 3831 px width x 3101 px height
Tiles size: 256 x 256 px
Vertical "raster" coordinates are increasing while going down (whereas in the Leaflet tutorial, they increase going up, like latitude).
Tile 0/0/0 actually covers more than the original image, as if the latter were 4096 x 4096 px (the rest is filled with white)
Determination of the new CRS:
Tile 0/0/0 should cover coordinates from top-left [0, 0] to bottom-right [4096, 4096] (i.e. 256 * 2^4 = 256 * 16 = 4096) => transformation coefficients a and c should be 1/16
No offset needed => offsets b and d are 0
No reversion of y vertical coordinate => c is positive
Therefore the new CRS to be used would be:
L.CRS.MySimple = L.extend({}, L.CRS.Simple, {
// coefficients: a b c d
transformation: new L.Transformation(1 / 16, 0, 1 / 16, 0)
});
Now your flyTo is very similar, but many other things are also compatible:
map.flyTo(
xy(feature.geometry.coordinates),
feature.properties['zoom']
);
Demo adapted from leaflet-rastercoords example, and using an extra plugin to demonstrate compatibility: https://plnkr.co/edit/Gvei5I0S9yEo6fCYXPuy?p=preview

Graphing 2d plane in 3d space using equation and/or vectors

I'm trying to make a linear regression plane visualization tool for a math project. Currently I have the math parts completed, but I am not sure how to graph the plane. I have a equation in the form of z=C+xD+yE, where C, D, and E are known constants. How do I graph the plane using these information? Thanks.
github page: https://saxocellphone.github.io/LAProject/
z=C+xD+yE
This equation gives full information about the plane. What else you need to graph (plot, draw?) it? Probably it depends on your graphic software possibilities.
Canonical form of given equation:
xD + yE - z + C = 0
Normal to the plane is (D, E, -1). Distance to the coordinate origin Abs(C)/Sqrt(D^2+E^2+1).
Plane intersects coordinate axes at values (-C/D), (-C/E), (C)
I see your problem is not with math, but with three,
as WestLangley pointed out in his comment you can play with rotations etc. or create a simple triangle which is the easiest way
since you have your equation for the plane create 3 points to form a triangle
// z=C+xD+yE
// i assume here that the plane is not aligned with any axis
// and does not pass through the origin, otherwise choose the points in another way
var point1 = new THREE.Vector3(-C/D,0,0);//x axis intersection
var point2 = new THREE.Vector3(0,-C/E,0);//y axis intersection
var point3 = new THREE.Vector3(0,0,C);//z axis intersection
now form a new geometry as in How to make a custom triangle in three.js
var geom = new THREE.Geometry();
geom.vertices.push(point1);// adding vertices to geometry
geom.vertices.push(point2);
geom.vertices.push(point3);
// telling geometry that vertices 0,1,2 form a face = triangle
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
create a simple material and add it to a scene
var material = new THREE.MeshBasicMaterial({
color: 0xff0000, // RGB hex color for material
side: THREE.DoubleSide // do not hide object when viewing from back
});
scene.add(new THREE.Mesh(geometry,material));
that should get you going, you can add another triangles, or make it larger with choosing points that are further apart

How to get rid of tile outlines when tiling textures to a mesh?

I'm creating a sphere and attaching images to each face of the sphere. In my code I have sphere 12 sections by 6 sections high. I've managed to tile the textures by setting the wrap to repeating and setting the repeat size like so:
var texture = new THREE.ImageUtils.loadTexture( path );
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( geo_width, geo_height );
return new THREE.MeshBasicMaterial({ map: texture, side: THREE.BackSide, overdraw: true });
It works but now I have these lines between each texture. Is there a way to get rid of them or is there another technique for face-tiling that I should be using?
The lines are the opposite edge of each texture tile appearing at the edges of the geometry. This is what repeating textures do, which is not appropriate in this case.
You don't say what happens when you don't use repeating, but it sounds like what you need to do is adjust the texture coordinate generation so that the coordinates are 0...1 on each tile rather than only on the “0th tile” of the entire sphere.
I don't know Three.js so I can't advise you specifically on its API, sorry.

Categories