THREE.JS: отталкивание частиц по всем осям [XYZ] - PullRequest
1 голос
/ 30 мая 2019

var blob = "data:image/png;base64,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";

var parentDiv = "logo";
var renderer, scene, camera, controls, origins, helper;
var mouse = new THREE.Vector2(0.0, 0.0);

var OBJECT_SCALE = 128.0;
var OBJECT_SUBDIVISION = 2;
var PARTICLE_SIZE = 16.0;

var particles = new THREE.Geometry();

var loader = new THREE.OBJLoader();

loader.load(

	"https://raw.githubusercontent.com/OpenGLInsights/OpenGLInsightsCode/master/Chapter%2026%20Indexing%20Multiple%20Vertex%20Arrays/article/suzanne.obj",
    
	function ( object_ ) {

        var vertices = [];
        
        //subdivider
        var geometry = new THREE.Geometry().fromBufferGeometry( object_.children[0].geometry );
        var modifier = new THREE.SubdivisionModifier(OBJECT_SUBDIVISION);
        geometry = modifier.modify(geometry);
        
        //scale
        geometry.scale(OBJECT_SCALE, OBJECT_SCALE, OBJECT_SCALE);
        
        inits(geometry.vertices);

	}
);

function inits(vertices_){
    
    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize( window.innerWidth, window.innerHeight );
    var div = document.getElementById(parentDiv);
    div.appendChild( renderer.domElement );

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );

    camera.position.y = -450;
    camera.position.z = 450;

    var light = new THREE.AmbientLight( 0xFFFFFF, 1 );
    scene.add(light);

    controls = new THREE.OrbitControls( camera );
    controls.update();

    //helper
    var geometry = new THREE.SphereGeometry( 5, 32, 32 );
    var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
    helper = new THREE.Mesh( geometry, material );
    scene.add( helper );
    
    var vertices = vertices_;
    var positions = new Float32Array( vertices.length * 3 );
    var colors = new Float32Array( vertices.length * 3 );
    var sizes = new Float32Array( vertices.length );
    var vertex;
    var color = new THREE.Color();
    for ( var i = 0, l = vertices.length; i < l; i ++ ) {
        
        vertex = vertices[ i ];
        vertex.toArray( positions, i * 3 );
        color.setHSL( 0.01 + 0.1 * ( i / l ), 1.0, 0.5 );
        color.toArray( colors, i * 3 );
        sizes[ i ] = PARTICLE_SIZE * 0.5;
        
    }
    
    var geometry = new THREE.BufferGeometry();
    geometry.addAttribute( "position", new THREE.BufferAttribute( positions, 3 ) );
    geometry.addAttribute( "customColor", new THREE.BufferAttribute( colors, 3 ) );
    geometry.addAttribute( "size", new THREE.BufferAttribute( sizes, 1 ) );

    var material = new THREE.ShaderMaterial( {
        
    uniforms: {
        
        color: { value: new THREE.Color( 0xFFFFFF ) },
        texture: { value: new THREE.TextureLoader().load( blob ) }
        
    },
        
    vertexShader: document.getElementById( "vertexshader" ).textContent,
    fragmentShader: document.getElementById( "fragmentshader" ).textContent,
    alphaTest: 0.9
        
    } );
 
    particles = new THREE.Points( geometry, material );
    particles.geometry.dynamic = true;
    scene.add(particles);

    window.addEventListener("resize", resizeWindow, false)
    renderer.domElement.addEventListener("mousemove", onMouseMove, false);
    
    origins = particles.geometry.attributes.position.array.slice(0);
    
    animate();

}

function animate() {

    controls.update();
    
	window.requestAnimationFrame( animate );
	renderer.render( scene, camera );
    
}

function onMouseMove(event) {
            
    var threshold = 32.0;

    camera.clearViewOffset();

    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    //mouse screen to world
      vector = new THREE.Vector3(),
      vector.set(mouse.x, mouse.y, 0.5);
      vector.unproject(camera);
  
    var direction = vector.sub(camera.position).normalize(),
        distance = - camera.position.z / direction.z,
        scaled = direction.multiplyScalar(distance),
        mouseXYZ = camera.position.clone().add(scaled);

    for(var i = 0, l = particles.geometry.attributes.position.count; i < l; i++){
        
        var x = particles.geometry.attributes.position.array[i * 3];
        var y = particles.geometry.attributes.position.array[i * 3 + 1];
        var z = particles.geometry.attributes.position.array[i * 3 + 2];
        
        x += ((Math.cos(angleTo(mouseXYZ, new THREE.Vector3(x, y, z))) * (Math.pow(threshold, 2.0) / mouseXYZ.distanceTo(new THREE.Vector3(x, y, 0.0)))) + (origins[i * 3] - x)) * 0.1;
        
        //y += ((Math.sin(angleTo(mouseXYZ, new THREE.Vector3(x, y, z))) * (Math.pow(threshold, 2.0) / mouseXYZ.distanceTo(new THREE.Vector3(x, y, 0.0)))) + (origins[i * 3 + 1] - y)) * 0.1;
        
        z += ((Math.sin(angleTo(mouseXYZ, new THREE.Vector3(x, y, z))) * (Math.pow(threshold, 2.0) / mouseXYZ.distanceTo(new THREE.Vector3(x, y, 0.0)))) + (origins[i * 3 + 2] - z)) * 0.1;
        
        particles.geometry.attributes.position.array[i * 3] = x;
        //particles.geometry.attributes.position.array[i * 3 + 1] = y;
        particles.geometry.attributes.position.array[i * 3 + 2] = z;
        
    }
    
    helper.position.set(mouseXYZ.x, mouseXYZ.y, 0.0);
    
    particles.geometry.attributes.position.needsUpdate = true;

}

function angleTo(a_, b_){ return Math.atan2(b_.y - a_.y, b_.x - a_.x); }
function distance(a_, b_){ return a_.distanceTo(b_); }

function resizeWindow(){
    
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );
    
}

function screenToWorld(_screenPos)
{
    var worldPos = _screenPos.clone();
    worldPos.x = worldPos.x / window.innerWidth/2 - 1;
    worldPos.y = - worldPos.y / window.innerWidth/2 + 1;
    worldPos.unproject( camera );
    return worldPos;                    
}
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>
<script src="https://unpkg.com/three@0.104.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://unpkg.com/three@0.104.0/examples/js/loaders/OBJLoader.js"></script>
<script src="https://unpkg.com/three@0.104.0/examples/js/modifiers/SubdivisionModifier.js"></script>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html lang="en"> 
<head>

<meta http-equiv="content-type" content="text/html; charset=utf-8"> 
<title>BlockPros Logo 3D Demo β</title> 
    
<style> body { margin: 0px; } </style>
    
</head> 
    
<script type="x-shader/x-vertex" id="vertexshader">

    attribute float size;
    attribute vec3 customColor;
    varying vec3 vColor;
    
    void main() {
    
        vColor = customColor;
        vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
        gl_PointSize = size * ( 300.0 / -mvPosition.z );
        gl_Position = projectionMatrix * mvPosition;
        
    }
    
</script>

<script type="x-shader/x-fragment" id="fragmentshader">

    uniform vec3 color;
    uniform sampler2D texture;
    varying vec3 vColor;
    
    void main() {
    
        gl_FragColor = vec4( color * vColor, 1.0 );
        gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
        if ( gl_FragColor.a < ALPHATEST ) discard;
        
    }
    
</script>
    
<body> 

<div id="logo"></div>

    
</body> 
</html>

Существует модель, загруженная через OBJLoader и отображаемая как точки [THREE.Points].

Я пытаюсь достичь визуальной эстетики, подобной этому простому 2D-примеру https://youtu.be/vIxX23ozQ7c

Теперь у меня есть это https://youtu.be/ZTxi3iyiVSY

Это работает нормально, когдамодель была рассмотрена сверху, но глючно с других сторон.

Я уверен, что мне нужно настроить этот код:

x += ((Math.cos(angleTo(mouseXYZ, new THREE.Vector3(x, y, z))) * (Math.pow(threshold, 2.0) / mouseXYZ.distanceTo(new THREE.Vector3(x, y, 0.0)))) + (origins[i * 3] - x)) * 0.1;

y += ((Math.sin(angleTo(mouseXYZ, new THREE.Vector3(x, y, z))) * (Math.pow(threshold, 2.0) / mouseXYZ.distanceTo(new THREE.Vector3(x, y, 0.0)))) + (origins[i * 3 + 1] - y)) * 0.1;

//z += ((Math.sin(angleTo(mouseXYZ, new THREE.Vector3(x, y, z))) * (Math.pow(threshold, 2.0) / mouseXYZ.distanceTo(new THREE.Vector3(x, y, 0.0)))) + (origins[i * 3 + 2] - z)) * 0.1;

И попытаться использовать углы фи и тета от полярныхкоординаты, но еще не нашли правильного решения.

Есть предложения?

...