Apple's Best Practices for OpenGL ESは、フラグメントシェーダで計算された結果を分岐することを推奨していません。しかし、Phong shadingは、光源が表面の「間違った」側にあるときに鏡面用語をスキップすることを一般に含みます。そのためには、単位法線方向N
と光方向L
を点にして肯定的な結果が得られるかどうかを確認します。GLSL ESのデバイス/ OSの矛盾Phongシェーダの結果
私は私のシェーダでの分岐せずにこれを実行しようとしました代わりにif
ステートメントを使用して、私は鏡面用語のためのすべての計算を行うと、dot(N, L)
は、そうでない場合はゼロと0.0
よりも大きければ、それに1.0
ある係数を与えます。 (私は組み込みstep()
関数を使用してこれを達成する。max()
とsign()
を組み合わせると同じ結果を生成するが、少し遅くなると言われている。)
しかし、これは、奇妙な、デバイスに、および/またはIOSバージョン固有につながるように見え結果:私のiPhone上で
- 4のiOS 6.0を実行して、ブランチとバージョンが広いスペキュラハイライト(左画像)を持っています。ブランチがなければ、「光沢」指数が同じままであるにもかかわらず、狭い鏡面ハイライト(右画像)が見えます。
- iOS 6.0シミュレータでは、シェーダの両方のバージョンで2番目のイメージが表示されます。
- 私の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デバイス間で低値が丸められる方法に大きな違いがあることを知っています。特に、 'cos_alpha'と' materialShininess'につながる値のいくつかにlowpを使用しても、あなたの 'pow()'操作で奇妙なことが起きるのではないかと思います。私はmediumpを使用するとあなたをあまりにも遅くするとは思わない。 –
どちらのアプローチも完全に同等ではありません。 (0.0、0)がfalseであるため、step()はcos_theta = 0のとき1を返すため、step(0.0、cos_theta)は "if(cos_theta> = 0)"に相当します。私はそれがとにかく差をつけているとは思わない。 –
これをもう少し試してみるのに時間がかかりましたが、@BradLarsonは正しいです: 'materialShininess'の精度はそれです。これが 'mediump'になると、デバイスとOSの不一致がなくなり、すべて正しい方法(右側のイメージ)になります。 (実際、ブランチ/ノーブランチのものは赤いニシンであることが判明しました。)回答として投稿し、私は受け入れます。 – rickster