Search code examples
three.jswebglparticle-system

Particles follow texture


I have a sphere created with particles in three.js that works perfectly. Now I wanted to put these particles on top of a texture that I have of a world map simulating a 3D planet, I searched the internet but I did not find any information on how to do it, when I put the texture instead of it being outside it ends up getting inside each particle, how could I do that? Any idea ? Thank you all

here is my code

$( document ).ready(function() {

var globe = document.getElementById('globe')    
var Maxwidth = window.innerWidth
var Maxheight = window.innerHeight


var scene = new THREE.Scene();

var renderer = new THREE.WebGLRenderer({antilias:true});

renderer.setPixelRatio( window.devicePixelRatio );

renderer.setSize(Maxwidth,Maxheight)

globe.appendChild(renderer.domElement)


var camera = new THREE.PerspectiveCamera(60, Maxwidth / Maxheight,1,1000);

camera.position.z = 50;

var controls = new THREE.OrbitControls( camera, renderer.domElement );

controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled

controls.dampingFactor = 0.25;

controls.panningMode = THREE.HorizontalPanning; // default is 
THREE.ScreenSpacePanning


controls.maxPolarAngle = Math.PI / 2;

var geometry = new THREE.SphereGeometry( 200, 42, 42 );

geometry.widthSegments = 42;

var colors = [];

for( var i = 0; i < geometry.vertices.length; i++ ) {

    // random color
    colors[i] = new THREE.Color();


    //colors[i].setHSV( Math.random(), 1.0, 1.0 );

}
geometry.colors = colors;

// texture
var texture = new THREE.Texture( generateTexture( ) );
texture.needsUpdate = true; // important

// particle system material
var material = new THREE.ParticleBasicMaterial( {
    size: 5,
    map: texture,
    blending: THREE.AdditiveBlending, // required
    depthTest: false, // required
    transparent: true,
    opacity: 0.7,
    vertexColors: true // optional
} );

material.map = THREE.ImageUtils.loadTexture('../img/point_picker.png')

material.anisotropy = 0;
material.magFilter = THREE.NearestFilter;
material.minFilter = THREE.NearestFilter;

var union = new THREE.ParticleSystem( geometry, material );

function generateTexture( ) {

var size = 128;

var canvas = document.createElement( 'canvas' );
canvas.width = size;
canvas.height = size;

var context = canvas.getContext( '2d' );

var centerX = size / 2;
var centerY = size / 2;
var radius = size / 2;

context.beginPath();
context.arc( centerX, centerY, radius, 0, 2 * Math.PI, false );
context.fillStyle = "#FFFFFF";
context.fill();

return canvas;

}

scene.add(union)

renderer.setClearColor(0x2675AD)

renderer.render(scene,camera)

controls.update();

function render(delta){


 requestAnimationFrame(render);
 renderer.render(scene,camera)

 union.rotation.y  += 0.0009


 }

 render()



 });

I need something like this

enter image description here


Solution

  • So, this is the option I was talking about in my comment:

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(1.25, 7, 7);
    camera.lookAt(scene.position);
    var renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setClearColor(0x080808);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    var geom = new THREE.SphereBufferGeometry(5, 120, 60);
    var colors = [];
    var color = new THREE.Color();
    var q = 0xffffff * 0.25;
    for (let i = 0; i < geom.attributes.position.count; i++) {
      color.set(Math.random() * q + q * 3);
      color.toArray(colors, i * 3);
    }
    geom.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
    
    var loader = new THREE.TextureLoader();
    loader.setCrossOrigin('');
    var texture = loader.load('https://learningthreejs.com/data/2013-09-16-how-to-make-the-earth-in-webgl/demo/bower_components/threex.planets/images/earthspec1k.jpg');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.repeat.set(1, 1);
    var disk = loader.load('https://threejs.org/examples/textures/sprites/circle.png');
    
    var points = new THREE.Points(geom, new THREE.ShaderMaterial({
      vertexColors: THREE.VertexColors,
      uniforms: {
        visibility: {
          value: texture
        },
        shift: {
          value: 0
        },
        shape: {
          value: disk
        },
        size: {
          value: 0.125
        },
        scale: {
          value: window.innerHeight / 2
        }
      },
      vertexShader: `
                    
          uniform float scale;
          uniform float size;
          
          varying vec2 vUv;
          varying vec3 vColor;
          
          void main() {
          
            vUv = uv;
            vColor = color;
            vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
            gl_PointSize = size * ( scale / length( mvPosition.xyz ) );
            gl_Position = projectionMatrix * mvPosition;
    
          }
      `,
      fragmentShader: `
          uniform sampler2D visibility;
          uniform float shift;
          uniform sampler2D shape;
          
          varying vec2 vUv;
          varying vec3 vColor;
          
    
          void main() {
            
            vec2 uv = vUv;
            uv.x += shift;
            vec4 v = texture2D(visibility, uv);
            if (length(v.rgb) > 1.0) discard;
    
            gl_FragColor = vec4( vColor, 1.0 );
            vec4 shapeData = texture2D( shape, gl_PointCoord );
            if (shapeData.a < 0.5) discard;
            gl_FragColor = gl_FragColor * shapeData;
            
          }
      `,
      transparent: true
    }));
    scene.add(points);
    
    var blackGlobe = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({
      color: 0x000000
    }));
    blackGlobe.scale.setScalar(0.99);
    points.add(blackGlobe);
    
    var clock = new THREE.Clock();
    var time = 0;
    
    render();
    
    function render() {
      requestAnimationFrame(render);
      time += clock.getDelta();
      points.material.uniforms.shift.value = time * 0.1;
      renderer.render(scene, camera);
    }
    body {
      overflow: hidden;
      margin: 0;
    }
    <script src="https://cdn.jsdelivr.net/npm/three@0.91.0/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.91.0/examples/js/controls/OrbitControls.js"></script>