Android Opengl:gl_FragColor 的替代值
Android Opengl: Alternate values for gl_FragColor
我正在编写一个 Android 应用程序,它利用 opengl 对相机输出执行一些更改。我以这样一种方式编写了我的代码,以至于我终于弄清楚了导致性能问题的原因。
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES sTexture;
uniform vec4 vColor;
const int MAX_COLORS = 6;
uniform float vHues[MAX_COLORS];
uniform float vOffsets[MAX_COLORS];
varying vec2 v_CamTexCoordinate;
float rgb2hue(vec4 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return abs(q.z + (q.w - q.y) / (6.0 * d + e));
}
bool isInRange(float a1,float a2, float vOffset) {
if(a2 < 0.0) {
return false;
} else if(abs(a1-a2) < vOffset) {
return true;
} else if( a1 > a2) {
return (1.0 - a1 + a2) < vOffset;
} else {
return (1.0 + a1 - a2) < vOffset;
}
}
vec4 getGrey(vec4 c) {
float grey = (c.r + c.g + c.b) / 3.0;
return vec4(grey, grey, grey, c.a);
}
void main() {
vec4 c = texture2D(sTexture, v_CamTexCoordinate);
bool hasColor = vHues[0] >= 0.0;
float hue = rgb2hue(c);
vec4 test = getGrey(c);
for(int i=0; i < MAX_COLORS; i++) {
if(isInRange(hue, vHues[i], vOffsets[i])) {
//If I uncomment this line the performance gets terrible
//test = c;
}
}
gl_FragColor = test;
}
当我取消注释上面的行时,性能会受到很大影响(跳过很多帧)。我基本上有时想使用原始颜色,并根据某些情况使用不同的颜色。它有效,但有没有更有效的方法来做到这一点?还有,为什么这个表现这么差?
当您注释掉该行时,您几乎可以肯定地允许编译器删除整个循环并将代码片段缩减为:
gl_FragColor = getNewColor(color);
我认为我们需要先查看 MAX_COLORS、vColors 和 isValid 函数的值,然后才能了解性能如此差的原因。
通常,OpenGLES 上片段着色器中的条件是个坏消息。如果可能,那么用某种查找 table(即可以采样的纹理)替换循环将解决您的性能问题。
这样做是否可行取决于您要解决的问题以及没有足够的上下文。
编辑:好的,现在发布了更多信息,我可以看到您的色调比较函数 (isInRange) 是一维的,很适合更改为查找 table。您应该着眼于用纹理查找替换整个循环。这样:
vec4 test = getGrey(c);
for(int i=0; i < MAX_COLORS; i++) {
if(isInRange(hue, vHues[i], vOffsets[i])) {
//If I uncomment this line the performance gets terrible
//test = c;
}
}
gl_FragColor = test;
会变成这样:
gl_FragColor = mix(getGrey(c), c, texture1d(sLookupTableTex, hue).r);
构造这样的查找 table 无疑会很繁琐,如果 vHues 和 vOffsets 一直在变化,那么查找 table 也需要一直变化,这将有它本身对性能有影响,但我认为用纹理查找替换循环和所有条件的好处将是巨大的。
If 语句和分支通常不是一个好主意,因为您的 GPU 无法正确优化。看
Efficiency of branching in shaders
更多细节。
你应该尽量避免分支,即使这意味着更多的计算。
下面的代码应该可以解决问题
//summarise your isInRange function
bool isInRange = a2 >= 0.0 && ((abs(a1-a2) < vOffset) || (1.0 - max(a1, a2) + min(a1, a2)) < vOffset);
//this will be 0.0 if false and 1.0 if true
float isInRangef = float(isInRange);
//multiply by the float condition to set the value
gl_FragColor = isInRangef * c + (1.0 - isInRangef) * getGrey(c);
作为一般公式
- 将您要分支的 bool 转换为 float 条件
- 将您的目标值设置为
(condition)*valueIfTrue + (1.0 - condition)*valueIfFalse
我正在编写一个 Android 应用程序,它利用 opengl 对相机输出执行一些更改。我以这样一种方式编写了我的代码,以至于我终于弄清楚了导致性能问题的原因。
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES sTexture;
uniform vec4 vColor;
const int MAX_COLORS = 6;
uniform float vHues[MAX_COLORS];
uniform float vOffsets[MAX_COLORS];
varying vec2 v_CamTexCoordinate;
float rgb2hue(vec4 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return abs(q.z + (q.w - q.y) / (6.0 * d + e));
}
bool isInRange(float a1,float a2, float vOffset) {
if(a2 < 0.0) {
return false;
} else if(abs(a1-a2) < vOffset) {
return true;
} else if( a1 > a2) {
return (1.0 - a1 + a2) < vOffset;
} else {
return (1.0 + a1 - a2) < vOffset;
}
}
vec4 getGrey(vec4 c) {
float grey = (c.r + c.g + c.b) / 3.0;
return vec4(grey, grey, grey, c.a);
}
void main() {
vec4 c = texture2D(sTexture, v_CamTexCoordinate);
bool hasColor = vHues[0] >= 0.0;
float hue = rgb2hue(c);
vec4 test = getGrey(c);
for(int i=0; i < MAX_COLORS; i++) {
if(isInRange(hue, vHues[i], vOffsets[i])) {
//If I uncomment this line the performance gets terrible
//test = c;
}
}
gl_FragColor = test;
}
当我取消注释上面的行时,性能会受到很大影响(跳过很多帧)。我基本上有时想使用原始颜色,并根据某些情况使用不同的颜色。它有效,但有没有更有效的方法来做到这一点?还有,为什么这个表现这么差?
当您注释掉该行时,您几乎可以肯定地允许编译器删除整个循环并将代码片段缩减为:
gl_FragColor = getNewColor(color);
我认为我们需要先查看 MAX_COLORS、vColors 和 isValid 函数的值,然后才能了解性能如此差的原因。
通常,OpenGLES 上片段着色器中的条件是个坏消息。如果可能,那么用某种查找 table(即可以采样的纹理)替换循环将解决您的性能问题。
这样做是否可行取决于您要解决的问题以及没有足够的上下文。
编辑:好的,现在发布了更多信息,我可以看到您的色调比较函数 (isInRange) 是一维的,很适合更改为查找 table。您应该着眼于用纹理查找替换整个循环。这样:
vec4 test = getGrey(c);
for(int i=0; i < MAX_COLORS; i++) {
if(isInRange(hue, vHues[i], vOffsets[i])) {
//If I uncomment this line the performance gets terrible
//test = c;
}
}
gl_FragColor = test;
会变成这样:
gl_FragColor = mix(getGrey(c), c, texture1d(sLookupTableTex, hue).r);
构造这样的查找 table 无疑会很繁琐,如果 vHues 和 vOffsets 一直在变化,那么查找 table 也需要一直变化,这将有它本身对性能有影响,但我认为用纹理查找替换循环和所有条件的好处将是巨大的。
If 语句和分支通常不是一个好主意,因为您的 GPU 无法正确优化。看 Efficiency of branching in shaders 更多细节。 你应该尽量避免分支,即使这意味着更多的计算。
下面的代码应该可以解决问题
//summarise your isInRange function
bool isInRange = a2 >= 0.0 && ((abs(a1-a2) < vOffset) || (1.0 - max(a1, a2) + min(a1, a2)) < vOffset);
//this will be 0.0 if false and 1.0 if true
float isInRangef = float(isInRange);
//multiply by the float condition to set the value
gl_FragColor = isInRangef * c + (1.0 - isInRangef) * getGrey(c);
作为一般公式
- 将您要分支的 bool 转换为 float 条件
- 将您的目标值设置为
(condition)*valueIfTrue + (1.0 - condition)*valueIfFalse