递增显示 three.js TubeGeometry
Incrementally display three.js TubeGeometry
我可以显示一个 THREE.TubeGeometry 数字如下
下面的代码,link 到 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>
但是,我想逐步显示,就像正在加载的弧线一样,它作为起点开始,逐步绘制,最后看起来如下所示完成后弧形。
我一直在努力,并且能够通过存储弧覆盖的所有 points/coordinates 并在连续坐标之间画线来做到这一点,这样我就得到了 'arc loading incrementally' 感觉。但是,有没有更好的方法来实现这一目标?这是 link 到 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 的平滑增量加载。
我对 three.js 不是很熟悉。但我想我可以提供帮助。我有两个解决方案给你。两者都基于相同的原则:围绕新曲线构建新的 TubeGeometry 或重建当前的 TubeGeometry。
解决方案 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);
});
您定义了一种新型曲线,它只是从给定曲线中选择一段。用法:
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 可能没那么有用。
我希望你从中得到了一些有用的东西。
THREE.TubeGeometry
returns一个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 );
}
fiddle: http://jsfiddle.net/k73pxyL2/
编辑:另一种方法,参见。
three.js r.75
通常你可以使用.getPointAt()到"get a vector for point at relative position in curve according to arc length"的方法在曲线长度的一定百分比处得到一个点。
所以通常情况下如果你想绘制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 并演示如何动态使用它
我可以显示一个 THREE.TubeGeometry 数字如下
下面的代码,link 到 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>
但是,我想逐步显示,就像正在加载的弧线一样,它作为起点开始,逐步绘制,最后看起来如下所示完成后弧形。
我一直在努力,并且能够通过存储弧覆盖的所有 points/coordinates 并在连续坐标之间画线来做到这一点,这样我就得到了 'arc loading incrementally' 感觉。但是,有没有更好的方法来实现这一目标?这是 link 到 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 的平滑增量加载。
我对 three.js 不是很熟悉。但我想我可以提供帮助。我有两个解决方案给你。两者都基于相同的原则:围绕新曲线构建新的 TubeGeometry 或重建当前的 TubeGeometry。
解决方案 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);
});
您定义了一种新型曲线,它只是从给定曲线中选择一段。用法:
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 可能没那么有用。
我希望你从中得到了一些有用的东西。
THREE.TubeGeometry
returns一个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 );
}
fiddle: http://jsfiddle.net/k73pxyL2/
编辑:另一种方法,参见
three.js r.75
通常你可以使用.getPointAt()到"get a vector for point at relative position in curve according to arc length"的方法在曲线长度的一定百分比处得到一个点。
所以通常情况下如果你想绘制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 并演示如何动态使用它