在将数据传递给像素着色器时如何避免int-> float转换?

时间:2015-12-29 17:30:02

标签: opengl pixel-shader

我有一个像素着色器:

varying vec2 f_texcoord;
uniform vec4 mycolor_mult;
uniform sampler2D mytexture;
void main(void) {
    gl_FragColor = (texture2D(mytexture, f_texcoord) * mycolor_mult);
};

和相应的C ++代码:

GLint m_attr = glGetUniformLocation(m_program, "mycolor_mult");
// ...
unsigned int myColor = ...; // 0xAARRGGBB format
float a = (myColor >> 24) / 255.f;
float r = ((myColor >> 16) & 0xFF) / 255.f;
float g = ((myColor >> 8) & 0xFF) / 255.f;
float b = (myColor & 0xFF) / 255.f;
glUniform4f(m_attr, r, g, b, a);

我将精灵的颜色保持为unsigned int,并且必须将其转换为4个浮点数才能将其传递给着色器。

可以优化吗?我的意思是我可以传递不浮点数,但是无符号字符作为着色器的组件并避免“除以255”操作吗?我应该在着色器和C ++代码中进行哪些更改?

2 个答案:

答案 0 :(得分:5)

这个问题有几个方面。

值得优化吗?

我同意@ Nick的评论。你很有可能试图优化那些对性能不重要的东西。例如,如果此代码每帧仅执行一次,则此代码的执行时间绝对无关紧要。如果每帧执行多次次,事情可能会有所不同。使用分析器可以告诉您在此代码中花费了多少时间。

你正在优化正确的事情吗?

确保glGetUniformLocation()调用仅在链接着色器后进行一次,而不是每次都设置制服。否则,该调用很可能比其余代码昂贵得多。如果您已经这样做,那么从代码中就不完全清楚了。

您可以使用更有效的OpenGL调用吗?

实际上,如果您需要在着色器中将值设置为浮点数。制服没有自动格式转换,因此您不能简单地使用glUniform*()系列中的其他来电。来自规范:

  

对于所有其他统一类型,使用的Uniform *命令必须与着色器中声明的制服的大小和类型相匹配。没有进行类型转换。

代码可以优化吗?

如果您真的想进行微优化,可以通过乘法替换除法。在大多数CPU上,划分比乘法更昂贵。然后代码如下所示:

const float COLOR_SCALE = 1.0f / 255.f;
float a = (myColor >> 24) * COLOR_SCALE;
float r = ((myColor >> 16) & 0xFF) * COLOR_SCALE;
float g = ((myColor >> 8) & 0xFF) * COLOR_SCALE;
float b = (myColor & 0xFF) * COLOR_SCALE;

您不能指望编译器为您执行此转换,因为更改操作可能会影响操作的精度/舍入。一些编译器具有标志以实现这些类型的优化。请参阅示例Optimizing a floating point division and conversion operation

答案 1 :(得分:2)

使用现代OpenGL(GLSL> = 4.1),有unpackUnorm4x8 GLSL函数可以完全按照您的要求执行:它需要一个32位uint并从中创建一个标准化的浮点向量。你只需要调整结果以匹配你的字节顺序,该函数会将最低有效字节解释为第一个通道。

uniform uint mycolor_packed;
//...
vec4 mycolor_mult=unpackUnorm4x8(mycolor_packed).bgra;

这可能是在着色器本身中进行转换的最有效方法。但是,如果在GPU上每个片段执行一次这样的操作更有效率,而且每次在CPU上进行一次调用只执行一次,那么仍然值得怀疑。