具有连接线的Threejs粒子系统。编程逻辑? [英] Threejs Particle System with joining lines. Programming logic?

查看:117
本文介绍了具有连接线的Threejs粒子系统。编程逻辑?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基于我最近发布的上一个问题:
如何在ThreeJS中的附近粒子之间创建线?

Based on a previous question I had recently posted: How to create lines between nearby particles in ThreeJS?

我能够创建连接附近粒子的单独线条。然而,由于粒子系统的逻辑,线被绘制两次。这是因为原始2D粒子系统的工作原理:
https://awingit.github.io /粒子/

I was able to create individual lines joining nearby particles. However, the lines are being drawn twice because of the logic of the particle system. This is because of how the original 2D particle system worked: https://awingit.github.io/particles/

这也画了两次。对于连接线的每对粒子,绘制线。

This also draws the lines twice. For each pair of particles connecting a line, the line is drawn.

我认为这不是性能的理想选择。我怎么只为每个加入点画一次线?

I do not think this is ideal for performance. How would I only draw a line once for each joining points?

P.S。这正是我想要实现的效果,但无法理解代码:
http://freelance-html-developer.com/clock/

P.S. Here is exactly the effect I would like to achieve, but cannot make sense of the code: http://freelance-html-developer.com/clock/

我想了解基本逻辑。

更新:

我创建了一个 jsfiddle 随着我的进步。

I have created a jsfiddle with my progress.

    var canvas, canvasDom, ctx, scene, renderer, camera, controls, geocoder, deviceOrientation = false;
    var width = 800,
        height = 600;
    var particleCount = 20;


    var pMaterial = new THREE.PointsMaterial({
        color: 0x000000,
        size: 0.5,
        blending: THREE.AdditiveBlending,
        //depthTest: false,
        //transparent: true
    });
    var particles = new THREE.Geometry;
    var particleSystem;
    var line;
    var lines = {};
    var lineGroup = new THREE.Group();
    var lineMaterial = new THREE.LineBasicMaterial({
        color: 0x000000,
        linewidth: 1
    });

    var clock = new THREE.Clock();
    var maxDistance = 15;

    function init() {
        canvasDom = document.getElementById('canvas');
        setupStage();
        setupRenderer();
        setupCamera();
        setupControls();
        setupLights();
        clock.start();
        window.addEventListener('resize', onWindowResized, false);
        onWindowResized(null);
        createParticles();
        scene.add(lineGroup);
        animate();
    }

    function setupStage() {
        scene = new THREE.Scene();
    }

    function setupRenderer() {
        renderer = new THREE.WebGLRenderer({
            canvas: canvasDom,
            logarithmicDepthBuffer: true
        });
        renderer.setSize(width, height);
        renderer.setClearColor(0xfff6e6);
    }

    function setupCamera() {
        camera = new THREE.PerspectiveCamera(70, width / height, 1, 10000);
        camera.position.set(0, 0, -60);
    }

    function setupControls() {
        if (deviceOrientation) {
            controls = new THREE.DeviceOrientationControls(camera);
            controls.connect();
        } else {
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.target = new THREE.Vector3(0, 0, 0);
        }
    }

    function setupLights() {
        var light1 = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
        var light2 = new THREE.PointLight(0xffffff, 1, 0);

        light2.position.set(100, 200, 100);

        scene.add(light1);
        scene.add(light2);
    }

    function animate() {
        requestAnimationFrame(animate);
        controls.update();
        animateParticles();
        updateLines();
        render();
    }

    function render() {
        renderer.render(scene, camera);
    }

    function onWindowResized(event) {
        width = window.innerWidth;
        height = window.innerHeight;
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        renderer.setSize(width, height);
    }

    function createParticles() {
        for (var i = 0; i < particleCount; i++) {
            var pX = Math.random() * 50 - 25,
                pY = Math.random() * 50 - 25,
                pZ = Math.random() * 50 - 25,
                particle = new THREE.Vector3(pX, pY, pZ);
            particle.diff = Math.random() + 0.2;
            particle.default = new THREE.Vector3(pX, pY, pZ);
            particle.offset = new THREE.Vector3(0, 0, 0);
            particle.velocity = {};
            particle.velocity.y = particle.diff * 0.5;
            particle.nodes = [];
            particles.vertices.push(particle);
        }
        particleSystem = new THREE.Points(particles, pMaterial);
        particleSystem.position.y = 0;
        scene.add(particleSystem);
    }

    function animateParticles() {
        var pCount = particleCount;
        while (pCount--) {
            var particle = particles.vertices[pCount];
            var move = Math.sin(clock.getElapsedTime() * (1 * particle.diff)) / 4;

            particle.offset.y += move * particle.velocity.y;
            particle.y = particle.default.y + particle.offset.y;

            detectCloseByPoints(particle);
        }
        particles.verticesNeedUpdate = true;
        particleSystem.rotation.y += 0.01;
        lineGroup.rotation.y += 0.01;
    }

    function updateLines() {
        for (var _lineKey in lines) {
            if (!lines.hasOwnProperty(_lineKey)) {
                continue;
            }
            lines[_lineKey].geometry.verticesNeedUpdate = true;
        }
    }

    function detectCloseByPoints(p) {
        var _pCount = particleCount;
        while (_pCount--) {
            var _particle = particles.vertices[_pCount];
            if (p !== _particle) {

                var _distance = p.distanceTo(_particle);
                var _connection = checkConnection(p, _particle);

                if (_distance < maxDistance) {
                    if (!_connection) {
                        createLine(p, _particle);
                    }
                } else if (_connection) {
                    removeLine(_connection);
                }
            }
        }
    }

    function checkConnection(p1, p2) {
        var _childNode, _parentNode;
        _childNode = p1.nodes[particles.vertices.indexOf(p2)] || p2.nodes[particles.vertices.indexOf(p1)];
        if (_childNode && _childNode !== undefined) {
            _parentNode = (_childNode == p1) ? p2 : p1;
        }
        if (_parentNode && _parentNode !== undefined) {
            return {
                parent: _parentNode,
                child: _childNode,
                lineId: particles.vertices.indexOf(_parentNode) + '-' + particles.vertices.indexOf(_childNode)
            };
        } else {
            return false;
        }
    }

    function removeLine(_connection) {
        // Could animate line out
        var childIndex = particles.vertices.indexOf(_connection.child);
        _connection.parent.nodes.splice(childIndex, 1);
        deleteLine(_connection.lineId);
    }

    function deleteLine(_id) {
        lineGroup.remove(lines[_id]);
        delete lines[_id];
    }

    function addLine(points) {
        var points = points || [new THREE.Vector3(Math.random() * 10, Math.random() * 10, Math.random() * 10), new THREE.Vector3(0, 0, 0)];
        var _lineId = particles.vertices.indexOf(points[0]) + '-' + particles.vertices.indexOf(points[1]);
        var lineGeom = new THREE.Geometry();
        if (!lines[_lineId]) {
            lineGeom.dynamic = true;
            lineGeom.vertices.push(points[0]);
            lineGeom.vertices.push(points[1]);
            var curLine = new THREE.Line(lineGeom, lineMaterial);
            curLine.touched = false;
            lines[_lineId] = curLine;
            lineGroup.add(curLine);
            return curLine;
        } else {
            return false;
        }
    }

    function createLine(p1, p2) {
        p1.nodes[particles.vertices.indexOf(p2)] = p2;
        addLine([p1, p2]);
    }

    $(document).ready(function() {
        init();
    });

我非常接近,但我不确定它是否已经过优化。似乎有闪烁的线条,有时一条线只是卡在原地。

I am really close, but I am not sure if its optimized. There seem to be flickering lines, and sometimes a line just stays stuck in place.

所以这是我的想法。我点击了我所要做的就是让行的Vector3点等于相关的粒子Vector3点。我只需要更新每一行geometry.verticesNeedUpdate = true;

So here are my thoughts. I clicked that all I have to do is make the Vector3 points of the lines equal to the relevant particle Vector3 points. I just need to update each lines geometry.verticesNeedUpdate = true;

另外,我如何管理这些行是我使用2点的索引创建一个唯一的ID,例如: lines ['8-2'] = line

Also, how I manage the lines is I create a unique ID using the indexes of the 2 points, e.g. lines['8-2'] = line

推荐答案

你实际上要解决的问题是循环你的点数列表,您将成功比赛的数量增加一倍。

The problem you're actually trying to solve is that while looping through your list of points, you're doubling the number of successful matches.

示例:

考虑一个点列表, [A,B,C,D] 。您的循环测试每个点与所有其他点。对于此示例, A C 是唯一足够接近附近的点。

Consider a list of points, [A, B, C, D]. Your looping tests each point against all other points. For this example, A and C are the only points close enough to be considered nearby.

在第一次迭代中, A 与所有,你发现 A C 就在附近,所以你添加了一行。但是当你为 C 进行迭代时,你也会发现 A 就在附近。这会导致你要避免的第二行。

During the first iteration, A vs. all, you find that A and C are nearby, so you add a line. But when you're doing your iteration for C, you also find that A is nearby. This causes the second line, which you want to avoid.

修复它:

解决方案很简单:不要重新访问已经检查过的节点。这是有效的,因为距离A到C 的答案与 C>到的距离没有什么不同。

The solution is simple: Don't re-visit nodes you already checked. This works because the answer of distance from A to C is no different from distance from C to A.

执行此操作的最佳方法是调整检查循环的索引:

The best way to do this is adjust your indexing for your check loop:

// (Note: This is example code, and won't "just work.")
for(var check = 0, checkLength = nodes.length; check < checkLength; ++check){
    for(var against = check + 1, against < checkLength; ++against){
        if(nodes[check].distanceTo(nodes[against]) < delta){
            buildThatLine(nodes[check], nodes[against]);
        }
    }
}

在内循环中,索引设置为:

In the inner loop, the indexing is set to:


  1. 跳过当前节点

  2. 跳过当前节点之前的所有节点。

这是通过将内部索引初始化为外部索引+ 1来完成的。

This is done by initializing the inner indexing to the outer index + 1.

警告:

此特定逻辑假定您丢弃每帧的所有行。这不是实现效果的最有效方法,但我会让它更有效地为你锻炼。

This particular logic assumes that you discard all your lines for every frame. It's not the most efficient way to achieve the effect, but I'll leave making it more efficient as an exercise for you.

这篇关于具有连接线的Threejs粒子系统。编程逻辑?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆