Open GL在桌面GPU和移动GPU上的不同结果
Open GL Different Results on Desktop GPU and Mobile GPU
我一直在尝试将 this 着色器移植到移动设备。我在 OpenGL ES 2.0
的 android 设备上执行此操作。这是来自上述站点的片段着色器代码以供参考:
void main(void)
{
// clamp pixel posiiton in [-1,1]
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
vec2 uv;
// calculate angle of current pixel from origin
// atan return values are in [-pi, pi]
float a = atan(p.y,p.x);
// distance of point from origin
//float r = sqrt(dot(p,p));
float power = 7.0;
// http://en.wikipedia.org/wiki/Minkowski_distance
float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2.0*power) );
// add global time for a moving tunnel
uv.x = .2/r + iGlobalTime/2.0;
uv.y = a/(3.1416);
// multiplication by r to give a darkened effect in center
vec3 col = texture2D(iChannel0, uv).xyz * (1.0-r);
//vec3 col = vec3(uv.y, 0.0,0.0);
gl_FragColor = vec4(col,1.0);
}
我在手机上得到以下结果 phone(Moto G、三星 Galaxy S Advance)。请注意纹理是如何平坦的并且有点夹在中间
当模拟器上的相同代码为 运行 时(Nexus 5 API 21)(具有模拟主机 gpu 选项)
这是预期的输出。
我的纹理环绕模式设置为GL_REPEAT
。可能有什么问题?
这是一个精度问题。 mediump
,这是ES 2.0 shaders保证支持的最高精度,浮点量级范围为[2^-14, 2^14],如table上所列规范的第 33 页("The OpenGL ES Shading Language",版本 1.00,可在 https://www.khronos.org/registry/gles/ 找到)。
下面的语句序列会很快产生下溢:
float power = 7.0;
float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2.0*power) );
查看这个子表达式:
pow(p.x*p.x,power)
根据着色器开始处的 calculation/comment,p.x
的范围是 [-1, 1]。使用 0.1 作为值:
pow(0.1 * 0.1, 7.0) = pow(0.01, 7.0) = 10^-14
这正好在 medium
值下溢的极限附近。因此,只要 p.x
或 p.y
接近范围 [-0.1, 0.1].
,这些子表达式就会下溢为零
最好的解决方法是什么并不明显。一些尝试的想法:
- 如果您的设备支持,请使用
highp
精度。 highp
支持在 ES 2.0 中是可选的,只有在定义了 GL_FRAGMENT_PRECISION_HIGH
时才可用。
- 试试比 7.0 更低的指数,看看视觉结果是否仍然满足您的要求。
- 对会下溢的像素使用某种形式的限制。您可以丢弃它们,也可以将它们涂成黑色。虽然它不会在中心渲染他的墙,但它至少可以避免丑陋的人工制品。
我一直在尝试将 this 着色器移植到移动设备。我在 OpenGL ES 2.0
的 android 设备上执行此操作。这是来自上述站点的片段着色器代码以供参考:
void main(void)
{
// clamp pixel posiiton in [-1,1]
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
vec2 uv;
// calculate angle of current pixel from origin
// atan return values are in [-pi, pi]
float a = atan(p.y,p.x);
// distance of point from origin
//float r = sqrt(dot(p,p));
float power = 7.0;
// http://en.wikipedia.org/wiki/Minkowski_distance
float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2.0*power) );
// add global time for a moving tunnel
uv.x = .2/r + iGlobalTime/2.0;
uv.y = a/(3.1416);
// multiplication by r to give a darkened effect in center
vec3 col = texture2D(iChannel0, uv).xyz * (1.0-r);
//vec3 col = vec3(uv.y, 0.0,0.0);
gl_FragColor = vec4(col,1.0);
}
我在手机上得到以下结果 phone(Moto G、三星 Galaxy S Advance)。请注意纹理是如何平坦的并且有点夹在中间
当模拟器上的相同代码为 运行 时(Nexus 5 API 21)(具有模拟主机 gpu 选项)
我的纹理环绕模式设置为GL_REPEAT
。可能有什么问题?
这是一个精度问题。 mediump
,这是ES 2.0 shaders保证支持的最高精度,浮点量级范围为[2^-14, 2^14],如table上所列规范的第 33 页("The OpenGL ES Shading Language",版本 1.00,可在 https://www.khronos.org/registry/gles/ 找到)。
下面的语句序列会很快产生下溢:
float power = 7.0;
float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2.0*power) );
查看这个子表达式:
pow(p.x*p.x,power)
根据着色器开始处的 calculation/comment,p.x
的范围是 [-1, 1]。使用 0.1 作为值:
pow(0.1 * 0.1, 7.0) = pow(0.01, 7.0) = 10^-14
这正好在 medium
值下溢的极限附近。因此,只要 p.x
或 p.y
接近范围 [-0.1, 0.1].
最好的解决方法是什么并不明显。一些尝试的想法:
- 如果您的设备支持,请使用
highp
精度。highp
支持在 ES 2.0 中是可选的,只有在定义了GL_FRAGMENT_PRECISION_HIGH
时才可用。 - 试试比 7.0 更低的指数,看看视觉结果是否仍然满足您的要求。
- 对会下溢的像素使用某种形式的限制。您可以丢弃它们,也可以将它们涂成黑色。虽然它不会在中心渲染他的墙,但它至少可以避免丑陋的人工制品。