【问题标题】:Device/OS inconsistencies in GLSL ES Phong shader resultsGLSL ES Phong 着色器结果中的设备/操作系统不一致
【发布时间】:2012-10-04 06:54:12
【问题描述】:

Apple's Best Practices for OpenGL ES 建议不要对在片段着色器中计算的结果进行分支。但是Phong shading 通常涉及当光源位于表面的“错误”一侧时跳过镜面反射项,直接的方法是点单位法线方向N 和光方向L 并检查阳性结果。

我试图在我的着色器中没有分支的情况下执行此操作:我没有使用if 语句,而是对镜面反射项进行所有计算,然后给它一个系数1.0 如果dot(N, L) 更大大于零和0.0 否则。 (我使用内置的step() 函数来实现这一点。结合max()sign() 会产生相同的结果,但据说会慢一些。)

但是,这似乎会导致奇怪的、特定于设备和/或 iOS 版本的结果:

  • 在我运行 iOS 6.0 的 iPhone 4 上,带有分支的版本具有宽高光(左图);没有分支,我看到一个狭窄的镜面高光(右图),尽管“光泽度”指数保持不变。
  • 在 iOS 6.0 Simulator 上,我看到第二张图片带有两个版本的着色器。
  • 在我的 iPad(原始 2010 型号,卡在 iOS 5.1)上,我看到了带有两个版本的着色器的第一张图像。

显然,问题不在于分支或缺乏分支。 (顺便说一下,右边的图像是“正确的”渲染。)

这是我的片段着色器:

precision mediump float;

uniform lowp vec3 ambientLight;
uniform lowp vec3 light0Color;
uniform lowp vec3 materialAmbient;
uniform lowp vec3 materialDiffuse;
uniform lowp vec3 materialSpecular;
uniform lowp float materialShininess;

// input variables from vertex shader (in view coordinates)
varying vec3 NormDir;
varying vec3 ViewDir;
varying vec3 LightDir;

void main (void)
{
    vec3 L = normalize(LightDir);
    vec3 N = normalize(NormDir);
    vec3 E = normalize(ViewDir);

    lowp vec3 ambient = ambientLight * materialAmbient;

    float cos_theta = dot(L, N);

    lowp vec3 diffuse = materialDiffuse * light0Color * max(0.0, cos_theta);

    lowp vec3 specular = vec3(0.0, 0.0, 0.0);
//  if (cos_theta > 0.0) {
        lowp vec3 R = reflect(-L, N);
        lowp vec3 V = normalize(-E);
        float cos_alpha = dot(R, V);
        specular =  step(0.0, cos_theta) *  materialSpecular * light0Color * pow(max(0.0, cos_alpha), materialShininess);
        //          ~~~~~~~~~~~~~~~~~~~~~~
        //          should produce the same results as the commented branch, right?
//  }
    gl_FragColor = vec4(ambient + diffuse + specular, 1.0);
}

我也欢迎您提出进一步的建议,以改进此着色器在 iOS 硬件上的性能!

【问题讨论】:

  • 这可能是一个精密工件吗?我知道我已经看到各种 iOS 设备之间的 lowp 值四舍五入的方式存在显着差异。特别是,我想知道对导致cos_alphamaterialShininess 的某些值使用lowp 是否不会导致pow() 操作中发生奇怪的事情。我不认为在那里使用 mediump 会减慢你的速度。
  • 两种方法并不完全等价。 step(0.0, cos_theta) 等价于“if(cos_theta >= 0)”,因为当 cos_theta = 0 时 step() 返回 1,因为 (0.0
  • 我花了一段时间才抽出时间进行更多试验,但@BradLarson 是对的:materialShininess 的精度就是这样。一旦那是mediump,设备/操作系统的不一致就会消失,并且都会以“正确”的方式呈现(右图)。 (真的,分支/无分支的东西原来是一个红鲱鱼。)作为答案发布,我会接受。
  • @BradLarson 如果您不想将其发布为答案,我也可以这样做! ;-)
  • @BradLarson 您真的应该将此作为答案发布,这是仅从理论中很难学习的事情之一。

标签: ios opengl-es opengl-es-2.0 glsl fragment-shader


【解决方案1】:

正如@BradLarson 的评论中所述,materialShininess 上的lowp 限定符原来是问题所在;相反,将其设置为mediump,它可以在我手头的所有设备和操作系统版本上正确渲染(右侧图像),无论使用的是分支还是无分支(step)版本的着色器.

(将lowpmediump 用于计算cos_alpha 的输入V 不会产生任何明显的差异,这是有道理的:这些是​​归一化向量,因此它们的分量具有幅度在 0.0 到 1.0 范围内。这与颜色分量的范围相同,lowp 似乎是为此而设计的。)

【讨论】:

    猜你喜欢
    • 2011-05-21
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-11
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    相关资源
    最近更新 更多