精华内容
下载资源
问答
  • Infinite Tubes:利用WebGL技术实现的隧道体验
  • 一种可以在基于WebGL技术的主流浏览器上显示巨量二维矢量文本文字而不会明显增加浏览内存消耗和明显增加前端网页转化文本矢量数据所需时间的技术。通过此技术可以在一些低配置的设备上展示WebGL时当展示内容含有巨量...
  • 利用WebGL技术改进机器人实验效果,黄嵘,文福安,本文提出了采用WebGL技术的方法,运用于虚拟实验室的应用。将传统的需要客户端进行展示的机器人实验,在浏览器里流畅地展示出来,�
  • 基于WebGL技术和Oak3D引擎的交互式三维地球模型研究
  • 使用 WebGL 技术在 3D 空间中表示分析的结果。 用 在浏览器中打开 index.html(双击文件)。 在幻灯片 2 和 3 上,您可以使用鼠标在空间中移动相机。 内容概要 演示文稿的目的是比较爱沙尼亚语中使用的字符的频率。...
  • 基于HTML5的WebGL技术电信3D机房漫游源代码

    千次下载 热门讨论 2015-05-01 23:02:24
    基于HTML5的WebGL技术电信3D机房漫游源代码
  •  可视化展示,WebGL技术
    
    可视化展示,WebGL技术
    展开全文
  • 基于WebGL技术的Web环境3D对象和动画的交互式查看器 3D查看器的实时示例: : 该查看器允许在同一页面上使用多个实例,并可以以针对Web环境优化的开源文件格式SEA3D加载3D模型。 观看者的用户可以通过鼠标控件或...
  • WebGL 技术储备指南

    千次阅读 2016-07-02 10:44:04
    WebGL 虽然还未有广泛应用,但极具潜力和想象空间。本文是我学习 WebGL 时梳理知识脉络的产物,花点时间整理出来与大家分享。 示例 WebGL 很酷,有以下 demos 为证: 寻找奥兹国 赛车游戏 划船的男孩(Goo...

    WebGL 是 HTML 5 草案的一部分,可以驱动 Canvas 渲染三维场景。WebGL 虽然还未有广泛应用,但极具潜力和想象空间。本文是我学习 WebGL 时梳理知识脉络的产物,花点时间整理出来与大家分享。

    示例

    WebGL 很酷,有以下 demos 为证:

    寻找奥兹国
    赛车游戏
    划船的男孩(Goo Engine Demo)

    本文的目标

    本文的预期读者是:不熟悉图形学,熟悉前端,希望了解或系统学习 WebGL 的同学。

    本文不是 WebGL 的概述性文章,也不是完整详细的 WebGL 教程。本文只希望成为一篇供 WebGL 初学者使用的提纲。

    Canvas

    熟悉 Canvas 的同学都知道,Canvas 绘图先要获取绘图上下文:

    var context = canvas.getContext('2d');
    

    context上调用各种函数绘制图形,比如:

    // 绘制左上角为(0,0),右下角为(50, 50)的矩形
    context.fillRect(0, 0, 50, 50);
    

    WebGL 同样需要获取绘图上下文:

    var gl = canvas.getContext('webgl'); // 或 experimental-webgl
    

    但是接下来,如果想画一个矩形的话,就没这么简单了。实际上,Canvas 是浏览器封装好的一个绘图环境,在实际进行绘图操作时,浏览器仍然需要调用 OpenGL API。而 WebGL API 几乎就是 OpenGL API 未经封装,直接套了一层壳。

    Canvas 的更多知识,可以参考:

    矩阵变换

    三维模型,从文件中读出来,到绘制在 Canvas 中,经历了多次坐标变换。

    假设有一个最简单的模型:三角形,三个顶点分别为(-1,-1,0),(1,-1,0),(0,1,0)。这三个数据是从文件中读出来的,是三角形最初始的坐标(局部坐标)。如下图所示,右手坐标系。

    模型通常不会位于场景的原点,假设三角形的原点位于(0,0,-1)处,没有旋转或缩放,三个顶点分别为(-1,-1,-1),(1,-1,-1),(0,1,-1),即世界坐标。

    绘制三维场景必须指定一个观察者,假设观察者位于(0,0,1)处而且看向三角形,那么三个顶点相对于观察者的坐标为(-1,-1,-2),(1,-1,-2),(0,1,-2),即视图坐标。

    观察者的眼睛是一个点(这是透视投影的前提),水平视角和垂直视角都是90度,视野范围(目力所及)为[0,2]在Z轴上,观察者能够看到的区域是一个四棱台体。

    将四棱台体映射为标准立方体(CCV,中心为原点,边长为2,边与坐标轴平行)。顶点在 CCV 中的坐标,离它最终在 Canvas 中的坐标已经很接近了,如果把 CCV 的前表面看成 Canvas,那么最终三角形就画在图中橙色三角形的位置。

    上述变换是用矩阵来进行的。

    局部坐标 –(模型变换)-> 世界坐标 –(视图变换)-> 视图坐标 –(投影变换)–> CCV 坐标。

    以(0,1,0)为例,它的齐次向量为(0,0,1,1),上述变换的表示过程可以是:

    上面三个矩阵依次是透视投影矩阵,视图矩阵,模型矩阵。三个矩阵的值分别取决于:观察者的视角和视野距离,观察者在世界中的状态(位置和方向),模型在世界中的状态(位置和方向)。计算的结果是(0,1,1,2),化成齐次坐标是(0,0.5,0.5,1),就是这个点在CCV中的坐标,那么(0,0.5)就是在Canvas中的坐标(认为 Canvas 中心为原点,长宽都为2)。

    上面出现的(0,0,1,1)是(0,0,1)的齐次向量。齐次向量(x,y,z,w)可以代表三维向量(x,y,z)参与矩阵运算,通俗地说,w 分量为 1 时表示位置,w 分量为 0 时表示位移。

    WebGL 没有提供任何有关上述变换的机制,开发者需要亲自计算顶点的 CCV 坐标。

    关于坐标变换的更多内容,可以参考:

    比较复杂的是模型变换中的绕任意轴旋转(通常用四元数生成矩阵)和投影变换(上面的例子都没收涉及到)。

    关于绕任意轴旋转和四元数,可以参考:

    关于齐次向量的更多内容,可以参考。

    着色器和光栅化

    在 WebGL 中,开发者是通过着色器来完成上述变换的。着色器是运行在显卡中的程序,以 GLSL 语言编写,开发者需要将着色器的源码以字符串的形式传给 WebGL 上下文的相关函数。

    着色器有两种,顶点着色器和片元(像素)着色器,它们成对出现。顶点着色器任务是接收顶点的局部坐标,输出 CCV 坐标。CCV 坐标经过光栅化,转化为逐像素的数据,传给片元着色器。片元着色器的任务是确定每个片元的颜色。

    顶点着色器接收的是 attribute 变量,是逐顶点的数据。顶点着色器输出 varying 变量,也是逐顶点的。逐顶点的 varying 变量数据经过光栅化,成为逐片元的 varying 变量数据,输入片元着色器,片元着色器输出的结果就会显示在 Canvas 上。

    着色器功能很多,上述只是基本功能。大部分炫酷的效果都是依赖着色器的。如果你对着色器完全没有概念,可以试着理解下一节 hello world 程序中的着色器再回顾一下本节。

    关于更多着色器的知识,可以参考:

    程序

    这一节解释绘制上述场景(三角形)的 WebGL 程序。点这个链接,查看源代码,试图理解一下。这段代码出自WebGL Programming Guide,我作了一些修改以适应本文内容。如果一切正常,你看到的应该是下面这样:

    解释几点(如果之前不了解 WebGL ,多半会对下面的代码困惑,无碍):

    1. 字符串 VSHADER_SOURCE 和 FSHADER_SOURCE 是顶点着色器和片元着色器的源码。可以将着色器理解为有固定输入和输出格式的程序。开发者需要事先编写好着色器,再按照一定格式着色器发送绘图命令。

    2. Part2 将着色器源码编译为 program 对象:先分别编译顶点着色器和片元着色器,然后连接两者。如果编译源码错误,不会报 JS 错误,但可以通过其他 API(如gl.getShaderInfo等)获取编译状态信息(成功与否,如果出错的错误信息)。

      // 顶点着色器
      var vshader = gl.createShader(gl.VERTEX_SHADER);
      gl.shaderSource(vshader, VSHADER_SOURCE);
      gl.compileShader(vshader);
      // 同样新建 fshader
      var program = gl.createProgram();
      gl.attachShader(program, vshader);
      gl.attachShader(program, fshader);
      gl.linkProgram(program);
      
    3. program 对象需要指定使用它,才可以向着色器传数据并绘制。复杂的程序通常有多个 program 对 象,(绘制每一帧时)通过切换 program 对象绘制场景中的不同效果。

      gl.useProgram(program);
      
    4. Part3 向正在使用的着色器传入数据,包括逐顶点的 attribute 变量和全局的 uniform 变量。向着色器传入数据必须使用 ArrayBuffer,而不是常规的 JS 数组。

      var varray = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0])
      
    5. WebGL API 对 ArrayBuffer 的操作(填充缓冲区,传入着色器,绘制等)都是通过 gl.ARRAY_BUFFER 进行的。在 WebGL 系统中又很多类似的情况。

      // 只有将 vbuffer 绑定到 gl.ARRAY_BUFFER,才可以填充数据
      gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
      // 这里的意思是,向“绑定到 gl.ARRAY_BUFFER”的缓冲区中填充数据
      gl.bufferData(gl.ARRAY_BUFFER, varray, gl.STATIC_DRAW);
      // 获取 a_Position 变量在着色器程序中的位置,参考顶点着色器源码
      var aloc = gl.getAttribLocation(program, 'a_Position');
      // 将 gl.ARRAY_BUFFER 中的数据传入 aloc 表示的变量,即 a_Position
      gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);
      gl.enableVertexAttribArray(aloc);
      
    6. 向着色器传入矩阵时,是按列存储的。可以比较一下 mmatrix 和矩阵变换一节中的模型矩阵(第 3 个)。

    7. 顶点着色器计算出的 gl_Position 就是 CCV 中的坐标,比如最上面的顶点(蓝色)的 gl_Position 化成齐次坐标就是(0,0.5,0.5,1)。

    8. 向顶点着色器传入的只是三个顶点的颜色值,而三角形表面的颜色渐变是由这三个颜色值内插出的。光栅化不仅会对 gl_Position 进行,还会对 varying 变量插值。

    9. gl.drawArrays()方法驱动缓冲区进行绘制,gl.TRIANGLES 指定绘制三角形,也可以改变参数绘制点、折线等等。

    关于 ArrayBuffer 的详细信息,可以参考:

    关于 gl.TRIANGLES 等其他绘制方式,可以参考下面这张图或这篇博文

    引用自http://3dgep.com/rendering-primitives-with-opengl/

    深度检测

    当两个表面重叠时,前面的模型会挡住后面的模型。比如这个例子,绘制了两个交叉的三角形( varray 和 carray 的长度变为 18,gl.drawArrays 最后一个参数变为 6)。为了简单,这个例子去掉了矩阵变换过程,直接向着色器传入 CCV 坐标。

    顶点着色器给出了 6 个顶点的 gl_Position ,经过光栅化,片元着色器获得了 2X 个片元(假设 X 为每个三角形的像素个数),每个片元都离散的 x,y 坐标值,还有 z 值。x,y 坐标就是三角形在 Canvas 上的坐标,但如果有两个具有相同 x,y 坐标的片元同时出现,那么 WebGL 就会取 z 坐标值较小的那个片元。

    在深度检测之前,必须在绘制前开启一个常量。否则,WebGL 就会按照在 varray 中定义的顺序绘制了,后面的会覆盖前面的。

    gl.enable(gl.DEPTH_TEST);
    

    实际上,WebGL 的逻辑是这样的:依次处理片元,如果渲染缓冲区(这里就是 Canvas 了)的那个与当前片元对应的像素还没有绘制时,就把片元的颜色画到渲染缓冲区对应像素里,同时把片元的 z 值缓存在另一个深度缓冲区的相同位置;如果当前缓冲区的对应像素已经绘制过了,就去查看深度缓冲区中对应位置的 z 值,如果当前片元 z 值小,就重绘,否则就放弃当前片元。

    WebGL 的这套逻辑,对理解蒙版(后面会说到)有一些帮助。

    顶点索引

    gl.drawArrays()是按照顶点的顺序绘制的,而 gl.drawElements()可以令着色器以一个索引数组为顺序绘制顶点。比如这个例子

    这里画了两个三角形,但只用了 5 个顶点,有一个顶点被两个三角形共用。这时需要建立索引数组,数组的每个元素表示顶点的索引值。将数组填充至gl.ELEMENT_ARRAY,然后调用 gl.drawElements()。

    var iarray = new Uint8Array([0,1,2,2,3,4]);
    var ibuffer = gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW);
    

    纹理

    attribute 变量不仅可以传递顶点的坐标,还可以传递其他任何逐顶点的数据。比如 HelloTriangle 程序把单个顶点的颜色传入了 a_Color,片元着色器收到 v_Color 后直接赋给 gl_FragmentColor,就决定了颜色。

    attribute 变量还可以帮助绘制纹理。绘制纹理的基本原理是,为每个顶点指定一个纹理坐标(在(0,0)与(1,1,)的正方形中),然后传入纹理对象。片元着色器拿到的是对应片元的内插后的纹理坐标,就利用这个纹理坐标去纹理对象上取颜色,再画到片元上。内插后的纹理坐标很可能不恰好对应纹理上的某个像素,而是在几个像素之间(因为通常的图片纹理也是离散),这时可能会通过周围几个像素的加权平均算出该像素的值(具体有若干种不同方法,可以参考)。

    比如这个例子

    纹理对象和缓冲区对象很类似:使用 gl 的 API 函数创建,需要绑定至常量 gl.ARRAY_BUFFER 和 gl.TEXTURE_2D ,都通过常量对象向其中填入图像和数据。不同的是,纹理对象在绑定时还需要激活一个纹理单元(此处的gl.TEXTURE0),而 WebGL 系统支持的纹理单元个数是很有限的(一般为 8 个)。

    var texture = gl.createTexture();
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, textureImage);
    var sloc = gl.getUniformLocation(program, 'u_Sampler');
    gl.uniform1i(sloc, 0);
    

    片元着色器内声明了 sampler2D 类型的 uniform 变量,通过texture2D函数取样。

    precision mediump float;
    uniform sampler2D u_Sampler;
    varying vec2 v_TexCoord;
    void main() {
      gl_FragColor = texture2D(u_Sampler, v_TexCoord);
    };
    

    混合与蒙版

    透明效果是用混合机制完成的。混合机制与深度检测类似,也发生在试图向某个已填充的像素填充颜色时。深度检测通过比较z值来确定像素的颜色,而混合机制会将两种颜色混合。比如这个例子

    混合的顺序是按照绘制的顺序进行的,如果绘制的顺序有变化,混合的结果通常也不同。如果模型既有非透明表面又有透明表面,绘制透明表面时开启蒙版,其目的是锁定深度缓冲区,因为半透明物体后面的物体还是可以看到的,如果不这样做,半透明物体后面的物体将会被深度检测机制排除。

    开启混合的代码如下。gl.blendFunc方法指定了混合的方式,这里的意思是,使用源(待混合)颜色的 α 值乘以源颜色,加上 1-[源颜色的 α]乘以目标颜色。

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    

    所谓 α 值,就是颜色的第 4 个分量。

    var carray = new Float32Array([
      1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,
      0,0,1,0.4,0,0,1,0.4,0,0,1,0.4
      ]);
    

    浏览器的WebGL系统

    WebGL 系统各个组成部分在既定规则下互相配合。稍作梳理如下。

    这张图比较随意,箭头上的文字表示 API,箭头方向大致表现了数据的流动方向,不必深究。

    光照

    WebGL 没有为光照提供任何内置的方法,需要开发者在着色器中实现光照算法。

    光是有颜色的,模型也是有颜色的。在光照下,最终物体呈现的颜色是两者共同作用的结果。

    实现光照的方式是:将光照的数据(点光源的位置,平行光的方向,以及光的颜色和强度)作为 uniform 变量传入着色器中,将物体表面每个顶点处的法线作为 attribute 变量传入着色器,遵循光照规则,修订最终片元呈现的颜色。

    光照又分为逐顶点的和逐片元的,两者的区别是,将法线光线交角因素放在顶点着色器中考虑还是放在片元着色器中考虑。逐片元光照更加逼真,一个极端的例子是:

    此时,点光源在距离一个表面较近处,表面中央 A 处较亮,四周较暗。但是在逐顶点光照下,表面的颜色(的影响因子)是由顶点内插出来的,所以表面中央也会比较暗。而逐片元光照直接使用片元的位置和法线计算与点光源的交角,因此表面中央会比较亮。

    复杂模型

    复杂模型可能有包括子模型,子模型可能与父模型有相对运动。比如开着雨刮器的汽车,雨刮器的世界坐标是受父模型汽车,和自身的状态共同决定的。若要计算雨刮器某顶点的位置,需要用雨刮器相对汽车的模型矩阵乘上汽车的模型矩阵,再乘以顶点的局部坐标。

    复杂模型可能有很多表面,可能每个表面使用的着色器就不同。通常将模型拆解为组,使用相同着色器的表面为一组,先绘制同一组中的内容,然后切换着色器。每次切换着色器都要重新将缓冲区中的数据分配给着色器中相应变量。

    动画

    动画的原理就是快速地擦除和重绘。常用的方法是大名鼎鼎的 requestAnimationFrame 。不熟悉的同学,可以参考正美的介绍

    WebGL库

    目前最流行的 WebGL 库是 ThreeJS,很强大,官网代码

    调试工具

    比较成熟的 WebGL 调试工具是WebGL Inspector

    网络资源和书籍

    英文的关于 WebGL 的资源有很多,包括:

    国内最早的 WebGL 教程是由郝稼力翻译的,放在 hiwebgl 上,目前 hiwebgl 已经关闭,但教程还可以在这里找到。郝稼力目前运营着Lao3D

    国内已经出版的 WebGL 书籍有:

    展开全文
  • WebGL技术储备指南

    2016-07-13 01:38:37
    WebGL 虽然还未有广泛应用,但极具潜力和想象空间。本文是我学习 WebGL 时梳理知识脉络的产物,花点时间整理出来与大家分享。 文章出处:http://www.open-open.com/news/view/1eb8555 WebGL 是 HTML 5 ...

    摘要:WebGL 是 HTML 5 草案的一部分,可以驱动 Canvas 渲染三维场景。WebGL 虽然还未有广泛应用,但极具潜力和想象空间。本文是我学习 WebGL 时梳理知识脉络的产物,花点时间整理出来与大家分享。

    WebGL 是 HTML 5 草案的一部分,可以驱动 Canvas 渲染三维场景。WebGL 虽然还未有广泛应用,但极具潜力和想象空间。本文是我学习 WebGL 时梳理知识脉络的产物,花点时间整理出来与大家分享。

    示例

    WebGL 很酷,有以下 demos 为证:

    寻找奥兹国
    赛车游戏
    划船的男孩(Goo Engine Demo)

    本文的目标

    本文的预期读者是:不熟悉图形学,熟悉前端,希望了解或系统学习 WebGL 的同学。

    本文不是 WebGL 的概述性文章,也不是完整详细的 WebGL 教程。本文只希望成为一篇供 WebGL 初学者使用的提纲。

    Canvas

    熟悉 Canvas 的同学都知道,Canvas 绘图先要获取绘图上下文:

    var context = canvas.getContext('2d'); 

    context上调用各种函数绘制图形,比如:

    // 绘制左上角为(0,0),右下角为(50, 50)的矩形
    context.fillRect(0, 0, 50, 50); 

    WebGL 同样需要获取绘图上下文:

    var gl = canvas.getContext('webgl'); // 或 experimental-webgl 

    但是接下来,如果想画一个矩形的话,就没这么简单了。实际上,Canvas 是浏览器封装好的一个绘图环境,在实际进行绘图操作时,浏览器仍然需要调用 OpenGL API。而 WebGL API 几乎就是 OpenGL API 未经封装,直接套了一层壳。

    Canvas 的更多知识,可以参考:

    矩阵变换

    三维模型,从文件中读出来,到绘制在 Canvas 中,经历了多次坐标变换。

    假设有一个最简单的模型:三角形,三个顶点分别为(-1,-1,0),(1,-1,0),(0,1,0)。这三个数据是从文件中读出来的,是三角形最初始的坐标(局部坐标)。如下图所示,右手坐标系。

     WebGL技术储备指南

    模型通常不会位于场景的原点,假设三角形的原点位于(0,0,-1)处,没有旋转或缩放,三个顶点分别为(-1,-1,-1),(1,-1,-1),(0,1,-1),即世界坐标。

     WebGL技术储备指南

    绘制三维场景必须指定一个观察者,假设观察者位于(0,0,1)处而且看向三角形,那么三个顶点相对于观察者的坐标为(-1,-1,-2),(1,-1,-2),(0,1,-2),即视图坐标。

     WebGL技术储备指南

    观察者的眼睛是一个点(这是透视投影的前提),水平视角和垂直视角都是90度,视野范围(目力所及)为[0,2]在Z轴上,观察者能够看到的区域是一个四棱台体。

     WebGL技术储备指南

    将四棱台体映射为标准立方体(CCV,中心为原点,边长为2,边与坐标轴平行)。顶点在 CCV 中的坐标,离它最终在 Canvas 中的坐标已经很接近了,如果把 CCV 的前表面看成 Canvas,那么最终三角形就画在图中橙色三角形的位置。

     WebGL技术储备指南

    上述变换是用矩阵来进行的。

    局部坐标 –(模型变换)-> 世界坐标 –(视图变换)-> 视图坐标 –(投影变换)–> CCV 坐标。

    以(0,1,0)为例,它的齐次向量为(0,0,1,1),上述变换的表示过程可以是:

     WebGL技术储备指南

    上面三个矩阵依次是透视投影矩阵,视图矩阵,模型矩阵。三个矩阵的值分别取决于:观察者的视角和视野距离,观察者在世界中的状态(位置和方向),模 型在世界中的状态(位置和方向)。计算的结果是(0,1,1,2),化成齐次坐标是(0,0.5,0.5,1),就是这个点在CCV中的坐标,那么 (0,0.5)就是在Canvas中的坐标(认为 Canvas 中心为原点,长宽都为2)。

    上面出现的(0,0,1,1)是(0,0,1)的齐次向量。齐次向量(x,y,z,w)可以代表三维向量(x,y,z)参与矩阵运算,通俗地说,w 分量为 1 时表示位置,w 分量为 0 时表示位移。

    WebGL 没有提供任何有关上述变换的机制,开发者需要亲自计算顶点的 CCV 坐标。

    关于坐标变换的更多内容,可以参考:

    比较复杂的是模型变换中的绕任意轴旋转(通常用四元数生成矩阵)和投影变换(上面的例子都没收涉及到)。

    关于绕任意轴旋转和四元数,可以参考:

    关于齐次向量的更多内容,可以参考。

    着色器和光栅化

    在 WebGL 中,开发者是通过着色器来完成上述变换的。着色器是运行在显卡中的程序,以 GLSL 语言编写,开发者需要将着色器的源码以字符串的形式传给 WebGL 上下文的相关函数。

    着色器有两种,顶点着色器和片元(像素)着色器,它们成对出现。顶点着色器任务是接收顶点的局部坐标,输出 CCV 坐标。CCV 坐标经过光栅化,转化为逐像素的数据,传给片元着色器。片元着色器的任务是确定每个片元的颜色。

    顶点着色器接收的是 attribute 变量,是逐顶点的数据。顶点着色器输出 varying 变量,也是逐顶点的。逐顶点的 varying 变量数据经过光栅化,成为逐片元的 varying 变量数据,输入片元着色器,片元着色器输出的结果就会显示在 Canvas 上。

     WebGL技术储备指南

    着色器功能很多,上述只是基本功能。大部分炫酷的效果都是依赖着色器的。如果你对着色器完全没有概念,可以试着理解下一节 hello world 程序中的着色器再回顾一下本节。

    关于更多着色器的知识,可以参考:

    程序

    这一节解释绘制上述场景(三角形)的 WebGL 程序。点这个链接,查看源代码,试图理解一下。这段代码出自WebGL Programming Guide,我作了一些修改以适应本文内容。如果一切正常,你看到的应该是下面这样:

     WebGL技术储备指南

    解释几点(如果之前不了解 WebGL ,多半会对下面的代码困惑,无碍):

    1. 字符串 VSHADER_SOURCE 和 FSHADER_SOURCE 是顶点着色器和片元着色器的源码。可以将着色器理解为有固定输入和输出格式的程序。开发者需要事先编写好着色器,再按照一定格式着色器发送绘图命令。

    2. Part2 将着色器源码编译为 program 对象:先分别编译顶点着色器和片元着色器,然后连接两者。如果编译源码错误,不会报 JS 错误,但可以通过其他 API(如gl.getShaderInfo等)获取编译状态信息(成功与否,如果出错的错误信息)。

      // 顶点着色器
      var vshader = gl.createShader(gl.VERTEX_SHADER);
      gl.shaderSource(vshader, VSHADER_SOURCE);
      gl.compileShader(vshader);
      // 同样新建 fshader
      var program = gl.createProgram();
      gl.attachShader(program, vshader);
      gl.attachShader(program, fshader);
      gl.linkProgram(program); 
    3. program 对象需要指定使用它,才可以向着色器传数据并绘制。复杂的程序通常有多个 program 对 象,(绘制每一帧时)通过切换 program 对象绘制场景中的不同效果。

      gl.useProgram(program); 
    4. Part3 向正在使用的着色器传入数据,包括逐顶点的 attribute 变量和全局的 uniform 变量。向着色器传入数据必须使用 ArrayBuffer,而不是常规的 JS 数组。

      var varray = new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0]) 
    5. WebGL API 对 ArrayBuffer 的操作(填充缓冲区,传入着色器,绘制等)都是通过 gl.ARRAY_BUFFER 进行的。在 WebGL 系统中又很多类似的情况。

      // 只有将 vbuffer 绑定到 gl.ARRAY_BUFFER,才可以填充数据
      gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
      // 这里的意思是,向“绑定到 gl.ARRAY_BUFFER”的缓冲区中填充数据
      gl.bufferData(gl.ARRAY_BUFFER, varray, gl.STATIC_DRAW);
      // 获取 a_Position 变量在着色器程序中的位置,参考顶点着色器源码
      var aloc = gl.getAttribLocation(program, 'a_Position');
      // 将 gl.ARRAY_BUFFER 中的数据传入 aloc 表示的变量,即 a_Position
      gl.vertexAttribPointer(aloc, 3, gl.FLOAT, false, 0, 0);
      gl.enableVertexAttribArray(aloc); 
    6. 向着色器传入矩阵时,是按列存储的。可以比较一下 mmatrix 和矩阵变换一节中的模型矩阵(第 3 个)。

    7. 顶点着色器计算出的 gl_Position 就是 CCV 中的坐标,比如最上面的顶点(蓝色)的 gl_Position 化成齐次坐标就是(0,0.5,0.5,1)。

    8. 向顶点着色器传入的只是三个顶点的颜色值,而三角形表面的颜色渐变是由这三个颜色值内插出的。光栅化不仅会对 gl_Position 进行,还会对 varying 变量插值。

    9. gl.drawArrays()方法驱动缓冲区进行绘制,gl.TRIANGLES 指定绘制三角形,也可以改变参数绘制点、折线等等。

    关于 ArrayBuffer 的详细信息,可以参考:

    关于 gl.TRIANGLES 等其他绘制方式,可以参考下面这张图或这篇博文

     WebGL技术储备指南

    深度检测

    当两个表面重叠时,前面的模型会挡住后面的模型。比如这个例子,绘制了两个交叉的三角形( varray 和 carray 的长度变为 18,gl.drawArrays 最后一个参数变为 6)。为了简单,这个例子去掉了矩阵变换过程,直接向着色器传入 CCV 坐标。

     WebGL技术储备指南

     WebGL技术储备指南

    顶点着色器给出了 6 个顶点的 gl_Position ,经过光栅化,片元着色器获得了 2X 个片元(假设 X 为每个三角形的像素个数),每个片元都离散的 x,y 坐标值,还有 z 值。x,y 坐标就是三角形在 Canvas 上的坐标,但如果有两个具有相同 x,y 坐标的片元同时出现,那么 WebGL 就会取 z 坐标值较小的那个片元。

    在深度检测之前,必须在绘制前开启一个常量。否则,WebGL 就会按照在 varray 中定义的顺序绘制了,后面的会覆盖前面的。

    gl.enable(gl.DEPTH_TEST); 

    实际上,WebGL 的逻辑是这样的:依次处理片元,如果渲染缓冲区(这里就是 Canvas 了)的那个与当前片元对应的像素还没有绘制时,就把片元的颜色画到渲染缓冲区对应像素里,同时把片元的 z 值缓存在另一个深度缓冲区的相同位置;如果当前缓冲区的对应像素已经绘制过了,就去查看深度缓冲区中对应位置的 z 值,如果当前片元 z 值小,就重绘,否则就放弃当前片元。

    WebGL 的这套逻辑,对理解蒙版(后面会说到)有一些帮助。

    顶点索引

    gl.drawArrays()是按照顶点的顺序绘制的,而 gl.drawElements()可以令着色器以一个索引数组为顺序绘制顶点。比如这个例子

     WebGL技术储备指南

    这里画了两个三角形,但只用了 5 个顶点,有一个顶点被两个三角形共用。这时需要建立索引数组,数组的每个元素表示顶点的索引值。将数组填充至gl.ELEMENT_ARRAY,然后调用 gl.drawElements()。

    var iarray = new Uint8Array([0,1,2,2,3,4]);
    var ibuffer = gl.createBuffer(gl.ARRAY_BUFFER, ibuffer);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, iarray, gl.STATIC_DRAW); 

    纹理

    attribute 变量不仅可以传递顶点的坐标,还可以传递其他任何逐顶点的数据。比如 HelloTriangle 程序把单个顶点的颜色传入了 a_Color,片元着色器收到 v_Color 后直接赋给 gl_FragmentColor,就决定了颜色。

    attribute 变量还可以帮助绘制纹理。绘制纹理的基本原理是,为每个顶点指定一个纹理坐标(在(0,0)与(1,1,)的正方形中),然后传入纹理对象。片元着色器拿 到的是对应片元的内插后的纹理坐标,就利用这个纹理坐标去纹理对象上取颜色,再画到片元上。内插后的纹理坐标很可能不恰好对应纹理上的某个像素,而是在几 个像素之间(因为通常的图片纹理也是离散),这时可能会通过周围几个像素的加权平均算出该像素的值(具体有若干种不同方法,可以参考)。

    比如这个例子

     WebGL技术储备指南

    纹理对象和缓冲区对象很类似:使用 gl 的 API 函数创建,需要绑定至常量 gl.ARRAY_BUFFER 和 gl.TEXTURE_2D ,都通过常量对象向其中填入图像和数据。不同的是,纹理对象在绑定时还需要激活一个纹理单元(此处的gl.TEXTURE0),而 WebGL 系统支持的纹理单元个数是很有限的(一般为 8 个)。

    var texture = gl.createTexture();
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, textureImage);
    var sloc = gl.getUniformLocation(program, 'u_Sampler');
    gl.uniform1i(sloc, 0); 

    片元着色器内声明了 sampler2D 类型的 uniform 变量,通过texture2D函数取样。

    precision mediump float;
    uniform sampler2D u_Sampler;
    varying vec2 v_TexCoord;
    void main() {
      gl_FragColor = texture2D(u_Sampler, v_TexCoord);
    }; 

    混合与蒙版

    透明效果是用混合机制完成的。混合机制与深度检测类似,也发生在试图向某个已填充的像素填充颜色时。深度检测通过比较z值来确定像素的颜色,而混合机制会将两种颜色混合。比如这个例子

     WebGL技术储备指南

    混合的顺序是按照绘制的顺序进行的,如果绘制的顺序有变化,混合的结果通常也不同。如果模型既有非透明表面又有透明表面,绘制透明表面时开启蒙版, 其目的是锁定深度缓冲区,因为半透明物体后面的物体还是可以看到的,如果不这样做,半透明物体后面的物体将会被深度检测机制排除。

    开启混合的代码如下。gl.blendFunc方法指定了混合的方式,这里的意思是,使用源(待混合)颜色的 α 值乘以源颜色,加上 1-[源颜色的 α]乘以目标颜色。

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 

    所谓 α 值,就是颜色的第 4 个分量。

    var carray = new Float32Array([
      1,0,0,0.7,1,0,0,0.7,1,0,0,0.7,
      0,0,1,0.4,0,0,1,0.4,0,0,1,0.4
      ]); 

    浏览器的WebGL系统

    WebGL 系统各个组成部分在既定规则下互相配合。稍作梳理如下。

     WebGL技术储备指南

    这张图比较随意,箭头上的文字表示 API,箭头方向大致表现了数据的流动方向,不必深究。

    光照

    WebGL 没有为光照提供任何内置的方法,需要开发者在着色器中实现光照算法。

    光是有颜色的,模型也是有颜色的。在光照下,最终物体呈现的颜色是两者共同作用的结果。

    实现光照的方式是:将光照的数据(点光源的位置,平行光的方向,以及光的颜色和强度)作为 uniform 变量传入着色器中,将物体表面每个顶点处的法线作为 attribute 变量传入着色器,遵循光照规则,修订最终片元呈现的颜色。

    光照又分为逐顶点的和逐片元的,两者的区别是,将法线光线交角因素放在顶点着色器中考虑还是放在片元着色器中考虑。逐片元光照更加逼真,一个极端的例子是:

     WebGL技术储备指南

    此时,点光源在距离一个表面较近处,表面中央 A 处较亮,四周较暗。但是在逐顶点光照下,表面的颜色(的影响因子)是由顶点内插出来的,所以表面中央也会比较暗。而逐片元光照直接使用片元的位置和法线计算与点光源的交角,因此表面中央会比较亮。

    复杂模型

    复杂模型可能有包括子模型,子模型可能与父模型有相对运动。比如开着雨刮器的汽车,雨刮器的世界坐标是受父模型汽车,和自身的状态共同决定的。若要计算雨刮器某顶点的位置,需要用雨刮器相对汽车的模型矩阵乘上汽车的模型矩阵,再乘以顶点的局部坐标。

    复杂模型可能有很多表面,可能每个表面使用的着色器就不同。通常将模型拆解为组,使用相同着色器的表面为一组,先绘制同一组中的内容,然后切换着色器。每次切换着色器都要重新将缓冲区中的数据分配给着色器中相应变量。

    动画

    动画的原理就是快速地擦除和重绘。常用的方法是大名鼎鼎的 requestAnimationFrame 。不熟悉的同学,可以参考正美的介绍

    WebGL库

    目前最流行的 WebGL 库是 ThreeJS,很强大,官网代码

    调试工具

    比较成熟的 WebGL 调试工具是WebGL Inspector

    网络资源和书籍

    英文的关于 WebGL 的资源有很多,包括:

    国内最早的 WebGL 教程是由郝稼力翻译的,放在 hiwebgl 上,目前 hiwebgl 已经关闭,但教程还可以在这里找到。郝稼力目前运营着Lao3D

    国内已经出版的 WebGL 书籍有:

    来自: http://taobaofed.org/blog/2015/12/21/webgl-handbook/

    展开全文
  • WebGL技术学习之路

    千次阅读 2016-07-05 15:11:20
    相比其他前端技术WebGL最大的特点的就是学习曲线异常之陡,入门感觉比较难。另外在这方面的资料也非常少,一部分因为该技术出现的时间还比较晚,另外它和OpenGL这样成熟的技术类似,很多人熟悉了OpenGL之后并不再...


     相比其他前端技术,WebGL最大的特点的就是学习曲线异常之陡,入门感觉比较难。另外在这方面的资料也非常少,一部分因为该技术出现的时间还比较晚,另外它和OpenGL这样成熟的技术类似,很多人熟悉了OpenGL之后并不再需要单独介绍WebGL。这里沉淀了目前LBS-WEB所积累下来的WebGL资源,供大家学习。

    这里给大家一些学习方法和经验:

    找一本入门书籍开始阅读,作者最好是该行业从业多年或该领域的专家,其代码会比较规范,基础概念也会讲得很清楚。针对每个知识点做小demo进行学习,尝试修改参数并查看结果。了解一些图形渲染、GPU方面的知识,加深对WebGL渲染流水线的认知。什么时候都不该忽略性能,WebGL也如此,看一些性能相关的最佳实践,从源头避免写出性能很差的3D程序。在OpenGL ES、OpenGL方向扩展。如果你只是想利用WebGL技术快速做出一些效果,可以选择目前较为成熟的库,例如Three.js。

    Learning WebGL。一个专门介绍WebGL技术、进展的网站,里面提供了一系列的基础课程,这个课程就目前来说是比较经典的,推荐学习。了解基本的WebGL程序写法和风格。该网站的作者也是《WebGL: Up and Running》一书的作者。WebGL规范。目前浏览器应用的规范标准。

    WebGL Techniques and Performance(Youtube,需要翻):Google I/O 2011年的一个分享,推荐。可以了解WebGL性能优化的基本思想和方法。分享人是Gregg Travares,在Chrome GPU团队,主要推动了Chrome的GPU渲染以及WebGL在Chrome上的实现,在Stackoverflow上非常活跃,经常回答WebGL相关问题。WebGL Fundamentals 还是Gregg Travares写的,在HTML5Rocks上。HTML5Rocks上关于WebGL的资源。可以自行搜索。WebGL课程中文翻译版

    Debugging and Optimizing WebGL Applications。Google的两位工程师介绍WebGL性能相关的议题。

    Thinking in WebGL: Reducing Memory Usage。关于内存方面的考虑。

    WebGL Debugging and Profiling Tools。一篇介绍WebGL调试工具的文章。

    这里列出了一些读过的WebGL书籍,并附有简短的说明供大家参考,其他书籍信息可自行在amazon、china-pub上查找。

    WebGL Beginner's Guide 入门书籍,强烈推荐。

    Beginning WebGL for HTML5 另一本入门,推荐。

    Professional WebGL Programming: Developing 3D Graphics for the Web 比较深入的一本书,适合进阶阅读,强烈推荐。LBS图书馆有英文版。

    WebGL: Up and Running 入门书籍,有比较多的three.js部分介绍,对于想从底层掌握WebGL同学不太适合。该书作者也同时维护一个学习WebGL的网站:Learning WebGL。上面的课程非常不错。

    WebGL Programming Guide: Interactive 3D Graphics Programming with WebGL (OpenGL) 基础知识比较全面。推荐看这本书的高阶部分,介绍的内容也比较丰富。

    Graphics ShadersTheory and Practice 专门介绍图形着色器的书,有翻译版(吐槽一下清华大学出版社的翻译版本,排版差,翻译更差,可见就是糊弄出来的一本书)。[G

    PU编程与CG语言之阳春白雪下里巴人]16 好奇怪的书名,这是国内一位作者所写,开篇痛诉国内科研之现状,作者对待做学问的态度还是很不错的。少有的优秀中文资料,推荐。

    展开全文
  • 热力图-基于Threejs/Webgl技术项目实战 多年IT从业经验,主要在...
  • SuperMap WebGL技术

    2021-06-16 13:41:05
    1.图层加载 1.工作空间下单图层加载 ...var promise = viewer.scene.addS3MTilesLayerByScp(url,{name:name}); promise.then(function (map) { // 可在此处设置场景属性,添加属性查询等功能 },function (e) {
  • 针对webgl的库threejs框架的热力图功能项目实战详细的讲解,热力图功能在真实项目中应用,主要包括厂区、生产线、机房、库房等实时监控热力分布,建筑或园区人员密集实时监控等综合场景应用。
  • 3DCityDB WEBGL技术

    千次阅读 2019-01-28 19:22:34
    cecium编译及使用 http://blog.csdn.net/huapenguag/article/details/54137663 http://blog.csdn.net/u014529917/article/details/53898357 简单,我使用这个编译的   cesium数据发布 ...gi...
  • 国外大神分享的一款H5游戏源码,整体浏览非常流畅,交互也很酷
  • Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: yarn start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...
  • WebGL”是一种仅使用浏览器进行3D渲染的技术,无需插件,近年来,更加复杂的丰富3D表达成为可能。在这里,我们将介绍最新的服务,可以用WebGL做些什么。严肃的WebGL时代到来了!不要错过这个潮流近年来,消费者...
  • 基于HTML5 WebGL技术校园全景漫游系统研究基于HTML5 WebGL技术校园全景漫游系统研究摘要:该文对校园全景漫游系统设计进行了探究,该系统全景的实现是基于图像建模的,取材于"校园区实景图片",通过使用相机及全景...
  • 发布时间:2020-01-13 09:26:562019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级的...
  • HT for Web 提供了多种基础类型供用户建模使用,不同于传统的 3D 建模方式,HT 的建模核心都是基于 API 的接口方式,通过 HT 预定义的图元类型和参数接口,进行设置达到三维模型的构建。接下来我们就来谈谈预定义的 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,640
精华内容 5,456
关键字:

webgl技术