摘要:了解 WebGL 的基础概念并进行实践,更好的理解不同框架带来的便捷及优势。

文|Web SDK 开发团队

前言

1、什么是 WebGL ?

WebGL 的全称是 Web Graphics Library,是一种 3D 绘图协议。

WebGL 允许把 JavaScript 和 OpenGL ES 2.0 结合在一起,通过增加 OpenGL ES 2.0 的一个JavaScript 绑定,WebGL 可以为 HTML5 Canvas 提供硬件 3D 加速渲染。Web 开发人员就可以借助系统显卡来在浏览器里更流畅地展示 3D 场景和模型,还能创建复杂的导航和数据视觉化。

2、OpenGL 与 GLSL

在了解 WebGL 之前,我们需了解下什么是 OpenGL 与 GLSL。

OpenGL(英语:Open Graphics Library,译名为开放图形库或者“开放式图形库”)是用于渲染 2D、3D 矢量图形的跨语言、跨平台的应用程序编程接口(API), GLSL 则是用来在 OpenGL 中着色编程的语言。

从上文中可以看到 WebGL 实际上是 JavaScript 操作一些 OpenGL 接口,也就意味着,可能会编写一部分 GLSL ES 2.0 的代码,没错你猜对了,WebGL 只是绑定了一层,内部的一些核心内容,例如着色器、材质、灯光等都是需要借助 GLSL ES 语法来操作的。 

基于 WebGL 周边也衍生了众多的第三方库,比如开发应用类的 Three.js,开发游戏类的 Egert.js 等,都大大的降低了学习 WebGL 的成本,但是本着有问题解决问题,没问题制造问题再解决问题的程序猿态度,还是觉得应该稍微了解一下 WebGL 的一些基本概念,以便能更好的去理解不同框架带来的便捷以及优势!

WebGL 概述

今天还是用 5W1H 分析法,围绕 WebGL 给大家做一下介绍:

1、Who: 谁发明的?

WebGL 起源于 Mozilla 员工弗拉基米尔·弗基西维奇的一项称为 Canvas 3D 的实验项目。

2006 年,弗基西维奇首次展示了 Canvas 3D 的原型。

2007 年底在 Firefox 和 Opera 被实现。

在 2009 年初,非营利技术联盟 Khronos Group 启动了 WebGL 的工作组,最初的工作成员包括 AppleGoogleMozillaOpera 等。

2011 年 3 月发布 WebGL 1.0 规范。截至2012年3月,工作组的主席由肯·罗素(Ken Russell,全名“Kenneth Bradley Russell”)担任。

WebGL 的早期应用包括 Zygote Body。

2、When: 什么时候开始广泛应用?

WebGL 2.0 规范的发展始于 2013 年,并于 2017 年 1 月完成。该规范基于 OpenGL ES 3.0产生,首度实现在 Firefox 51、Chrome 56 和 Opera 43 中。市面上除了反人类的 IE 外基本上均支持, 兼容性很好。

3、Where: 主要在什么地方(场景)使用?

3D 的数据可视化、3D 游戏开发、H5 广告 / Web 宣传页、图像处理等。

说一下 3D 的数据可视化。3D 的可视化利用技术和视觉感官从信息中提取价值。当我们分析典型 2D 格式的数据时,通常由电子表格或统计图中的数字组成,我们实际可以获取并用于规划,制定决策,定位客户等等的信息是有限的,3D可视化技术使我们能够看到在传统的图表看不到的内容。

4、Why: 为什么用它?

WebGL 完美地解决了现有的 Web 交互式三维动画的两个问题:

问题一:它通过 HTML脚本 本身实现 Web 交互式三维动画的制作,无需任何浏览器插件支持;

问题二:它利用底层的图形硬件加速功能进行的图形渲染,是通过统一的、标准的、跨平台的OpenGL 接口实现的。

5、How: 怎么使用?

检测浏览器是否支持 WebGL。使用前,可以通过检测页面可以看到 当前使用的浏览器对 WebGL的支持情况,包括支持的规范版本。

6、关于着色器

WebGL 只关心两件事:裁剪空间中的坐标值和颜色值。你需要提供两个着色器来做这两件事,一个顶点着色器提供裁剪空间坐标值,一个片断着色器提供颜色值。

顶点着色器的作用是计算顶点的位置。根据计算出的一系列顶点位置,WebGL 可以对点、线和三角形在内的一些图元进行光栅化处理(把顶点数据转换为片元的过程,将图转化为一个个栅格组成的图像)。当对这些图元进行光栅化处理时需要使用片断着色器方法。片断着色器的作用是计算出当前绘制图元中每个像素的颜色值。

1)顶点着色器

// 一个属性值,将会从缓冲中获取数据
attribute vec4 a_position;

// 所有着色器都有一个main方法
void main() {

  // gl_Position 是一个顶点着色器主要设置的变量
  gl_Position = a_position;
}

2)片断着色器

// 片断着色器没有默认精度,所以我们需要设置一个精度
// mediump是一个不错的默认值,代表“medium precision”(中等精度)
precision mediump float;

void main() {
  // gl_FragColor是一个片断着色器主要设置的变量
  gl_FragColor = vec4(1, 0, 0.5, 1); // 返回“红紫色”
}

着色器语言使用的是 GLSL ES 语言,在 JavaScript 中无法直接执行,需要将之存放在字符串中,等待调用。

编译着色器中包含几个内置变量:gl_Position、gl_PointSize、gl_FragColor。

gl_Position: 为一种 vec4 类型的变量,且必须被赋值。四维坐标矢量,我们称之为齐次坐标,即(x,y,z,w),等价于三维坐标(x/w,y/w,z/w),w 相当于深度,没有特殊要求设置为 1.0即可;

gl_PointSize:表示顶点的尺寸,不填则默认显示为 1.0;

gl_FragColor:表示颜色,是片元着色器中唯一的内置变量,也是一个 vec4 类型变量,分别代表(R、G、B、A),不过颜色范围是从 0.0-1.0 对应 Javascript 中的 #00-#FF。

基本用法

1、创建 WebGL 对象

不同浏览器声明 WebGL 对象方式有所区别,虽然大部分浏览器都支持 experimental-webgl,而且以后会变成 WebGL,所以创建时做一下兼容处理:

var canvas = document.getElementById("glcanvas");
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");

2、创建顶点着色器

var VSHADER_SOURCE = 
  'void main() {\n' +
    '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' +
    '  gl_PointSize = 10.0;\n' + 
  '}\n';

3、创建片元着色器

var FSHADER_SOURCE =
    'void main() {\n' +
    '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';

着色器代码需要载入到一个程序中,WebGL 使用此程序才能调用着色器。

var program = gl.createProgram();
// 创建顶点着色器 
var vShader = gl.createShader(gl.VERTEX_SHADER);
// 创建片元着色器 
var fShader = gl.createShader(gl.FRAGMENT_SHADER);
// shader容器与着色器绑定 
gl.shaderSource(vShader, VSHADER_SOURCE);
gl.shaderSource(fShader, FSHADER_SOURCE);
// 将GLSE语言编译成浏览器可用代码 
gl.compileShader(vShader);
gl.compileShader(fShader);
// 将着色器添加到程序上 
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
// 链接程序,在链接操作执行以后,可以任意修改shader的源代码,
//对shader重新编译不会影响整个程序,除非重新链接程序 
gl.linkProgram(program);
// 加载并使用链接好的程序 
gl.useProgram(program);

接着绘制一个点:

gl.clearColor(0.0,0.0,0.0,1.0); 
gl.clear(gl.COLOR_BUFFER_BIT); 
gl.drawArrays(gl.POINTS, 0 ,1);

​​​​​​​

美颜滤镜

我们已经了解了 WebGL 的一些基本概念和基本使用方法,下面我们将介绍 WebGL 的图像处理及其如何运用到 WebRTC。

要想做到例如美白、红润等效果,我们需要掌握大量的 WebGL 知识和图像算法,本次分享先让大家对滤镜有一个初步的认识,理解如何绘制图片。

图像处理

在 WebGL 中绘制图片需要使用纹理,和 WebGL 渲染时需要裁剪空间坐标相似, 渲染纹理时需要纹理坐标,而不是像素坐标。无论纹理是什么尺寸,纹理坐标范围始终是 0.0 到 1.0 。

因为我们只用画一个矩形(其实是两个三角形),所以需要告诉 WebGL 矩形中每个顶点对应的纹理坐标。我们将使用一种特殊的叫做 varying 的变量将纹理坐标从顶点着色器传到片断着色器,它叫做“可变量”是因为它的值有很多个,WebGL 会用顶点着色器中的值进行插值,然后传给对应像素执行的片断着色器。

attribute vec2 a_texCoord;
...
varying vec2 v_texCoord;

void main() {
   ...
   // 将纹理坐标传给片断着色器
   // GPU会在点之间进行插值
   v_texCoord = a_texCoord;
}

<script id="fragment-shader-2d" type="x-shader/x-fragment">
precision mediump float;

// 纹理
uniform sampler2D u_image;

// 从顶点着色器传入的纹理坐标
varying vec2 v_texCoord;

void main() {
   // 在纹理上寻找对应颜色值
   gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>

1、添加滤镜

添加滤镜的关键点在于 shader(着色器),在片元着色器中我们可以看到这样一段代码:

void main() {
   // 在纹理上寻找对应颜色值
   gl_FragColor = texture2D(u_image, v_texCoord);
}

这里 texture2D(u_image, v_texCoord) 代表着图像解析后的 RGBA 值,当我们直接赋值给 gl_FragColor 时则原图输出,那么滤镜的核心也就在这里,我们需要对其进行改写,下面我们先从最简单的灰度滤镜效果做例子,从 RGB 色转为灰度色的算法我们可以很简单的从网上找出,这里取其中一种 Gray = R0.299 + G0.587 + B*0.114,实际运用如下:

void main () {
    vec4 color = texture2D(u_image, v_texCoord);
    float gray = 0.2989*color.r+0.5870*color.g+0.1140*color.b;
    gl_FragColor = vec4(gray,gray,gray , color.a);
}

2、传输到 WebRTC

在完成滤镜后,又该如何将其传输到 WebRTC 中呢?

有的同学使用过 WebRTC 会知道 WebRTC 的传输离不开媒体流(MediaStream),MediaStream 是一个媒体内容的流,一个流包含几个轨道,比如视频和音频轨道。

前面我们提到了,WebGL 的使用是在 canvas 中进行的,我们可以通过 canvas 的 caputureStream API 获取到 mediaStream 对象,获取后即可进行传输。 

总结

通过上述的学习,相信大家对 WebGL 的概念有了一定的了解。当然,要想实现复杂的美颜滤镜效果还有很多内容需要学习。

此外,即构在 Web 端提供了成熟的美颜功能,只需简单几步,即可实现不同的美颜效果,感兴趣的同学可以通过链接了解更多:ZegoExpressEngine

获取更多帮助

获取更多商务活动热门产品,可提交 信息联系商务
注册即构ZEGO开发者帐号,快速开始;

Logo

致力于链接即构和开发者,提供实时互动和元宇宙领域的前沿洞察、技术分享和丰富的开发者活动,共建实时互动世界。

更多推荐