专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  GPU:DeepSeek ... ·  22 小时前  
程序员的那些事  ·  清华大学:DeepSeek + ... ·  3 天前  
程序员的那些事  ·  OpenAI ... ·  2 天前  
程序猿  ·  “未来 3 年内,Python 在 AI ... ·  5 天前  
程序员的那些事  ·  惊!小偷“零元购”后竟向 DeepSeek ... ·  4 天前  
51好读  ›  专栏  ›  SegmentFault思否

WebGL 初探

SegmentFault思否  · 公众号  · 程序员  · 2017-11-13 08:00

正文

该文章于一天前发表在 github,若有问题可提至 github。

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

简介

用更专业的描述讲,WebGL (Web Graphics Library) 是一个用以渲染交互式 3D 和 2D 图形的无需插件且兼容下一代浏览器的 JavaScript API,通过 HTML5 中 元素实现功能。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 中的 元素。为了使用 WebGL 进行 3D 渲染,你首先需要一个 canvas 元素。这里创建了一个 canvas 元素,并使用 onload 事件创建来初始化 WebGL 上下文。

  1. onload="start()">

  2.   id="glcanvas" width="640" height="480">

  3.    Your browser doesn't appear to support the HTML5 element.

  4.  

获取 WebGL 上下文

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

  1. function initWebGL(canvas) {

  2.  // 创建全局变量

  3.  window.gl = null;

  4.   try {

  5.    // 尝试获取标准上下文,如果失败,回退到试验性上下文

  6.    gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");

  7.  }

  8.  catch(e) {

  9.    throw '创建失败。';

  10.  }

  11.  // 如果没有GL上下文,马上放弃

  12.  if (!gl) {

  13.    alert("WebGL初始化失败,可能是因为您的浏览器不支持。");

  14.    gl = null;

  15.  }

  16.  return gl;

  17. }      

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

清空绘图区

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

  1. var gl; // WebGL的全局变量

  2. function start() {

  3.  var canvas = document.getElementById("glcanvas");

  4.  // 初始化 WebGL 上下文

  5.  gl = initWebGL(canvas);  

  6.  // 只有在 WebGL 可用的时候才继续

  7.  if (gl) {

  8.    // 设置清除颜色为黑色,不透明

  9.    gl.clearColor(0.0, 0.0, 0.0, 1.0);    

  10.     // 清除颜色和深度缓存

  11.    gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);    

  12.  }

  13. }

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

更进一步

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

编写着色器程序

WebGL 是无法像 OpenGL 利用固定渲染管线,代替它的是可编辑渲染管线中的 GLSL 着色语言。下面是顶点及片元着色器 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. var FSHADER_SOURCE =

  9.  'void main() {\n' +

  10.  '  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n' + // 设置点的颜色,此处为白色

  11.   '}\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.  if ( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {

  7.    var info = gl.getShaderInfoLog( shader );

  8.    console.log( "无法编译 WebGL 程序。 \n\n" + info);

  9.    gl.deleteShader(shader);

  10.    return null;

  11.  }

  12.   return shader;

  13. }

连接到可用程序

此时,着色器仍是不可用的,需要将其赋值到 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.  var program = gl.createProgram();

  10.  if (!program) {

  11.     return null;

  12.  }

  13.  // 赋值已创建的着色器对象

  14.  gl.attachShader(program, vertexShader);

  15.  gl.attachShader(program, fragmentShader);

  16.  // 连接编程对象

  17.  gl.linkProgram(program);







请到「今天看啥」查看全文