Edited at

Three.jsのコードは間違い! WebGLが使える環境かどうかのホントの判定方法 Android対応版

More than 1 year has passed since last update.

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が非対応の旨の表示などもありません。

Android 4.1.2のブラウザでThree.jsのサンプルコードを見た結果


原因は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メソッドが存在しません。


WebGLRenderingContextと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を使うならば、このような環境のユーザーで正しい表示がされるかどうかに配慮しましょう。