Canvasで網目模様を描く方法を教えてください。その5の派生


地球のテクスチャーマッピング
基本的なところを勉強中。
マウスで操作できるのだが、イベントハンドラあたりを書き直した。
こちらは問題ないのだが、その5に適用すると球体の反対側を表にしたとき動作が逆になる。


あぁ〜時間がほしい。また忙しくなる。

参照元
http://jsdo.it/totetero/without_polygon

しかし、マッピングする方法が目指しているものと違う。
逆行列だ!?う〜〜〜ん、悩む
http://d.hatena.ne.jp/gyuque/20090211

<!DOCTYPE html>
  <meta charset="UTF-8">
  <title>3D</title>
  <style>
    canvas { background : #aaa; }
  </style>
<body>
<img style="display:none;" src="data:image/gif;base64,
 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">


<canvas id='world' width="600" height="400"></canvas>
<script>

function load (){
  var canvas = document.getElementById("world");
  var context = canvas.getContext("2d");
  var canvasdat = context.createImageData(canvas.width, canvas.height);
  var imgdat = null;
  var rotq = [];
  
  // メインループ
  var renderScene = function(){
  
    rotq = ctrl.rotq;
    for(var i = 0; i < canvasdat.height; i++){
      for(var j = 0; j < canvasdat.width; j++){
        var index = i * canvasdat.width + j;
        var x = (j - canvasdat.width * 0.5) / 150;
        var y = (i - canvasdat.height * 0.5) / 150;
        var r = x * x + y * y;
        if(r <= 1){
          // 描画対象点の球上座標を得る
          var z = Math.sqrt(1 - r);
          // 球上座標にクォータニオン回転を適用する
          var ix =  rotq[3] * x + rotq[1] * z - rotq[2] * y;
          var iy =  rotq[3] * y + rotq[2] * x - rotq[0] * z;
          var iz =  rotq[3] * z + rotq[0] * y - rotq[1] * x;
          var iw = -rotq[0] * x - rotq[1] * y - rotq[2] * z;
          x = ix * rotq[3] - iw * rotq[0] - iy * rotq[2] + iz * rotq[1];
          y = iy * rotq[3] - iw * rotq[1] - iz * rotq[0] + ix * rotq[2];
          z = iz * rotq[3] - iw * rotq[2] - ix * rotq[1] + iy * rotq[0];
          // 球上座標をテクスチャ座標に変換する
          var u = Math.atan2(z, x) / (2 * Math.PI) + 0.5;
          var v = Math.acos(y) / Math.PI;
          u = Math.floor((1 - u) * imgdat.height);
          v = Math.floor((1 - v) * imgdat.width);
          var index2 = v * imgdat.width + u;
          canvasdat.data[index * 4 + 0] = imgdat.data[index2 * 4 + 0];
          canvasdat.data[index * 4 + 1] = imgdat.data[index2 * 4 + 1];
          canvasdat.data[index * 4 + 2] = imgdat.data[index2 * 4 + 2];
          canvasdat.data[index * 4 + 3] = 255;
        }else{
          canvasdat.data[index * 4 + 0] = 0;
          canvasdat.data[index * 4 + 1] = 0;
          canvasdat.data[index * 4 + 2] = 0;
          canvasdat.data[index * 4 + 3] = 255;
        }
      }
    }
    context.putImageData(canvasdat, 0, 0);
    setTimeout(renderScene, 40);
  }
  
  // 画像の読み込み
  var img = new Image();
  img.onload = function(){
    var imgcanvas = document.createElement('canvas');
    imgcanvas.setAttribute('width', 360);
    imgcanvas.setAttribute('height', 360);
    var imgcontext = imgcanvas.getContext('2d');
    imgcontext.drawImage(img, 0, 0, imgcanvas.width, imgcanvas.height);
    imgdat = imgcontext.getImageData(0, 0, imgcanvas.width, imgcanvas.height);
    // 処理開始
    renderScene();
  }

  img.src = document.querySelector ('img').src;

}


//マウスのドラッグで、3Dオブジェクトを回転させる
(function () {

  function CanvasController (canvas) {
    this.canvas = canvas;
    this.mouseX = 0;
    this.mouseY = 0;
    this.touchF = false;
    this.rotq = [0, 0, 0, 1];
    this.gain = 100; // mouse移動の感度
  }
  
  
  function handleEvent (event) {
    var e, x, y, dx, dy, a;
    
    switch (event.type) {

    case 'mousedown' :
    case 'touchstart' :
      this.touchF = true;
      break;
    
    case 'mouseup' :
    case 'touchend' :
      this.touchF = false;
      break;
    
    case 'mousemove' :
    case 'touchmove' :
      e = event.target.getBoundingClientRect ();
      x = event.clientX - e.left;
      y = event.clientY - e.top;

      if (this.touchF){
        // クオータニオンによる回転
        dx = -(x - this.mouseX) / this.gain;
        dy =  (y - this.mouseY) / this.gain;
        a = Math.sqrt (dx * dx + dy * dy);
        if(a != 0){
          var ar = a * 0.5;
          var as = Math.sin(ar) / a;
          var qax = this.rotq[0], qay = this.rotq[1], qaz = this.rotq[2], qaw = this.rotq[3];
          var qbx = dy * as;
          var qby = dx * as;
          var qbw = Math.cos(ar);
          this.rotq = [
            qax * qbw + qaw * qbx             - qaz * qby,
            qay * qbw + qaw * qby + qaz * qbx,
            qaz * qbw             + qax * qby - qay * qbx,
            qaw * qbw - qax * qbx - qay * qby
          ];
        }
      }
      this.mouseX = x;
      this.mouseY = y;
      break;
    
    default :
      break;
    }
  
  }
  
  
  function create (canvas) {
    var obj;
    
    if (1 > arguments.length)
      throw new Error ('引数がない');
    if ('CANVAS' !== canvas.tagName)
      throw new Error ('Canvas 要素ではない');
    
    obj = new CanvasController (canvas);
    
    canvas.addEventListener ('mousedown', obj, false);
    canvas.addEventListener ('touchstart', obj, false);
    canvas.addEventListener ('mouseup', obj, false);
    canvas.addEventListener ('touchend', obj, false);
    canvas.addEventListener ('mousemove', obj, false);
    canvas.addEventListener ('touchmove', obj, false);

    canvas = null;

    return obj;
  }
  
  //__
  
  CanvasController.prototype.handleEvent = handleEvent;

  //__
  CanvasController.create = create;
  
  this.CanvasController = CanvasController;

}) ();



var ctrl = CanvasController.create (document.querySelector ('canvas'));


addEventListener ('load', load, false);
</script>