实用 WebGL 图像解决入门
技术社区里有种很有意思的现象,那就是不少人们口耳相传中的强大技术,往往由于上手难度高而显得曲高和寡。从这个角度看来,WebGL 和函数式编程有些相似,都属于优势已被论证了多年,却一直较为不温不火的技术。但是,一旦这些技术的易用性跨越了某个临界点,它们其实并没有那么遥不可及。这次我们就将以 WebGL 为例,尝试降低它的入门门槛,讲解它在前台图像解决领域的应用入门。
临近 2020 年的今天,社区里已经有了许多 WebGL 教程。为什么还要另起炉灶再写一篇呢?这来自于笔者供职的稿定科技前台团队,在 WebGL 基础库层面进行技术创新的努力。前一段时间,我们开源了自主研发的 WebGL 基础库 Beam。它以 10KB 不到的体积,将传统上入门时动辄几百行的 WebGL 渲染逻辑降低到了几十行的量级,并在性能上也毫不妥协。开源两周内,Beam 的 Star 数量就达到了 GitHub 全站 WebGL Library 搜索条目下的前 10%,在国内也还没有定位相当的竞品。这次我们将借助 Beam 来编写 WebGL 渲染逻辑,用精炼的代码和概念告诉大家,该如何硬核而不失优雅地手动操控 GPU 渲染管线,实现多样的前台图像解决能力。
本文将覆盖的内容如下所示。我们希望能带着感兴趣的同学从零基础入门,直通具有实用价值的图像滤镜能力开发:
WebGL 概念入门
WebGL 示例入门
如何用 WebGL 渲染图像
如何为图像添加滤镜
如何叠加多个图像
如何组合多个滤镜
如何引入 3D 效果
如何封装自定渲染器
为了照顾没有基础的同学,在进入实际的图像解决部分前,我们会重新用 Beam 入门一遍 WebGL。熟习相关概念的同学可以直接跳过这些部分。
WebGL 概念入门
Beam 的一个设计目标,是让使用者即使没有相关经验,也能靠它快速搞懂 WebGL。但这并不意味着它像 Three.js 那样可以几乎完全不用懂图形学,拿来就是一把梭。相比之下,Beam 选择对 WebGL 概念做高度的笼统。在学习了解这些概念后,你就不仅能了解 GPU 渲染管线,还能用简单的代码来操作它了。毕竟这篇文章本身,也是本着授人以渔的理念来写作的。
本节来自?如何设计一个 WebGL 基础库?一文,熟习的同学可跳过。
WebGL 体系有很多琐碎之处,一头扎进代码里,容易使我们只见树木不见森林。然而我们真正需要关心的概念,其实可以被高度浓缩为这几个:
Shader?着色器,是存放图形算法的对象。 相比于在 CPU 上单线程执行的 JS 代码,着色器在 GPU 上并行执行,计算出每帧数百万个像素各自的颜色。
Resource?资源,是存放图形数据的对象。就像 JSON 成为 Web App 的数据那样,资源是传递给着色器的数据,包括大段的顶点数组、纹理图像,以及全局的配置项等。
Draw?绘制,是选好资源后运行着色器的请求。要想渲染真实际的场景,一般需要多组着色器与多个资源,来回绘制屡次才能完成一帧。每次绘制前,我们都需要选好着色器,并为其关联好不同的资源,也都会启动一次图形渲染管线。
Command?命令,是执行绘制前的配置。WebGL 是非常有状态的。每次绘制前,我们都必需小心地解决好状态机。这些状态变更就是通过命令来实现的。Beam 基于少量商定大幅简化了人工的命令管理,当然你也可以定制自己的命令。
这些概念是如何协同工作的呢?请看下图:

图中的 Buffers / Textures / Uniforms 都属于典型的资源(后面会详述它们各自的用途)。一帧当中可能存在屡次绘制,每次绘制都需要着色器和相应的资源。在绘制之间,我们通过命令来管理好 WebGL 的状态。仅此而已。
了解这个思维模型很重要。由于 Beam 的 API 设计就是完全依据这个模型而实现的。让我们进一步看看一个实际的场景吧:

图中我们绘制了很多质感不同的球体。这一帧的渲染,则可以这样解构到上面的这些概念下:
着色器无疑就是球体质感的渲染算法。对经典的 3D 游戏来说,要渲染不同质感的物体,经常需要切换不同的着色器。但现在基于物理的渲染算法流行后,这些球体也不难做到使用同一个着色器来渲染。
资源包括了大段的球体顶点数据、材质纹理的图像数据,以及光照参数、变换矩阵等配置项。
绘制是分屡次进行的。我们选择每次绘制一个球体,而每次绘制也都会启动一次图形渲染管线。
命令则是相邻的球体绘制之间,所执行的那些状态变更。
如何了解状态变更呢?不妨将 WebGL 想象成一个具有大量开关与接口的仪器。每次按下启动键(执行绘制)前。你都要配置好一堆开关,再连接好一条接着色器的线,和一堆接资源的线,就像这样:

还有很重要的一点,那就是尽管我们已经知道,一帧画面可以通过屡次绘制而生成,而每次绘制又对应执行一次图形渲染管线的执行。但是,所谓的图形渲染管线又是什么呢?这对应于这张图:

渲染管线,一般指的就是这样一个 GPU 上由顶点数据到像素的过程。对现代 GPU 来说,管线中的某些阶段是可编程的。WebGL 标准里,这对应于图中蓝色的顶点着色器和片元着色器阶段。你可以把它们想象成两个需要你写 C-style 代码,跑在 GPU 上的函数。它们大体上分别做这样的工作:
顶点着色器输入原始的顶点坐标,输出经过你计算出的坐标。
片元着色器输入一个像素位置,输出根据你计算出的像素颜色。
下面,我们将进一步讲解如何应用这些概念,搭建出一个完整的 WebGL 入门示例。
WebGL 示例入门
本节同样来自?如何设计一个 WebGL 基础库?一文,但为承接后续的图像解决内容,叙述有所调整。
在苦口婆心的概念详情后,就要来到真刀真枪的编码阶段了。因为四大概念中的命令可以被自动化,我们只为 Beam 定义了三个核心 API,分别是:
beam.shader
beam.resource
beam.draw
显然地,它们各自管理着色器、资源和绘制。让我们看看怎么基于 Beam,来绘制 WebGL 中的 Hello World 彩色三角形吧:

三角形是最简单的多边形,而多边形则是由顶点组成的。WebGL 中的这些顶点是有序排列,可通过下标索引的。以三角形和矩形为例,这里使用的顶点顺序如下所示:

Beam 的代码示例如下,压缩后一律代码体积仅有 6KB:

下面一一详情少量重要的 API 片段。首先自然是初始化 Beam 了:constcanvas=document.querySelector(‘canvas’)
constbeam=newBeam(canvas)? ? ?大家在入门学习前台的过程当中有遇见任何关于学习,行业方面的问题,都可以申请加入我的前台学习扣扣裙,282549184。缺乏相关的基础教程也可以直接来找我要,我这里整理了一套最新的前台基础教程,学习前台的这个过程当中我也收集了很多前台学习手册,面试题,开发工具,PDF文档书籍教程,可以直接分享给你们。
而后我们用?beam.shader?来实例化着色器,这里的?MyShader?稍后再说:constshader=beam.shader(MyShader)
着色器准备好之后,就是准备资源了。为此我们需要使用?beam.resource?API 来创立三角形的数据。这些数据装在不同的 Buffer 里,而 Beam 使用?VertexBuffers?类型来表达它们。三角形有 3 个顶点,每个顶点有两个属性 (attribute),即?position?和?color,每个属性都对应于一个独立的 Buffer。这样我们就不难用普通的 JS 数组(或者 TypedArray)来公告这些顶点数据了。Beam 会替你将它们上传到 GPU:
注意区分 WebGL 中的顶点和坐标概念。顶点 (vertex) 不仅可以包含一个点的坐标属性,还可以包含法向量、颜色等其它属性。这些属性都可以输入顶点着色器中来做计算。

装顶点的 Buffer 通常会使用很紧凑的数据集。我们可以定义这份数据的一个子集或者者超集来用于实际渲染,以便于减少数据冗余并复用更多顶点。为此我们需要引入 WebGL 中的?IndexBuffer概念,它指定了渲染时用到的顶点下标。这个例子里,0 1 2 这样的每个下标,都对应顶点数组里的 3 个位置:

最后我们即可以进入渲染环节啦。首先用?beam.clear?来清空当前帧,而后为?beam.draw?传入一个着色器对象和任意多个资源对象就可:

我们的?beam.draw?API 是非常灵活的。假如你有多个着色器和多个资源,可以随便组合它们来链式地完成绘制,渲染出复杂的场景。就像这样:

别忘了还有个遗漏的地方:如何决定三角形的渲染算法呢?这是在?MyShader?变量里指定的。它其实是个着色器的 Schema,像这样:

Beam 中的着色器 Schema,需要提供?fs / vs / buffers?等字段。这里的少量要点包括如下:
可以粗略认为,顶点着色器对三角形每个顶点执行一次,而片元着色器则对三角形内的每个像素执行一次。
顶点着色器和片元着色器,都是用 WebGL 标准中的 GLSL 语言编写的。这门语言其实就是 C 语言的变体,vec4?则是其内置的 4 维向量数据类型。
在 WebGL 中,顶点着色器将?gl_Position?变量作为坐标位置输出,而片元着色器则将?gl_FragColor?变量作为像素颜色输出。本例中的顶点和片元着色器,执行的都只是最简单的赋值操作。
名为?vColor?的 varying 变量,会由顶点着色器传递到片元着色器,并自动插值。最终三角形在顶点位置呈现我们定义的红绿蓝纯色,而其余位置则被渐变填充,这就是插值计算的结果。
变量前的?highp?修饰符用于指定精度,也可以在着色器最前面加一行?precision highp float;?来省略为每个变量手动指定精度。在现在这个时代,基本可以全部用高精度了。
这里?position?和?color?这两个 attribute 变量,和前面?vertexBuffers?中的 key 相对应。这也是 Beam 中的隐式商定。
尽管到此为止的信息量可能比较大,但现在只需区区几十行代码,我们即可以清晰地用 Beam 来手动控制 WebGL 渲染了。接下来让我们看看,该如何把渲染出的三角形换成矩形。有了上面的铺垫,这个改动就显得非常简单了,略微改几行代码就行。
我们的目标如下图所示:

这对应于这样的代码:

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 实用 WebGL 图像解决入门