Apple's Best Practices for OpenGL ES zaleca się rozgałęzianie wyników obliczanych w module cieniującym fragmentu. Ale ogólnie, Phong shading ogólnie obejmuje pomijanie określenia lustrzanego, gdy źródło światła znajduje się na "niewłaściwym" boku powierzchni, dla którego proste podejście polega na dot w jednostce kierunku normalnym N
i kierunku światła i sprawdzić wynik dodatni.Niespójności urządzenia/systemu operacyjnego w wynikach algorytmu GLSL ES Phong:
próbowałem to zrobić bez oddziału w moim shader: zamiast używać oświadczenie if
, robię wszystkie obliczenia dla terminu zwierciadlanym, a następnie nadać mu współczynnik, który jest 1.0
jeśli dot(N, L)
jest większa niż zero i 0.0
inaczej . (I to osiągnąć za pomocą wbudowanego step()
funkcję. Łącząc max()
i sign()
daje te same wyniki, ale mówi się, że nieco wolniej.)
to jednak wydaje się prowadzić do nieparzyste, od urządzenia i/lub iOS w wersji specyficznych wyniki:
- na moim iPhone 4 z systemem iOS 6.0, wersja z branży posiada szeroki podświetlenie zwierciadlane (lewy obraz); bez gałęzi widzę wąski podświetlony punkt (prawy obrazek), pomimo tego, że wykładnik "lśnienia" pozostaje taki sam.
- Na symulatorze iOS 6.0 widzę drugi obraz z obu wersjami modułu cieniującego.
- Na moim iPadzie (oryginalny model 2010, zatrzymany na iOS 5.1), widzę pierwszy obraz z obu wersjami modułu cieniującego.
To jest problem, nie jego rozgałęzienia ani braku. (The prawy obraz jest "poprawne" rendering, nawiasem mówiąc).
Oto mój fragment shader:
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);
}
Cieszę dalsze sugestie dotyczące poprawy wydajności tego modułu cieniującego na sprzęcie z iOS, też!
Czy może to być artefakt precyzyjny? Wiem, że widziałem znaczące różnice w sposobie, w jaki wartości lowp są zaokrąglane pomiędzy różnymi urządzeniami iOS. W szczególności zastanawiam się, czy użycie lowp dla niektórych wartości prowadzących do 'cos_alpha' i' materialShininess' nie mogło spowodować dziwnych rzeczy w twojej operacji 'pow()'. Nie sądzę, że użycie mediump zwolniłoby cię zbyt mocno. –
Oba podejścia nie są całkowicie równoważne. step (0.0, cos_theta) jest równoważne "if (cos_theta> = 0)", ponieważ step() zwraca 1, gdy cos_theta = 0, ponieważ (0.0 <0) jest fałszywe. ale nie sądzę, że to czyni różnicę. –
Zajęło mi trochę czasu, aby znaleźć czas na eksperymenty z tym więcej, ale @BradLarson ma rację: precyzja na 'materialShininess' jest to. Gdy to jest 'mediump', niespójności urządzenia/systemu OS znikną i wszystkie będą renderować" prawidłowy "sposób (obraz po prawej stronie). (Naprawdę, oddziału/bez gałęzi rzeczy okazało się być czerwonym śledziem.) Napisz jako odpowiedź i przyjmuję. – rickster