Apple's Best Practices for OpenGL ES建議針對片段着色器中計算的結果進行分支。但是Phong shading通常涉及在光源位於表面的「錯誤」一側時跳過鏡面術語,爲此直接方法是將單位法線方向點N
和光線方向L
並檢查陽性結果。GLSL ES Phong着色器結果中的設備/操作系統不一致性
我試圖這樣做沒有在我的着色器的一個分支:而不是使用if
聲明,我做所有的計算了鏡面項,然後給它一個係數,它是1.0
如果dot(N, L)
大於零且0.0
否則更大。 (I實現此使用內置step()
功能。結合max()
和sign()
產生相同的結果,但被認爲是慢一點。)
然而,這似乎導致奇數,裝置 - 和/或IOS版本特定,結果:
- 在我的iPhone 4運行iOS 6.0,與分支版本具有廣泛的鏡面高光(左圖);儘管「光澤度」指數保持不變,但沒有分支我會看到較窄的鏡面高光(右圖)。
- 在iOS 6.0模擬器上,我看到了具有兩種着色器版本的第二張圖像。
- 在我的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值四捨五入方式的重大差異。特別是,我想知道在'pow()'操作中使用lowp來引導'cos_alpha'和'materialShininess'的某些值不會導致奇怪的事情發生。我不認爲在那裏使用中性會減慢你太多。 –
兩種方法都不完全等效。 (0.0,cos_theta)等於「if(cos_theta> = 0)」,因爲(0.0 <0)爲假,因爲當cos_theta = 0時,step()返回1。但我不認爲這是造成差異無論如何。 –
我花了一段時間才找到時間來試試這個,但@BradLarson是正確的:'materialShininess'上的精度就是這樣。一旦這是'中立',設備/操作系統的不一致就會消失,並且全都呈現「正確」的方式(右側圖像)。 (真的,分支/不分行的東西竟然是一隻紅鯡魚。)作爲回答,我會接受。 – rickster