glslのトラブルがあったときにこちらに追加していきます。
トラブルに遭遇した際にはWebGLのパラメーターを確認しておきましょう
-
http://alteredqualia.com/tmp/webgl-maxparams-test/
- High Float Precision in Fragment Shader
- Medium Float Precision in Fragment Shader
- Low Float Precision in Fragment Shader
WebGLのパラメーターとしては、上記3つの浮動小数点の精度が見るべき項目ですが、全く当てになりません。
実際に計測してみると、上記浮動小数点の精度以下ということが多々あります。
- https://wlog.flatlib.jp/index.php?virtualpath=item/1552
- https://stackoverflow.com/questions/4414041/what-is-the-precision-of-highp-floats-in-glsl-es-2-0-for-iphone-ipod-touch-ipad/27771575#27771575
- https://dench.flatlib.jp/opengl/gpuspecs
モザイクを掛けたようになってしまう
- 浮動小数点の精度不足が原因かもしれません
- 加算・減算をforで繰り返すようなロジックの場合、浮動小数点の精度不足でモザイクどころか、真っ白か、真っ黒になる場合もあります。
- GLSLの規格で、ざっくり浮動小数点の最低の精度が決まっていますが、実際の端末で使われる浮動小数点の精度が環境によってバラバラで特にモバイル環境で動作しない場合が多いです。
- 特に影響を受けるのがノイズ関数で、ちゃんと意図した通り表示されない場合は大抵これが原因です。glsl sandboxでiPhoneで意図したとおりに見えないのは大抵これが原因です。
glslのプログラミングで良く使われる以下の関数を
float rand(vec2 co){
return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 43758.5453);
}
↓
highp float rand(vec2 co){
highp float a = 12.9898;
highp float b = 78.233;
highp float c = 43758.5453;
highp float dt= dot(co.xy ,vec2(a,b));
highp float sn= mod(dt,3.14);
return fract(sin(sn) * c);
}
に書き換えてあげることで動作するようになります。
画面にグリッジみたいにブロックノイズが乗る
変数の初期化忘れが原因、Apple系(mac os,iOSで良く発生)
変数の初期化しないで、加算をする場合など
大抵の環境では、初期化しない場合は0.に初期化してくれるが、環境によっては初期化されず均一ではないランダムな値が入る。ブロックノイズっぽい感じになるので環境依存で、グリッジに利用されていたことがあったとか。変数定義時は必ず値を入れて初期化しておく。
vec3 color;
color+=vec3(.1);
↓
vec3 color=vec3(0.);
color+=vec3(.1);
Edgeのマクロエラー
edgeのマクロ(define)は単なる置換なので、defineで1文字のdefineは書かない
例えばs(x)というdefineを定義すると、cosなど最後にsが付く関数がすべて引っ掛かってしまう。co『s(1.)』、chrome,firefoxなどでは、こういったことは発生しない
#define s(x) clamp(x,0.,1.);
float c = cos(1.);
↓
#define saturate(x) clamp(x,0.,1.);
float c = cos(1.);
環境によってコンパイルエラー
だいたい0除算エラーです。
glslの仕様では0で割ったときの値はundefinedとされており、GLSL ES 2.0では、仕様上の規定は明記されていません。(GLSL ES 3.0から明記)ただし、浮動小数点の仕様 IEEE754ではInfinityと規定されています。実際のところ、実装はマチマチです。だいたいはInfinityが実装されていますが、1を返したり、最悪エラーでコンパイルが止まったりします。基本的に0除算にならないように三項演算子などでマスクしておく方が賢明です。
float a = 0.;
a == 0 ? 0 : 3/ a;
GLSL ES 2.0 ではInfinity、NaNの判定関数は存在しませんが以下のようにして判定が可能です。※ ただし、すべての環境で実装されている保証はないので、結局マスクするのが良いかと思われます。
bool isinf(float val) {
return (val != 0.0 && val * 2.0 == val) ? true : false;
}
ついでにNaNの判定も
bool isNan( float val )
{
return ( val < 0.0 || 0.0 < val || val == 0.0 ) ? false : true;
// important: some nVidias failed to cope with version below.
// Probably wrong optimization.
/*return ( val <= 0.0 || 0.0 <= val ) ? false : true;*/
}
GLSL ES 3.0 からは組み込みでisinf isnanが用意されています。
尚、Windows環境のChromeではAngleというエンジンを使って内部的にglslをhlslにコンパイルしているため、hlslにコンパイル後に0除算エラーで止まる場合があります。
例えば、smoothstep
smoothstep(0.,0.,x);
edge0、edge1同じ場合0除算が発生し、環境によってエラーで止まる場合がある
smoothstepでedge0、edge1が同じ値になることは、まぁありえませんけどね。
float smoothstep (float edge0, float edge1, float x)
{
// Scale, bias and saturate x to 0..1 range
x = saturate((x - edge0) / (edge1 - edge0));
// Evaluate polynomial
return x*x*(3-2*x);
}
HLSLのコードは
var ext = gl.getExtension('WEBGL_debug_shaders');
console.log(ext.getTranslatedShaderSource(fragmentShaderSrc));
console.log(ext.getTranslatedShaderSource(vertexShaderSrc));
で取得できます。