GLSL中的精度限定符

6,861 阅读5分钟

        之前在写GLSL初识的时候漏掉了这部分相关的内容,自己也没重视这一块,结果今天在项目中就遇到了这方面的坑,所以特此记录一下,避免再次踩坑。

      我们先来看下面两段代码:

uniform sampler2D colorMap;
varying lowp vec2 varyingCoordPos;
void main() {
    vec4 color = texture2D(colorMap,varyingCoordPos);
    gl_FragColor = color;
}

uniform sampler2D colorMap;
varying lowp vec2 varyingCoordPos;
void main() {
    gl_FragColor = texture2D(colorMap,varyingCoordPos);
}

      这两段代码都只有一个最简单的逻辑——提取纹理的颜色赋值给gl_FragColor.可是这两段代码都对吗?如果不对,问题又出在哪里?提示一下第一段代码是错误的,具体错在哪里看完本篇文章就知道了。

三种精度限定符

       在GLSL中只有三种精度限定符:

  • lowp:低精度
  • mediump:中精度
  • highp:高精度

精度限定符放在数据类型前面,存储限定符后面,比如:

lowp float color;
varying mediump vec2 Coord; 
highp mat4 m;

精度范围

      注意:字符常量和布尔型变量是没有精度修饰符的。 

对于浮点型变量的精度范围

  • highp (-2的62次方, 2的62次方); 
  • mediump (-2的14次方, 2的14次方);
  • lowp (-2,2);

对于整型变量的精度范围

  • highp (-2的16次方, 2的16次方); 
  • mediump (-2的10次方, 2的10次方); 
  • lowp (-2的8次方, 2的8次方);

      从这两种数据类型的精度范围我们可以知道:对于高精度和中精度,整数变量可以准确的的转化为同样精度修饰符修饰的浮点型变量。

      例如:highp int 可以转换为 highp float ,mediump int 可以转换为 mediump float;但是lowp int 缺不可以转化为 lowp float。这是为什么呢,很简单和C语言类似,只要范围小的向范围高的转化,反过来不行。比如这里:lowp int 变量的值如果 超出了 lowp float 的范围(-2,2)这当然就不能转化了。

默认精度

     前面我们说了可以直接在变量类型前面加上精度限定符来指定变量的精度,但是如果每个整型、浮点型(包括整型向量、浮点型向量、浮点型矩阵)都要去加个精度限定符是不是过于繁琐了。有没有什么方法直接指定一个默认的精度范围呢?

      可以用precision关键字来指定默认精度,具体语法如下:

precision precision-qualifier type;
* precision可以用来确认默认精度修饰符
* precision-qualifie可以是highp,mediump,lowp三种精度修饰符中的一个,且只能是这三个中的一个。

     如果type是float类型,那么该精度(precision-qualifie)将适用于任何无精度修饰符的浮点数声明,包括标量、向量和矩阵。 

 如果type是int类型,那么该精度(precision-qualifie)将适用于任何无精度修饰符的整型数据声明,包括标量、向量(GLSL中没有整型的矩阵)。 

     包括全局变量声明、函数返回值声明、函数参数声明以及本地变量声明等,只要是没有声明精度修饰符的变量将使用和它最近的precision语句中的精度。

     具体写法如下:

precision highp float;
precision mediump int;

预定义精度

     并不是一定要为int和 float 数据声明精度,GLSL为我们预定义了一些数据类型的精度。

顶点着色器中的预定义精度

在顶点着色器中有如下预定义的全局默认精度语句:

  • precision highp float; 
  • precision highp int; 
  • precision lowp sampler2D; 
  • precision lowp samplerCube;

片元着色器中的预定义精度

在片元着色器中有如下预定义的全局默认精度语句:

  • precision mediump int; 
  • precision lowp sampler2D; 
  • precision lowp samplerCube;

由此我们可以总结出如下几点:

  1. 在Vertex Shader中,如果没有默认的精度,则float和int精度都为highp;
  2. 在Vertex Shader,Fragment Shader中,sampler2D和samplerCube都有个默认精度lowp;
  3. 在Fragment Shader中,只有整型有默认精度,为mediump;float没有默认精度

      现在我们知道前面那段代码为什么错了吗?问题就出在这句代码上 vec4 color = texture2D(colorMap,varyingCoordPos); 这里我们在片元着色器中,声明了个临时变量vec4 color 但是却没有指定精度,而片元着色器中浮点数是没有默认精度的,所以就出错了,感觉这就是个巨坑啊,所以以后在片元着色器不管有没有必要还是加上precision highp float这句比较保险,养成一个好的代码规范。 当然如果你不加也是可以的,可以直接改成 vec4 mediump color 也是行的。

    那为什么第二段代码没问题呢,我们知道gl_FragColor是个内建变量,既然是GLSL内建的变量那肯定已经设置好了精度,所以直接gl_FragColor = texture2D(colorMap,varyingCoordPos); 这么写是没有问题的。