のぐそんブログ

暗いおじさんがシコシコ書くブログです。

Three.jsでGLSLを触るための基礎基礎メモ

GLSLの基礎

「WebGLを使って三角形を描画する為の基礎知識」でも少しかきましたが、GLSLは頂点シェーダーと、フラグメントシェーダーがあります。

頂点シェーダーではgl_Positionという組み込み変数に頂点データを必ず渡す必要があります。 フラグメントシェーダーには、gl_FragColorという組み込み変数がありピクセルの色を指定します。

gl_Positionには、後述するカメラの視野範囲の空間であるクリッピング座標が入ります。

Three.jsで定義されている行列

Thress.jsでは、座標変換用の行列が用意されています。

名前 修飾子 説明
modelMatrix uniform mat4 オブジェクト座標からワールド座標へ変換する
viewMatrix uniform mat4 ワールド座標から視点座標へ変換
modelViewMatrix uniform mat4 modelMatrixとviewMatrixの積算
projectionMatrix uniform mat4 カメラの各種パラメータから3次元を2次元に射影し、クリップ座標系に変換する行列
normalMatrix uniform mat3 頂点法線ベクトルを視点座標系に変換する行列

Three.jsで定義されている変数

Thress.jsでは、座標系の変数が用意されています。

名前 修飾子 説明
cameraPosition uniform vec3 カメラの位置
position attribute vec3 頂点座標
normal attribute vec3 頂点法線ベクトル
uv attribute vec2 テクスチャを貼るためのUV座標

※フラグメントシェーダーはviewMatrixcameraPositionのみ利用可能。

GLSLの組み込み変数

主に頂点シェーダーで使う変数

名前 説明
gl_Position vec4 頂点座標
gl_PointSize float ポイントスプライトのサイズ

フラグメントシェーダで使う変数

名前 説明
gl_FragColor vec4 ピクセルの描画色
gl_FragCoord vec4 ピクセルの座標
gl_PointCoord vec2 ポイントスプライト内の座標
gl_FrontFacing bool ピクセルの表裏。表ならtrue、裏ならfalse

レンダリングの流れ

下記は3Dモデルが画面に表示されるまでの、レンダリングパイプラインの一部です。

Kobito.CH2qKd.png

ShaderMaterial

Three.jsでカスタムシェーダーを使うためにはShaderMaterialを使用します。 ShaderMaterialを利用する場合は、uniformattribute変数が自動で挿入されます。

ShaderMaterialを使うため、projectionMatrix、modelViewMatrix、positionの定義が不要。

<body>
    <div id="webgl"></div>
    <script id="vs" type="x-shader/x-vertex">
        void main() { 
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
         }
    </script>
    <script id="fs" type="x-shader/x-fragment">
        void main() {
            gl_FragColor =vec4(1.0, 1.0, 1.0, 1.0);
        }
    </script>
</body>
var geometry = new THREE.PlaneGeometry(2, 2, 5, 5);

var material = new THREE.ShaderMaterial({
      vertexShader: document.getElementById('vs').textContent,
      fragmentShader: document.getElementById('fs').textContent
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

See the Pen KqZWRB by nogson (@satofaction) on CodePen.

RawShaderMaterial

RawShaderMaterialはShaderMaterialと似ているのですが、uniformattribute変数が自動で挿入されないので、自分で定義して上げる必要があります。 勝手に挿入されるのが嫌な場合はこちらを使います。

<body>
    <div id="webgl"></div>
    <script id="vs" type="x-shader/x-vertex">
        attribute vec3 position;
        uniform mat4 projectionMatrix;
        uniform mat4 modelViewMatrix;
         
        void main() { 
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
         }
    </script>
    <script id="fs" type="x-shader/x-fragment">
        void main() {
            gl_FragColor =vec4(1.0, 1.0, 1.0, 1.0);
        }
    </script>
</body>

See the Pen wepJge by nogson (@satofaction) on CodePen.

ShaderMaterialでカスタムシェーダーを管理する際に使えるフィールド

名前 説明
vertexShader 頂点シェーダーのソースコード
fragmentShader フラグメントシェーダーのソースコード
uniforms 共通パラメータの定義
attributes 各頂点データの定義

THREE.Geometryで頂点座標を定義する

頂点座標を指定してジオメトリを作成する場合は、THREE.Geometryを利用します。 ジオメトリの頂点座標をverticesに配列でいれます。 facesには頂点の結ぶ順番を配列でいれます。

        let geometry = new THREE.Geometry();

        let vertices = [
            new THREE.Vector3(0, 1, 0),
            new THREE.Vector3(-1, -1, 0),
            new THREE.Vector3(1, -1, 0),
            new THREE.Vector3(0, 4, 0),
            new THREE.Vector3(-1, 2, 0),
            new THREE.Vector3(1, 2, 0),
        ];

        let faces = [
            new THREE.Face3(0, 1, 2),
             new THREE.Face3(3, 4, 5)
        ];

        geometry.vertices = vertices;
        geometry.faces = faces;

        var material = new THREE.ShaderMaterial({
            vertexShader: document.getElementById('vs').textContent,
            fragmentShader: document.getElementById('fs').textContent
        });
        var cube = new THREE.Mesh(geometry, material);
        scene.add(cube);

See the Pen XgZmgg by nogson (@satofaction) on CodePen.

ShaderMaterialで色を変更してみる

uniformsに色を指定し、GLSLで受け取る。 ただし、uniformで渡しているので全ポリゴンが同じ色になる。

Kobito.8TCJh7.png

var material = new THREE.ShaderMaterial({
        vertexShader: document.getElementById('vs').textContent,
        fragmentShader: document.getElementById('fs').textContent,
        uniforms: {
             uColor: {
             type: "c",
             value: new THREE.Color(0xFF0068)
             }
        }
});
<script id="fs" type="x-shader/x-fragment">
    uniform vec3 uColor;

    void main() {
       gl_FragColor =vec4(uColor, 1.0);
     }
</script>

多角形を書いてみる

1枚のポリゴンは基本的に三角形です。 そこで、三角形を組み合わせて多角形を描いてみます。

多角形の書き方も、いくつかパターンがありそうです。 例えば下記のような五角形でも3つの三角形で作る場合と、5つで作る場合があります。

今回の例だと、5つで作るほうが簡単そうなので、5つでやってみます。 (わかりやすいように、wireframeで表示しています)

See the Pen EXRYMq by nogson (@satofaction) on CodePen.

円周上の座標の計算式から、五角形の頂点座標を算出します。 あとは、中心点から円周上の座標を繋いでいきます。

let vertices = [];
let faces = [];
let split = 6;
let theta = 360 / split;
let radian = theta * Math.PI / 180;
let size = 1;

//中心の頂点を追加
vertices.push(new THREE.Vector3(0, 0, 0));

//頂点の座標を決める
for (let i = 0; i < split; i++) {
    var x = Math.cos(radian * i) * size;
    var y = Math.sin(radian * i) * size;
    vertices.push(new THREE.Vector3(x, y, 0));
}


//頂点のindexを決める
for (let i = 0; i <= split; i++) {
    let index;

    if (i < split) {
        index = new THREE.Face3(0, i + 1, i + 2);

    } else {
        index = new THREE.Face3(0, i, 1);

    }
    faces.push(index);
}

頂点の数だけ変更すれば、他の多角形にもそのまま利用できます。

まとめ

面ごとに色を指定してみたかったのですが、やり方がわかりませんでした。。。

参考

threejsでGLSLをいじるための基礎知識