在过去的几个月中,我一直在开发一个即将发布的名为Arcentry的应用程序,该应用程序使用户可以创建后端和云体系结构的等轴测图。
除了用VueJS编写的一些常规控件外,Arcentry首先也是一个大型交互式画布,用户可以在其上放置,移动,编辑和连接对象,线条,区域,标签,图标以及所有其他组成现代感的东西建筑。

在此画布上,我们使用了WebGL,这是基于浏览器的OpenGL 3D标准的实现-值得一提的是。
为什么选择WebGL?

现代浏览器使使用CSS和HTML来构建Web应用程序变得非常容易-这是我自从事Arcentry以来变得更加欣赏的事实。 希望此元素为红色? 有圆角? 悬停时更改颜色? 动画那种颜色变化? 附加点击处理程序? 没问题! 而且您不必担心世界上如何以及何时将HTML呈现到屏幕上。
使用WebGL,您必须自己照顾好所有这些事情。 那么,为什么任何有主见的人都可以使用它来构建复杂的Web应用程序? 出于三个原因:
- HTML有些事情是做不到的。 真正的3D图形就是其中之一。 诚然,CSS为您提供了“ 3D”变换,并且一些疯狂的天才将其扩展到构建功能齐全的第一人称射击游戏,但与完整的多边形模型和光线追踪相比,它仍然是一个非常谦虚的方法。
- WebGL非常灵活。 需要景深效果,色彩融合或以不同的分辨率和角度多次渲染同一场景吗? WebGL可以。
- WebGL很快。 真快。 2018图形卡已经成为工程学的绝对奇迹,WebGL可以卸载昂贵的对象计算并进行渲染,同时还可以释放CPU来运行应用程序逻辑。
我学到的是
我发现了在Crytek(Crysis和早期Farcry游戏的制造商)工作的3D开发的基础知识,并且对WebGL进行了Chrome实验工作,但我的专业知识首先是后端技术和HTML应用程序。 结果,此博客文章从Web应用程序开发人员的角度记录了我的学习经验-如果您一生都在致力于制作Triple-A FPS游戏,那么您很可能会发现此信息非常基础和不言而喻,但如果您是网络专家考虑使用3D可能会提供正确的视角。
让我们从头开始:
1.您必须选择一个框架俄罗斯选举风格
构建任何应用程序的首选通常是语言和框架之一。 对于WebApp,您面临着一个生态系统,该生态系统可以描述为从饱和到碎片化再到令人费解的任何事物:Angular,React,Vue和众多较小的库可以与您选择的CSS编译器,transpiler,微库和许多其他库结合使用其他事情。
对于WebGL,事情更容易:使用ThreeJS。 是的,还有诸如巴比伦之类的替代方案,以及诸如A-Frame(WebVR),PlayCanvas(游戏)或Pixi(2D图形)之类的更专门构建的高级库,但是Three被广泛采用,通用通用3D工具。

如果您曾经使用过3D建模软件,您将立即熟悉Three的概念。 正如您在“三”中用各种HTML元素在浏览器中构建DOM树一样,您也用各种3D对象构建了“场景图”。 这些可以是具体且可见的,例如“网格”(由“几何和材质”组成的3D对象),灯光和地图,也可以是抽象的,例如组,摄像机或曲线。
2.如何在预算上建立模型

有ThreeJS导出器可用于许多流行的3D建模工具,例如Maya或Cinema 4D,但我们使用的是Blender。 它特别直观还是易于使用? 绝对不! 我不得不看一个三分钟的教程,只是为了了解如何关闭面板。
但是它有据可查,带有大量社区插件,最重要的是:它是免费的! 在一个类别中,大多数已建立的替代方案(Autodesk Maya,3D Studio Max,Cinema 4D)的成本每年高达2000美元,这是不容小at的。
三个带有用于Blender的易于使用的导出器,该导出器吐出JSON文件,列出了几何图形的各个顶点,边和面。

3.互动完全取决于您

WebGL将场景渲染到平面像素画布上。 就浏览器而言,当您单击该画布时,单击的只是图像。 要将2D光标位置与虚拟空间中的3D对象相关联,您需要一条“光线”,即一条抽象线,它从您的相机无限延伸到空间之外,并与物体相交。
但是,即使配备了这样的光线并且您的光线投射器都已设置好,您也开始意识到浏览器为您提供了多少好处:
- 射线与路径上的所有对象相交-由您决定哪个对象位于顶部,哪个对象可单击,依此类推-z索引和指针事件在DOM中提供的某种东西。
- 您的事件独立于任何对象层次结构而存在。 事件冒泡,停止传播以及将事件路由到正确的处理程序完全取决于您。
4.酥脆的动态纹理真的很难
ThreeJS允许您将HTML5 Canvas元素的像素内容作为纹理投影到3D对象的表面上— Arcentry广泛使用这种方法来处理线条,箭头,区域,图标等。
但是有一个陷阱。 您的画布是由像素组成的2D曲面。 需要将它们投影到3D空间中的表面上-这个过程称为“纹理贴图”。 但是,您应将多少个像素映射到纹理的一个“纹理”? 这取决于两个因素:
- 最终渲染图像中纹理表面的大小。 如果距离相机较远,则可以降低分辨率。
- 您的用户硬件可以支持多少像素。 创建和映射高分辨率动态纹理非常昂贵。
Arcentry支持1000×1000像元的巨大绘图平面。 如果每个单元格为128px,这意味着我们需要一个超过160亿像素的画布作为2D形状纹理的基础。 显然是不可能的。 (测试表明,大于4096×4096的任何内容都会立即使浏览器崩溃。
我们通过创建最小的平面解决了这一问题-旋转了60度的正方形,直达可见区域的各个角。

这很漂亮,但出奇的棘手。 为什么? 正方形基本上贴在相机上,这意味着每次用户平移或缩放视图时,正方形都必须相应地移动和调整大小。
这也意味着我们的画布必须保留所有绘制步骤的分辨率独立日志,以在缩放时复制它们-但结果似乎证明增加了复杂性。
5.渲染文字更加困难

对于大面积的单色区域,与清晰呈现相关的问题可能并不明显,但对于文本来说却很明显。 默认情况下,投影到表面上的文本看起来模糊,并带有灰色边缘。 正确完成它需要三个步骤:
- 使用抗锯齿:这是一种渲染比所需更大的图像并将其缩小的技术。 在此过程中会内插多个像素,从而使边缘更平滑。
- 增加各向异性:简而言之,各向异性过滤是渲染表面受其渲染角度影响的范围。 当对象相对于3D视图变得更陡峭时,它可以降低像素密度(任何计算机科学教授都有很好的机会将胖红色F置于此定义之下)。 长话短说,对于像Arcentry这样的等距应用程序,您希望将各向异性提高到最大。
- 使用自定义混合:最后但并非最不重要的一点是,您的画布纹理将具有透明或半透明像素(例如,所有非文本像素)。 混合影响将它们如何混合到基础图像中。 ThreeJS的默认Alpha混合可能有点粗糙,并且在透明区域的边缘周围留有灰色边框,但是将其设置为具有简单,可靠的混合源因子1的自定义混合效果实在令人称奇。
6.您决定要渲染的内容以及何时渲染

看看上面的GIF。 它显示了Chrome的HTML控件重绘区域。 如您所见,它只会重绘绝对必要的内容。
我们需要在WebGL应用程序中实现相同目标吗? 不必要。 渲染可能是一项昂贵的操作,但是GPU的发展速度如此之快,以至于像Arcentry这样的简单,低多边形和低效果的场景不再是一个挑战。 结果,在每个动画帧上渲染它们通常都很好,通常每秒约60帧。
但这还不是全部。 是的,GPU速度很快,但是将信息复制到它们却不是。 每当移动对象,改变颜色或缩放相机时,都会有一些信息从CPU /内存传递到GPU。
为了保持较高的性能,您希望尽可能少且尽可能高效地执行此操作。 尤其是从动态纹理画布复制大像素缓冲区(例如从动态纹理画布中获得的图像数据)的成本非常高。
对于Arcentry,我们以多种方式解决了这一问题。 我们仅加载已更改的纹理数据,将纹理平面分为多层-一层用于线条和区域,一层用于像素对象(例如标签,图标或图像),以及频繁更新的平面以进行交互,例如悬停标记或选择矩形,我们从底层对象分离了实际的渲染,从而允许渲染器执行大量的脏/更改检查,并在不需要更新时跳过。
7.您决定是什么规模

WebGL使用三轴笛卡尔坐标系; x,y和z轴从中心的点0,0,0延伸到+和-无穷大。 0,0,0点在哪里? 没关系 一个轴上有多少个单位? 随你(由你决定。
面对在空旷,无限空间中导航有一种哲学上的美-但出于现实世界的原因,您需要决定一个单元的任意定义,并将其作为所有其他度量的未来基准:纹理分辨率(以像素为单位单位),模型比例(搅拌器使用自己的独立坐标系)等。
结论
ew,听起来很头疼-的确如此。 我们会再次选择WebGL吗? 绝对。 结果不仅真正值得,而且还有一个学习角度,使每一点进步都令人深感满意。 一旦您发现了抗锯齿,各向异性和自定义混合等奥秘概念的正确组合,并且您的视觉效果最终看起来清晰而吸引人,您会觉得自己已经做了些黑魔法-不仅是打开一些CSS下拉菜单,而且阴影,让浏览器完成艰苦的工作。
感谢您陪伴我这么久。 如果您想尝试Arcentry,可以在https://arcentry.com上注册以进行早期Beta访问,或者在Twitter(https://twitter.com/arcentry)上关注它,也可以关注我以了解更多信息(https: //twitter.com/wolframhempel)。