WebGL 是如何工作的?
Posted
技术标签:
【中文标题】WebGL 是如何工作的?【英文标题】:How WebGL works? 【发布时间】:2011-11-11 19:26:55 【问题描述】:我希望深入了解 WebGL 的工作原理。我想在大多数人不太关心的水平上获得知识,因为这些知识对普通的 WebGL 程序员没有必要有用。例如,整个渲染系统的每个部分(浏览器、图形驱动程序等)在将图像显示在屏幕上时扮演什么角色? 是否每个浏览器都必须创建一个 javascript/html 引擎/环境才能在浏览器中运行 WebGL?为什么 chrome 在兼容 WebGL 方面领先于其他所有人?
那么,有什么好的入门资源呢?对于我想要的东西,kronos 规范有点缺乏(从我浏览了几分钟的情况来看)。我主要想知道这是如何在浏览器中完成/实现的,以及您的系统上还需要进行哪些更改才能使其成为可能。
【问题讨论】:
【参考方案1】:希望这篇小文章对您有所帮助。它概括了我所学到的关于 WebGL 和 3D 的大部分内容。顺便说一句,如果我有任何错误,请有人纠正我——因为我也在学习!
架构
浏览器就是这样,一个网络浏览器。它所做的只是公开 WebGL API(通过 JavaScript),程序员可以用它来做其他事情。
据我所知,WebGL API 本质上只是一组(浏览器提供的)JavaScript 函数,它们围绕着 OpenGL ES 规范。因此,如果您了解 OpenGL ES,则可以很快采用 WebGL。不过,不要将此与纯 OpenGL 混淆。 “ES”很重要。
WebGL 规范被刻意留得很低级,留下了很多东西 从一个应用程序到下一个应用程序重新实现。这取决于 社区为自动化编写框架,并由开发人员决定 选择使用哪个框架(如果有)。这并不完全困难 推出自己的,但这确实意味着花费大量的开销 重新发明***。 (FWIW,我一直在自己工作 WebGL framework called Jax 一段时间 现在。)
图形驱动程序提供实际运行代码的 OpenGL ES 实现。此时,它在机器硬件上运行,甚至低于 C 代码。虽然这首先使 WebGL 成为可能,但它也是一把双刃剑,因为 OpenGL ES 驱动程序中的错误(我已经注意到很多)会出现在您的 Web 应用程序中,而您不会除非您可以依靠您的用户群来提交连贯的错误报告,包括操作系统、视频硬件和驱动程序版本,否则您必须知道它。 Here's what the debug process for such issues ends up looking like.
在 Windows 上,WebGL API 和硬件之间存在一个额外的层:ANGLE, or "Almost Native Graphics Layer Engine"。因为 Windows 上的 OpenGL ES 驱动程序通常很糟糕,所以 ANGLE 接收这些调用并将它们转换为 DirectX 9 调用。
3D 绘图
既然您已经知道这些部分是如何组合在一起的,那么让我们看一下关于如何将所有部分组合在一起以生成 3D 图像的较低层次的解释。
JavaScript
首先,JavaScript 代码从 HTML5 canvas 元素中获取 3D 上下文。然后它注册一组着色器,这些着色器是用 GLSL([Open] GL 着色语言)编写的,本质上类似于 C 代码。
流程的其余部分非常模块化。您需要使用在着色器中定义的制服和属性将顶点数据和您打算使用的任何其他信息(例如顶点颜色、纹理坐标等)向下传递到图形管道,但确切的布局和命名此信息很大程度上取决于开发人员。
JavaScript 设置初始数据结构并将它们发送到 WebGL API,后者将它们发送到 ANGLE 或 OpenGL ES,最终将其发送到图形硬件。
顶点着色器
一旦信息可供着色器使用,着色器必须分两个阶段转换信息以生成 3D 对象。第一个阶段是顶点着色器,它设置网格坐标。 (这个阶段完全在视频卡上运行,在上面讨论的所有 API 之下。)大多数情况下,在顶点着色器上执行的过程看起来像这样:
gl_Position = PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX * VERTEX_POSITION
其中VERTEX_POSITION
是一个 4D 向量(x、y、z 和 w,通常设置为 1); VIEW_MATRIX
是一个 4x4 矩阵,代表相机对世界的看法; MODEL_MATRIX
是一个 4x4 矩阵,它将对象空间坐标(即,在应用旋转或平移之前对象的局部坐标)转换为世界空间坐标; PROJECTION_MATRIX
代表相机的镜头。
大多数情况下,
VIEW_MATRIX
和MODEL_MATRIX
是预先计算的,并且 叫MODELVIEW_MATRIX
。有时,所有 3 个都被预先计算为MODELVIEW_PROJECTION_MATRIX
或只是MVP
。这些通常是指 作为优化,虽然我想找时间做一些基准测试。它是 JavaScript 中的预计算可能实际上更慢,如果它是 每一帧都完成,因为 JavaScript 本身并不是那么快。在 在这种情况下,通过对 GPU 可能比在 JavaScript 中的 CPU 上执行更快。我们可以 当然希望未来的 JS 实现能够解决这个潜力 只需更快地解决问题。
剪辑坐标
当所有这些都被应用后,gl_Position
变量将具有一组范围为 [-1, 1] 的 XYZ 坐标和一个 W 分量。这些被称为剪辑坐标。
值得注意的是,剪辑坐标是顶点着色器真正唯一的东西 需要生产。您可以完全跳过矩阵变换 上面执行,只要你产生一个剪辑坐标结果。 (我什至有 尝试用四元数替换矩阵;有效 很好,但我放弃了这个项目,因为我没有得到 我希望的性能改进。)
在向gl_Position
提供剪辑坐标后,WebGL 将结果除以gl_Position.w
,生成所谓的标准化设备坐标。
从那里开始,将像素投影到屏幕上很简单,只需将屏幕尺寸乘以 1/2,然后再加上屏幕尺寸的 1/2。[1] 以下是一些剪辑坐标转换的示例在 800x600 显示器上转换为 2D 坐标:
clip = [0, 0]
x = (0 * 800/2) + 800/2 = 400
y = (0 * 600/2) + 600/2 = 300
clip = [0.5, 0.5]
x = (0.5 * 800/2) + 800/2 = 200 + 400 = 600
y = (0.5 * 600/2) + 600/2 = 150 + 300 = 450
clip = [-0.5, -0.25]
x = (-0.5 * 800/2) + 800/2 = -200 + 400 = 200
y = (-0.25 * 600/2) + 600/2 = -150 + 300 = 150
像素着色器
一旦确定了应该在哪里绘制像素,该像素就会被传递给像素着色器,由像素着色器选择像素的实际颜色。这可以通过多种方式完成,从简单地硬编码特定颜色到纹理查找到更高级的法线和视差映射(本质上是“欺骗”纹理查找以产生不同效果的方法)。
深度和深度缓冲区
现在,到目前为止,我们已经忽略了剪辑坐标的 Z 分量。这是怎么回事。当我们乘以投影矩阵时,第三个剪辑分量产生了一些数字。如果该数字大于 1.0 或小于 -1.0,则该数字超出了投影矩阵的视野范围,分别对应于矩阵 zFar 和 zNear 值。
因此,如果它不在 [-1, 1] 范围内,则它会被完全裁剪。如果它是在那个范围内,那么Z值被缩放到0到1[2]并与深度缓冲区比较[3]。深度缓冲区等于屏幕尺寸,因此如果使用 800x600 的投影,深度缓冲区为 800 像素宽和 600 像素高。我们已经有了像素的 X 和 Y 坐标,因此将它们插入深度缓冲区以获取当前存储的 Z 值。如果 Z 值大于新 Z 值,则新 Z 值更接近比之前绘制的任何值,并替换它[4]。此时点亮相关像素是安全的(或者在 WebGL 的情况下,将像素绘制到画布上),并将 Z 值存储为新的深度值。
如果 Z 值大于存储的深度值,则认为它在“后面”已经绘制的任何内容,并且该像素被丢弃。
[1]实际转换使用gl.viewport
设置将标准化设备坐标转换为像素。
[2]它实际上被缩放到gl.depthRange
设置。它们默认为 0 到 1。
[3]假设您有一个深度缓冲区并且您已使用 gl.enable(gl.DEPTH_TEST)
开启深度测试。
[4]您可以设置Z值与gl.depthFunc
的比较方式@
【讨论】:
非常好的描述。谢谢。 解释的杰作:) OpenGL ES 从未用于在桌面卡上渲染 WebGL,OpenGL ES 仅用于移动硬件,在具有 Windows 以外的操作的桌面上,浏览器引擎将 WebGL API 调用转换为 OpenGL 调用。这个信息也可以在这个非常好的文章中找到:codeflow.org/entries/2013/feb/02/why-you-should-use-webgl 写下您关于解决驱动程序错误的评论。请在crbug.com 或 firefox 等处提交错误。浏览器不希望您处理驱动程序错误,他们会尽最大努力解决这些错误,并使 WebGL 在任何地方都有一致的行为。这包括编写新的测试,这样驱动程序就不会倒退。但是他们只有在知道错误的情况下才能这样做,而且只有在您提交错误时他们才能知道。【参考方案2】:我会阅读这些文章
http://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
假设这些文章有帮助,剩下的就是 WebGL 在浏览器中运行。它渲染到画布标签。您可以将 canvas 标签想象为 img 标签,只是您使用 WebGL API 生成图像而不是下载图像。
与其他 HTML5 标记一样,canvas 标记可以使用 CSS 设置样式,位于页面其他部分的下方或上方。与页面的其他部分合成(混合)。与页面的其他部分一起被 CSS 转换、旋转、缩放。这与 OpenGL 或 OpenGL ES 有很大的不同。
【讨论】:
以上是关于WebGL 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章