WebGL如何工作? [英] How WebGL works?

查看:70
本文介绍了WebGL如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻求对WebGL工作原理的深入了解.我想获得大多数人不太关心的知识,因为这些知识对于普通的WebGL程序员不是必需的.例如,整个渲染系统的每个部分(浏览器,图形驱动程序等)在屏幕上获取图像时起什么作用? 每个浏览器是否都必须创建一个javascript/html引擎/环境才能在浏览器中运行WebGL?为什么chrome在与WebGL兼容方面是其他所有人的头?

I'm looking for deep understanding of how WebGL works. I'm wanting to gain knowledge at a level that most people care less about, because the knowledge isn't necessary useful to the average WebGL programmer. For instance, what role does each part(browser, graphics driver, etc..) of the total rendering system play in getting an image on the screen? Does each browser have to create a javascript/html engine/environment in order to run WebGL in browser? Why is chrome a head of everyone else in terms of being WebGL compatible?

那么,有什么好的入门资源? kronos规范缺少(我浏览几分钟后看到的)我想要的东西.我主要想在浏览器中如何完成/实现此功能,以及在系统上需要进行哪些其他更改才能使其成为可能.

So, what's some good resources to get started? The kronos specification is kind of lacking( from what I saw browsing it for a few minutes ) for what I'm wanting. I'm wanting mostly how is this accomplished/implemented in browsers and what else needs to change on your system to make it possible.

推荐答案

希望这篇小文章对您有所帮助.总的来说,它概述了我所学到的有关WebGL和3D的大部分内容.顺便说一句,如果我做错了什么,请有人纠正我-因为我也在学习!

Hopefully this little write-up is helpful to you. It overviews a big chunk of what I've learned about WebGL and 3D in general. BTW, if I've gotten anything wrong, somebody please correct me -- because I'm still learning, too!

浏览器就是Web浏览器.它所做的只是公开了WebGL API(通过JavaScript),程序员可以使用它进行其他所有操作.

The browser is just that, a Web browser. All it does is expose the WebGL API (via JavaScript), which the programmer does everything else with.

据我所知,WebGL API本质上只是一组(浏览器提供的)JavaScript函数,这些函数围绕OpenGL ES规范.因此,如果您了解OpenGL ES,则可以很快采用WebGL.不过,不要将其与纯OpenGL混淆. "ES"很重要.

As near as I can tell, the WebGL API is essentially just a set of (browser-supplied) JavaScript functions which wrap around the OpenGL ES specification. So if you know OpenGL ES, you can adopt WebGL pretty quickly. Don't confuse this with pure OpenGL, though. The "ES" is important.

故意将WebGL规范留得很低级,给我们留下了很多 从一个应用程序重新实现到下一个应用程序.这取决于 社区编写自动化框架,并由开发人员决定 选择要使用的框架(如果有).这并不完全困难 自己动手,但这确实意味着花费了很多的开销 重新发明轮子. (FWIW,我一直在研究自己的 WebGL 名为Jax 的框架已有一段时间 现在.)

The WebGL spec was intentionally left very low-level, leaving a lot to be re-implemented from one application to the next. It is up to the community to write frameworks for automation, and up to the developer to choose which framework to use (if any). It's not entirely difficult to roll your own, but it does mean a lot of overhead spent on reinventing the wheel. (FWIW, I've been working on my own WebGL framework called Jax for a while now.)

图形驱动程序提供了实际运行您的代码的OpenGL ES的实现.此时,它正在机器硬件上运行,甚至在C代码之下.虽然这首先使WebGL成为可能,但这也是一把双刃剑,因为OpenGL ES驱动程序中的错误(我已经注意到很多错误)将显示在Web应用程序中,而您不会除非您可以依靠用户群来提交连贯的错误报告(包括操作系统,视频硬件和驱动程序版本),否则必定知道这一点. 这是此类问题的调试过程的最终结果.

The graphics driver supplies the implementation of OpenGL ES that actually runs your code. At this point, it's running on the machine hardware, below even the C code. While this is what makes WebGL possible in the first place, it's also a double edged sword because bugs in the OpenGL ES driver (which I've noted quite a number of already) will show up in your Web application, and you won't necessarily know it unless you can count on your user base to file coherent bug reports including OS, video hardware and driver versions. Here's what the debug process for such issues ends up looking like.

在Windows上,WebGL API和硬件之间存在一个额外的层: ANGLE或几乎是本机图形层引擎" .由于Windows上的OpenGL ES驱动程序通常很烂,因此ANGLE会收到这些调用并将它们转换为DirectX 9调用.

On Windows, there's an extra layer which exists between the WebGL API and the hardware: ANGLE, or "Almost Native Graphics Layer Engine". Because the OpenGL ES drivers on Windows generally suck, ANGLE receives those calls and translates them into DirectX 9 calls instead.

现在,您知道各个部分是如何组合在一起的,让我们看一下对所有东西如何组合在一起以生成3D图像的较低解释.

Now that you know how the pieces come together, let's look at a lower level explanation of how everything comes together to produce a 3D image.

首先,JavaScript代码从HTML5 canvas 元素获取3D上下文.然后,它注册一组着色器,这些着色器是使用GLSL([Open] GL着色语言)编写的,本质上类似于C代码.

First, the JavaScript code gets a 3D context from an HTML5 canvas element. Then it registers a set of shaders, which are written in GLSL ([Open] GL Shading Language) and essentially resemble C code.

剩余的过程是非常模块化的.您需要使用着色器中定义的统一和属性,获取顶点数据和打算使用的任何其他信息(例如,顶点颜色,纹理坐标等),直到图形管线,但要确定着色器的确切布局和命名.这些信息完全取决于开发人员.

The rest of the process is very modular. You need to get vertex data and any other information you intend to use (such as vertex colors, texture coordinates, and so forth) down to the graphics pipeline using uniforms and attributes which are defined in the shader, but the exact layout and naming of this information is very much up to the developer.

JavaScript设置初始数据结构并将其发送到WebGL API,然后将其发送到ANGLE或OpenGL ES,最终将其发送到图形硬件.

JavaScript sets up the initial data structures and sends them to the WebGL API, which sends them to either ANGLE or OpenGL ES, which ultimately sends it off to the graphics hardware.

一旦信息可供着色器使用,则着色器必须分两个阶段转换信息以生成3D对象.第一阶段是顶点着色器,它设置了网格坐标. (此阶段完全在视频卡上运行,位于上面讨论的所有API之下.)通常,在顶点着色器上执行的过程如下所示:

Once the information is available to the shader, the shader must transform the information in 2 phases to produce 3D objects. The first phase is the vertex shader, which sets up the mesh coordinates. (This stage runs entirely on the video card, below all of the APIs discussed above.) Most usually, the process performed on the vertex shader looks something like this:

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代表相机的镜头.

where VERTEX_POSITION is a 4D vector (x, y, z, and w which is usually set to 1); VIEW_MATRIX is a 4x4 matrix representing the camera's view into the world; MODEL_MATRIX is a 4x4 matrix which transforms object-space coordinates (that is, coords local to the object before rotation or translation have been applied) into world-space coordinates; and PROJECTION_MATRIX which represents the camera's lens.

VIEW_MATRIXMODEL_MATRIX通常是预先计算的, 称为MODELVIEW_MATRIX.有时,所有3个都预先计算为 MODELVIEW_PROJECTION_MATRIX或仅MVP.这些通常是指 作为优化,尽管我想抽出时间做一些基准测试.它是 如果可能的话,JavaScript中的预计算实际上会变慢 每帧都完成了,因为JavaScript本身并没有那么快.在 在这种情况下,通过对 GPU可能比在JavaScript中在CPU上执行速度更快.我们可以 当然希望将来的JS实现能够解决这一潜在问题 仅仅通过更快就能搞定.

Most often, the VIEW_MATRIX and MODEL_MATRIX are precomputed and called MODELVIEW_MATRIX. Occasionally, all 3 are precomputed into MODELVIEW_PROJECTION_MATRIX or just MVP. These are generally meant as optimizations, though I'd like find time to do some benchmarks. It's possible that precomputing is actually slower in JavaScript if it's done every frame, because JavaScript itself isn't all that fast. In this case, the hardware acceleration afforded by doing the math on the GPU might well be faster than doing it on the CPU in JavaScript. We can of course hope that future JS implementations will resolve this potential gotcha by simply being faster.

剪辑坐标

所有这些都应用后,gl_Position变量将具有一组范围在[-1,1]之内的XYZ坐标,以及一个W分量.这些称为剪辑坐标.

Clip Coordinates

When all of these have been applied, the gl_Position variable will have a set of XYZ coordinates ranging within [-1, 1], and a W component. These are called clip coordinates.

值得注意的是,剪辑坐标是顶点着色器真正唯一的功能 需要生产.您可以完全跳过矩阵转换 只要您产生剪辑坐标结果,就可以执行上述操作. (我什至 尝试将矩阵换成四元数;有效 很好,但我取消了该项目,因为我没有得到 我希望能改善性能.)

It's worth noting that clip coordinates is the only thing the vertex shader really needs to produce. You can completely skip the matrix transformations performed above, as long as you produce a clip coordinate result. (I have even experimented with swapping out matrices for quaternions; it worked just fine but I scrapped the project because I didn't get the performance improvements I'd hoped for.)

将剪辑坐标提供给gl_Position之后,WebGL将结果除以gl_Position.w来产生所谓的规范化设备坐标. 从那里开始,将像素投影到屏幕上很简单,只需将屏幕尺寸乘以1/2,然后再将屏幕尺寸乘以1/2. [1] 以下是一些剪辑坐标转换后的示例在800x600显示器上转换为2D坐标:

After you supply clip coordinates to gl_Position WebGL divides the result by gl_Position.w producing what's called normalized device coordinates. From there, projecting a pixel onto the screen is a simple matter of multiplying by 1/2 the screen dimensions and then adding 1/2 the screen dimensions.[1] Here are some examples of clip coordinates translated into 2D coordinates on an 800x600 display:

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

像素着色器

一旦确定了应该在哪里绘制像素,该像素就会移交给像素着色器,后者选择像素的实际颜色.这可以通过多种方式完成,从简单地对特定颜色进行硬编码到纹理查找,再到更高级的法线和视差映射(本质上是欺骗"纹理查找以产生不同效果的方式).

Pixel Shaders

Once it's been determined where a pixel should be drawn, the pixel is handed off to the pixel shader, which chooses the actual color the pixel will be. This can be done in a myriad of ways, ranging from simply hard-coding a specific color to texture lookups to more advanced normal and parallax mapping (which are essentially ways of "cheating" texture lookups to produce different effects).

现在,到目前为止,我们已经忽略了剪辑坐标的Z分量.这是如何解决的.当我们乘以投影矩阵时,第三个剪辑成分得到了一些数字.如果该数字大于1.0或小于-1.0,则该数字超出了投影矩阵的可视范围,分别对应于矩阵zFar和zNear值.

Now, so far we've ignored the Z component of the clip coordinates. Here's how that works out. When we multiplied by the projection matrix, the third clip component resulted in some number. If that number is greater than 1.0 or less than -1.0, then the number is beyond the view range of the projection matrix, corresponding to the matrix zFar and zNear values, respectively.

因此,如果它不在[-1,1]范围内,则将其完全剪切掉.如果在该范围内,则Z值将缩放为0到1 [2] ,并与深度缓冲区 [3] .深度缓冲区等于屏幕尺寸,因此,如果使用800x600的投影,则深度缓冲区的宽度为800像素,高度为600像素.我们已经有了像素的X和Y坐标,因此将它们插入深度缓冲区以获取当前存储的Z值.如果Z值大于新的Z值,则新的Z值比先前绘制的值更接近 ,并替换为 [4] .此时,可以安全地点亮有问题的像素(对于WebGL,则可以将像素绘制到画布上),并将Z值存储为新的深度值.

So if it's not in the range [-1, 1] then it's clipped entirely. If it is in that range, then the Z value is scaled to 0 to 1[2] and is compared to the depth buffer[3]. The depth buffer is equal to the screen dimensions, so that if a projection of 800x600 is used, the depth buffer is 800 pixels wide and 600 pixels high. We already have the pixel's X and Y coordinates, so they are plugged into the depth buffer to get the currently stored Z value. If the Z value is greater than the new Z value, then the new Z value is closer than whatever was previously drawn, and replaces it[4]. At this point it's safe to light up the pixel in question (or in the case of WebGL, draw the pixel to the canvas), and store the Z value as the new depth value.

如果Z值比存储的深度值更大,那么无论已绘制的Z值都被认为位于后面",并且像素将被丢弃.

If the Z value is greater than the stored depth value, then it is deemed to be "behind" whatever has already been drawn, and the pixel is discarded.

[1] 实际的转换使用gl.viewport设置将规范化的设备坐标转换为像素.

[1]The actual conversion uses the gl.viewport settings to convert from normalized device coordinates to pixels.

[2] 它实际上已缩放为gl.depthRange设置.它们默认为0到1.

[2]It's actually scaled to the gl.depthRange settings. They default 0 to 1.

[3] 假设您有一个深度缓冲区,并且已经使用gl.enable(gl.DEPTH_TEST)启用了深度测试.

[4] 您可以设置如何将Z值与gl.depthFunc

这篇关于WebGL如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆