Bystander001
Bystander001

Reputation: 121

Three.js lines normal to a sphere

My goal is to create an interactive Earth that has lines normal to the surface so that you can click on them and it pulls up pictures that my health care team has taken from around the world. I have the world completely coded (or more accurately someone else did it and I made a few small changes).

Below is the code for the Earth which functions as expected. What I want to know is how to make lines normal to the surface and have them be clickable. It would be optimal if the lines faded and disappeared as they went to the back of the earth rotated or the user rotated the earth and the lines on the side the user couldn't see faded.

I thought about making an array of cities and having a location on the sphere be associated with it but I'm not really sure how to do that. I am very new to Three.js and HTML/JS in general.

it may be helpful to know that I am using three.mins.js, Detector.js, and TrackballControl.js

Code so far as follows:

(function () {

var webglEl = document.getElementById('webgl');

if (!Detector.webgl) {
    Detector.addGetWebGLMessage(webglEl);
    return;
}

var width  = window.innerWidth,
    height = window.innerHeight;

// Earth params
var radius   = 0.5,
    segments = 32,
    rotation = 6;  

var scene = new THREE.Scene();

var uniforms, mesh, meshes =[];

var camera = new THREE.PerspectiveCamera(45, width / height, 0.01, 1000);
camera.position.z = 1.5;

var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);

scene.add(new THREE.AmbientLight(0x333333));

var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5,3,5);
scene.add(light);

var sphere = createSphere(radius, segments);
sphere.rotation.y = rotation; 
scene.add(sphere)

var clouds = createClouds(radius, segments);
clouds.rotation.y = rotation;
scene.add(clouds)

var stars = createStars(90, 64);
scene.add(stars);

var controls = new THREE.TrackballControls(camera);

webglEl.appendChild(renderer.domElement);

render();

function render() {
    controls.update();
    sphere.rotation.y += 0.0005;
    clouds.rotation.y += 0.0007;        
    requestAnimationFrame(render);
    renderer.render(scene, camera);
}

function createSphere(radius, segments) {
    return new THREE.Mesh(
        new THREE.SphereGeometry(radius, segments, segments),
        new THREE.MeshPhongMaterial({
            map:         THREE.ImageUtils.loadTexture('images/Color_Map.jpg'),
            bumpMap:     THREE.ImageUtils.loadTexture('images/elev_bump_4k.jpg'),
            bumpScale:   0.005,
            specularMap: THREE.ImageUtils.loadTexture('images/water_4k.png'),
            specular:    new THREE.Color('grey')                                
        })
    );
}

function createClouds(radius, segments) {
    return new THREE.Mesh(
        new THREE.SphereGeometry(radius + 0.003, segments, segments),           
        new THREE.MeshPhongMaterial({
            map:         THREE.ImageUtils.loadTexture('images/fair_clouds_4k.png'),
            transparent: true
        })
    );      
}

function createStars(radius, segments) {
    return new THREE.Mesh(
        new THREE.SphereGeometry(radius, segments, segments), 
        new THREE.MeshBasicMaterial({
            map:  THREE.ImageUtils.loadTexture('images/galaxy_starfield.png'), 
            side: THREE.BackSide
        })
    );
}

}());

The hope is that it would look like this link but with Earth and not a building (http://3d.cl3ver.com/uWfsD?tryitlocation=3) [also click explore when you go there].

Upvotes: 1

Views: 1367

Answers (1)

kdrnic
kdrnic

Reputation: 511

I built a quick demo that most faithfully represents what I think your needs are. It shows some images that seem to be attached to an Earth sphere through lines. It uses sprites to create those images (and the lines themselves, actually). I think it resembles quite well that demo of a building that you linked to. Here is the technique:

  1. Images are added using GIMP to this template and saved as PNGs.
  2. Those images are loaded as textures in the Js apps.
  3. The sprite is created, using the loaded texture.
  4. The sprite is added to a Object3D and its position set to (0,0,radiusOfTheEarthSphere)
  5. The Object3D is added to the sphere, and rotated until the center of the sprite lies in the position in Earth that you want it to rest in.
  6. Each frame, a dot product between a vector from the center of Earth to the camera and a vector from the center of the Earth to each sprite is used to calculate the sprite's opacity.

That equation in 6 is:

opacity = ((|cameraPosition - centerOfEarth| x |spriteCenter - centerOfEarth|) + 1) * 0.5

where "x" is dot product and "||" denotes normalization.

Also note that sprite center is different from its position due to the Object3D used as parent, I calculate its center using the .localToWorld(vec) method.

Please see the demo here: https://33983769c6a202d6064de7bcf6c5ac7f51fd6d9e.googledrive.com/host/0B9scOMN0JFaXSE93YTZRTE5XeDQ/test.html

It is hosted in my Google Drive and it may take some time to load. Three.js will give some errors in the console untill all textures are loaded because I coded it quickly just to show you my implementation ideas.

Upvotes: 1

Related Questions