我正在寻找对WebGL如何工作的深刻理解.我希望获得大多数人不太关心的知识水平,因为这些知识对于普通的WebGL程序员来说并不是必需的.例如,整个渲染系统的每个部分(浏览器,图形驱动程序等)在屏幕上获取图像时起什么作用?每个浏览器是否必须创建一个javascript/html引擎/环境才能在浏览器中运行WebGL?为什么Chrome与WebGL兼容?
那么,有什么好的资源可以入手?对于我想要的东西,kronos规范有点缺乏(从我看到的浏览它几分钟).我想要的主要是如何在浏览器中实现/实现,以及还需要在系统上进行哪些更改才能实现.
sin*_*unk 45
希望这篇小小的文章对你有所帮助.它概述了我在WebGL和3D方面所学到的很大一部分内容.顺便说一句,如果我有什么不妥,有人请指正 - 因为我还在学习!
浏览器只是一个Web浏览器.它所做的就是公开WebGL API(通过JavaScript),程序员用它来完成其他任务.
就我所知,WebGL API本质上只是一组(浏览器提供的)JavaScript函数,它们围绕着OpenGL ES规范.因此,如果你了解OpenGL ES,你可以很快采用WebGL.但是,不要将它与纯OpenGL混淆."ES"很重要.
WebGL规范故意保留在非常低级别,从一个应用程序到下一个应用程序需要重新实现.社区可以编写自动化框架,并由开发人员选择使用哪个框架(如果有的话).推出自己的产品并不是一件难事,但它确实意味着在重新发明轮子上花费了大量开销.(FWIW,我一直在研究我自己的WebGL框架,称为Jax.)
图形驱动程序提供实际运行代码的OpenGL ES的实现.此时,它在机器硬件上运行,甚至低于C代码.虽然这是使WebGL成为可能的原因,但它也是一把双刃剑,因为OpenGL ES驱动程序中的错误(我已经注意到很多)已经出现在你的Web应用程序中,你不会必须知道它,除非您可以指望您的用户群提交一致的错误报告,包括操作系统,视频硬件和驱动程序版本.以下是这些问题的调试过程最终结果如何.
在Windows上,WebGL API和硬件之间存在一个额外的层:ANGLE或"Almost Native Graphics Layer Engine".由于Windows上的OpenGL ES驱动程序通常很糟糕,因此ANGLE接收这些调用并将其转换为DirectX 9调用.
现在您已经了解了各个部分是如何组合在一起的,让我们看一下关于如何将所有内容组合在一起以生成3D图像的较低级别说明.
首先,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
Run Code Online (Sandbox Code Playgroud)
其中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本身并不是那么快.在这种情况下,通过在GPU上进行数学计算所提供的硬件加速可能比在JavaScript中使用CPU进行数学加速更快.我们当然希望未来的JS实现能够通过更快地解决这个潜在问题.
当所有这些都被应用时,gl_Position变量将具有一组XYZ坐标,范围在[-1,1]和W分量中.这些称为剪辑坐标.
值得注意的是,剪辑坐标是顶点着色器真正需要生成的唯一内容.只要生成剪辑坐标结果,就可以完全跳过上面执行的矩阵变换.(我甚至尝试过换掉四元数的矩阵;它工作得很好但我废弃了项目,因为我没有得到我希望的性能改进.)
向gl_PositionWebGL 提供剪辑坐标后,通过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
Run Code Online (Sandbox Code Playgroud)
一旦确定了应该绘制像素的位置,就将像素切换到像素着色器,像素着色器选择像素的实际颜色.这可以通过多种方式完成,从简单地将特定颜色硬编码到纹理查找到更高级的正常和视差映射(这实质上是"欺骗"纹理查找以产生不同效果的方式).
现在,到目前为止,我们忽略了剪辑坐标的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
gma*_*man 11
我会读这些文章
http://webglfundamentals.org/webgl/lessons/webgl-how-it-works.html
假设这些文章很有用,其余部分是WebGL在浏览器中运行.它呈现为canvas标签.除了使用WebGL API生成图像而不是下载图像之外,您可以将画布标记视为img标记.
与其他HTML5标签一样,canvas标签可以使用CSS设置样式,位于页面的其他部分之下或之上.与页面的其他部分合成(混合).通过CSS以及页面的其他部分进行转换,旋转,缩放.这与OpenGL或OpenGL ES有很大不同.