资讯专栏INFORMATION COLUMN

WebGL 初探

whjin / 1020人阅读

摘要:下面是顶点及片元着色器程序,用字符串表示,它将直接运行在浏览器之上。最后,使用将和着色器连接。创建着色器对象创建编程对象赋值已创建的着色器对象连接编程对象检查链接结果链接程序失败使用可用着色器程序这一步主要使用方法告诉使用程序。

该文章于一天前发表在 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 上下文。


  
    Your browser doesn"t appear to support the HTML5  element.
  
获取 WebGL 上下文

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

function initWebGL(canvas) {
  // 创建全局变量
  window.gl = null;
  
  try {
    // 尝试获取标准上下文,如果失败,回退到试验性上下文
    gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
  }
  catch(e) {
    throw "创建失败。";
  }
  
  // 如果没有GL上下文,马上放弃
  if (!gl) {
    alert("WebGL初始化失败,可能是因为您的浏览器不支持。");
    gl = null;
  }
  return gl;
}       

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

清空绘图区

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

var gl; // WebGL的全局变量

function start() {
  var canvas = document.getElementById("glcanvas");

  // 初始化 WebGL 上下文
  gl = initWebGL(canvas);   
  
  // 只有在 WebGL 可用的时候才继续
  
  if (gl) {
    // 设置清除颜色为黑色,不透明
    gl.clearColor(0.0, 0.0, 0.0, 1.0);     
    // 清除颜色和深度缓存
    gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);     
  }
}

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

更进一步

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

编写着色器程序

WebGL 是无法像 OpenGL 利用固定渲染管线,代替它的是可编辑渲染管线中的 GLSL 着色语言。下面是顶点及片元着色器 GLSL 程序,用字符串表示,它将直接运行在浏览器之上。

// 顶点着色器程序
var VSHADER_SOURCE = 
  "void main() {
" +
  "  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
" + // 设置顶点位置
  "  gl_PointSize = 10.0;
" +                    // 设置点的大小
  "}
";

// 片元着色器程序
var FSHADER_SOURCE =
  "void main() {
" +
  "  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
" + // 设置点的颜色,此处为白色
  "}
";

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

编译着色器

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

function createShader (gl, type, sourceCode) {
  // 编译着色器类型:顶点着色器及片元着色器。
  var shader = gl.createShader( type );
  gl.shaderSource( shader, sourceCode );
  gl.compileShader( shader );

  if ( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
    var info = gl.getShaderInfoLog( shader );
    console.log( "无法编译 WebGL 程序。 

" + info);
    gl.deleteShader(shader);
    return null;
  }

  return shader;
}
连接到可用程序

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

function createProgram(gl, vshader, fshader) {
  // 创建着色器对象
  var vertexShader = createShader(gl, gl.VERTEX_SHADER, vshader);
  var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fshader);
  if (!vertexShader || !fragmentShader) {
    return null;
  }

  // 创建编程对象
  var program = gl.createProgram();
  if (!program) {
    return null;
  }

  // 赋值已创建的着色器对象
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);

  // 连接编程对象
  gl.linkProgram(program);

  // 检查链接结果
  var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (!linked) {
    var error = gl.getProgramInfoLog(program);
    console.log("链接程序失败:" + error);
    gl.deleteProgram(program);
    gl.deleteShader(fragmentShader);
    gl.deleteShader(vertexShader);
    return null;
  }
  return program;
}
使用可用着色器程序

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

function initShaders(gl, vshader, fshader) {
  var program = createProgram(gl, vshader, fshader);
  if (!program) {
    console.log("创建程序失败。");
    return false;
  }

  gl.useProgram(program);
  gl.program = program;

  return true;
}
绘制一个点

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

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

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

结束语

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80634.html

相关文章

  • WebGL 绘制三角形

    摘要:我们的目标是将位置信心从程序中传递到顶点着色器,而变量就传输的是那些和顶点相关的数据。缓冲区对象是中的一块存储区域,其中保存了大量的顶点数据,可以一次性的向顶点着色器传入多个顶点到变量。三角形一系列单独的三角形。系列文章初探绘制三角形 该文章于一天前发表在 github,若有问题可提至 github。 在上一篇文章WebGL 初探中,我们熟悉了 WebGL 的简单背景以及如何绘制一个简...

    Lin_R 评论0 收藏0
  • Web存储之LocalStorage初探

    摘要:存储之初探的发布和定稿为前端界带来巨大的变化,新增的和特性给业务带来了更多可能性,让用户体验拥有了更可能的丰富。只读返回一个整数,表示存储在对象中的数据项数量。会在过期时间之后销毁。安全性方面,中一般不建议存储敏感信息。 Web存储之LocalStorage初探 HTML5的发布和定稿为前端界带来巨大的变化,新增的API和特性给业务带来了更多可能性,让用户体验拥有了更可能的丰富。 · ...

    wpw 评论0 收藏0
  • SegmentFault 技术周刊 Vol.35 - WebGL:打开网页看大片

    摘要:在文末,我会附上一个可加载的模型方便学习中文艺术字渲染用原生可以很容易地绘制文字,但是原生提供的文字效果美化功能十分有限。 showImg(https://segmentfault.com/img/bVWYnb?w=900&h=385); WebGL 可以说是 HTML5 技术生态链中最为令人振奋的标准之一,它把 Web 带入了 3D 的时代。 初识 WebGL 先通过几个使用 Web...

    objc94 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<