3Dプログラミング入門講座・その5:応用その1・メッシュアニメーション、動的テクスチャ



同次座標がw,x,y,z,...の順番で定義されたものの説明です。順番が違うだけで、機能に変わりはありませんが・・・


次に、メッシュアニメーションをしたいと思います。
サンプルはこちら
ファイルはこちら



コードはこちら。
usatest.htm

...省略...

<script language="JavaScript" type="text/javascript">

var TMan = new N6LTimerMan();  //タイマーマネージャー
var IDs = new Array(
'MAIN_001', 'MAIN_002', 'MAIN_003', 'MAIN_004', 
'MAIN_005', 'MAIN_006', 'MAIN_007', 'MAIN_008', 
'MAIN_009', 'MAIN_010', 'MAIN_011', 'MAIN_012', 
'MAIN_013', 'MAIN_014', 'MAIN_015', 'MAIN_016', 
'MAIN_017', 'MAIN_018', 'MAIN_019', 'MAIN_020', 
'MAIN_021', 'MAIN_022', 'MAIN_023', 'MAIN_024', 
'MAIN_025', 'MAIN_026', 'MAIN_027', 'MAIN_028', 
'MAIN_029', 'MAIN_030', 'MAIN_031', 'MAIN_032' 
);
var act = 0;
var intvl = 50;

jQuery(document).ready(function(){
  TMan.add();
  TMan.timer[0].setalerm(function() { GLoop(0); }, intvl);  //メインループセット
});

//メインループ
function GLoop(id){
    var elm = document.getElementById(IDs[act]);
    elm.setAttribute('render', false);
    act++;
    if(31 < act) act = 0;
    elm = document.getElementById(IDs[act]);
    elm.setAttribute('render', true);

  TMan.timer[id].setalerm(function() { GLoop(id); }, intvl);  //メインループ再セット
}

</script>

...省略...

<x3d id='x3dabs' width="600" height="600">
<scene>
  <Viewpoint id='viewp001' position='0 0 -10' orientation='0 1 0 3.14' description='camera'></Viewpoint>
    <directionalLight id="directional" direction='1 -1 1' on ="TRUE" intensity='1.0' shadowIntensity='0.0'></directionalLight> 
    <Transform scale="0.1 0.1 0.1">
      <Inline url="./img/usa_001.x3d" id= "MAIN_001" nameSpaceName="MAIN_001" mapDEFToID="true" render = "false"></Inline>
    </Transform>
    <Transform scale="0.1 0.1 0.1">
      <Inline url="./img/usa_002.x3d" id= "MAIN_002" nameSpaceName="MAIN_002" mapDEFToID="true" render = "false"></Inline>
    </Transform>

...途中同様、省略...

    <Transform scale="0.1 0.1 0.1">
      <Inline url="./img/usa_032.x3d" id= "MAIN_032" nameSpaceName="MAIN_032" mapDEFToID="true" render = "false"></Inline>
    </Transform>
</scene>
</x3d>

...省略...


何をしているのか、説明すると*.x3dファイルが、3Dモデラーのblenderでは、今のところ(私がやり方を知らないだけかも)
瞬間ポーズしかエクスポートできないので、瞬間ポーズを全種類<inline>で読み込んで、
メインループで、elm.setAttribute('render', true/false);で表示ポーズを選んでいます。


さらに、動的テクスチャに進みたいと思います。
サンプルはこちら
ファイルはこちら



コードはこちら。
frsnel.htm

...省略...


<body text="black" link="#3333cc" vlink="#663399" alink="#cc0000" bgcolor="#faebf1" background="./img/kumausagineko.jpg" onload="initKeyBoard(TMan, function() { chkKeyBoard(); });main();">

...省略...

      <background DEF='BG' skyColor='0.4 0.4 0.5' backUrl='"./img/BK.png"' bottomUrl='"./img/DN.png"' frontUrl='"./img/FR.png"' leftUrl='"./img/LF.png"' rightUrl='"./img/RT.png"' topUrl='"./img/UP.png"'></background>

      <Transform DEF='mirrorTrans' translation='0 0 0' rotation='1 0 0 0'>


        <shape>
          <appearance>

            <texture hidechildren="false">
              <canvas width="32" height="32" id="myCanvas" style="border: 1px solid black; border-image: none; left: 800px; top: 20px; position: absolute; display:none;">
              </canvas>
            </texture> 
          </appearance>
          <box size="2000 0.1 2000"></box>
        </shape>
      </Transform>


...省略...


frsnel.js

var TMan = new N6LTimerMan();  //タイマーマネージャー
var x3domRuntime;
var loadtex; //読み込み中テクスチャ
var f0 = 0.2; //フレネル係数
//var f0 = 0.8; //フレネル係数
var r = 255;
var g = 0;
var b = 0;
var s = 0;
var tipsize = 32;
var texsize = 32;
var texsize2 = texsize / 2;
var boxsize = 2000;
var boxsize2 = boxsize / 2;
var CVID = ["myCanvasFR", "myCanvasRT", "myCanvasBK", "myCanvasLF", "myCanvasUP", "myCanvasDN"]
var diff = [0.25, 0.25, 0.35]; //色減衰係数
var hil = [0.25, 0.25, 0.25]; //色増加係数
var intvl = 150;
var time = 0;
var add = 0.0625;
var ddd = 8;
var wc = [[15, 18, 1], [24, 5, 2], [4, 13, 0.5], [19, 22, 1]];

//フレネルの式
function fresnel(f0, th){
  var c = Math.cos(th);
  var c1 = 1 - c;
  var c5 = c1 * c1 * c1 * c1 * c1;
  return f0 + (1 - f0) * c5;
}


//色取得u,vテクスチャ位置
function getcol(u, v){
  //テクスチャ中央中心座標系にして座標を求める
  var uu = u - (texsize2);
  var vv = v - (texsize2);
  var xx = 0;
  var yy = 0;
  if(uu) xx = (uu / (texsize2)) * (boxsize2);
  if(vv) yy = (vv / (texsize2)) * (boxsize2);
  //水面
  var mirrorPos    = new N6LVector([1, xx, 0, yy], true);
  var mirrorNormal = new N6LVector([1, 0, 1, 0], true);
  var mirrorUp     = new N6LVector([1, 0, 0, -1], true);
  var mirrorRight  = new N6LVector([1, 1, 0, 0], true);
  //視点
  var vPos = A.Pos();
  var vVecNor = mirrorPos.Sub(vPos);
  vVecNor      = vVecNor.NormalVec();
  var ax = new N6LVector(4, true).UnitVec(1);
  var ay = new N6LVector(4, true).UnitVec(2);
  var nz = new N6LVector(4, true).UnitVec(3).Mul(-1);
  //反射を求める
  var rt = ay.Theta(vVecNor);
  var rf = fresnel(f0, rt);
  if(Math.PI < rf) rf = -(Math.PI / 2);
  else rf = (Math.PI / 2) - rf;
  var ref = new N6LVector(vVecNor);
  ref.x[2] = 0;
  var rc = new N6LVector(4, true);
  if(ref.EpsEqual(new N6LVector([1, 0, 0, 0], true))){
    ref.x[2] = -1;
    rc = new N6LVector(ax);
  }
  else {
    rc = ref.Cross(ay);
    ref = ref.RotAxis(rc, rf).NormalVec()
    if(rf < 0) { ref.x[1] = -ref.x[1]; ref.x[3] = -ref.x[3]; }
  }
  //反射から色を取得するテクスチャ位置を求める
  var th = nz.Theta(ref);
  if(ref.x[1] < 0) th = -th
  var ph = ay.Theta(ref);
  var zface = th / (Math.PI / 4);
  if(zface < -3) zface = 2;
  else if(zface < -1) zface = 3;
  else if(zface < 1) zface = 0;
  else if(zface < 3) zface = 1;
  else zface = 2;
  var yface = ph / (Math.PI / 4);
  var face = zface;
  if(yface < 1) face = 4;
  else if(3 < yface) face = 5;
  var myCanvas = document.getElementById(CVID[face]);
  if ( ! myCanvas || ! myCanvas.getContext ) { return false; }
  var ctx = myCanvas.getContext("2d");
  var th2 = (th + (Math.PI * 9 / 4)) % (Math.PI / 2);
  var ph2 = (-ph + (Math.PI * 5 / 4)) % (Math.PI / 2);
  var x = (th2 / (Math.PI / 2)) * tipsize;
  var y = (ph2 / (Math.PI / 2)) * tipsize;
  //色取得
  var imgdata = ctx.getImageData(0, 0, texsize, texsize);
  var data = imgdata.data;  
  var i = ((Math.floor(y) * texsize) + Math.floor(x)) * 4;
  var rr = Math.floor(data[i + 0] * diff[0]);
  var gg = Math.floor(data[i + 1] * diff[1]);
  var bb = Math.floor(data[i + 2] * diff[2]);
  var aa = data[i + 3];
  var m;
  var i;
  var j;
  var m;
  //水面のきらめき
  for(m = 0; m < wc.length; m++){
    i = u - wc[m][0];
    j = v - wc[m][1];
    var r = Math.floor(Math.sqrt(i * i + j * j));
    var n = (time / add) % (ddd * wc[m][2]);
    if(r % (ddd * wc[m][2]) == n){
      rr = Math.floor((255 - rr) * hil[0] + rr);
      gg = Math.floor((255 - gg) * hil[1] + gg);
      bb = Math.floor((255 - bb) * hil[2] + bb);
      break;
    }
  }
  var col = [rr, gg, bb, aa];
  return col;
}

//動的テクスチャ
function fillpixel(ctx, u, v){
  var col = getcol(u, v);
  ctx.fillStyle = 'rgb(' + col[0] + ',' + col[1] + ',' + col[2] + ')';
  ctx.fillRect(u, v, 2, 2);
}

...省略...

//メインループ
function GLoop(id)
{
  //rainbow();

  moveobj(t, pyr); //視点処理

  //動的テクスチャ
  var myCanvas = document.getElementById("myCanvas");
  if ( ! myCanvas || ! myCanvas.getContext || loadtex) {
    TMan.timer[id].setalerm(function() { GLoop(id); }, intvl);  //メインループセット
    return false;
  }
  //loadtexがあるとここまで来ない、つまり、ここまで来たらテクスチャ読み込み済み
  var ctx = myCanvas.getContext('2d');
  
  var u;
  var v;

  //fillpixel(ctx, 0, 0);
  //fillpixel(ctx, texsize2 - 1, texsize2 - 1);
  //fillpixel(ctx, texsize2 + 0, texsize2 - 1);
  //fillpixel(ctx, texsize2 + 1, texsize2 - 1);
  //for(v = 0; v <= texsize2; v++) fillpixel(ctx, texsize2 - v, texsize2);
  //for(v = 0; v <= texsize2; v++) fillpixel(ctx, v, texsize2);
  for(v = 0; v < texsize; v++) for(u = 0; u < texsize; u++) fillpixel(ctx, u, v);

  myCanvas.parentNode._x3domNode.invalidateGLObject(); //テクスチャ更新


  //タイマーを進める
  time += add;
  if(1 <= time) time = 0;
  TMan.timer[id].setalerm(function() { GLoop(id); }, intvl);  //メインループセット
}

//テクスチャ読み込み
function drawcv(id, fn) {
  var canvas = document.getElementById(id);
  if ( ! canvas || ! canvas.getContext ) { return false; }
  var ctx = canvas.getContext('2d');
  var img = new Image();
  img.src = fn;
  img.onload = function() {
    ctx.drawImage(img, 0, 0, tipsize, tipsize);
    loadtex--;
  };
}

function main() 
{
  var myCanvas = document.getElementById("myCanvas");
  var context = myCanvas.getContext("2d");
  context.fillStyle = 'rgb(255,255,255)';
  context.fillRect(0, 0, myCanvas.width, myCanvas.height);
  myCanvas.parentNode._x3domNode.invalidateGLObject();

  loadtex = 6; //読み込み中枚数
  drawcv('myCanvasFR', './img/FR.png');
  drawcv('myCanvasRT', './img/RT.png');
  drawcv('myCanvasBK', './img/BK.png');
  drawcv('myCanvasLF', './img/LF.png');
  drawcv('myCanvasDN', './img/DN.png');
  drawcv('myCanvasUP', './img/UP.png');

  x3domRuntime = document.getElementById('myX3DWorld').runtime;

  TMan.add();
  TMan.timer[0].setalerm(function() { GLoop(0); }, intvl);  //メインループセット
}

...省略...


<body onload="main();">が、コードのエントリーポイントです。このタイミングでないとうまく表示されません。
<texture>タグに<canvas>タグを埋め込んで動的テクスチャを実現します。
loadtex = 6;で読み込み中枚数を指定してdrawcv(ID, FNAME);で読み込んだらloadtexを1減らす。
メインループのloadtexが0になったら先に進みます。
u、vごとに色を打って、myCanvas.parentNode._x3domNode.invalidateGLObject();でテクスチャを更新して適用します。
色取得は
var imgdata = ctx.getImageData(0, 0, texsize, texsize);
var data = imgdata.data;
var i = ((Math.floor(y) * texsize) + Math.floor(x)) * 4;
var rr = Math.floor(data[i + 0] * diff[0]);
var gg = Math.floor(data[i + 1] * diff[1]);
var bb = Math.floor(data[i + 2] * diff[2]);
var aa = data[i + 3];
コンテキストからgetImageData()で取ってきます。
rgbaは実数だと動作しないので、整数にするためfloor()をしましょう。







 ■■■ 3Dプログラミング入門講座 ■■■ 
NAS6LIB
ポリゴンテスト解説・x3dom使い方
その1:ベクトル
その2:行列
その3:クォータニオン
その4:基本総復習・実践
その5:応用その1・メッシュアニメーション、動的テクスチャ
その6:応用その2・GLSL、カスタムシェーダー、キーボード、ファイル
その7:応用その3・ゲームプログラミング、タグの動的追加
その8:応用その4・GLSL、シェーダー、その2
その9:物理演算その1・電卓で相対性理論を解く方法
その10:物理演算その2・相対性理論的ニュートン力学
その11:物理演算その3・ケプラー方程式で惑星軌道シミュレーターを作る

その12:物理演算その4・ルンゲクッタ法で作った 相対性理論的ニュートン力学物理エンジンで惑星軌道シミュレーターを作る

その13:経路探索(A*:A-STAR)&巡回セールスマン問題 : 巨大サイズ : くろにゃんこ

その14:プログラミングにおける配列テーブルテクニック
その15:javascriptのクラス活用法
その16:透視射影公式テスト

その17:ケプラー方程式カプセルライブラリ使用法
その18:CSVファイル処理
その19:物理演算その5・重力多体問題
その20:同次座標について(3D座標系の基本の基本)
その21:おさらいコモンクラスの宣言
その22:物理エンジンライブラリ解説(ケプラー方程式・ルンゲクッタ・相対論的万有引力)


 ■■■ THREE.JSプログラミング講座 ■■■ 
THREE.JSテスト解説・THREE.JS使い方
THREE.JS examplesをいじってみた(フレネル反射透過シェーダー)

THREE.JS (半透明シェーダー)

THREE.JS 3D演算で必要な計算(具体例)★とても重要★
THREE.JS THREE-VRM をいじってみた



<<prev 基本総復習・実践 : GLSL、カスタムシェーダー、キーボード、ファイル next>>





戻る