如何创建宽度和厚度的Three.js 3D系列?

时间:2013-12-23 06:52:27

标签: javascript three.js geometry lines

有没有办法创建宽度和厚度的Three.js 3D系列线?

即使Three.js行对象支持linewidth,WebGL中所有平台上的所有浏览器都不支持此属性。

这是你在Three.js中设置线宽的地方:

    var material = new THREE.LineBasicMaterial({
        color: 0xff0000,
        linewidth: 5
    });

最近放弃了具有宽度的Three.js功能区对象。

Three.js管对象生成3D挤出,但是 - 基于贝塞尔 - 线不通过控制点。

有人能想到在Three.js中绘制线条系列(折线,情节线)的方法,它具有某种用户可定义的“体积”,例如宽度,厚度或半径吗?

这个问题可能是对这个问题的重述: Extruding a graph in three.js

鉴于我认为没有现成的方法,我很乐意参与创建一个简单的函数来回应这个问题。

但是指出现有可行方法的反应会很酷......

正如WestLangley建议的那样,一种可能的解决方案包括折线具有恒定的像素宽度 - 目前可用于Three.js画布渲染器。

这里显示了两个渲染器的比较:

Canvas and WebGL Lines Compared via GitHub Pages

Canvas and WebGL Lines Compared via jsFiddle

enter image description here

您可以在两个渲染器上指定线宽和类似结果的解决方案非常酷。

然而,还有其他思考3D线的方法,其中线条具有实际的物理结构。他们投下阴影,他们回应事件。这些也需要进行研究。

以下是GitHub页面的链接,其中包含两个由多个网格组成的线条演示:

Spheres and Cubes Polyline

Sphere and Cylinder Polylines

'昂贵的解决方案。每个关节都由一个完整的球体组成。

Cubes Polylines

Cubes Polylines

我的猜测是,将这些中的任何一个构建为平滑的单个网格对于要解决的问题将是复杂的。所以在此期间,这里有一个链接到3D线条的部分可视化,这些线条很宽并且有高度:

3D Box Line on jsFiddle

3d box lines

目标是必须以低复杂度编码 - 换句话说 - 对于假人来说。因此,3D线应该像添加球体或立方体一样简单和熟悉。几何+材质=网格>现场。在创建顶点和面时,几何图形应该非常经济。

线条应该有宽度和高度。向上总是在Y方向。该演示展示了这一点。演示没有显示的是角落被很好地消除了......

2 个答案:

答案 0 :(得分:7)

我制定了一个可能的解决方案,我认为这个解决方案符合您的大部分要求:

http://codepen.io/garciahurtado/pen/AGEsf?editors=001

enter image description here

这个概念非常简单:在"线框模式"中渲染任意几何体,然后对其应用全屏GLSL着色器以增加线框线的粗细。

着色器的灵感来自ThreeJS发行版中的模糊着色器,它基本上沿着水平轴和垂直轴复制图像。我自动化了该过程并使副本数量成为用户定义的参数,同时确保副本偏移了1个像素。

我在我的演示中使用了3D立方体网格(使用了相机),但将它转换为折线应该是微不足道的。

这个东西真正的肉和土豆在自定义着色器(片段着色器部分)中:

    uniform sampler2D tDiffuse;
    uniform int edgeWidth;
    uniform int diagOffset;
    uniform float totalWidth;
    uniform float totalHeight;
    const int MAX_LINE_WIDTH = 30; // Needed due to weird limitations in GLSL around for loops
    varying vec2 vUv;

    void main() {
        int offset = int( floor(float(edgeWidth) / float(2) + 0.5) );
        vec4 color = vec4( 0.0, 0.0, 0.0, 0.0);

        // Horizontal copies of the wireframe first
        for (int i = 0; i < MAX_LINE_WIDTH; i++) {
            float uvFactor = (float(1) / totalWidth);
            float newUvX = vUv.x + float(i - offset) * uvFactor;
            float newUvY = vUv.y + (float(i - offset) * float(diagOffset) ) * uvFactor;  // only modifies vUv.y if diagOffset > 0
            color = max(color, texture2D( tDiffuse, vec2( newUvX,  newUvY  ) ));    
            // GLSL does not allow loop comparisons against dynamic variables. Workaround below
            if(i == edgeWidth) break; 
        }

        // Now we create the vertical copies
        for (int i = 0; i < MAX_LINE_WIDTH; i++) {
            float uvFactor = (float(1) / totalHeight);
            float newUvX = vUv.x + (float(i - offset) * float(-diagOffset) ) * uvFactor; // only modifies vUv.x if diagOffset > 0
            float newUvY = vUv.y + float(i - offset) * uvFactor;
            color = max(color, texture2D( tDiffuse, vec2( newUvX, newUvY ) ));  
            if(i == edgeWidth) break;
        }

        gl_FragColor = color;
    }

优点:

  • 不需要超出行顶点的其他几何
  • 线条粗细是用户可定义的
  • 全屏着色器在GPU上应该相对温和
  • 可以在WebGL画布中完全实现

缺点:

  • 线条厚度在水平和垂直边缘处接近完美像素,但在对角线边缘略微偏离。这是由于使用的算法,并且是解决方案的限制。话虽如此,对于低线厚度和复杂的几何形状,用肉眼几乎看不到这一点。
  • 线条之间的关节将显示足够大的线条粗细的间隙。您可以使用Codepen演示来查看我的意思。我开始通过添加第二个&#34;对角线#34来实现这个解决方案,但它有点毛茸茸,我认为这只会是更高线宽(+8像素)或极端线角度的问题。如果您对此解决方案感兴趣,可以查看原始来源,了解我的目标。
  • 由于这使用全屏滤镜,因此您只能使用WebGL上下文来显示此厚度的对象。显示各种线宽需要额外的渲染过程。

答案 1 :(得分:2)

作为一种潜在的解决方案。您可以获取3d点,然后使用THREE.Vector3.project方法计算屏幕空间坐标。然后只需使用canvas及其lineTomoveTo操作。 Canvas 2d上下文确实支持可变线条粗细。

var w = renderer.domElement.innerWidth;
var h = renderer.domElement.innerHeight;
vector.project(camera);
context2d.lineWidth = 3;
var x = (vector.x+1)*(w/2);
var y = h - (vector.y+1)*(h/2);
context2d.lineTo(x,y);

另外,我不认为你可以使用相同的画布,所以它必须是你的gl渲染上下文画布上方的一个图层(另一个画布)。

如果您不经常更改相机 - 也可以构造多边形线并根据相机变换更新它的顶点位置。对于正交相机,这将最有效,因为只有旋转才需要顶点位置操作。

最后,您可以禁用画布清除并在圆圈或方框内使用偏移多次绘制线条。之后,您可以重新启用清除。这将需要几次额外的绘制操作,但它可能是最具扩展性的方法。

由于ANGLE的工作原理,以及我在Chrome和Firefox中的使用情况,开箱即用的原因并非如此,它通过DirectX模拟OpenGL 。来自ANGLE的人士表示,WebGL规范只需要支持线厚度达到1,因此他们不会将其视为一个错误并且不打算修复&#34;它。但是,在不使用ANGLE的情况下,线宽应适用于非Windows操作系统。