mozilla
Your Search Results

    Adding 2D content to a WebGL context

     

    WebGL コンテキストの作成 に成功したら、レンダリングを始めることができます。もっとも簡単にできることは、テクスチャが貼り付けられていない単純な 2D のオブジェクトを描画することですので、まずは正方形を描画するコードを作っていきましょう。

    シーンのライティング

    レンダリングを始める前に理解しておくべきもっとも重要なことは、今回の例では 2 次元のオブジェクトをレンダリングしますが、それは 3 次元の空間に描画されるということです。そのためオブジェクトを描画するのと同じように、シーンを照らすシェーダーを立ち上げる必要があります。これは、正方形がどのように照らされるかを設定します。

    シェーダーの初期化

    シェーダーは OpenGL ES Shading Languageで明示されています。コンテンツの管理や更新を簡単にするため、JavaScript ですべての構築を行う代わりに、HTML 文書の中にシェーダーを読み込むコードを記述することができます。それではこの役割を担う initShaders() ルーチンを見てみましょう:

    function initShaders() {
      var fragmentShader = getShader(gl, "shader-fs");
      var vertexShader = getShader(gl, "shader-vs");
      
      // Create the shader program
      
      var shaderProgram = gl.createProgram();
      gl.attachShader(shaderProgram, vertexShader);
      gl.attachShader(shaderProgram, fragmentShader);
      gl.linkProgram(shaderProgram);
      
      // If creating the shader program failed, alert
      
      if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Unable to initialize the shader program.");
      }
      
      gl.useProgram(shaderProgram);
      
      vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
      gl.enableVertexAttribArray(vertexPositionAttribute);
    }
    

    このルーチンでは 2 つのシェーダープログラムが読み込まれます。一つ目はフラグメントシェーダーが、ID 値 "shader-fs" の script 要素から読み込まれます。二つ目はバーテックスシェーダーで、ID 値 "shader-vs" の script 要素から読み込まれます。getShader() 関数については、次の章で確認します (このルーチンは、DOM からシェーダープログラムを引き出す操作を行っています)。

    その後 WebGL オブジェクトの createProgram() 関数を呼び出すことで、2 つのシェーダーが付属しリンクされているシェーダープログラムを作成します。そして、プログラムが正常にリンクされたことを確認するために gl オブジェクトの LINK_STATUSパラメータをチェックします。チェックが通れば、新しいシェーダープログラムをアクティブにします。

    DOM からシェーダーをロードする

    getShader() ルーチンは、DOM から指定された名称のシェーダープログラムを取り出し、コンパイルされたシェーダープログラムを呼び出し元へ返すか、シェーダーの読み込みやコンパイルができなかった場合に null を返します。

    function getShader(gl, id) {
        var shaderScript, theSource, currentChild, shader;
        
        shaderScript = document.getElementById(id);
        
        if (!shaderScript) {
            return null;
        }
        
        theSource = "";
        currentChild = shaderScript.firstChild;
        
        while(currentChild) {
            if (currentChild.nodeType == currentChild.TEXT_NODE) {
                theSource += currentChild.textContent;
            }
            
            currentChild = currentChild.nextSibling;
        }
    

    指定された ID の要素が見つかった場合は、そのテキストを変数 theSource に格納します。

      if (shaderScript.type == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
      } else if (shaderScript.type == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
      } else {
         // Unknown shader type
         return null;
      }
    

    シェーダーのコードが読み込まれたら、シェーダーオブジェクトがバーテックスシェーダー (MIME タイプ "x-shader/x-vertex") あるいはフラグメントシェーダー (MIME タイプ "x-shader/x-fragment") であることを判断するために MIME タイプの確認を行い、そして取り出されたソースコードから適切なタイプのシェーダーを生成します。

      gl.shaderSource(shader, theSource);
        
      // Compile the shader program
      gl.compileShader(shader);  
        
      // See if it compiled successfully
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {  
          alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));  
          return null;  
      }
        
      return shader;
    }
    

    最後に、ソースコードはシェーダーに渡され、そしてコンパイルされます。シェーダーのコンパイル時にエラーが発生した場合は、警告を表示して null 値を返します。エラーが発生しなかった場合は、コンパイル済みのシェーダーを呼び出し元に返します。

    シェーダー

    この後、文書を記述している HTML にシェーダープログラム自体を追加する必要があります。シェーダーがどのように動作するかの詳細はこの記事で扱う範囲を超えており、それはシェーダー言語の構文法になります。

    フラグメントシェーダー

    ポリゴン内の各ピクセルは、GL の用語で フラグメント と呼ばれます。フラグメントシェーダーは、各ピクセルの色を決定します。今回の例では、各ピクセルには単純に白色を割り当てます。

    gl_FragColor は、フラグメントの色に用いられる GL の組み込み変数です。以下のように値を設定することで、ピクセルの色を定義します。

    <script id="shader-fs" type="x-shader/x-fragment">
      void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      }
    </script>
    

    バーテックスシェーダー

    バーテックスシェーダーは、各頂点の位置や形状を定義します。

    <script id="shader-vs" type="x-shader/x-vertex">
      attribute vec3 aVertexPosition;
    
      uniform mat4 uMVMatrix;
      uniform mat4 uPMatrix;
      
      void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
      }
    </script>
    

    オブジェクトの作成

    正方形を描画する前に、その頂点情報を収めるバッファを作成する必要があります。initBuffers() 関数を呼び出すことでそれを行います。WebGL のより高度な概念をたどることで、このルーチンはさらに多くの (かつ複雑な) 3D オブジェクトを作成するように拡張されます。

    var horizAspect = 480.0/640.0;
    
    function initBuffers() {
      squareVerticesBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
      
      var vertices = [
        1.0,  1.0,  0.0,
        -1.0, 1.0,  0.0,
        1.0,  -1.0, 0.0,
        -1.0, -1.0, 0.0
      ];
      
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    }
    

    今回の例でこのルーチンは、単純にシーンの基本的な性質を与えます。それは頂点情報を保存するバッファを作り出す機能を持つ、gl オブジェクトの createBuffer() メソッドを呼び出すことから始まります。そしてこのバッファは、bindBuffer() メソッドを呼び出すことでコンテキストに結びつけられます。

    以上の手続きを行ったら、正方形の各頂点の座標を収めている JavaScript の配列を作成します。この配列は WebGL 浮動小数点数の配列に変換され、さらに gl オブジェクトの bufferData() メソッドに渡されてオブジェクトの頂点情報が成立します。

    シーンの描画

    シェーダーの確立とオブジェクトの構築を行ったら、実際にシーンを描画することができます。今回の例ではアニメーションを行いませんので、drawScene() 関数はとても単純なものになります。ここでは後ほど取り上げる、有用なルーチンをいくつか使用します。

    function drawScene() {
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      
      perspectiveMatrix = makePerspective(45, 640.0/480.0, 0.1, 100.0);
      
      loadIdentity();
      mvTranslate([-0.0, 0.0, -6.0]);
      
      gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
      gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
      setMatrixUniforms();
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    }
    

    まずは、コンテキストを背景色でクリアします。そしてカメラの遠近感を定義します。視野角を 45度、縦横の比を 640/480 (canvas の寸法) に設定しています。また、描画されるオブジェクトはカメラから 0.1 ~ 100 ユニットの範囲内のもののみにすることを指定します。

    そして正方形の位置を、初期位置への読み込みおよびカメラから 6 ユニット分遠方へ移動したところに定義します。その後正方形の頂点バッファをコンテキストに結びつけ、設定を行い、そして drawArrays() メソッドを呼び出してオブジェクトの描画を行います。

    WebGL 対応のブラウザを使用している場合は、こちらをクリックして デモを実行してみてください。

    行列の実用的な操作方

    行列の操作はとてもわかりにくいものです。行列の操作を行うコードのすべてを自分で記述したがる人はいません。Nobody really wants to write all the code needed to handle them on their own. 幸い、JavaScript でベクトルや行列の操作を行うのに役立つライブラリである Sylvester があります。

    今回のデモで使用している glUtils.js ファイルは、Web 上にあるかなりの WebGL デモで使用されています。これは由来がはっきりしていないようですが、特別な種類の行列を構築するメソッドを追加することで、それを表示するための HTML 出力や、Sylvester の利用の簡素化を実現します。

    加えて、このデモでは特定の作業のためにこれらのライブラリとの橋渡しを行う、有用なルーチンをいくつか定義しています。これらが何を行っているかについては今回のデモで扱う範囲を超えていますが、インターネット上には行列に関する多くのすばらしい参考文書があります。そのいくつかが See also の章にありますのでご覧ください。

    function loadIdentity() {
      mvMatrix = Matrix.I(4);
    }
    
    function multMatrix(m) {
      mvMatrix = mvMatrix.x(m);
    }
    
    function mvTranslate(v) {
      multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
    }
    
    function setMatrixUniforms() {
      var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
      gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix.flatten()));
    
      var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
      gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
    }
    

    参考情報

    ドキュメントのタグと貢献者

    Contributors to this page: ethertank, yyss
    最終更新者: ethertank,