WebGL-Shader入门(5.着色器语言GLSL ES 内置变量和内置函数)

1. 内置变量

前面的文章中,在着色器程序中你一定会发现有 gl_Position和gl_FragColor 变量,但并没有声明,它们就是WebGL着色器提供的内置变量,WebGL提供了很多内置变量,先来简单列举一下常用内置变量:

内置变量 数据类型 描述
gl_PointSize float 绘制点模式时,绘制的点的大小
gl_Position vec4 逐顶点处理时,当前顶点位置坐标
gl_FragColor vec4 逐片元处理时,当前片元颜色
gl_FragCoord vec2 片元在canvas坐标系中的坐标
gl_PointCoord vec2 绘制点模式时,当前片元在所属点内的坐标(从0.0到1.0)

1.1 gl_PointSize

内置变量 gl_PointSiz 表示绘制模式为gl.POINTS时,绘制的点的大小。换而言之就是调用gl.drawArrays()绘制图形时的绘制模式是点模式gl.POINTS的时候,才会用到内置变量gl_PointSize。
看一下示例程序是如何使用内置变量gl_PointSize设置绘制的点的大小

//顶点着色器
var VSHADER_SOURCE = '' +
  'void main(){\\n' +
  //设置顶点位置(坐标原点)
  '  gl_Position =vec4(0.0,0.0,0.0,1.0);\\n' +
  //设置点的大小(20px)
  '  gl_PointSize=20.0;\\n' +
  '}\\n';

//片元着色器
var FSHADER_SOURCE = '' +
  'void main(){\\n' +
  //设置片元颜色(红色)
  ' gl_FragColor = vec4(1.0,0.0,0.0,1.0);\\n' +
  '}\\n';

...
//开始绘制,绘制顶点,从第一个顶点开始绘制,绘制一个顶点
gl.drawArrays(gl.POINTS, 0, 1);

1.2 gl_Position

gl_Position变量表示在逐顶点处理时,当前顶点的坐标,通常在JavaScript程序中会传入一系列的顶点,在顶点着色器中将每个处理完的顶点存放在该gl_Position变量中供片元着色器使用,通过下面这个例子来了解gl_Position如何使用

var VSHADER_SOURCE = '' +
  'attribute vec4 a_Position;\\n' + //声明attribute变量a_Position,用来存放顶点位置信息
  'attribute vec4 a_Color;\\n' + //声明attribute变量a_Color,用来存放顶点颜色信息
  'uniform mat4 u_MvpMatrix;\\n' + //声明uniform变量u_MvpMatrix,用来存放模型视图投影组合矩阵
  'varying vec4 v_Color;\\n' + //声明varying变量v_Color,用来向片元着色器传值顶点颜色信息
  'void main(){\\n' +
  '  gl_Position = u_MvpMatrix * a_Position;\\n' + //将模型视图投影组合矩阵与顶点坐标相乘赋值给顶点着色器内置变量gl_Position
  '  v_Color = a_Color;\\n' + //将顶点颜色信息传给片元着色器,
  '}\\n';

1.3 gl_FragColor

gl_FragColor逐片元处理时,当前片元的颜色,通常会在片元着色器中使用,WebGL绘制图形时,经过光栅化处理得到一个个的片元,这时在片元着色器中可以对这些片元逐个处理,处理完成后写入颜色缓冲区供WegGL输出到屏幕上,来看一个逐片元处理漫反射和环境光的例子加深一下理解

//片元着色器
var FSHADER_SOURCE = '' +
  '#ifdef GL_ES\\n' +
  ' precision mediump float;\\n' + // 设置精度
  '#endif\\n' +
  'uniform vec3 u_PointLightColor;\\n' + //声明uniform变量u_PointLightColor,用来存放点光源颜色
  'uniform vec3 u_PointLightPosition;\\n' + //声明uniform变量u_PointLightPosition,用来存放点光源位置
  'uniform vec3 u_AmbientLightColor;\\n' + //声明uniform变量u_AmbientLightColor,用来存放环境光颜色
  'varying vec4 v_Color;\\n' + //声明varying变量v_Color,用来接收顶点着色器传送的片元颜色信息
  'varying vec3 v_Normal;\\n' + //声明varying变量v_Normal,用来接收顶点着色器传送的顶点法向量
  'varying vec3 v_Position;\\n' + //声明varying变量v_Position,用来接收顶点着色器传送的顶点坐标
  'void main(){\\n' +
  '  vec3 normal = normalize(v_Normal);\\n' + //对法线进行归一化处理,因为内插后长度不一定是1.0
  '  vec3 lightDirection = normalize(u_PointLightPosition - v_Position);\\n' + //计算点光源照射物体表面的光线方向,并归一化
  '  float nDotL = max(dot(lightDirection, normal), 0.0);\\n' + //计算光线方向和法向量点积
  '  vec3 diffuse = u_PointLightColor * vec3(v_Color) * nDotL;\\n' + //计算漫反射光的颜色
  '  vec3 ambient = u_AmbientLightColor * vec3(v_Color);\\n' + //计算环境漫反射光的颜色
  '  gl_FragColor = vec4(diffuse + ambient, v_Color.a);\\n' + //将漫反射光的颜色和环境漫反射光的颜色相加的结果赋值给内置变量gl_FragColor
  '}\\n';

1.4 gl_FragCoord

内置变量gl_FragCoord 表示绘制模式为gl.POINTS时,片元在canvas坐标系中的坐标。canvas坐标系以画布的左上角为原点,水平向右为X轴正方向,垂直向下为Y轴正方向,用gl_FragCoord.x、gl_FragCoord.y 可以获取到当前片元在canvas坐标系中的坐标,来看一个通过内置变量gl_FragCoord模拟着色器进行线性内插的过程

//片元着色器
var FSHADER_SOURCE = '' +
  'precision mediump float;\\n' + // 设置精度
  'uniform float u_Width;\\n' + //声明uniform变量u_Width,用来接收canvas画布的宽度
  'uniform float u_Height;\\n' + //声明uniform变量u_Height,用来接收canvas画布的高度
  'void main(){\\n' +
  //使用内置变量gl_FragCoord计算颜色并赋值给内置变量gl_FragColor
  ' gl_FragColor = vec4(gl_FragCoord.x/u_Width, 0.0, gl_FragCoord.y/u_Height, 1.0);\\n' +
  '}\\n';

1.5 gl_PointCoord

gl_PointCoord表示绘制模式为gl.POINTS时,当前片元在所属点内的坐标,所属点内的坐标原点为当前点的左上角,也是水平向右为X轴正方向,垂直向下为Y轴正方向,X轴和Y轴的坐标范围都是从0.0到1.0。看一个使用gl_PointCoord将一个方形的点绘制成圆形的例子

//片元着色器
var FSHADER_SOURCE = '' +
  '#ifdef GL_ES\\n' +
  ' precision mediump float;\\n' + // 设置精度
  '#endif\\n' +
  'varying vec4 v_Color;\\n' + //声明varying变量v_Color,用来接收顶点着色器传送的片元颜色信息
  'void main(){\\n' +
  '  float d = distance(gl_PointCoord, vec2(0.5, 0.5));\\n' + //计算像素距离中心点的距离
  '  if(d < 0.5) {\\n' + //距离大于0.5放弃片元,小于0.5保留片元
  '    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\\n' +
  '  } else { discard; }\\n' +
  '}\\n'

2. 内置函数

GLSL ES 提供了大量的内置函数方便开发人员调用,这些函数按功能可以分成7类,来一一认识一下。
说明:下文中的类型T 没有特殊说明表示可以是 float, vec2, vec3, vec4,矢量类型逐分量处理

2.1 通用函数

函数名 描述
T abs(T x) 返回x的绝对值
T sign(T x) 判断参数符号,x是正数返回1.0;x是0.0返回0.0,x是负数返回-1.0
T floor(T x) 向下取整,即抹去小数点后面的数
T ceil(T x) 向上取整,小数点后面有数就进1
T fract(T x) 获取x的小数部分
T mod(T x, T y) 、T mod(T x, float y) 取模,返回x除以y的余数,矢量会逐分量运算
T min(T x, T y) 、T min(T x, float y) 取小,返回x,y的最小值,矢量会逐分量运算
T max(T x, T y) 、T max(T x, float y) 取大,返回x,y的最大值,矢量会逐分量运算
T clamp(T x, T minVal, T maxVal) 、 T clamp(T x, float minVal,float maxVal) 将x限制在minVal和maxVal之间,返回min(max(x, minVal), maxVal)
T mix(T x, T y, T a) T mix(T x, T y, float a) 线性插值计算,取x,y的线性混合,x*(1-a)+y*a
T step(T edge, T x) T step(float edge, T x) 生成阶梯函数,如果 x<edge 返回 0.0 否则返回1.0
T smoothstep(T edge0, T edge1, T x) 经过Hermite插值的阶梯函数,如果x<edge0 返回 0.0 如果x>edge1返回1.0, 否则返回Hermite插值

2.2 角度和三角函数

函数名 描述
T radians(T degrees) 将角度转化为弧度
T degrees(T radians) 将弧度转化为角度
T sin(T angle) 正弦函数,参数是弧度
T cos(T angle) 余弦函数,参数是弧度
T tan(T angle) 正切函数,参数是弧度
T asin(T x) 反正弦函数,返回值是弧度
T acos(T x) 反余弦函数,返回值是弧度
T atan(T y, T x)、T atan(T y_over_x) 反正切函数,返回值是弧度,矢量会逐分量运算

2.3 指数函数

函数名 描述
T pow(T x, T y)

x

y

,

x

y

,

返回x的y次幂, 即x^{y} ,矢量会逐分量运算

xy,xy,若干x<0,则返回未定义值,若x=0而y<0,也返回未定义值

T exp(T x)

x

,

e

x

返回x的自然指数幂, 即e^{x}

x,ex

T log(T x)

x

,

y

使

x

=

e

y

,

x

<

0

,

返回x的自然对数 , 即返回y使其满足 x = e^{y},如果x<0,则返回未定义值

x,y使x=ey,x<0,

T exp2(T x)

2

x

,

2

x

返回2的x次幂, 即2^{x}

2x,2x

T log2(T x)

2

l

o

g

2

,

y

使

x

=

2

y

,

x

<

=

0

,

返回2为底的对数 log2 , 即返回y使其满足 x = 2^{y},如果x<=0,则返回未定义值

2log2,y使x=2y,x<=0,

T sqrt(T x) 开根号 √x,如果x<0,则返回未定义值
T inversesqrt(T x) 先开根号,在取倒数,就是 1/√x,如果x<0,则返回未定义值

2.4 几何函数

函数名 描述
float length(T x) 返回矢量x的长度
float distance(T p0, T p1) 返回p0 p1两点之间的距离
float dot(T x, T y) 返回x和y的点积
vec3 cross(vec3 x, vec3 y) 返回x和y的叉积
T normalize(T x) 对x进行归一化,保持矢量方向不变,长度变为1,即x/length(x)
T faceforward(T N, T I, T Nref) 法向量反向操作,根据矢量N与Nref调整法向量,如果dot(Nref,I)<0则返回N,否则返回-N
T reflect(T I, T N) 计算反射矢量,入射矢量为I,表面法向量为N,返回 I – 2 * dot(N,I) * N, 结果是入射矢量 I 关于法向量N的 镜面反射矢量,注意计算时法向量N必须已经被归一化
T refract(T I, T N, float eta) 根据入射光和介质特性计算折射现象,入射光方向为I,表面法向量为N,介质折射率为eta,被折射后的光线方向,注意计算时入射光I和表面法向量N必须已经被归一化

2.5 矩阵函数

说明:下表中的类型T 表示可以是 mat2, mat3, mat4

函数名 描述
T matrixCompMult(T x, T y) 返回矩阵x和矩阵y逐元素相乘的结果

r

e

s

=

m

a

t

r

i

x

C

o

m

p

M

a

t

r

i

x

(

x

,

y

)

,

r

e

s

[

i

]

[

j

]

=

x

[

i

]

[

j

]

y

x

[

i

]

[

j

]

如果res=matrixCompMatrix(x,y),则res[i][j]=x[i][j]*yx[i][j]

res=matrixCompMatrix(x,y),res[i][j]=x[i][j]yx[i][j]

2.6 矢量函数

说明:下表中的类型T 表示可以是 vec2,vec3,vec4,ivec2,ivec3,ivec4;BT表示可以是 bvec2,bvec3, bvec4*

函数名 描述
BT lessThan(T x, T y) 逐分量比较x < y是否成立
BT lessThanEqual(T x, T y) 逐分量比较 x <= y是否成立
BT greaterThan(T x, T y) 逐分量比较 x > y是否成立
BT greaterThanEqual(T x, T y) 逐分量比较 x >= y是否成立
BT equal(T x, T y) 逐分量比较 x == y是否成立,每个对应元素都相等返回真
BT notEqual(T x, T y) 逐分量比较 x!= y是否成立,每个对应元素都不相等返回真
bool any(BT x) 如果矢量x的任意一个分量是true,则返回true
bool all(BT x) 如果矢量x的所有分量是true,则返回true
BT not(BT x) 矢量x逐分量取反

2.7 纹理查询函数

纹理查询函数主要用于处理WebGL的纹理图像,通过uv坐标从纹理上获取纹素,纹理有两种,一种是平面2d纹理,另一种是盒纹理,针对不同的纹理使用不同的访问方法

2.7.1 texture2D()

texture2D()函数的功能是从二维纹理中提取纹素,主要有以下两种形式的声明

vec4 texture2D(sampler2D sampler, vec2 coord);
vec4 texture2D(sampler2D sampler, vec2 coord, float bias);

参数说明

  • sampler:绑定到sampler的二维纹理
  • coord:纹理贴图的UV坐标
  • bias:当sampler是MIPMAP纹理时,执行实际纹理查找操作之前添加偏差
    注意,第三个参数bias,只可以在片元着色器中使用

texture2D()使用示例

//片元着色器
var FSHADER_SOURCE = '' +
  'precision mediump float;\\n' + // 设置精度
  'uniform sampler2D u_Sampler;\\n' + //声明uniform变量u_Sampler,用来存放纹理单元编号
  'varying vec2 v_TexCoord;\\n' + //声明varying变量v_TexCoord,用来接收顶点着色器传送的纹理坐标
  'void main(){\\n' +
  //使用texture2D函数抽取纹素,并赋值给内置变量gl_FragColor
  ' gl_FragColor = texture2D(u_Sampler,v_TexCoord);\\n' +
  '}\\n';

2.7.2 textureCube()

textureCube()函数的功能是从盒纹理中提取纹素,也有两种形式的声明

vec4 textureCube(samplerCube sampler, vec2 coord);
vec4 textureCube(samplerCube sampler, vec2 coord, float bias);

参数说明

  • sampler:绑定到sampler的盒纹理
  • coord:纹理贴图的UV坐标
  • bias:当sampler是MIPMAP纹理时,执行实际纹理查找操作之前添加偏差
    注意,第三个参数bias,只可以在片元着色器中使用

textureCube()的使用与texture2D()的使用雷同,请参照texture2D()的使用示例

2.7.3 texture2DProj()

texture2DProj()函数的功能是从投影纹理中提取纹素,与texture2D相比texture2DProj内部会除以coord的最后一个分量,所以纹理的坐标需要从coord的最后一个分量中解析出来,vec4类型的第三个分量将会被忽略。他有以下四种形式的声明

vec4 texture2DProj(sampler2D sampler, vec3 coord);
vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);
vec4 texture2DProj(sampler2D sampler, vec4 coord);
vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias);

参数说明

  • sampler:绑定到sampler的投影纹理
  • coord:纹理贴图的UV坐标
  • bias:当sampler是MIPMAP纹理时,执行实际纹理查找操作之前添加偏差
    注意,第三个参数bias,只可以在片元着色器中使用

2.7.4 texture2DLod()、texture2DProjLod()、textureCubeLod()

通过名称可以发现这些函数有个共同特点就是都有Lod字符,前面说的三类纹理查询函数都是第三个参数存在时,只能在片元着色器中使用,而带有Lod字符的函数就是支持第三个参数给顶点着色器来使用的,接下来看看它们的声明形式

vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod);
vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod);
vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod);

参数说明

  • sampler:绑定到sampler的纹理
  • coord:纹理贴图的UV坐标
  • lod:当sampler是MIPMAP纹理时,执行实际纹理查找操作之前添加偏差

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容