使用Three.js绘制一个圆形(无填充)

17

我正在尝试绘制一个与这个网站上轨道模式非常相似的圆形。 我想使用Three.js而不是纯WebGL。

13个回答

28

Three.js r50加入了 CircleGeometry 。您可以在WebGL Geometries example中看到它(虽然带有面)。

几何图形中的第一个顶点创建在圆的中心处(在r84中,请参见CircleGeometry.js line 71,在r65中,请参见CircleGeometry.js line 18),如果您追求“完整的Pac-Man”或“无信息饼图”的外观,这是很好的。哦,而且如果您要使用除 LineBasicMaterial / LineDashedMaterial 之外的任何材质,似乎也是必需的。

我已经验证了以下代码在r60和r65中都有效:

var radius   = 100,
    segments = 64,
    material = new THREE.LineBasicMaterial( { color: 0x0000ff } ),
    geometry = new THREE.CircleGeometry( radius, segments );

// Remove center vertex
geometry.vertices.shift();

// Non closed circle with one open segment:
scene.add( new THREE.Line( geometry, material ) );

// To get a closed circle use LineLoop instead (see also @jackrugile his comment):
scene.add( new THREE.LineLoop( geometry, material ) );

PS: “docs”现在包括一个漂亮的CircleGeometry交互式示例:https://threejs.org/docs/#api/geometries/CircleGeometry


6
当使用THREE.Line方法时,第一个和最后一个顶点之间会有一个间隔。为了获得完全闭合的圆形,请改用THREE.LineLoop方法。https://threejs.org/docs/#api/objects/LineLoop - jackrugile
不再完全适用。请参考我的答案更新:https://dev59.com/d2Yr5IYBdhLWcg3wVY3J#61312034 - Andrew
此代码已被弃用,适用于THREE.REVISION = "150"。请参见下面的我的代码https://dev59.com/d2Yr5IYBdhLWcg3wVY3J#75658328以获取解决方案。 - Andrej

13

在较新版本的threejs中,API略有更改。

var segmentCount = 32,
    radius = 100,
    geometry = new THREE.Geometry(),
    material = new THREE.LineBasicMaterial({ color: 0xFFFFFF });

for (var i = 0; i <= segmentCount; i++) {
    var theta = (i / segmentCount) * Math.PI * 2;
    geometry.vertices.push(
        new THREE.Vector3(
            Math.cos(theta) * radius,
            Math.sin(theta) * radius,
            0));            
}

scene.add(new THREE.Line(geometry, material));

根据您的场景需要,修改segmentCount以使圆更加平滑或锯齿状。对于小圆来说,32个细分段会很平滑。对于像您链接的网站上的轨道,您可能希望有几百个细分段。

修改Vector3构造函数中三个组件的顺序以选择圆的方向。按照这里给出的方式,圆将与x/y平面对齐。


当我使用 THREE.Mesh 时,这个不起作用...有什么想法吗? - GvSharma

5

我不得不这样做,哈哈:

function createCircle() {
    let circleGeometry = new THREE.CircleGeometry(1.0, 30.0);
    circleGeometry.vertices.splice(0, 1); //<= This.
    return new THREE.LineLoop(circleGeometry,
        new THREE.LineBasicMaterial({ color: 'blue' }));
}
let circle = createCircle();
原因:否则,它不会绘制一个“纯”圆,有一条线从中心到圆的边缘,即使你使用LineLoop而不是Line。从数组中拼接(删除)第一个顶点是一种技巧,但似乎能达到预期效果。:)
(请注意,根据mrienstra的回答,“噢,如果你要使用除了LineBasicMaterial / LineDashedMaterial之外的任何材料,似乎这是必需的。”)

但是,如果你想要厚度,你就废了“由于WebGL渲染器在大多数平台上对OpenGL Core Profile的限制,线宽始终为1,无论设置的值是多少。”)...除非你使用:https://github.com/spite/THREE.MeshLine

这里有一个代码示例:https://dev59.com/N2gu5IYBdhLWcg3wByq1#61312721


1
此代码已被弃用,适用于THREE.REVISION = "150"。请参见下面的我的代码https://dev59.com/d2Yr5IYBdhLWcg3wVY3J#75657438以获取解决方案。 - Andrej

4

我使用了Mr.doob在这篇github文章中提到的代码。

var resolution = 100;
var amplitude = 100;
var size = 360 / resolution;

var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial( { color: 0xFFFFFF, opacity: 1.0} );
for(var i = 0; i <= resolution; i++) {
    var segment = ( i * size ) * Math.PI / 180;
    geometry.vertices.push( new THREE.Vertex( new THREE.Vector3( Math.cos( segment ) * amplitude, 0, Math.sin( segment ) * amplitude ) ) );         
}

var line = new THREE.Line( geometry, material );
scene.add(line);

1
当我使用以下代码时出现问题: var mesh = new THREE.Mesh( geometry, material ); 你有什么想法吗? - GvSharma

3

以下是 Three.js 文档中的示例:

var material = new THREE.MeshBasicMaterial({
    color: 0x0000ff
});

var radius = 5;
var segments = 32; //<-- Increase or decrease for more resolution I guess

var circleGeometry = new THREE.CircleGeometry( radius, segments );              
var circle = new THREE.Mesh( circleGeometry, material );
scene.add( circle );

2

我在这里遇到了一些问题 -- 特别是,CircleGeometry 在圆心处有一个额外的点,而我不喜欢试图删除该顶点的hack。

EllipseCurve可以做到我想要的(在r135中验证):

const curve = new THREE.EllipseCurve(
  0.0, 0.0,            // Center x, y
  10.0, 10.0,          // x radius, y radius
  0.0, 2.0 * Math.PI,  // Start angle, stop angle
);

const pts = curve.getSpacedPoints(256);
const geo = new THREE.BufferGeometry().setFromPoints(pts);

const mat = new THREE.LineBasicMaterial({ color: 0xFF00FF });
const circle = new THREE.LineLoop(geo, mat);
scene.add(circle);

2

嗯,我不知道它们是何时添加的-但是TorusGeometry应该可以胜任... THREE TorusGeometry THREE TorusGeometry

const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const torus = new THREE.Mesh( geometry, material );
scene.add( torus );

我不确定,但我认为它的价格不应该比那个线条东西贵太多,它是一个缓冲几何体,你可以调整大小和材料等...


1
var getStuffDashCircle2 = function () {

var segment = 100, radius = 100;

var lineGeometry = new THREE.Geometry();
var vertArray = lineGeometry.vertices;
var angle = 2 * Math.PI / segment;
for (var i = 0; i < segment; i++) {
    var x = radius * Math.cos(angle * i);
    var y = radius * Math.sin(angle * i);
    vertArray.push(new THREE.Vector3(x, y, 0));
}
lineGeometry.computeLineDistances();
var lineMaterial = new THREE.LineDashedMaterial({ color: 0x00cc00, dashSize: 4, gapSize: 2 });
var circle = new THREE.Line(lineGeometry, lineMaterial);

circle.rotation.x = Math.PI / 2;
circle.position.y = cylinderParam.trackHeight+20;
return   circle;
}

1

1

不必使用固定厚度的三维对象(在缩放时会变得明显),而是可以制作一个固定厚度的圆,保持成一条细线,这非常适合 UI 叠加,就像 OP 的示例链接一样。

absarc()setFromPoints() 在这里非常有用。

//Draws an orbit at 0,0 with given distance

let pts = new THREE.Path().absarc(0, 0, orbitDist, 0, Math.PI * 2).getPoints(90);
let g = new THREE.BufferGeometry().setFromPoints(pts);
let m = new THREE.LineBasicMaterial( { color: 0x00ffff, transparent: true, opacity: 0.5 } );
g.rotateX( - Math.PI / 2);
let l = new THREE.Line(g, m);
    
scene.add( l );

由于它在2D中形成了一个弧线,如果您希望在3D中移动它,则可以使用.position.set(x,y,z),或者无论如何都将其添加到此处的3D原点(0,0,0)中。我旋转了我的3D透视相机角度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接