2016-04-05 28 views
5

sono in grado di visualizzare una figura THREE.TubeGeometry come segue enter image description hereincrementale visualizzazione Three.js TubeGeometry

codice qui sotto, link per 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> 

Tuttavia, vorrei visualizzazione incrementale, come in, come un arco che si sta caricando, in modo tale che inizi come punto di partenza, disegna in modo incrementale e alla fine sembra l'arco sotto il completamento.

Ho messo un po 'di sforzo, e sono stato in grado di farlo memorizzando tutti i punti/coordinate coperte dall'arco, e disegnando linee tra le coordinate consecutive, in modo tale da ottenere la sensazione di' arco che carica in modo incrementale '. Tuttavia, c'è un modo migliore per raggiungere questo obiettivo? Questo è il link per jsbin

Aggiungendo il codice qui pure

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

Svantaggio: Lo svantaggio di farlo nel modo sopra è che, alla fine della giornata, sto disegnando una linea tra punti consecutivi , quindi ho perso molti degli effetti possibili in TubeGeometry come spessore, trasparenza, ecc.

Per favore suggeriscimi un modo alternativo per ottenere un carico incrementale regolare per TubeGeometry.

risposta

13

THREE.TubeGeometry restituisce un THREE.Geometry. Convertendo la geometria per

THREE.BufferGeometry, si ha accesso a una proprietà drawRange che è possibile impostare per animare il disegno della maglia:

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

} 

violino: http://jsfiddle.net/k73pxyL2/

EDIT: Per un altro approccio, vedi this SO answer.

Three.js r.75

+0

grazie mille, è grandioso. Ho passato un po 'di tempo a cercare di capirlo ed è stato in grado di sintonizzare i miei gusti (violino come prossimo commento). Ho appena fatto un paio di domande. Q1. Capisco nMax = geometry.attributes.position.count Che cosa è esattamente nMax? La mia geometria è stata convertita in 24576 punti? Questi punti definiscono la geometria che voglio disegnare? Nel tuo violino, points.length == 18 and nMax == 245762. Q2. Nel tuo commento hai menzionato che assegni nStep a 90, perché 30 facce * 3 vertici/faccia. Non ho capito, il tuo violino sembra un intestino tenue, non vedo 30 facce. :) – PepperBoy

+0

Il mio violino: http://jsbin.com/taqareyeqi/edit?html,js,output – PepperBoy

+2

Consiglio amichevole: studia e capisci "BufferGeometry". Sarebbe utile per te farlo. Studia anche il violino così capisci ogni riga di esso. (1) 'nMax' = numero totale di vertici (posizioni) nella geometria del buffer. 'points' è una matrice temporanea utilizzata per definire la curva. (2) 'nStep' ora sono molti altri vertici per disegnare ogni fotogramma. In questo caso, 30 volti aggiuntivi sono resi ogni fotogramma. (2b) I volti sono piccoli. Ce ne sono molti. :) – WestLangley

1

Non ho molta familiarità con three.js. Ma penso di poter essere di aiuto. Ho due soluzioni per te. Entrambi si basano sullo stesso principio: crea una nuova TubeGeometry o ricostruisci quella corrente, attorno a una nuova curva.

Soluzione 1 (semplice):

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

si definisce un nuovo tipo di curva che seleziona solo un segmento di una data curva. Utilizzo:

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

Ora è possibile costruire un nuovo tubo.

Soluzione 2 (Mathematics!):

Si utilizza per il vostro arco di una curva di Bézier quadratica, che è impressionante! Questa curva è una parabola. Vuoi solo un segmento di quella parabola e quella è di nuovo una parabola, solo con altri limiti.

Ciò di cui abbiamo bisogno è una sezione della curva di Bezier. Diciamo che la curva è definita da A (inizio), B (direzione), C (fine). Se vogliamo cambiare l'inizio in un punto D e la fine in un punto F abbiamo bisogno del punto E che è la direzione della curva in D e F. Quindi le tangenti alla nostra parabola in D e F devono intersecare in E . Così il seguente codice ci darà il risultato desiderato:

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

questo è un modo molto matematico, ma se avete bisogno di particolari proprietà di una curva di Bezier, questo è la strada da percorrere.

Nota: La prima soluzione è la più semplice. Non ho familiarità con Three.js quindi non saprei quale sia il modo più efficiente per implementare l'animazione. Three.js non sembra utilizzare le proprietà speciali di una curva di bezier, quindi forse la soluzione 2 non è così utile.

Spero che tu abbia ottenuto qualcosa di utile da questo.

+0

grazie mille toonijn :) La matematica è grande. Apprezzo davvero l'aiuto. Sto solo cercando di sporcarmi le mani con three.js – PepperBoy

2

Normalmente si sarebbe in grado di utilizzare il metodo di .getPointAt()"ottenere un vettore per il punto alla posizione relativa in curva secondo lunghezza dell'arco" ottenere un punto ad una certa percentuale della lunghezza della curva.

Quindi normalmente se si desidera disegnare il 70% della curva e viene disegnata una curva completa in 100 segmenti. Poi si potrebbe fare:

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

ma penso che questo non funziona per il vostro curveQuad poiché il metodo getPointAt non è implementato per questo tipo. Un lavoro intorno è quello di ottenere un 100 punti per la curva in un array come questo:

points = curve.getPoints(100); 

E poi si può fare quasi lo stesso:

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

adesso la tua curvePath detiene i segmenti di linea che si desidera da utilizzare per tracciare il tubo:

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

Here a fiddle con una dimostrazione su come usare questo dinamicamente

+0

grazie mille Wilt. Ho passato un po 'a cercare di capirlo, e poi sono riuscito a fare alcune modifiche. JS fiddle: http://jsbin.com/gexexefalu/edit?html,js,output. Ho ragione nel dire che la prima volta che getGeometry viene chiamato, viene tracciato solo il primo punto, nella quinta volta si disegna la linea da 1 -2, 2-3, 3-4, 4-5 e nella 70esima volta, per esempio, attinge da 1-2, 2-3 .... 69-70. Quindi, stiamo ridisegnando la linea da 1-2 a 70 volte invece di usare la linea disegnata nella precedente iterazione (o 'corrente')? Capisco che potrebbe non essere ottimizzato, questo è solo per chiarire la mia comprensione. – PepperBoy

+0

@PepperBoy infatti questo codice non è del tutto ottimale, poiché la precedente mesh (e geometria) è stata rimossa e totalmente ridisegnata nella successiva iterazione. Se si acquisisce familiarità con la classe BufferGeometry e il suo metodo setDrawRange come menzionato nella risposta accettata, la soluzione è decisamente più ottimizzata. La mia risposta ti dà un risultato simile senza usare BufferGeometry. – Wilt