WebGL初探

开发 前端
目前,我们有很多方案可以快速的接触到 WebGL 并绘制复杂的图形,但最后发现我们忽视了很多细节性的东西。当然,这对初学 WebGL 是有必要的,它能迅速提起我们对 WebGL 的学习兴趣。当学习到更加深入的阶段时,我们更想了解 WebGL 的工作机制,这也将对我们编程有极大的帮助。

目前,我们有很多方案可以快速的接触到 WebGL 并绘制复杂的图形,但***发现我们忽视了很多细节性的东西。当然,这对初学 WebGL 是有必要的,它能迅速提起我们对 WebGL 的学习兴趣。当学习到更加深入的阶段时,我们更想了解 WebGL 的工作机制,这也将对我们编程有极大的帮助。以上也是我想写这样一个系列的原因。

简介

用更专业的描述讲,WebGL (Web Graphics Library) 是一个用以渲染交互式 3D 和 2D 图形的无需插件且兼容下一代浏览器的 JavaScript API,通过 HTML5 中 <canvas> 元素实现功能。WebGL 是由 Khronos Group 集团制定,而非 W3C 组织。目前,我们可以使用的是 WebGL ***个版本,它继承自 OpenGL ES 2.0 。而 OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA 和游戏主机等嵌入式设备而设计。以下是各版本之间的关系图:

Hello World

首先,我们将通过实现一个简单的 WebGL 程序(清空绘图区)叩开 WebGL 的大门。下面将实现一个最简单的 WebGL 功能:

 

创建 canvas 元素

WebGL 采用 HTML5 中的 <canvas> 元素。为了使用 WebGL 进行 3D 渲染,你首先需要一个 canvas 元素。这里创建了一个 canvas 元素,并使用 onload 事件创建来初始化 WebGL 上下文。

  1. <body onload="start()"
  2.   <canvas id="glcanvas" width="640" height="480"
  3.     Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element. 
  4.   </canvas> 
  5. </body> 

获取 WebGL 上下文

目前,各浏览器基本都实现了对 WebGL 的支持,但 IE11 及 Edge 浏览器稍微有些不同。以下是对初始化 WebGL 的基本封装:

  1. function initWebGL(canvas) { 
  2.   // 创建全局变量 
  3.   window.gl = null
  4.    
  5.   try { 
  6.     // 尝试获取标准上下文,如果失败,回退到试验性上下文 
  7.     gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); 
  8.   } 
  9.   catch(e) { 
  10.     throw '创建失败。'
  11.   } 
  12.    
  13.   // 如果没有GL上下文,马上放弃 
  14.   if (!gl) { 
  15.     alert("WebGL初始化失败,可能是因为您的浏览器不支持。"); 
  16.     gl = null
  17.   } 
  18.   return gl; 
  19. }     

这里通过采用 canvas 的 getContext(contextType, contextAttributes) 方法判断浏览器是否支持 WebGL,并创建其上下文。当返回值是 canvas 的上下文时,浏览器可支持 WebGL,为 null 时,则创建失败。注意,在 IE11 及 Edge 浏览器下,需要使用 "experimental-webgl" 创建 WebGL,此处做了兼容处理。

清空绘图区

下面将背景颜色设置为黑色,并清空缓存区。

  1. var gl; // WebGL的全局变量 
  2.  
  3. function start() { 
  4.   var canvas = document.getElementById("glcanvas"); 
  5.  
  6.   // 初始化 WebGL 上下文 
  7.   gl = initWebGL(canvas);    
  8.    
  9.   // 只有在 WebGL 可用的时候才继续 
  10.    
  11.   if (gl) { 
  12.     // 设置清除颜色为黑色,不透明 
  13.     gl.clearColor(0.0, 0.0, 0.0, 1.0);      
  14.     // 清除颜色和深度缓存 
  15.     gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);      
  16.   } 
  17.  

这样,我们可以在浏览器中看到一块黑色区域。你可能已经注意到,WebGL 遵循的是传统 OpenGL 颜色分量的取值范围,从 0.0 到 1.0。RGB 的值越高,颜色越亮。注意,clear() 方法在这里清除颜色和深度缓存,而不是绘制区域的 <canvas>,该方法继承自 OpenGL(基于多缓存模型)。实际还有模版缓存,但实际很少会被用到。

更进一步

上面我们完成了***个 WebGL 程序,但是我们还未接触到 WebGL 的核心:可编程着色器。接下来,我们将使用可编程着色器在屏幕上绘制点。可编程着色器是一个较为复杂的概念,也有自己的编程语言 GLSL,后面将会又专门的文章具体讲解可编程着色器。这里我们只需要简单了解绘制的流程:

编写着色器程序

  1. // 顶点着色器程序 
  2. var VSHADER_SOURCE =  
  3.   'void main() {\n' + 
  4.   '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 设置顶点位置 
  5.   '  gl_PointSize = 10.0;\n' +                    // 设置点的大小 
  6.   '}\n'
  7.  
  8. // 片元着色器程序 
  9. var FSHADER_SOURCE = 
  10.   'void main() {\n' + 
  11.   '  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n' + // 设置点的颜色,此处为白色 
  12.   '}\n' 

上面程序是不是有中似曾相识的感觉?没错,GLSL 语言和 C 语言很类似。着色器程序中包含一个主函数,且返回值为空。其中vec4() 构造函数用于生成一个四维向量(x,y,z,w)。

编译着色器

首先,需要用 createShader( type ) 方法生成相应类型的 WebGLShader。接着,使用 shaderSource( shader, sourceCode ) 作为 GLSL 源码的钩子函数。***使用 compileShader( shader ) 完成对着色器的编译。程序中我们做了编译后的校验,当着色器编译失败时,会报出失败并删除着色器。

  1. function createShader (gl, type, sourceCode) { 
  2.   // 编译着色器类型:顶点着色器及片元着色器。 
  3.   var shader = gl.createShader( type ); 
  4.   gl.shaderSource( shader, sourceCode ); 
  5.   gl.compileShader( shader ); 
  6.  
  7.   if ( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) { 
  8.     var info = gl.getShaderInfoLog( shader ); 
  9.     console.log( "无法编译 WebGL 程序。 \n\n" + info); 
  10.     gl.deleteShader(shader); 
  11.     return null
  12.   } 
  13.  
  14.   return shader; 
  15.  

连接到可用程序

此时,着色器仍是不可用的,需要将其赋值到 WebGLProgram 上。这里主要进行了三步操作,首先,需要使用 createProgram() 方法创建和初始化一个 WebGLProgram 对象。接着,使用 gl.attachShader(program, shader) 将该对象结合两个已经编译的着色器。***,使用 linkProgram(program) 将 WebGLProgram 和着色器连接。

  1. function createProgram(gl, vshader, fshader) { 
  2.   // 创建着色器对象 
  3.   var vertexShader = createShader(gl, gl.VERTEX_SHADER, vshader); 
  4.   var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fshader); 
  5.   if (!vertexShader || !fragmentShader) { 
  6.     return null
  7.   } 
  8.  
  9.   // 创建编程对象 
  10.   var program = gl.createProgram(); 
  11.   if (!program) { 
  12.     return null
  13.   } 
  14.  
  15.   // 赋值已创建的着色器对象 
  16.   gl.attachShader(program, vertexShader); 
  17.   gl.attachShader(program, fragmentShader); 
  18.  
  19.   // 连接编程对象 
  20.   gl.linkProgram(program); 
  21.  
  22.   // 检查链接结果 
  23.   var linked = gl.getProgramParameter(program, gl.LINK_STATUS); 
  24.   if (!linked) { 
  25.     var error = gl.getProgramInfoLog(program); 
  26.     console.log('链接程序失败:' + error); 
  27.     gl.deleteProgram(program); 
  28.     gl.deleteShader(fragmentShader); 
  29.     gl.deleteShader(vertexShader); 
  30.     return null
  31.   } 
  32.   return program; 
  33.  

使用可用着色器程序

这一步主要使用 useProgram(program) 方法告诉 GPU 使用程序。

  1. function initShaders(gl, vshader, fshader) { 
  2.   var program = createProgram(gl, vshader, fshader); 
  3.   if (!program) { 
  4.     console.log('创建程序失败。'); 
  5.     return false
  6.   } 
  7.  
  8.   gl.useProgram(program); 
  9.   gl.program = program; 
  10.  
  11.   return true
  12.  

绘制一个点

***,使用 drawArrays(mode, first, count) 绘制一个点,该函数是一个非常强大的渲染函数,后续文章会有详细介绍。此处只需要知道传入 "POINTS" 绘制了一个点。

  1. // 绘制一个点 
  2. gl.drawArrays(gl.POINTS, 0, 1);    

至此,我们已经完成了绘制一个点的全部程序。当运行以上程序时,我们会在浏览器中看到一个白色的点。

结束语

到现在,我们虽然还没有使用 WebGL 绘制三维图形,但我们已经进入了 WebGL 世界。我们已经使用 WebGL 绘制了简单的图形。但是这只是 WebGL 的绘制的冰山一角,我们使用 WebGL 当然不是为了绘制这样一个简单的图形。为了绘制更复杂的图形,我们还有很多的细节需要去了解。但是无论如何,我们都已经开启了 WebGL 的***步,其实问题也并没有我们想象的那么难。

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2017-04-06 15:10:08

WebGLJavascriptWeb

2009-06-24 13:22:27

Glassfish

2010-06-03 12:57:06

Hadoop

2011-04-18 09:53:08

Ruby

2011-05-17 14:11:06

Dijkstra

2014-11-20 15:44:40

Apple Watch

2012-02-29 15:03:30

2015-01-21 16:35:49

Apple WatchWatchKit

2012-06-16 16:57:52

WebGL

2012-04-05 13:50:38

Java

2012-07-09 10:22:28

Mono for An

2021-04-14 09:33:58

Kubernetes通信网络模型

2011-08-24 09:30:29

JavaJVM

2013-04-10 11:23:27

2017-12-26 17:42:12

前端WebGLThree.js

2023-03-16 14:33:23

WebGL初始化绘制

2011-06-16 10:25:29

AndroidAIR

2017-05-29 08:18:11

Serverless架构软件系统

2013-09-09 09:41:34

2010-09-08 17:26:46

JavaScript
点赞
收藏

51CTO技术栈公众号