vlambda博客
学习文章列表

webgl基础:着色器基础

关注「豆皮范儿」,回复“加群

豆皮粉儿,又见面啦!今天由字节跳动的"yancy"童鞋给大家重磅推出一篇 WebGL的干货"WebGL基础:着色器基础"!准备好了吗?接下来让我们开启奇幻旅程,进入 3D 的世界。

1. 认识3D

首先我们要介绍的是几个概念,这是我们要进入到 3D 不可或缺的内容。认识一下它们吧。

1.1 视点,视线,目标点,上方向

这几个概念在 WebGL中属于最常见的内容。

  • 视点:可以简易的理解为眼睛,也叫观察点

  • 目标点:可以理解为我们要看的物体(任何物体)

  • 上方向:头顶的方向。

实际生活中,我们的目光总是以我们的眼睛为起始点,到达我们想要看到的物体,同时,随着我们观察的角度不同,物体也会呈现不一样的形态。以一张图说明吧。

webgl基础:着色器基础

如此几个内容,创建出了 3D世界的基本显示模型,由此可见其重要程度。后面我们也会说到如何在 WebGL 中设定这几个内容。也会有的小伙伴把视点称为相机,目标点称为画布。其实是一样的道理。按照自己的理解记忆就好。

1.2 可视范围

可视范围指的是我们所能看到的最大范围。如:一般情况下我们看不到自己身后的事物。

众所周知,三维物体具有深度的概念。在我们的理解中,深度就是 z 轴。

虽然我们可以将物体放置在三维空间中的任何位置,但是在 WebGL中,可视范围之外的物体是不被绘制的,这也是为了节省开销。

1.3 可视空间

水平视角、垂直视角、可视深度 定义了可视空间的概念。

可视空间分为两种。

  • 正射投影:与物体的远近无关,通常用在建筑设计和建模上。

webgl基础:着色器基础

  • 透视投影:我们平时观察的真实世界都是透视投影。更有深度的感觉。

webgl基础:着色器基础

1.3 着色器

如果想渲染 3D 图形,就需要经过一系列的步骤,这些步骤称为渲染管线。在开发 WebGL 程序时,我们就需要通过着色器语言跟GPU进行沟通,用来设定我们需要渲染和显示的图形。

由此可见:着色器是编写 WebGL时最重要的一点(没有之一)。我们之所以能生成并操作 3D图像,都是因为着色器在起作用。WebGL中着色器分为两种。顶点着色器和片元着色器。

1.3.1 顶点着色器

这里的顶点代表的是组成物体的每一个点。

顶点着色器的功能主要是将位置数据经过矩阵变换、计算光照之后生成顶点颜色、变换纹理坐标。并将生成的数据输出到片元着色器。

1.3.2 片元着色器

片元着色器的作用是将光栅化阶段生成的每个片元,计算出每个片元的最终元素。

注:

由于着色器内容比较重要,这里我们先引入这两个概念,先简单理解就可以,后面专门对着色器进行分享。

2. 绘制图形

2.1 获取绘图上下文

了解了第一小节的内容之后,我们开始进入到 WebGL开发实战中。

还记得 Canvas中第一步需要干什么吗?

没错,需要获取 Canvas 元素和绘图上下文。WebGL 开发也不例外,也需要首先获取元素和绘图上下文。形如下方代码所示:

webgl基础:着色器基础

2.2 初始化着色器

2.2.1. 编写着色器代码

获取到绘图上下文之后,我们需要初始化 WebGL 的着色器了,着色器代码是以字符串的形式嵌入到渲染程序中,所以我们需要编写两个着色器的字符串。

webgl基础:着色器基础

两个着色器代码都是以字符串的形式存在,并在执行渲染时嵌入到渲染流程内。

说明:

  • voidmain(){}: 创建一个主函数。

  • gl_Position: 指定绘制的坐标,接收一个拥有4个浮点分量的 vec4数据。分别代表 x,y,z,w数据

  • gl_PointSize: 表示要绘制图形的尺寸大小。

  • gl_FragColor: 定义图形颜色, 1.00.00.01.0 分别代表 r g b a

2.2.2. 创建着色器

当然,只是编写完着色器代码依然不能完成渲染工作,接下来我们就需要将着色器添加到渲染流程内

webgl基础:着色器基础

2.2.3. 着色器编译

完成上述两步之后,我们就需要将着色器代码添加到着色器中。看下例子。

webgl基础:着色器基础

2.2.4. 创建 program

完成编译之后,我们需要将着色器添加到渲染程序中。

webgl基础:着色器基础

2.2.5. 绘制图形

完成上述步骤之后,就可以绘制我们的图形了。这里我们以一个点为例。在画布上绘制一个点出来。

 
   
   
 
  1. gl.drawArrays(gl.POINTS, 0, 1);

此时就可以打开页面,看到我们绘制的这个点了。

总结代码:

webgl基础:着色器基础

3. 渲染管线

3.1 基础内容介绍

WebGL 的渲染依赖底层 GPU 的渲染能力。所以 WebGL 渲染流程和 GPU 内部的渲染管线是相符的。

渲染管线的作用是将3D模型转换为2维图像。

在早期,渲染管线是不可编程的,叫做固定渲染管线,工作的细节流程已经固定,修改的话需要调整一些参数。

现代的 GPU 所包含的渲染管线为可编程渲染管线,可以通过编程 GLSL,着色器语言 来控制一些渲染阶段的细节。

3.2 渲染过程

(心理承受弱的同学可跳过此小节)

WebGL 的渲染过程分为以下几项:

  • 顶点着色器

  • 图片装配

  • 光栅化

  • 片元着色器

  • 逐片段操作(本文不会分享此内容)

  • 裁剪测试

  • 多重采样操作

  • 背面剔除

  • 模板测试

  • 深度测试

  • 融合

  • 缓存

这里一系列的名词可能会吓到很多同学,千万别被名词吓到哟,接下来的过程中会详细说明。也希望通过本文可以让大家理解基本的渲染流程。附图解一张,助大家理解。

webgl基础:着色器基础

4. 顶点着色器

顶点着色器的作用是通过计算获得最终的顶点坐标, 如:

  • A-->()=>{…………}==>A1:坐标 A 经过一系列的计算,最终获取坐标 A1

  • B-->()=>{…………}==>B1:坐标 B 经过一系列的计算,最终获取坐标 B1

  • …………

顶点着色器计算出来的坐标将会渲染到最终的显示画布上。

此外,顶点着色器还会计算如下内容:颜色、纹理坐标、顶点尺寸……

在顶点着色器阶段通常会涉及到三个类型的变量。

  • attribute:针对单一顶点生效。

  • 通常用于:顶点位置、顶点大小等内容

  • uniform:影响所有顶点

  • 通常用于:旋转、平移、缩放等位置变换、颜色处理等内容。

  • varying:可通过顶点着色器传入到片元着色器。

attribute 这个变量之前我们就用到过,用来设置了顶点的位置和大小。回顾一下

webgl基础:着色器基础

其他的两个变量暂时没有用到,接下来的内容里会用到这两种类型的变量。敬请期待😁

5. 图元装配和光栅化

什么是图元?

官方解释:

描述各种图形元素的函数叫做图元,描述几何元素的称为几何图元(点,线段或多边形)。点和线是最简单的几何图元

经过顶点着色器计算之后的坐标会被组装成组合图元。

接下来通过一组图解来看看渲染器如何进行图元装配和光栅化

通俗解释

图元就是一个点、一条线段、或者是一个多边形。

5.1 图元装配

什么是图元装配呢?简单理解就是说将我们设置的顶点、颜色、纹理等内容组装称为一个可渲染的多边形的过程。

注意:

如何组装取决于 gl.drawArrays(type,count) 中的 type 类型,本文最后有详细的内容。

在之前的文章里,我们就使用 gl.POINTS 来绘制了一个点。

5.2 光栅化

片元:二维图象上每个点都包含了颜色、深度和纹理数据。这样一个点称为片元

光栅化可以简单理解为以下内容:

通过图元装配生成的多边形,计算像素并填充,剔除不可见的部分,剪裁掉不在可视范围内的部分。最终生成可见的带有颜色数据的图形并绘制。

光栅化流程图解:

webgl基础:着色器基础

剔除和剪裁
  • 剔除:举一个日常生活中的栗子来说明吧。

  • 在日常生活中,对于不透明物体,背面对于观察者来说是不可见的。同样,在 WebGL 中,我们也可以设定物体的背面不可见,那么在渲染过程中,就会将不可见的部分剔除,不参与绘制。节省渲染开销。

  • 剪裁:同样举例子来说明

  • 日常生活中不论是在看电视还是观察物体,都会有一个可视范围,在可视范围之外的事物我们是看不到的。类似的,图形生成后,有的部分可能位于可视范围之外,这一部分会被剪裁掉,不参与绘制。以此来提高性能。

6. 片元着色器

接收光栅化阶段生成的片元,在光栅化阶段中,已经计算出每个片元的颜色信息,这一阶段会将片元做逐片元挑选的操作,处理过的片元会继续向后面的阶段传递。

逐片元挑选

通过模板测试和深度测试来确定片元是否要显示,测试过程中会丢弃掉部分无用的片元内容,然后生成可绘制的二维图像绘制并显示。

  • 深度测试:就是对 z 轴的值做测试,值比较小的片元内容会覆盖值比较大的。(类似于近处的物体会遮挡远处物体)。

  • 模板测试:模拟观察者的观察行为,可以接为镜像观察。标记所有镜像中出现的片元,最后只绘制有标记的内容。

7. 实例分析

下面结合2.2.5的代码,我们进行流程分析,加深理解。

分析:

  • 首先创建了两个变量 VERTEX_SHADER顶点着色器 和 FRAGMENT_SHADER片元着色器。这两个内容会通过下面的代码添加到顶点着色器和片元着色器中。

  • 之后通过 gl.createShader() 创建了两个着色器。传入不同的参数即可创建不同的着色器。创建并关联着色器的步骤比较复杂。当你熟悉这个过程之后,可以将这个过程的代码封装起来作为备用。

  • 接下来,通过 将着色器代码添加到着色器中、编辑着色器、创建程序对象、将着色器添加到程序对象中、关联程序对象、使用程序对象 这一系列的步骤初始化着色器和程序对象。

  • 当所有的前置工作准备就绪之后,就可以调用 gl.drawArrays() 方法来绘制想要的图形。

7.1 gl.drawArrays(type, first, count)

1. type 代表要绘制的图形形状,值有以下几种:

gl.POINTS: 要绘制一系列的点

gl.LINES: 要绘制了一系列未连接直线段(单独行)gl.LINE_STRIP: 要绘制一系列连接的线段

*gl.LINE_LOOP *: 要绘制一系列连接的线段。它还连接第一个和最后一个顶点,以形成一个环

gl.TRIANGLES: 一系列单独的三角形

gl.TRIANGLE_STRIP: 绘制一个三角带

gl.TRIANGLE_FAN: 绘制一个扇形(三角扇)

2. first 代表从哪个点开始
3. count 代表需要使用几个点
栗子:

webgl基础:着色器基础

其他的形状可以自己尝试…………会有意想不到的效果。



福利时间

这次为大家准备了3个有纪念意义的奖品:

1、字节跳动 技术学院抱枕 3个

温软舒适,温暖的你的心

短毛绒,不掉毛,不起球



2、奖品获取方法