Help us understand the problem. What is going on with this article?

thee.jsあり/なしで見た目や実装の違いを比較してみた

More than 3 years have passed since last update.

はじめに

WebGL Advent Calendar 2016の22日目の記事になります。
今回はthree.jsの有無でどれほど見た目や実装に差がでるのか比較してみます。

three.jsとはwebGLをサポートした3DCGライブラリです。
webGLの実装する場合、ほとんどの方が使われているのではないでしょうか。
three.jsにどれだけ恩恵を受けているか、three.jsの中身がどうなっているのか少しでも理解したくて書きました。

ちなみに、はじめて生webGLに触ったためthree.jsなしのソースは自信がないです。間違っていたらご指摘お願いします...!!

準備

見た目が同じになるthree.jsあり/なしのデモを用意します。
立方体を描画しただけの簡単なデモです。
three.jsあり
three.jsなし
※three.jsなしでは行列の操作に'sylvester.js'と'glUtils.js'を使っています。

見た目の比較

(左)three.jsあり (右)three.jsなし
ew.png

three.jsありの方が、辺がジャギります。
これは、three.js内で描画を軽量化させる処理を行っているためらしい。

ソースコードの比較

全体のコードを比較すると大変なので気になった部分だけ書きます。

・立方体

thee.jsあり
  // CubeGeometryで立方体を定義
  var geometry = new THREE.CubeGeometry(10, 10, 10);

  // 各面の色を定義
  var materials = [
    new THREE.MeshBasicMaterial({color: 0x0000ff}),
    new THREE.MeshBasicMaterial({color: 0xff00ff}),
    new THREE.MeshBasicMaterial({color: 0x00ff00}),
    new THREE.MeshBasicMaterial({color: 0x00ffff}),
    new THREE.MeshBasicMaterial({color: 0xff0000}),
    new THREE.MeshBasicMaterial({color: 0xffff00}),
  ];
  var material = new THREE.MeshFaceMaterial(materials);
  var obj = new THREE.Mesh(geometry, material);
three.jsなし
  // 各頂点の位置を配列で定義
  var vertices = [
    // front
    -1.0, -1.0,  1.0,
     1.0, -1.0,  1.0,
     1.0,  1.0,  1.0,
    -1.0,  1.0,  1.0,
    // back
    -1.0, -1.0, -1.0,
    -1.0,  1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0, -1.0, -1.0,
    // top
    -1.0,  1.0, -1.0,
    -1.0,  1.0,  1.0,
     1.0,  1.0,  1.0,
     1.0,  1.0, -1.0,
    // bottom
    -1.0, -1.0, -1.0,
     1.0, -1.0, -1.0,
     1.0, -1.0,  1.0,
    -1.0, -1.0,  1.0,
    // right
     1.0, -1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0,  1.0,  1.0,
     1.0, -1.0,  1.0,
    // left
    -1.0, -1.0, -1.0,
    -1.0, -1.0,  1.0,
    -1.0,  1.0,  1.0,
    -1.0,  1.0, -1.0
  ];

  // 各面の色を定義
  var colors = [
    [1.0,  0.0,  0.0,  1.0],
    [1.0,  1.0,  0.0,  1.0],
    [0.0,  1.0,  0.0,  1.0],
    [0.0,  1.0,  1.0,  1.0],
    [0.0,  0.0,  1.0,  1.0],
    [1.0,  0.0,  1.0,  1.0]
  ];

  // 各面の色を各頂点の色情報に変換
  var generatedColors = [];
  for (j=0; j<6; j++) {
    var c = colors[j];
    for (var i=0; i<4; i++) {
      generatedColors = generatedColors.concat(c);
    }
  }

  // 各面を2つの三角形として作成し
  // 各三角形の位置を指定するために、頂点の配列を示す
  var obj_vertexIndices = [
    0,  1,  2,      0,  2,  3,    // front
    4,  5,  6,      4,  6,  7,    // back
    8,  9,  10,     8,  10, 11,   // top
    12, 13, 14,     12, 14, 15,   // bottom
    16, 17, 18,     16, 18, 19,   // right
    20, 21, 22,     20, 22, 23    // left
  ];

  var obj_verticesBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, obj_verticesBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

  var obj_erticesColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, obj_erticesColorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);

  var obj_erticesIndexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj_erticesIndexBuffer);

  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(obj_vertexIndices), gl.STATIC_DRAW);

three.jsなしのコードがだいぶ長くなりました。
オブジェクトの頂点、面を定義するのが大変です。
色の指定には00〜ffではなく0〜1で書かなければなりません。

・シーン,カメラ,レンダラー

three.jsあり
  // scene
  var scene = new THREE.Scene();

  // camera
  var camera = new THREE.PerspectiveCamera(45, 640 / 480, 1, 2000);
  camera.position.set(0, 0, 0);
  camera.lookAt(new THREE.Vector3(0, 0, 0));

  // renderer
  var renderer = new THREE.WebGLRenderer({
    canvas: canvas,
  });
  renderer.setSize(640, 480);
  renderer.setClearColor(new THREE.Color(0x000000, 0));
three.jsなし
 var shaderProgram;
 var vertexPositionAttribute;
 var vertexColorAttribute;
 var gl;
 gl = canvas.getContext('experimental-webgl');
 gl.clearColor(0.0, 0.0, 0.0, 1.0);
 gl.enable(gl.DEPTH_TEST);
 initShader(gl);

 function initShader(gl)
 {
   var fragmentShader = getShader(gl, 'shader-fs');
   var vertexShader = getShader(gl, 'shader-vs');
   shaderProgram = gl.createProgram();
   gl.attachShader(shaderProgram, vertexShader);
   gl.attachShader(shaderProgram, fragmentShader);
   gl.linkProgram(shaderProgram);
   gl.useProgram(shaderProgram);

   vertexPositionAttribute = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
   gl.enableVertexAttribArray(vertexPositionAttribute);

   vertexColorAttribute = gl.getAttribLocation(shaderProgram, 'aVertexColor');
   gl.enableVertexAttribArray(vertexColorAttribute);
 }

 function getShader(gl, id) {
   var shaderScript = document.getElementById(id);
   var theSource = '';
   var currentChild = shaderScript.firstChild;

   while(currentChild) {
     if (currentChild.nodeType == 3) {
       theSource += currentChild.textContent;
     }
     currentChild = currentChild.nextSibling;
   }
 }

これ以外にcameraの設定はrenderの中で決めていて
makePerspective(45, 640 / 480, 1, 2000);
となります。
シーン,カメラ,レンダラーごとに分けて説明したかったのですが、
分けづらくて諦めました...ゴメンナサイ

また、three.jsなしはこれに加えてシェーダーの記述が必要です。
ほぼ定形文みたいなやつ。

three.jsなし shader
<script id="shader-fs" type="x-shader/x-fragment">
  varying lowp vec4 vColor;

  void main(void) {
    gl_FragColor = vColor;
  }
</script>

<script id="shader-vs" type="x-shader/x-vertex">
  attribute vec3 aVertexPosition;
  attribute vec4 aVertexColor;

  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;

  varying lowp vec4 vColor;

  void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    vColor = aVertexColor;
  }
</script>

感想

こんな感じでthree.jsになると記述量がだいぶ増えます。
three.jsなしで実装を行うのは予想通り大変でした。
ただし、オブジェクトの定義などは比較しやすく構造もわかりやすかったかなと思います。
three.jsのdocumentとソースを読んでもっとwebGLや3DCGの世界の理解を深めていきたいです。

参考にしたサイト

・WebGL を用いた 3D オブジェクトの作成
https://developer.mozilla.org/ja/docs/Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL
 
 
 
 

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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした