2016-04-05 4 views
5

Je suis en mesure d'afficher un chiffre THREE.TubeGeometry comme suit enter image description hereaffichage incrémentalement Three.js TubeGeometry

code ci-dessous, lien vers 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> 

Cependant, je voudrais afficher progressivement, comme dans, comme un arc qui se charge, de telle sorte qu'il commence comme point de départ, dessine de façon incrémentielle et finalement regarde l'arc ci-dessous à la fin. J'ai fait un peu d'effort, et j'ai réussi à le faire en stockant tous les points/coordonnées couverts par l'arc, et en traçant des lignes entre les coordonnées consécutives, de manière à obtenir la sensation de 'chargement de l'arc incrémental'. Cependant, y a-t-il un meilleur moyen d'y parvenir? Ceci est le lien vers jsbin

Ajout du code ici et

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

Inconvénient: L'inconvénient de le faire de la manière ci-dessus est que, à la fin de la journée, je dessine une ligne entre les points consécutifs , et ainsi je perds sur beaucoup d'effets possibles dans TubeGeometry tels que, épaisseur, transparence etc.

Veuillez me suggérer une manière alternative d'obtenir une charge incrémentielle douce pour le TubeGeometry.

Répondre

13

THREE.TubeGeometry renvoie THREE.Geometry. En convertissant la géométrie à

THREE.BufferGeometry, vous avez accès à une propriété drawRange que vous pouvez définir pour animer le dessin du maillage:

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

} 

violon: http://jsfiddle.net/k73pxyL2/

EDIT: Pour une autre approche, voir this SO answer.

Three.js R.75

+0

merci beaucoup, c'est génial. J'ai passé un peu de temps à essayer de le comprendre et j'ai pu m'accorder à mon goût (jouer du violon comme commentaire suivant). Je viens d'avoir quelques questions. Q1.Je comprends nMax = geometry.attributes.position.count Qu'est-ce que nMax exactement? Est-ce que ma géométrie a été convertie en 24576 points? Est-ce que ces points définissent la géométrie que je veux dessiner? Dans votre violon, points.length == 18 et nMax == 245762. Q2.Vous mentionnez dans votre commentaire que vous affectez nStep à 90, car 30 faces * 3 sommets/face. Je n'ai pas compris, votre violon ressemble à un petit intestin, je ne vois pas 30 visages. :) – PepperBoy

+0

Mon violon: http://jsbin.com/taqareyeqi/edit?html,js,output – PepperBoy

+2

Conseil: étudiez et comprenez 'BufferGeometry'. Il serait utile que vous fassiez cela. Étudie aussi le violon pour que tu comprennes chaque ligne. (1) 'nMax' = nombre total de sommets (positions) dans la géométrie du tampon. 'points' est un tableau temporaire utilisé pour définir la courbe. (2) 'nStep' est maintenant beaucoup de vertices supplémentaires pour dessiner chaque image. Dans ce cas, 30 faces supplémentaires sont restituées à chaque image. (2b) Les visages sont minuscules. Il y en a beaucoup. :) – WestLangley

1

Je ne connais pas très bien three.js. Mais je pense que je peux être utile. J'ai deux solutions pour vous. Les deux sont basés sur le même principe: construire une nouvelle TubeGeometry ou reconstruire la nouvelle, autour d'une nouvelle courbe.

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

Vous définissez un nouveau type de courbe qui sélectionne tout un segment sur une courbe donnée. Utilisation:

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

Maintenant vous pouvez construire un nouveau tube.

Solution 2 (Mathématiques!):

Vous utilisez pour votre arc une courbe de Bézier, c'est génial! Cette courbe est une parabole. Vous voulez juste un segment de cette parabole et c'est encore une parabole, juste avec d'autres limites.

Nous avons besoin d'une section de la courbe de Bézier. Disons que la courbe est définie par A (début), B (direction), C (fin). Si nous voulons changer le début en un point D et la fin en un point F, nous avons besoin du point E qui est la direction de la courbe en D et F. Donc les tangentes à notre parabole en D et F doivent se croiser en E . Ainsi, le code suivant nous donner le résultat souhaité:

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

Ceci est une façon très mathématique, mais si vous avez besoin des propriétés particulières d'une courbe Bézier, c'est le chemin à parcourir.

Remarque: La première solution est la plus simple. Je ne suis pas familier avec Three.js donc je ne saurais pas quelle est la manière la plus efficace d'implémenter l'animation. Three.js ne semble pas utiliser les propriétés spéciales d'une courbe de Bézier alors peut-être que la solution 2 n'est pas très utile.

J'espère que vous avez obtenu quelque chose d'utile à partir de cela.

+0

merci beaucoup toonijn :) Les maths sont super. Vraiment apprécier l'aide. Juste essayer de me salir les mains avec trois.js – PepperBoy

2

Normalement, vous seriez en mesure d'utiliser la méthode .getPointAt()-« obtenir un vecteur pour le point à la position relative dans la courbe en fonction de la longueur d'arc » pour obtenir un point à un certain pourcentage de la longueur de la courbe. Donc, normalement, si vous voulez dessiner 70% de la courbe et une courbe complète est dessinée en 100 segments. Ensuite, vous pouvez faire:

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

Mais je pense que cela ne fonctionne pas pour votre curveQuad car la méthode getPointAt n'est pas mis en œuvre pour ce type. Un travail est autour d'obtenir un 100 points pour votre courbe dans un tableau comme celui-ci:

points = curve.getPoints(100); 

Et vous pouvez faire à peu près la même:

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

maintenant votre curvePath détient les segments de ligne que vous voulez à utiliser pour dessiner le tube:

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

Here a fiddle avec une démonstration sur la façon d'utiliser cette dynamique

+0

merci beaucoup Wilt. J'ai passé un peu de temps à essayer de le comprendre, puis j'ai pu faire quelques modifications. JS Fiddle: http://jsbin.com/gexexefalu/edit?html,js,output. Ai-je raison de dire que la première fois que getGeometry est appelé, seul le premier point est tracé, dans la cinquième fois qu'il dessine la ligne de 1 -2, 2-3, 3-4, 4-5 et dans la 70e fois, disons, il tire de 1-2, 2-3 .... 69-70. Donc, est-ce que nous redessinons la ligne de 1-2 à 70 fois au lieu de faire usage de la ligne tracée dans l'itération précédente (ou 'courant')? Je comprends que ce n'est peut-être pas optimisé, c'est juste pour clarifier ma compréhension. – PepperBoy

+0

@PepperBoy en effet ce code n'est pas totalement optimal, puisque le maillage précédent (et la géométrie) est supprimé et totalement redessiné lors de l'itération suivante. Si vous vous familiarisez avec la classe BufferGeometry et sa méthode setDrawRange comme mentionné dans la réponse acceptée, la solution est définitivement optimisée. Ma réponse vous obtient un résultat similaire sans utiliser BufferGeometry. – Wilt