2016-04-05 4 views
5

Я могу отобразить THREE.TubeGeometry фигуру следующим образом enter image description hereПоэтапно дисплей Three.js TubeGeometry

код ниже, ссылка на 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> 

Однако, я хотел бы дисплей постепенно, как в дуге, которая загружается, так что она начинается как начальная точка, рисуется постепенно и, наконец, выглядит позади дуги по завершении.

Я приложил некоторое усилие и смог сделать это, сохранив все точки/координаты, покрытые дугой, и нарисовал линии между последовательными координатами, чтобы я чувствовал «дуговую нагрузку постепенно». Однако есть ли лучший способ достичь этого? Это ссылка на jsbin

Добавление кода здесь, а

<!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> 

недостаток: недостаток делает это выше способ является то, что, в конце концов, я рисую линию между последовательными точками , и поэтому я теряю много эффектов, возможных в TubeGeometry, таких как толщина, прозрачность и т. д.

Пожалуйста, предложите мне альтернативный способ получения плавной инкрементной нагрузки для TubeGeometry.

ответ

13

THREE.TubeGeometry возвращает THREE.Geometry. Путь преобразования геометрии в

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); 

} 

скрипка: http://jsfiddle.net/k73pxyL2/

EDIT: Для другого подхода, см. this SO answer.

Three.js R.75

+0

большое спасибо, это здорово. Я потратил некоторое время, пытаясь понять это и смог настроиться по своему вкусу (скрипку в качестве следующего комментария). У меня было несколько вопросов. Q1.I понимает nMax = geometry.attributes.position.count Что такое nMax? Моя геометрия была преобразована в 24576 точек? Определяют ли эти точки геометрию, которую я хочу нарисовать? В вашей скрипке, points.length == 18 и nMax == 245762. Q2.Вы отмечаете, что вы назначили nStep 90, потому что 30 лиц * 3 вершины/лица. Я этого не понял, твоя скрипка выглядит как тонкая кишка, я не вижу 30 лиц. :) – PepperBoy

+0

Моя скрипка: http://jsbin.com/taqareyeqi/edit?html,js,output – PepperBoy

+2

Дружественный совет: Изучите и поймите 'BufferGeometry'. Было бы полезно для вас это сделать. Также изучите скрипку, чтобы вы понимали каждую ее линию. (1) 'nMax' = общее количество вершин (позиций) в геометрии буфера. 'points' - временный массив, используемый для определения кривой. (2) 'nStep' теперь много дополнительных вершин для рисования каждого кадра. В этом случае каждый кадр отображает 30 дополнительных граней. (2b) Лица крошечные. Их очень много. :) – WestLangley

1

Я не очень-то знаком с three.js. Но я думаю, что могу помочь. У меня есть два решения для вас. Оба основаны на том же принципе: постройте новую TubeGeometry или перестройте текущую, вокруг новой кривой.

Решение 1 (Simple):

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); 
}); 

Вы определяете новый тип кривой, которая просто выбирает сегмент из данной кривой. Использование:

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

Теперь вы можете построить новую трубку.

Решение 2 (Математика!):

Вы используете для вашей дуги квадратичной кривой Безье, это потрясающе! Эта кривая является параболой. Вы хотите только часть этой параболы, и это снова парабола, только с другими границами.

Нам нужна секция кривой Безье. Предположим, что кривая определяется A (начало), B (направление), C (конец). Если мы хотим изменить начало на точку 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); 
} 

Это очень математический способ, но если вам будут нужны специальные свойства кривой Безье, это путь.

Примечание: Первое решение является самым простым. Я не знаком с Three.js, поэтому я не знаю, какой самый эффективный способ реализовать анимацию. Three.js, похоже, не использует специальные свойства кривой безье, поэтому решение 2 не так полезно.

Надеюсь, вы получили что-то полезное из этого.

+0

Большое спасибо toonijn :) Математика велика. Очень ценю помощь. Просто пытаясь замарать руки с помощью трёх.js – PepperBoy

2

Обычно вы могли бы использовать метод .getPointAt() для «получить вектор для точки в относительном положении на кривой в зависимости от длины дуги», чтобы получить точку на определенное процент от длины кривой.

Как правило, если вы хотите нарисовать 70% кривой, а полная кривая будет нарисована в 100 сегментах. Тогда вы могли бы сделать:

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; 
} 

Но я думаю, что это не работает для вашего curveQuad, так как метод getPointAt не реализован для этого типа. Произведение вокруг, чтобы получить 100 баллов для вашей кривой в массиве, как это:

points = curve.getPoints(100); 

И тогда вы можете сделать почти то же самое:

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; 
} 

теперь ваш curvePath имеет отрезки, которые вы хотите использовать для рисования трубки:

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

Here a fiddle с демонстрацией того, как использовать это динамически

+0

спасибо большое Уилт. Я попытался понять это, а затем смог внести несколько изменений. JS скрипка: http://jsbin.com/gexexefalu/edit?html,js,output. Я правильно говорю, что при первом вызове getGeometry вызывается только первая точка, в пятый раз она рисует линию от 1 -2, 2-3, 3-4, 4-5 и в 70-е время, скажем, он рисует 1-2, 2-3 .... 69-70. Итак, мы перерисовываем линию с 1-2 до 70 раз вместо того, чтобы использовать линию, нарисованную на предыдущей итерации (или «current»)? Я понимаю, что это не может быть оптимизировано, это просто для того, чтобы прояснить мое понимание. – PepperBoy

+0

@PepperBoy действительно этот код не является полностью оптимальным, так как предыдущая сетка (и геометрия) удаляется и полностью перерисовывается на следующей итерации. Если вы знакомы с классом BufferGeometry и его методом setDrawRange, как указано в принятом ответе, решение определенно оптимизировано. Мой ответ дает вам аналогичный результат без использования BufferGeometry. – Wilt

Смежные вопросы