ギンの備忘録

デジタルアートやプログラミングやテクノロジー関連のこと。

p5.js+シェーダー(GLSL) - Boxの法線で面を塗り分ける

p5.jsで3D図形のBoxにシェーダーを適用させてみます。

Boxの法線

Box(立方体)は面を6面持つ3D図形です。 それぞれの面に対して垂直方向を表すのが法線になります。

6面の面はxyzの3方向に向いており、それぞれ+x方向、-x方向、+y方向、-y方向、+z方向、-z方向の法線となっています。

Boxの各面を塗り分ける

法線はattribute vec3 aNormal;で取り扱われています。 これを頂点シェーダーからフラグメントシェーダーへvarying vec3 vNormal;として渡します。 このvNormalがBoxでは

  • +x方向なら (1.0, 0.0, 0.0)
  • -x方向なら (-1.0, 0.0, 0.0)
  • +y方向なら (0.0, 1.0, 0.0)
  • -y方向なら (0.0, -1.0, 0.0)
  • +z方向なら (0.0, 0.0, 1.0)
  • -z方向なら (0.0, 0.0, -1.0)

となっています。

vNormalを使って、法線の方向(面の向き)に応じて、各面を以下の色で塗り分けます。

let vs = `
   precision highp float;
   precision highp int;

   attribute vec3 aPosition;
   attribute vec3 aNormal;

   varying vec3 vNormal;

   uniform mat4 uProjectionMatrix;
   uniform mat4 uModelViewMatrix;

   void main() {
      vec4 positionVec4 = vec4(aPosition, 1.0);

      vNormal = aNormal;

      gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
   }
`;

let fs = `
   precision highp float;
   precision highp int;

   varying vec3 vNormal;

   ${
      new Array(6)
      .fill()
      .map((_, i) => `uniform vec3 u_color${i};` )
      .join('')
   }

   void main() {
      vec3 color = (vNormal.x > 0.0) ? u_color0 :
                   (vNormal.x < 0.0) ? u_color1 :
                   (vNormal.y > 0.0) ? u_color2 :
                   (vNormal.y < 0.0) ? u_color3 :
                   (vNormal.z > 0.0) ? u_color4 :
                   (vNormal.z < 0.0) ? u_color5 :
                                       vec3(0.50) ;

      gl_FragColor = vec4(color, 1.0);
    }
`;

const w = 720;
let theShader;

function setup() {
   createCanvas(w, w, WEBGL);

   textureMode(NORMAL);
   colorMode(HSB);
   noStroke();

   theShader = createShader(vs, fs);
   shader(theShader);
   set_uniform(theShader);
}

function draw(){
   background(0);
   fill(0, 0, 0, 0);

   shader(theShader);

   rotateX(frameCount *  0.010);
   rotateY(frameCount *  0.011);
   rotateZ(frameCount *  0.012);
   box(w * 0.5);
}

function set_uniform(in_shader) {
   let u_c = new Array(6);

   u_c[0] = color(0, 100, 100);     //red
   u_c[1] = color(60, 100,100);     //yellow
   u_c[2] = color(120, 100, 100);   //green
   u_c[3] = color(180, 100,100);    //sky
   u_c[4] = color(240, 100, 100);   //blue
   u_c[5] = color(300, 100,100);    //pink

   new Array(6)
  .fill()
  .map(function(_, ii){
      u_c[ii] = color_set(u_c[ii]);
      in_shader.setUniform(`u_color${ii}`, u_c[ii]);
  });

}

function color_set(in_color) {
   let vec_color = [red(in_color)/255.0, green(in_color)/255.0, blue(in_color)/255.0];
   return vec_color;
}

Boxを回転させているので、実行した際は動画でみることができるはずです。 ここでは画像で実行時の一部を抜き出しました。

f:id:gin_graphic:20210117211424p:plain:w250 f:id:gin_graphic:20210117211427p:plain:w250

色の塗り分けは以下のようにしました。

  • +x方向なら 赤色
  • -x方向なら 黄色
  • +y方向なら 緑色
  • -y方向なら 水色
  • +z方向なら 青色
  • -z方向なら 桃色

実行画面を確認すると、各面が指定した色で塗られているのがわかります。 一色ずつ塗り分けがわかるように実行して確認してみると、より深く理解できると思います。

まとめ

3D図形のBoxで法線の方向(面の向き)を使って、各面を異なる色で塗り分けることができました。

各面に異なる模様をつけたり、応用が色々考えられると思います。

参考文献

www.opengl-tutorial.org

wgld.org