当前位置: 代码迷 >> 综合 >> WebGL-Shader入门(3.着色器语言的限定词-参数限定词/存储限定词/精度限定词)
  详细解决方案

WebGL-Shader入门(3.着色器语言的限定词-参数限定词/存储限定词/精度限定词)

热度:55   发布时间:2023-12-14 00:55:58.0

着色器语言的限定词

  • 1. 序
  • 2 参数限定词
  • 3 存储限定词
  • 4 精度限定词

1. 序

GLSL ES 中提供了三类限定词(又称限定字或限定符,都是一回事,本文统称为限定词),参数限定词、存储限定词和精度限定词,通过这些限定词可以控制参数的行为、限定变量的类型以及限定数据的精度来提高运行效率,减少内存开销,首先从参数限定词说起

2 参数限定词

参数限定词顾名思义就是来限制函数参数的,根据参数不同的行为可以将它们分为以下几类

  • 只向函数中传值
  • 在函数中被赋值
  • 既向函数中传值也在函数中被赋值

依据这几类不同的行为,GLSL ES提供了以下几种参数限定词

类别 规则 描述
in 向函数中传入值 参数传入函数,函数内可以使用参数值,也可以修改参数值,但内部修改不影响传入前的变量
const in 向函数中传入值 参数传入函数,函数内可以使用参数值, 但不能修改参数值
out 在函数重被赋值,并被传出 传入变量的引用,如果在函数内部被修改,会影响到函数外部传入的变量
inout 可同时传入传出 传入变量的引用,函数会用到变量的初始值,然后修改变量的值,会影响到函数外部传入的变量
将一个值传给函数 和限定词in一样

例如,给getBrightness()函数指定一个out参数来接收计算结果,与通过return返回让外部接收是一样的

void getBrightness (in vec3 color, out float brightness)  {
    brightness = 0.231 * color.r + 0.752 * color.g + 0.032 * color.b
}//调用
getBrightness (color, brightness);//计算结果存储在brightness中

3 存储限定词

存储限定词其实就是我们用来向着色器传值的变量类型,GLSL ES提供了attribute、uniform和varying三个限定词来声明特定用途的变量

  • attribute
    attribute限定词声明的变量用来表示顶点坐标信息,顶点颜色信息,顶点法向信息等和顶点相关的逐顶点的信息,只适用于顶点着色器,attribete变量的类型只能是:float,vec2,vec3,vec4,mat2,mat3,mat4,且要求是全局变量
  • uniform
    uniform限定词声明的变量既可以用在顶点着色器也可以用在片元着色器,要声明为全局变量,它是只读的,不支持数组和结构体类型 ,用来表示非逐顶点的各顶点/片元共享的信息,例如变换矩阵就是所有顶点共有的
  • varying
    varying限定词声明的变量也要求是全局变量,它担任着一个很重要的角色,就是从顶点着色器向片元着色器传值,使用时必须同时在顶点着色器和片元着色器声明同名同类型的varying变量,与attribute变量一样它的类型也只能是float,vec2,vec3,vec4,mat2,mat3,mat4

接下来用一个例子来看看这些变量是如何使用的

//顶点着色器
var VSHADER_SOURCE = '' +'attribute vec4 a_Position;\n' + //声明attribute变量a_Position,用来存放顶点位置信息'attribute vec4 a_Color;\n' + //声明attribute变量a_Color,用来存放顶点颜色信息'attribute vec4 a_Normal;\n' + //声明attribute变量a_Normal,用来存放法向量'uniform mat4 u_MvpMatrix;\n' + //声明uniform变量u_MvpMatrix,用来存放模型视图投影组合矩阵'uniform vec3 u_LightColor;\n' + //声明uniform变量u_LightColor,用来存放光线颜色'uniform vec3 u_LightDirection;\n' + //声明uniform变量u_LightDirection,用来存放入射光方向,归一化的世界坐标'varying vec4 v_Color;\n' + //声明varying变量v_Color,用来向片元着色器传值顶点颜色信息'void main(){\n' +' gl_Position = u_MvpMatrix * a_Position;\n' + //将模型视图投影组合矩阵与顶点坐标相乘赋值给顶点着色器内置变量gl_Position' vec3 normal = normalize(vec3(a_Normal));\n' + //对法向量归一化处理' float nDotL = max(dot(u_LightDirection, normal), 0.0);\n' + //计算光线方向和法向量点积' vec3 diffuse = u_LightColor * vec3(a_Color) * nDotL;\n' + //计算漫反射光的颜色' v_Color = vec4(diffuse, a_Color.a);\n' + //将顶点颜色信息传给片元着色器,'}\n';//片元着色器
var FSHADER_SOURCE = '' +'precision mediump float;\n' + // 设置精度'varying vec4 v_Color;\n' + //声明varying变量v_Color,用来接收顶点着色器传送的片元颜色信息'void main(){\n' +//将varying变量v_Color接收的颜色信息赋值给内置变量gl_FragColor' gl_FragColor = v_Color;\n' +'}\n';

4 精度限定词

为了提高着色器程序运行效率,减少内存开销,GLSL ES 引入了精度限定词,通常高精度需要更大的开销,低精度需要较少开销,使用精度限定词可以精细的控制程序在效能和开销之间的平衡
WebGL程序支持三种精度,限定词分别是 highp、mediump和lowp

精度限定词 描述 默认数值范围和精度Float 默认数值范围和精度Int
highp 高精度,顶点着色器的最低精度 ( ? 2 62 , 2 62 ) 精 度 2 ? 16 ( -2^{62},2^{62}) 精度2^{-16} (?262,262)2?16 ( ? 2 16 , 2 16 ) ( -2^{16},2^{16}) (?216,216)
mediump 中精度,介于高精度和低精度之间,片元着色器的最低精度 ( ? 2 14 , 2 14 ) 精 度 2 ? 10 ( -2^{14},2^{14}) 精度2^{-10} (?214,214)2?10 ( ? 2 10 , 2 10 ) ( -2^{10},2^{10}) (?210,210)
lowp 低精度,低于中精度,可以表示所有颜色 ( ? 2 , 2 ) 精 度 2 ? 8 ( -2,2) 精度2^{-8} (?2,2)2?8 ( ? 2 8 , 2 8 ) ( -2^{8},2^{8}) (?28,28)

需要注意一下,在某些WebGL环境中,片元着色器可能不支持highp精度
看一下几个声明变量精度的例子

mediump float size;
highp vec4 position;
lowp vec4 color;

你会发现为每一个变量设置精度太繁琐了,通常会使用 precision 关键字为某一类型的变量设置默认精度,这个设置必须放置在程序的顶部
使用方式如下
precision <精度限定词> <类型名称>

来看一下设置所有浮点数默认精度为中精度的例子

//片元着色器
var FSHADER_SOURCE = '' +'precision mediump float;\n' + // 所有浮点数默认精度为中精度'varying vec4 v_Color;\n' + //声明varying变量v_Color,用来接收顶点着色器传送的片元颜色信息'void main(){\n' +//将varying变量v_Color接收的颜色信息赋值给内置变量gl_FragColor' gl_FragColor = v_Color;\n' +'}\n';

如果你发现了在示例中只对片元着色器中float类型进行了默认精度的设置,并不是其他类型不需要限定精度,是因为其余的都内置了默认精度限定,具体请参照下表

着色器类型 数据类型 默认精度
顶点着色器 int highp
顶点着色器 float highp
顶点着色器 sampler2D lowp
顶点着色器 samplerCube lowp
片元着色器 int mediump
片元着色器 float
片元着色器 sampler2D lowp
片元着色器 samplerCube lowp

可以发现片元着色器中的float类型没有设定默认精度,需要手动设定,否则会导致编译错误
failed to compile shader:ERROR: 0:1 :No precision specified for (float)