「Three.js」はWebGLを使ったリッチな3D表現が使用できる人気のライブラリ。ところが、Three.js公式サイトで紹介しているWebGL有効判定はAndroid 4系のWebView向けのWebGL有効判定が不十分なので、WebGLコンテンツを表示させようとすると真っ黒になるという最悪の事態が発生します(2016/3/2現在 ver74)。Android 4.03からアップデートした4.4の一部でも同様の現象を確認しました。2016/3/2現在、Android 4系のユーザーは62%(参考:「Android Developers」)もいますので、スマートフォン向けWebGLコンテンツを作るならば決して無視できないバグです。
このエントリーではこの現象の原因と解決策を紹介します。
#何が起こっているのか
下記はAndroid 4.1.2の標準ブラウザ、iOS 9.2.1のMobile SafariでThree.jsのサンプルコードを見た結果です。Androidの方は、3Dのコンテンツ表示部分が真っ黒になっています。WebGLが非対応の旨の表示などもありません。
原因はDetector.js
Detector.jsでWebGLが使えるかどうかを判定している部分です。コードは下記です。
var Detector = {
webgl: ( function () {
try {
var canvas = document.createElement( 'canvas' );
return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
} catch ( e ) {
return false;
}
} )(),
// 以下省略
このコードを使うと、Android 4.0〜4.2の一部端末で下記のようなエラーが発生します。
Uncaught TypeError: Object #<WebGLRenderingContext> has no method 'getShaderPrecisionFormat'
現象が発生しているAndroid端末では、WebGLRenderingContext
オブジェクトはWebGLの暫定仕様版であるexperimental-webgl
によって取得されます。experimental-webgl
にはgetShaderPrecisionFormat
メソッドが存在しません。
// in Android 4.1.2
var canvas = document.createElement( 'canvas' );
!!canvas.getContext( 'webgl' ); // false
!!canvas.getContext( 'experimental-webgl' ); // true
!!canvas.getContext( 'experimental-webgl' ).getShaderPrecisionFormat; // false
Three.jsライブラリ内部ではgetShaderPrecisionFormat
メソッドを使用しているのでAndroid 4ではエラーが発生し、正しく動作しません。しかし、WebGLが有効(Detector.webgl == true
)と見なされているため、本来ならば表示されるべきWebGL非対応表示もできず、真っ黒になってしまいます。
したがって、WebGLが使えるどうかの判定にgetShaderPrecisionFormat
メソッドが存在するかどうかの判定を追加する必要があります。
#正しい判定式
Detector.jsを修正し、getShaderPrecisionFormat
メソッドの有無を判定します。
var Detector = {
webgl: (function () {
try {
var canvas = document.createElement('canvas');
// 修正コード開始
var webGLContext = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
return !!( window.WebGLRenderingContext && webGLContext && webGLContext.getShaderPrecisionFormat );
// return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
// 修正コード終了
} catch (e) {
return false;
}
})()
};
// test code
alert(Detector.webgl);
意図したWebGL非対応表示となる
判定式を修正すると、今回のエラーが発生するAndroidの環境はWebGL非対応環境とみなされ、WebGL非対応画面が表示されます。なお、非WebGL環境でのスタイルは、可視性を上げるため調整してあります。
Three.jsを使うならWebGLが使えない環境にも配慮を
今回の現象はAndroid4系で発生しうる可能性があります。2016/3/2現在、Android 4系ユーザーは62%(参考:「Android Developers」)もいるので、Three.jsを使うならば、このような環境のユーザーで正しい表示がされるかどうかに配慮しましょう。