three.jsでシェーダーを使う基本

three.jsを使って、板ポリに画像を表示させる手順の覚書。

まず、以下はthree.jsでブラウザにまっさらの画面を表示させるために必要な最小限の構成。

画面を表示する

three.js

//ページの読み込みを待つ
window.addEventListener('load', init);

function init() {

  //画面サイズを指定
  const width = 800;
  const height = 400;

  //レンダラーを作成
  const renderer = new THREE.WebGLRenderer({
        canvas: document.querySelector('#myCanvas')
      });
  renderer.setClearColor(new THREE.Color('grey'));//背景色の設定
  document.body.appendChild( renderer.domElement );
  renderer.setSize(width, height);

  //シーンを作成
  const scene = new THREE.Scene();

  //カメラを作成
  const camera = new THREE.PerspectiveCamera(45, width / height);
  camera.position.set(0, 0, 100);
  camera.lookAt(new THREE.Vector3(0, 0, 0));

  //ライトを設置
  const envlight = new THREE.AmbientLight(0xffffff, 1);
  scene.add(envlight);

 //レンダリング開始
  render();

  function render(){
      requestAnimationFrame(render);
      renderer.render(scene, camera);
  }
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <!-- 必要ソース読み込み -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.min.js"></script>
    <script src="three.js"></script>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

シェーダーを作成する

さらに、<BODY>内にシェーダーの記述を追加する。以下は、メッシュの全ピクセルを赤一色で塗るだけのシェーダー。

<div id="webgl"></div>
<!-- 頂点シェーダー -->
<script id="vert" type="x-shader/x-vertex">
    void main() {
        //positionはShaderMaterialで補完されるジオメトリの頂点情報
        //カメラ座標に変換したものを最終的にgl_Positionに代入
        vec4 pos = modelViewMatrix * vec4(position, 1.0);
        gl_Position = projectionMatrix * pos;
    }
</script>
<!-- フラグメントシェーダー -->
<script id="frag" type="x-shader/x-fragment">
    void main() {
        //全ピクセルを赤にする
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
</script>

Thee.js側でプレーン(板)を追加し、マテリアルに↑で作ったシェーダーを指定すると、赤い板が出てくるはず。。

//(1)Planeジオメトリ(座標)を作成
const geometry = new THREE.PlaneGeometry( 15, 20, 1 );
//(2)マテリアル(材質)にShaderMaterialを指定する
//htmlからvertとfragのソースを読み込んで指定
const vert = document.getElementById('vert').textContent;
const frag = document.getElementById('frag').textContent;
const material = new THREE.ShaderMaterial({
    vertexShader: vert,
    fragmentShader: frag,
});
//(3)ジオメトリとマテリアルからメッシュを作成
const plane = new THREE.Mesh( geometry, material );
//(4)メッシュをシーンに追加
scene.add( plane );

シェーダーを使ってテクスチャを表示させる

表示させたいpng画像を配置(ここではimgフォルダ配下にfire.pngを配置)。テクスチャとして適用する。pngのアルファ値を反映させたい場合はtransparantをtrueにする。

//テクスチャローダーを作成し画像を読み込む
const loader = new THREE.TextureLoader();
const tex = loader.load('img/fire.png');

//uniform変数uTexとしてテクスチャをシェーダーに渡す
const material = new THREE.ShaderMaterial({
    uniforms: { uTex: { value: tex } }, //uTexとしてテクスチャ情報をセット
    transparent: true, //画像の透明度を有効に
    vertexShader: vert,
    fragmentShader: frag,
 });
<script id="vert" type="x-shader/x-vertex"> 
    varying vec2 vUv; //フラグメントシェーダーに頂点情報を渡す用の変数
    void main() {
        vUv = uv; //頂点情報を格納する
        vec4 pos = modelViewMatrix * vec4(position, 1.0);
        gl_Position = projectionMatrix * pos;
    }
</script>

<script id="frag" type="x-shader/x-fragment">
    varying vec2 vUv; //頂点シェーダから渡された変数
    uniform sampler2D uTex; //three.jsから渡されたテクスチャ情報
    void main() {
        vec4 color = texture2D( uTex, vUv ).rgba; //頂点ごとのテクスチャの色取得
        gl_FragColor = color;
    }
</script>

ここでブラウザのクロスプラットフォームを無効化しないと描画されないので注意。