2012-10-04 46 views
5

Apple's Best Practices for OpenGL ES no recomiendan la bifurcación en los resultados calculados en un sombreador de fragmentos. Pero Phong shading generalmente implica omitir el término especular cuando la fuente de luz está en el lado "incorrecto" de la superficie, para lo cual el enfoque directo es salpicar la dirección normal de la unidad N y la dirección de la luz L y verificar que el resultado sea positivo.Inconsistencias del dispositivo/sistema operativo en los resultados de GLSL ES Phong shader

he tratado de hacer esto sin una sucursal en mi shader: en lugar de utilizar una declaración if, hago todos los cálculos para el término especular y luego darle un coeficiente que es 1.0 si dot(N, L) es mayor que cero y 0.0 lo contrario . (I lograr esto utilizando la función incorporada step(). Combinando max() y sign() produce los mismos resultados, pero se dice que ser un poco más lenta.)

Sin embargo, esto parecía conducir a iOS impar, dispositivo- y/o versión específica , los resultados:

with branchwithout branch

  • en mi iPhone 4 con iOS 6.0, la versión con la rama tiene un reflejo especular de ancho (imagen izquierda); sin la rama, veo un resaltado especular estrecho (imagen derecha), a pesar de que el exponente de "brillo" sigue siendo el mismo.
  • En el simulador de iOS 6.0, veo la segunda imagen con ambas versiones del sombreador.
  • En mi iPad (modelo original de 2010, atascado en iOS 5.1), veo la primera imagen con ambas versiones del sombreador.

Claramente, el problema no es la ramificación o la falta de ella. (La imagen de la derecha es la traducción "correcta", por cierto.)

Aquí es mi fragmento de sombreado:

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); 
} 

me agrado más sugerencias para mejorar el rendimiento de este shader en el hardware de iOS, también!

+6

¿Podría ser esto un artefacto de precisión? Sé que he visto diferencias significativas en la forma en que se redondean los valores lowp entre los distintos dispositivos iOS. En particular, me pregunto si el uso de lowp para algunos de los valores que llevan a 'cos_alpha' y' materialShininess' no podría causar que ocurran cosas extrañas en la operación 'pow()'. No creo que usar mediump allí te frene demasiado. –

+0

Ambos enfoques no son completamente equivalentes. paso (0.0, cos_theta) es equivalente a "if (cos_theta> = 0)", como step() devuelve 1 cuando cos_theta = 0 porque (0.0 <0) es falso. pero no creo que eso marque la diferencia de todos modos. –

+4

Me tomó un tiempo encontrar tiempo para experimentar con esto un poco más, pero @BradLarson tiene razón: la precisión en 'materialShininess' es eso. Una vez que se trata de 'mediump', las inconsistencias entre el dispositivo y el sistema operativo desaparecen y todas muestran la forma" correcta "(imagen de la derecha). (En realidad, las cosas de la rama/no-rama resultaron ser una pista falsa.) Publica como respuesta y aceptaré. – rickster

Respuesta

1

Como se señala en el comentario de @ BradLarson, el calificador lowp en materialShininess resultó ser el problema; con eso establecido en mediump, se procesa correctamente (imagen de la derecha) en todos los dispositivos y versiones de sistema operativo que tengo a mano, independientemente de si se utiliza la versión de rama o sin ramificación (con step) del sombreador.

(Usando lowp frente mediump para las entradas R y V de la que cos_alpha se calcula no hace ninguna diferencia visible, lo cual tiene sentido:. Esas son vectores normalizados, por lo que sus componentes tienen magnitudes en el intervalo de 0,0 a 1,0 Eso es el mismo rango que los componentes de color, para los cuales lowp parece estar destinado.)