在webgl中,调用了OpenGL-ES-2.0的API,而在OpenGL-ES专为嵌入式设备设计,其和其它设备一样,都是使用GLSL(GL Shading Language)来编写片段程序并执行于GPU的着色器上,来完成对对象的渲染。GLSL在其中起着相当重要的作用,所以要玩好webgl,我们就得把GLSL搞懂,本文主要介绍shader的基础使用及组成。
整个管线处理过程:
1.指定几何对象
- 顶点数组(直接将顶点数据传送至shader里)
- 顶点索引(将顶点数据保存于缓冲区中,用索引来从缓冲区获取数据传入shader)
2.逐个顶点操作
3.图元组装
根据指定的图元组装方式将若干顶点组成一个图元,OpenGL支持的几何图元有点、线、不闭合折线、闭合折线、多边形、三角形、线型连续填充三角形、扇形连续填充三角形、四边形以及连续填充四边形。
而webgl支持的图元为POINTS(点), LINE_STRIP(不闭合折线), LINE_LOOP(闭合折线), LINES(独立的线段), TRIANGLE_STRIP(顶点按顺序相连的三角形), TRIANGLE_FAN(扇形顺序组合三角形), TRIANGLES(每三个顶点组合成一个三角形)。
绘制三角形序列的三种方式解释了三种三角形绘制方法之间的不同。
4.图元处理
5.栅格化(生成片元fragment)
6.片元处理
7.逐个片元操作
8.帧缓冲区操作
在这八个步骤中,我们最重要的是对顶点和片元的操作,在整个管线中,我们可以加入的自己程序的部分则是顶点着色器和片元着色器部分。
着色器
顶点着色器:
操作的是顶点值和其关联的数据,它可完成下面这些操作:
- 顶点变换
- 法线变换以及规格化
- 纹理坐标生成
- 纹理坐标变换
- 光照
- 彩色材质应用
顶点着色器必须计算坐标在裁剪空间中的齐次位置并将结果存储在特殊的输出变量gl_Position中,它还有特殊的输出变量gl_ClipVertex,gl_PointSize。
顶点处理器的输出将被发送到后续的处理阶段,即:图元组装,用户裁剪,平截裁剪,透视划分,视口贴图,多边形偏移,多边形模式,阴影模式,消隐等。
片元着色器:
处理片元值及其相关联数据,它可执行传统的图形操作,如:
在插值等到的值上的操作
- 访问纹理
- 应用纹理
- 雾化
- 颜色汇总
片元着色器其有特殊的输入变量gl_FragCoord(片元的窗口相对坐标)和gl_FrontFacing(正面图元为true,反之为false),经过计算颜色和深度将这些值写入特殊的输出变量gl_FragColor和gl_FragDepth中,或者完全丢弃(使用discard关键字)片元。
片元处理器的一大优点是它可以任意多次地访问纹理内存,并可以任意方式结合所读取的值,一次纹理访问的结果可作为执行另一次纹理访问的基础。
有三个精度可选择:lowp highp mediump
精度可指定于变量或设置默认精度
顶点着色器中的float和init默认精度为highp
片元着色器中float没有默认精度,所以必须为其指定默认精度,如:precision mediump float;
着色器多个执行是可并行发生的,针对每个顶点都会执行一次顶点着色器,针对每个片元都会执行一次片元着色器。
限定符
我们要向着色器中传入数据,则要了解其里面变量的组成和输入方式
属性变量(attribute):
这些变量代表了非常频繁地从应用程序传递到顶点处理器的值,只应用于程序中定义顶点数据,所以只允许作为顶点着色器的一部分,该值可以像每个顶点那样经常变动
一致变量(uniform):
用来将数据值从应用程序传递到顶点处理器或片元处理器,一致变量通常用来提供不频繁变动的值。
易变变量(varying):
定义了从顶点处理器传递到片元处理器的数据。
常量变量(const):
如C中的常量变量
数据类型
标量:
支持使用浮点数(float)、整数(int)和布尔值(bool)
矢量:
浮点数矢量:
vec2 (2个浮点数的矢量)
vec3 (3个浮点数的矢量)
vec4 (4个浮点数的矢量)
整数矢量:
ivec2 (2个整数的矢量)
ivec3 (3个整数的矢量)
ivec4 (4个整数的矢量)
布尔矢量:
bvec2 (2个布尔值的矢量)
bvec3 (3个布尔值的矢量)
bvec4 (4个布尔值的矢量)
矩阵:
mat2 (2×2的浮点数矩阵)
mat3 (3×3的浮点数矩阵)
mat4 (4×4的浮点数矩阵)
mat矩阵就像是一个vec数组,它也可以使用数组来进行访问。
取样器:
sampler1D (访问一个一维纹理)
sampler2D (访问一个二维纹理)
sampler3D (访问一个三维纹理)
samplerCube (访问一个立方贴图纹理)
sampler1DShadow (访问一个带对比的一维深度纹理)
sampler2DShadow (访问一个带对比的二维深度纹理)
只能通过uniform限定的取样器从应用程序接收取样器
使用时: uniform sampler2D texture;
操作
如果要对矢量进行部分操作,则可用访问矢量中的部分来使用,在shader中,共有三组组合供使用:
- x y z w
- s t p q
- r g b a
这四个值只是分别读取矢量中的第一个、第二个、第三个、第四个值,只是为了编写方便,语义化了三组组合,分别为坐标、纹理、颜色,但是使用它们去读取出来的值是一样的,如:
vec4 values = vec4(1.0,2.0,3.0,4.0); values.z; //3.0 values.p; //3.0 values.b; //3.0
values[2]; //3.0
这三组使用时必须成组出现,不能混组出现,如:
vec4 values = vec4(1.0,2.0,3.0,4.0); vec2 combination1 = values.xy; //同一组,正确 vec3 combination2 = values.rgb; //同一组,正确 vec3 combination3 = values.xt; //不同组,不正确
对矩阵的读取可以像数组一样:
vec2 x,y; mat2 matrix; x = matrix[0]; y = matrix[1];
运算
对于矢量的计算:
vec3 v,u,w; w = v + u; //计算过程等价于 w.x = v.x + u.x; w.y = v.y + u.y; w.z = v.z + w.z;
对于矩阵和矢量的计算:
//该过程遵守线性代数中的计算规定,即做点乘的两个矩阵,前一个矩阵的行数等于后一个矩阵的列数 vec4 v,u; mat4 m; v * m; //行矢量与矩阵相乘 m * v; //矩阵与列矢量相乘 m * m; //矩阵与矩阵相乘
运算顺序:
//当多个矩阵同时施加加顶点矢量上时,则要以相反的顺序矩阵相乘 //如想实现先Ma再Mb的运算 vec4 v,u; mat4 Ma,Mb; u = Mb * (Ma * v); //即 u = (Mb * Ma) * v; //即 u = Mb * Ma * v;
与C和C++差异
着色语言作为一种处理数字的语言,而不是处理字符或字符串数据的语言,在其中没有包含对指针、字符串、字符或基于这些类型的任何操作支持。
并且为了使编译器和图形硬件的实现负担更小,该语言不支持双精度浮点数、字节、短整数、长整数或者这些类型的无符号变化形式。
其包括了C++的一些重要语言特性:支持函数重载,支持基本类型bool。
注:
在向顶点着色器中进行传值时,顶点着色器会从缓存中依次读取每个顶点,如果使用的是顶点数组方法:
如传入:new Float32Array([1.0,1.0,1.0,0.0,
0.5,0.5,0.5,0.0]);
那么在shader中,假设有attribute vec4 position;
会从缓存中依次读取四个数来作为position进行处理,则总共执行了两次顶点着色器,共两个顶点。
如果在shader中有attribute vec2 position;
则会从缓存中依次读取两个数来作为position进行处理,共四个顶点,顶点着色器执行四次。
附:
WebGL-1.0参考卡片:https://files.cnblogs.com/files/zhiyishou/webgl-reference-card-1_0.pdf
OpenGL-ES-2.0参考卡片:https://files.cnblogs.com/files/zhiyishou/OpenGL-ES-2_0-Reference-card.pdf
The end.