各位同学们大家好,又到了周末写文章的时间,之前群里有粉丝提问, 就是shader不是很理解。然后今天他就来了, 废话不多说,读完今天的这篇文章你可以学到以下几点:
- 为什么需要有shader ? shader的作用是什么????
- shader 中的每个参数到底是什么意思??怎么去用???
你如果会了,这篇文章你可以不用看👀,不用浪费时间,去看别的文章。如果哪里写的有问题欢迎大家指正,我也在不断地学习当中。
why need shader
这里我结合自己的思考🤔,讲讲webgl的整个的一个渲染过程。
渲染管线
「Webgl」的渲染依赖底层「GPU」的渲染能力。所以「WEBGL」 渲染流程和 「GPU」 内部的渲染管线是相符的。
「渲染管线的作用是将3D模型转换为2维图像。」
在早期,渲染管线是不可编程的,叫做「固定渲染管线」,工作的细节流程已经固定,修改的话需要调整一些参数。
现代的 「GPU」 所包含的渲染管线为「可编程渲染管线」,可以通过编程 「GLSL 着色器语言」 来控制一些渲染阶段的细节。
简单来说:就是使用「shader」,我们可以对画布中「每个像素点做处理」,然后就可以生成各种酷炫的效果了。
渲染过程
渲染过程大概经历了下面这么多过程, 因为本篇文章的重点其实是在着色器,所以我重点分析从「顶点着色器」—— 「片元着色器」的一个过程
- 「顶点着色器」
- 「图片装配」
- 「光栅化」
- 「片元着色器」
- 「逐片段操作(本文不会分享此内容)」
- 「裁剪测试」
- 「多重采样操作」
- 「背面剔除」
- 「模板测试」
- 「深度测试」
- 「融合」
- 「缓存」
顶点着色器
WebGL就是和GPU打交道,在GPU上运行的代码是一对着色器,一个是顶点着色器,另一个是片元着色器。每次调用着色程序都会先执行顶点着色器,再执行片元着色器。
一个顶点着色器的工作是生成裁剪空间坐标值,通常是以下的形式:
const vertexShaderSource = ` attribute vec3 position; void main() { gl_Position = vec4(position,1); } `
每个顶点调用一次(顶点)着色器,每次调用都需要设置一个特殊的全局变量 「gl_Position」。该变量的值就是裁减空间坐标值。这里有同学就问了, 什么是「裁剪空间的坐标值」???
其实我之前有讲过,我在讲一遍。
何为裁剪空间坐标?就是无论你的画布有多大,裁剪坐标的坐标范围永远是 -1 到 1 。
看下面这张图:
裁剪坐标系
如果运行一次顶点着色器, 那么gl_Position 就是**(-0.5,-0.5,0,1)** 记住他永远是个 「Vec4」, 简单理解就是对应「x、y、z、w」。即使你没用其他的,也要设置默认值, 这就是所谓的 3维模型转换到我们屏幕中。
顶点着色器需要的数据,可以通过以下四种方式获得。
- attributes 属性(从缓冲读取数据)
- uniforms 全局变量 (一般用来对物体做整体变化、 旋转、缩放)
- textures 纹理(从像素或者纹理获得数据)
- varyings 变量 (将顶点着色器的变量 传给 片元着色器)
Attributes 属性
属性可以用 float
, vec2
, vec3
, vec4
, mat2
, mat3
和 mat4
数据类型
所以它内建的数据类型例如vec2
, vec3
和 vec4
分别代表两个值,三个值和四个值, 类似的还有mat2
, mat3
和 mat4
分别代表 2x2, 3x3 和 4x4 矩阵。你可以做一些运算例如常量和矢量的乘法。看几个例子吧:
vec4 a = vec4(1, 2, 3, 4); vec4 b = a * 2.0; // b 现在是 vec4(2, 4, 6, 8);
向量乘法 和矩阵乘法 :
mat4 a = ??? mat4 b = ??? mat4 c = a * b; vec4 v = ??? vec4 y = c * v;
它还支持矢量「调制」,意味者你可以交换或重复分量。
v.yyyy === vec4(y, y, y,y ) v.bgra === vec4(v.b,v.g,v.r,v.a) vec4(v.rgb, 1) === vec4(v.r, v.g, v.b, 1) vec4(1) === vec4(1, 1, 1, 1)
这样你在处理图片的时候可以轻松进「行 颜色通道 对调」, 发现你可以实现各种各样的滤镜了。
后面的属性在下面实战中会讲解:我们接着往下走:
图元装配和光栅化
「什么是图元?」
❝「描述各种图形元素的函数叫做图元,描述几何元素的称为几何图元(点,线段或多边形)。点和线是最简单的几何图元」经过顶点着色器计算之后的坐标会被组装成「组合图元」。
❞
「通俗解释」:「图元就是一个点、一条线段、或者是一个多边形。」
「什么是图元装配呢?」
「简单理解就是说将我们设置的顶点、颜色、纹理等内容组装称为一个可渲染的多边形的过程。」
组装的类型取决于:你最后绘制选择的图形类型
gl.drawArrays(gl.TRIANGLES, 0, 3)
「如果是三角形的话,顶点着色器就执行三次」
光栅化
「什么是光栅化:」
通过图元装配生成的多边形,计算像素并填充,「剔除」不可见的部分,「剪裁」掉不在可视范围内的部分。最终生成可见的带有颜色数据的图形并绘制。
「光栅化流程图解:」
光珊化图解
剔除和剪裁
- 「剔除」:
在日常生活中,对于不透明物体,背面对于观察者来说是不可见的。同样,在「webgl」中,我们也可以设定物体的背面不可见,那么在渲染过程中,就会将不可见的部分剔除,不参与绘制。节省渲染开销。
- 「剪裁」:
日常生活中不论是在看电视还是观察物体,都会有一个可视范围,在可视范围之外的事物我们是看不到的。类似的,图形生成后,有的部分可能位于可视范围之外,这一部分会被剪裁掉,不参与绘制。以此来提高性能。这个就是「视椎体」, 在📷范围内能看到的东西,才进行绘制。
片元着色器
「光珊化后,每一个像素点都包含了 颜色 、深度 、纹理数据, 这个我们叫做片元」
❝小tips :每个像素的颜色由片元着色器的「gl_FragColor」提供
❞
接收光栅化阶段生成的片元,在光栅化阶段中,已经计算出每个片元的颜色信息,这一阶段会将片元做逐片元挑选的操作,处理过的片元会继续向后面的阶段传递。「片元着色器运行的次数由图形有多少个片元决定的」。
「逐片元挑选」
通过模板测试和深度测试来确定片元是否要显示,测试过程中会丢弃掉部分无用的片元内容,然后生成可绘制的二维图像绘制并显示。
- **深度测试:**就是对 「z」 轴的值做测试,值比较小的片元内容会覆盖值比较大的。(类似于近处的物体会遮挡远处物体)。
- **模板测试:**模拟观察者的观察行为,可以接为镜像观察。标记所有镜像中出现的片元,最后只绘制有标记的内容。