简体   繁体   English

增量显示three.js TubeGeometry

[英]Incrementally display three.js TubeGeometry

I am able to display a THREE.TubeGeometry figure as follows 我能够显示如下的THREE.TubeGeometry图 在此输入图像描述

Code below, link to jsbin 下面的代码,链接到jsbin

<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>

<script>
    // global variables
    var renderer;
    var scene;
    var camera;
    var geometry;

    var control;

    var count = 0;
    var animationTracker;

    init();
    drawSpline();

    function init()
    {
        // create a scene, that will hold all our elements such as objects, cameras and lights.
        scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render, sets the background color and the size
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor('lightgray', 1.0);
        renderer.setSize(window.innerWidth, window.innerHeight);

        // position and point the camera to the center of the scene
        camera.position.x = 0;
        camera.position.y = 40;
        camera.position.z = 40;
        camera.lookAt(scene.position);

        // add the output of the renderer to the html element
        document.body.appendChild(renderer.domElement);
    }

    function drawSpline(numPoints)
    {
        var numPoints = 100;
//        var start = new THREE.Vector3(-5, 0, 20);
        var start = new THREE.Vector3(-5, 0, 20);
        var middle = new THREE.Vector3(0, 35, 0);
        var end = new THREE.Vector3(5, 0, -20);

        var curveQuad = new THREE.QuadraticBezierCurve3(start, middle, end);

        var tube = new THREE.TubeGeometry(curveQuad, numPoints, 0.5, 20, false);
        var mesh = new THREE.Mesh(tube, new THREE.MeshNormalMaterial({
            opacity: 0.9,
            transparent: true
        }));

        scene.add(mesh);
        renderer.render(scene, camera);
    }
</script>
</body>
</html>

However, I would like to display incrementally , as in, like an arc that is loading, such that it starts as the start point, draws incrementally and finally looks the below arc upon completion. 但是,我想逐步显示 ,就像在加载的弧一样,它以起始点开始,逐渐绘制,最后在完成后查看下面的弧。

I have been putting in some effort, and was able to do this by storing all the points/coordinates covered by the arc, and drawing lines between the consecutive coordinates, such that I get the 'arc loading incrementally' feel. 我已经付出了一些努力,并且能够通过存储弧所覆盖的所有点/坐标以及在连续坐标之间绘制线来实现这一点,这样我就可以获得“弧加载”的感觉。 However, is there a better way to achieve this? 但是,有没有更好的方法来实现这一目标? This is the link to jsbin 这是jsbin的链接

Adding the code here as well 在这里添加代码

<!DOCTYPE html>
<html>
<head>
    <title>Incremental Spline Curve</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r75/three.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<script>

    // global variables
    var renderer;
    var scene;
    var camera;
    var splineGeometry;

    var control;

    var count = 0;
    var animationTracker;

//    var sphereCamera;
    var sphere;
    var light;

    function init() {

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render, sets the background color and the size
        renderer = new THREE.WebGLRenderer();
//        renderer.setClearColor(0x000000, 1.0);
        renderer.setClearColor( 0xffffff, 1 );
        renderer.setSize(window.innerWidth, window.innerHeight);

        // position and point the camera to the center of the scene
        camera.position.x = 0;
        camera.position.y = 40;
        camera.position.z = 40;
        camera.lookAt(scene.position);

        // add the output of the renderer to the html element
        document.body.appendChild(renderer.domElement);

//        //init for sphere
//        sphereCamera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
//        sphereCamera.position.y = -400;
//        sphereCamera.position.z = 400;
//        sphereCamera.rotation.x = .70;

        sphere = new THREE.Mesh(new THREE.SphereGeometry(0.8,31,31), new THREE.MeshLambertMaterial({
            color: 'yellow',
        }));

        light = new THREE.DirectionalLight('white', 1);
//        light.position.set(0,-400,400).normalize();
        light.position.set(0,10,10).normalize();

        //get points covered by Spline
        getSplineData();
    }

    //save points in geometry.vertices
    function getSplineData() {
        var curve = new THREE.CubicBezierCurve3(
                new THREE.Vector3( -5, 0, 10 ),
                new THREE.Vector3(0, 20, 0 ),
                new THREE.Vector3(0, 20, 0 ),
                new THREE.Vector3( 2, 0, -25 )
        );

        splineGeometry = new THREE.Geometry();
        splineGeometry.vertices = curve.getPoints( 50 );

        animate();
    }

    //scheduler loop
    function animate() {
        if(count == 50)
        {
            cancelAnimationFrame(animationTracker);
            return;
        }

        //add line to the scene
        drawLine();

        renderer.render(scene, camera);
  //      renderer.render(scene, sphereCamera);

        count += 1;
//        camera.position.z -= 0.25;
//        camera.position.y -= 0.25;
        animationTracker = requestAnimationFrame(animate);
    }

    function drawLine() {
        var lineGeometry = new THREE.Geometry();
        var lineMaterial = new THREE.LineBasicMaterial({
            color: 0x0000ff
        });
        console.log(splineGeometry.vertices[count]);
        console.log(splineGeometry.vertices[count+1]);
        lineGeometry.vertices.push(
                splineGeometry.vertices[count],
                splineGeometry.vertices[count+1]
        );

        var line = new THREE.Line( lineGeometry, lineMaterial );
        scene.add( line );
    }

    // calls the init function when the window is done loading.
    window.onload = init;

</script>
<body>
</body>
</html>

Drawback : The drawback of doing it the above way is that, end of the day, I'm drawing a line between consecutive points, and so I lose out on a lot of the effects possible in TubeGeometry such as, thickness, transparency etc. 缺点:以上述方式做到这一点的缺点是,在一天结束时,我在连续点之间绘制一条线,因此我在TubeGeometry中失去了很多可能的效果,例如厚度,透明度等。

Please suggest me an alternative way to get a smooth incremental load for the TubeGeometry. 请建议我另一种方法来获得TubeGeometry的平滑增量负载。

THREE.TubeGeometry returns a THREE.Geometry . THREE.TubeGeometry返回THREE.Geometry By converting the geometry to 通过将几何转换为

THREE.BufferGeometry , you have access to a property drawRange that you can set to animate the drawing of the mesh: THREE.BufferGeometry ,您可以访问属性drawRange ,您可以将其设置为动画网格的绘图:

var nEnd = 0, nMax, nStep = 90; // 30 faces * 3 vertices/face

...

// geometry
var geometry = new THREE.TubeGeometry( path, pathSegments, tubeRadius, radiusSegments, closed );

// to buffer goemetry
geometry = new THREE.BufferGeometry().fromGeometry( geometry );
nMax = geometry.attributes.position.count;

...

function animate() {

    requestAnimationFrame( animate );

    nEnd = ( nEnd + nStep ) % nMax;

    mesh.geometry.setDrawRange( 0, nEnd );

    renderer.render( scene, camera );

}

fiddle: http://jsfiddle.net/k73pxyL2/ 小提琴: http//jsfiddle.net/k73pxyL2/

EDIT: For another approach, see this SO answer . 编辑:对于另一种方法,请参阅此SO答案

three.js r.75 three.js r.75

Normally you would be able to use the method .getPointAt() to "get a vector for point at relative position in curve according to arc length" to get a point at a certain percentage of the length of the curve. 通常,您可以使用方法.getPointAt() “根据弧长获取曲线中相对位置处的点的矢量”,以获得曲线长度的某个百分比的点。

So normally if you want to draw 70% of the curve and a full curve is drawn in 100 segments. 因此,通常情况下,如果要绘制70%的曲线,则绘制100个线段的完整曲线。 Then you could do: 然后你可以这样做:

var percentage = 70;
var curvePath = new THREE.CurvePath();

var end, start = curveQuad.getPointAt( 0 );

for(var i = 1; i < percentage; i++){
    end = curveQuad.getPointAt( percentage / 100 );
    lineCurve = new THREE.LineCurve( start, end );
    curvePath.add( lineCurve );
    start = end;
}

But I think this is not working for your curveQuad since the getPointAt method is not implemented for this type. 但是我认为这对你的curveQuad因为没有为这种类型实现getPointAt方法。 A work around is to get a 100 points for your curve in an array like this: 解决方法是在数组中为曲线获得100点,如下所示:

points = curve.getPoints(100);

And then you can do almost the same: 然后你几乎可以做同样的事情:

var percentage = 70;
var curvePath = new THREE.CurvePath();

var end, start = points[ 0 ];

for(var i = 1; i < percentage; i++){
    end = points[ percentage ]
    lineCurve = new THREE.LineCurve( start, end );
    curvePath.add( lineCurve );
    start = end;
}

now your curvePath holds the line segments you want to use for drawing the tube: 现在,您的curvePath包含您要用于绘制管的线段:

// draw the geometry
var radius = 5, radiusSegments = 8, closed = false;
var geometry = new THREE.TubeGeometry(curvePath, percentage, radius, radiusSegments, closed);

Here a fiddle with a demonstration on how to use this dynamically 这里有一个演示如何动态使用它的小提琴

I'm not really that familiar with three.js. 我对three.js并不熟悉。 But I think I can be of assistance. 但我想我可以提供帮助。 I have two solutions for you. 我有两个解决方案。 Both based on the same principle: build a new TubeGeometry or rebuild the current one, around a new curve. 两者都基于相同的原则:围绕新曲线构建新的TubeGeometry或重建当前的TubeGeometry。

Solution 1 (Simple): 解决方案1(简单):

var CurveSection = THREE.Curve.create(function(base, from, to) {
  this.base = base;
  this.from = from;
  this.to = to;
}, function(t) {
  return this.base.getPoint((1 - t) * this.from + t * this.to);
});

You define a new type of curve which just selects a segment out of a given curve. 您可以定义一种新类型的曲线,它只选择给定曲线中的一个线段。 Usage: 用法:

var curve = new CurveSection(yourCurve, 0, .76); // Where .76 is your percentage

Now you can build a new tube. 现在你可以建造一个新管。

Solution 2 (Mathematics!): 解决方案2(数学!):

You are using for your arc a quadratic bezier curve, that's awesome! 你正在为你的弧使用二次贝塞尔曲线,这太棒了! This curve is a parabola. 该曲线是抛物线。 You want just a segment of that parabola and that is again a parabola, just with other bounds. 你只需要一段抛物线,这又是一个抛物线,只是与其他界限。

What we need is a section of the bezier curve. 我们需要的是贝塞尔曲线的一部分。 Let's say the curve is defined by A (start), B (direction), C (end). 假设曲线由A(开始),B(方向),C(结束)定义。 If we want to change the start to a point D and the end to a point F we need the point E that is the direction of the curve in D and F. So the tangents to our parabola in D and F have to intersect in E. So the following code will give us the desired result: 如果我们想要将起点更改为点D并将结束更改为点F,则需要点E,即D和F中曲线的方向。因此,D和F中抛物线的切线必须在E中相交那么下面的代码将给出我们想要的结果:

// Calculates the instersection point of Line3 l1 and Line3 l2.
function intersection(l1, l2) {
  var A = l1.start;
  var P = l2.closestPointToPoint(A);
  var Q = l1.closestPointToPoint(P);
  var l = P.distanceToSquared(A) / Q.distanceTo(A);
  var d = (new THREE.Vector3()).subVectors(Q, A);
  return d.multiplyScalar(l / d.length()).add(A);
}

// Calculate the tangentVector of the bezier-curve
function tangentQuadraticBezier(bezier, t) {
  var s = bezier.v0,
      m = bezier.v1,
      e = bezier.v2;
  return new THREE.Vector3(
    THREE.CurveUtils.tangentQuadraticBezier(t, s.x, m.x, e.x),
    THREE.CurveUtils.tangentQuadraticBezier(t, s.y, m.y, e.y),
    THREE.CurveUtils.tangentQuadraticBezier(t, s.z, m.z, e.z)
  );
}

// Returns a new QuadraticBezierCurve3 with the new bounds.
function sectionInQuadraticBezier(bezier, from, to) {
  var s = bezier.v0,
      m = bezier.v1,
      e = bezier.v2;

  var ns = bezier.getPoint(from),
      ne = bezier.getPoint(to);
  var nm = intersection(
    new THREE.Line3(ns, tangentQuadraticBezier(bezier, from).add(ns)),
    new THREE.Line3(ne, tangentQuadraticBezier(bezier, to).add(ne))
  );
  return new THREE.QuadraticBezierCurve3(ns, nm, ne);
}

This is a very mathematical way, but if you should need the special properties of a Bezier curve, this is the way to go. 这是一种非常数学的方法,但是如果你需要贝塞尔曲线的特殊属性,这就是你要走的路。

Note: The first solution is the simplest. 注意:第一种解决方案是最简单的。 I am not familiar with Three.js so I wouldn't know what the most efficient way to implement the animation is. 我不熟悉Three.js所以我不知道实现动画最有效的方法是什么。 Three.js doesn't seem to use the special properties of a bezier curve so maybe solution 2 isn't that useful. Three.js似乎没有使用贝塞尔曲线的特殊属性,因此解决方案2可能没那么有用。

I hope you have gotten something useful out of this. 我希望你能从中得到一些有用的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM