Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
79
Help us understand the problem. What is going on with this article?
@tonkotsuboy_com

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

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

79
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
tonkotsuboy_com
フロントエンド / 九州大学卒 / ウェブ制作 / JavaScriptコードレシピ集の著者 / CSS Nite 2017〜2019ベストセッション / TechFeed公認エキスパート /お仕事依頼はDMまで

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
79
Help us understand the problem. What is going on with this article?