草庐IT

javascript - 如何创建具有宽度和厚度的 Three.js 3D 线系列?

coder 2024-05-12 原文

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

尽管 Three.js 线对象支持线宽,但 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 渲染器目前可用。

此处显示了两个渲染器的比较:

Canvas and WebGL Lines Compared via GitHub Pages

Canvas and WebGL Lines Compared via jsFiddle

一个可以指定线宽并且在两个渲染器上都出现类似结果的解决方案会非常酷。

但是,还有其他的 3D 线条思考方式,其中线条具有实际的物理构造。他们转换阴影,他们对事件使用react。这些也需要研究。

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

Sphere and Cylinder Polylines

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

Cubes Polylines

我的猜测是,将其中任何一个构建为光滑的单个网格对于解决问题来说都是复杂的。因此,与此同时,这里有一个链接,指向宽高的 3D 线的部分可视化:

3D Box Line on jsFiddle

目标是必须“以低水平的复杂性——换句话说——为傻瓜编写代码”。因此,3D 线应该像添加球体或立方体一样简单和熟悉。几何体+ Material =网格>场景。就创建顶点和面而言,几何应该非常经济。

线条应该有宽度和高度。向上始终在 Y 方向。演示显示了这一点。演示没有显示的是 Angular 落被很好地斜接......

最佳答案

我制定了一个可能的解决方案,我相信它可以满足您的大部分要求:

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

这个概念相当简单:在“线框模式”中渲染任意几何体,然后对其应用全屏 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 Canvas 中实现

缺点:

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

关于javascript - 如何创建具有宽度和厚度的 Three.js 3D 线系列?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20738386/

有关javascript - 如何创建具有宽度和厚度的 Three.js 3D 线系列?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

随机推荐