ドラゴンのサンプル


▲JavaScriptプログラムの実行結果


まず以下のプログラムリストが「ドラゴンのプログラム」をJavaScriptの外部ファイルとして読み込んでいるHTMLです。

このファイル: canvas.html

<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">

<title>Dragon</title>

<script>
var modelTens = new Array();
var modelMens = new Array();
</script>

<script src="dragonModelData.js"></script>
<script src="program.js"></script>

</head>
<body onload="onloadx();">
<canvas id="canvasELID" width="640" height="480" style="border:solid 1px black;"></canvas><br>
▲静止画バージョンの実行結果<BR>

</body></html>

 新しいウィンドウで実行  [小WINで表示]

タグの内容を見ると、

  1. データを受けるための配列 modelTens, modelMens を用意したうえで、
  2. ドラゴンのデータ dragonModelData.js を読み込み、
  3. 静止画3DCGのプログラム program.js も読み込み、
  4. bodyタグのonload属性にて、そのプログラムのonloadx()を指定。
  5. また、CANVASタグをcanvasELIDというIDを付けて設置しています。
    このIDは program.jsがCANVASを描画する際に使っています。


次にドラゴンのデータファイルはこれです。

「Shade3D」というモデリングソフトでドラゴンをモデリングして、当サイトの「変換ツール」を使ってJavaScriptのデータへ変換して作成したものです。


そして、表示プログラム program.js は下記のように ごく短いです。

プログラムを見渡してみると、使われている数学が四則演算だけなのが分かると思います。

難しい数学がたくさん必要だったりすると、3DCGも大変困難なものになりますが、実際はこんなに楽なものなのです。

このファイル: program.js

//物体を移動できるようにしましょう。
modelPos = new Object();
modelPos.x = -150;
modelPos.y = 150;
modelPos.z = 3200;

//物体を拡大できるようにしましょう。
modelScale = 1;


//カメラのレンズのmmに相当する値です。とりあえず 50 にしておきます。
var s = 50;


var canvas;

function onloadx() {
var canvasEL = document.getElementById( "canvasELID" );
canvas = canvasEL.getContext( '2d' );

draw();
}

function draw() {

//ドラゴンを構成する各面を for文 で順に処理します。
var j;
for( j = 0; j < modelMens.length; j++ ) {

//1枚の面はそれを構成する頂点(3個か4個)の頂点番号を配列で持っている。
var chotens = modelMens[ j ];

//面を描くので、beginPath、moveTo、lineTo、closePathなどの関数を使用する。
canvas.beginPath();

//面を構成する頂点(3個か4個)を for文 で順に処理します。
for( var i = 0; i < chotens.length; i++ ) {

var chotenNum = chotens[ i ];

//頂点の3次元座標 x, y, z を取り出し、
var x = modelTens[ chotenNum ][ 0 ];
var y = modelTens[ chotenNum ][ 1 ];
var z = modelTens[ chotenNum ][ 2 ];

//3次元座標に移動分を加えます。
//拡大は移動前の座標を拡大するようにしてください。
x = x * modelScale + modelPos.x;
y = y * modelScale + modelPos.y;
z = z * modelScale + modelPos.z;

//3次元座標を henkan() に渡して 返り値として2次元座標 h, v を得ます。
var object = henkan( x, y, z, s );
var h = object.h;
var v = object.v;

//画面の中心を原点 0,0 にする。
//vが増えるほど上方へ描画するように方向転換。
h = h + 320;
v = -v + 240;

//頂点0番は面の最初の頂点なのでmoveTo、以降はlineToを使用する。
if( i == 0 ) {
canvas.moveTo( h, v );
} else {
canvas.lineTo( h, v );
}
}
canvas.closePath();

canvas.strokeStyle = "green";

//最後に描画命令を出し面を描画する。
canvas.stroke();
}
}


//Wikipediaの3DCGの原理の説明文中の数式をテキスト選択してコピーして、これをもとに関数を作成してしまいましょう。
function henkan( x, y, z, s ) {
var h=x*(s/z);
var v=y*(s/z);
var object = new Object();
object.h = h * 17.6;
object.v = v * 17.6;
return object;
}

説明が分かりにくいかもしれないので、ファイル一式のZIPをダウンロードして含まれるcanvas.htmlをブラウザで開いてください。
おてもとで実行されますので各ファイルの関係やプログラムの内容など確認してください 。


静止画だけだと、3DCGプログラムとしての可能性を感じることができないと思いますので、回転させましょう。
四則演算に加えて、高校数学の三角関数が必要になります。
「sin, cos, tan いったい何のため??」と思いながら勉強した人も多いと思います。
(宇浦冴香もそう歌っています)
こうやって遊ぶためだったのでしょうか?


▲下記JavaScriptプログラムの実際の出力(動画ファイルではありません)


このプログラムリストの中で、赤くマークされている部分は静止画バージョンから、回転するために変更を加えた部分です。
そんなにいっぱい変更しているわけではありませんよね?参考にしてください。
比較ファイル: program.js
このファイル: program2.js

//物体を移動できるようにしましょう。
modelPos = new Object();
modelPos.x = -150;
modelPos.y = 150;
modelPos.z = 3200;

//物体を拡大できるようにしましょう。
modelScale = 1;


//カメラのレンズのmmに相当する値です。とりあえず 50 にしておきます。
var s = 50;


var canvas;
var kakudo = 0;

function onloadx2() {
var canvasEL = document.getElementById( "canvasELID2" );
canvas = canvasEL.getContext( '2d' );

//100ミリ秒ごとにプログラムを実行する。
setInterval( "kakudo += .1; draw2();", 100 );
}

function draw2() {

canvas.clearRect( 0, 0, 640, 480 );

//ドラゴンを構成する各面を for文 で順に処理します。
var j;
for( j = 0; j < modelMens.length; j++ ) {

//1枚の面はそれを構成する頂点(3個か4個)の頂点番号を配列で持っている。
var chotens = modelMens[ j ];

//面を描くので、beginPath、moveTo、lineTo、closePathなどの関数を使用する。
canvas.beginPath();

//面を構成する頂点(3個か4個)を for文 で順に処理します。
for( var i = 0; i < chotens.length; i++ ) {

var chotenNum = chotens[ i ];

//頂点の3次元座標 x, y, z を取り出し、
var x = modelTens[ chotenNum ][ 0 ];
var y = modelTens[ chotenNum ][ 1 ];
var z = modelTens[ chotenNum ][ 2 ];

//水平(x,zについて)回転します
result = kaiten( x, z, kakudo );
x = result.X;
z = result.Y;

//3次元座標に移動分を加えます。
//拡大は移動前の座標を拡大するようにしてください。
x = x * modelScale + modelPos.x;
y = y * modelScale + modelPos.y;
z = z * modelScale + modelPos.z;

//3次元座標を henkan() に渡して 返り値として2次元座標 h, v を得ます。
var object = henkan( x, y, z, s );
var h = object.h;
var v = object.v;

//画面の中心を原点 0,0 にする。
//vが増えるほど上方へ描画するように方向転換。
h = h + 320;
v = -v + 240;

//頂点0番は面の最初の頂点なのでmoveTo、以降はlineToを使用する。
if( i == 0 ) {
canvas.moveTo( h, v );
} else {
canvas.lineTo( h, v );
}
}
canvas.closePath();

canvas.strokeStyle = "green";

//最後に描画命令を出し面を描画する。
canvas.stroke();
}

}


//Wikipediaの3DCGの原理の説明文中の数式をテキスト選択してコピーして、これをもとに関数を作成してしまいましょう。
function henkan( x, y, z, s ) {
var h=x*(s/z);
var v=y*(s/z);
var object = new Object();
object.h = h * 17.6;
object.v = v * 17.6;
return object;
}

//数学:方眼紙上で回転を考える
function kaiten( x, y, theta2 ) {
var result = new Object();
var theta1 = Math.atan2( y, x );
var hankei = Math.sqrt( x * x + y * y );
result.X = Math.cos( theta1 + theta2 ) * hankei;
result.Y = Math.sin( theta1 + theta2 ) * hankei;
return result;
}

「このドラゴンの1つ1つの三角形の面は著者が1つ1つ作ったのかな?」と思うかもしれませんが、 そういうことはなくて、3DCGの作成ソフトのShade3Dで、おおざっぱに形を組み合わせてドラゴンの形になるように調整した後、Shade3Dで

図形を右クリック>変換>ポリゴンメッシュに変換

を行うとこういう細かい三角形の面の組み合わせに変換してくれます。そのままだと細かすぎてデータ量がおおすぎになるので、

図形を右クリック>ポリゴンリダクション

を行って細かすぎる面を大雑把な面に直します。その結果がこの面をたくさん組み合わせたドラゴンの形です。

ちなみにおおざっぱに形を組み合わせてドラゴンの形にした、というのはこんな感じにおおざっぱです。


▲ドラゴンの頭部。直方体と四角錐(つの)だけで作られている。


ここで難しいのは「ドラゴンの形になるように調整すること(絵描き)」と、「Shade3Dの使い方」の2つだと思います。

絵描きについては、私にとってですが、絵を描くときに重要なのは、「もとの本物はどうだったか思い出せること」と、「目や鼻などのパーツのバランスを取ること」の2つです。私は小学1年生くらいのころドラえもんをよく描いていましたが描きながらこの2つが頭にあったのを覚えています。

以下の4つのドラえもんのうち、右の3つのドラえもんを描く人にとって、左端のドラえもんを描くのは、もしかしたら骨の折れる作業なのではと思います。 実際私も左端のドラえもんを描いた際には『集中力』『神経』を使ってバランスを取り、どんな形だったか一部忘れていたのでネットの本物のドラえもん画像も見ました。それなりに力を出して描くわけです。私も神経ゼロ、集中力ゼロでは左端のようには描けないと思います。

   
(※これは2次著作物にあたると思うので掲載します。しわしわは本物の紙ではなく画像処理です)

でもこうやって4つのドラえもんを並べてみると、いろいろなドラえもんがいてバリエーションがあって楽しい気がします。 「自分は集中力がないし神経を使うのも苦手でどうしても上手には描けない」と言って終わることと、「いろいろなドラえもんがいて楽しい」と良いところに気づくことは、きわめて大きな違いです。 みんながへたくそだと言って笑う中、良いところに気づくと、みんなが言う”へたくそな部分”についてはどうでもよくなります。よいところを探して「ここが良いんじゃないか」と気づいたとき、あなたはそれまでと次元の違う場所にいることに気づくと思います。笑いのネタになっていたものがネタの機能を失います。笑うのをちょっとやめて探して気づくだけなので、そんなに難しくはないと思います。そのとき大事なのは、ほんとに自分がそう思っていることだけを「ここが良いんじゃないか」と言うことです。何も気づかなければ何も言わないほうがいいでしょう。下手だと思って終わるか、いろいろなバリエーションがある楽しさに気づいて場の空気を変えられるか…。


あと、絵を描くのに大切なのは骨格です。

ドラゴンが口をアングリと開けているさまは、「ドラゴンは口をどのように開けていたっけ」というよりも、「アゴの関節はこの辺にあって、この辺を中心に回転するかな」という機械的な動きに注目しています。ガンダムのプラモデルと同じで関節さえ決まっていれば、あとのポーズは自由なんです。


ここで混乱させてしまったかもしれないのでまとめると、

「もとの本物はどうだったっけ…」というのは表面の見た目を見て描き移すテクニック(模写)です。人や物は目の位置、鼻の位置など寸法がくるっているといけないのでよく見て合わせる必要があるんです。

「関節はどこにあって…」というのは内部の仕組みを考えて、多様なポーズを描き出すテクニックです。目の前に1つのポーズしかとっていないモデルがあるとして、内部の仕組みをベースに考えれば、多様なポーズに変更できます。便利なツールみたいなテクニックです。

表面から入る、内面から入る、という2通りの方法があるんです。

以上2つは設計図的なテクニックです。

これをみんなが同じように極めると、みんなが同じ絵を描いてしまい、面白くないです。

私もなんとなくそうなっています。ドラゴンは、鳥山明とソーサリアンのイラストレーターの方に影響されていて、同じだ!と言われかねません。昔の絵のテレビ番組で「画風ドロボー」という言葉が、冗談交じりで使われていました。画風というのはわりといくらでもマネできてしまうという意味の言葉です。ではオリジナリティはどこから出てくるのかというと、設計図的なテクニックからは、たぶん生まれず、人物のクロッキーなど絵画の基本から入り、世界を旅して道端で似顔絵を描いてあげたり、風景画を描いたり、仕事でいっぱい絵を描いたり、そういった苦労から出てくるものなんじゃないかなぁと思います。また、この描き方が好きなんだ!という自分の好きなやり方からもオリジナリティは生まれそうです。たとえばファイナルファンタジーのあの天野なんとかさんのイラストみたいに。絵の具が好きで水彩画にこだわってみたり、Excelの図機能が好きでこだわってみたり。何を好きになるかは自分でもわからないと思います。

何かを見てこんな印象を受けた。それを描き出してみると、「あ、これ、わかるー」と共感してくれる人がいます。そのとき設計図のテクニックばかりを使っていてそうなると思いますか?


わたしも設計図のテクニックで描かれたと思うんですけど。
「あ、これ、わかるー」って言う人いっぱいると思いますがぁぁ…



……受けた印象を具体化するのって小説でもドラマでも絵画でも難しいことで、できれば作家になれると思います。

でも、これら3つのポイントをわかってもらって、いざ描こうとしても、たぶん描けないんじゃないでしょうか。

まぁ、心構えとして持っててもらうと良いんじゃないかと思います。


次に、Shade3Dは操作の理解がけっこう難しいですが、ほかのモデリングソフトが粘土をいじるような難しさがあるのに対して、Shade3Dは平面図形同志の間に面を張る方法で立体を作ることができます。Excelで図形を使うのと同じ感覚で利用できます。


▲(Shade3Dの画面) 平面図形同志の間に…

▲面を張る(2つの図形を”自由曲面”というフォルダに入れるとこうなる)




▲Excelで図形を使う場合と…

▲同じ感覚で利用できます


Shade3Dで、ポーズなしの状態の体を作ってから、「ボーン」というしくみを別途作成します。ボーン(骨)を関節で動かせば「ポーズなし」から「ポーズあり」に手軽に変形させることができます。ドラゴンの複雑なポーズもこのボーンを使っています。


▲Shade3Dの画面。ドラゴンのポーズなしの状態にボーンが入っている。ボーンを動かすとドラゴンの体が動く。

「自由曲面」や「ボーン」などShade3Dの機能を短期間で使いこなすようになるのは難しいと思うので、いきなり完成を目指さず、入門書を利用したりして少しずつ機能の動きを研究するのが良いと思います。


3DCGプログラムって難しいと思っていたでしょう?
実はこんなに短い、ちょっとの数学でできるものだったんです。

でも3DCGの入門書などで教えているように、行列など難しい数学を使っている場合があります。
原理の式を使えば『同じことができる』のに、どうして難しい数学をいっぱい使うのか…。

その理由はたぶん、
入門書を著者の方が執筆する際は、たぶんパソコンの中のグラフィックスを処理するICチップ(グラフィクスボード、略してグラボ)のハードウェアの仕様書や、そのハードウェアを使うドライバソフト(DirectXやOpenGL)の仕様書を見るんじゃないかと思います。
それらの仕様書ではたぶん当たり前で行列データを扱っているんだと思います。
そしてICチップやドライバソフトが行列データを選んで扱っている理由は、「行列」という1種類のデータ形式で済ましたほうが、IC同志のやりとりが統一されて、複数のデータ形式によるハードウェアの複雑さがなくなり、コスト(開発費)が抑えられるからでしょう。データ形式が複数だと電子回路も複雑になったりすると思います。(逆に言って電子回路は(とにかく速くするために)行列データを流すことを考えて配線されていると思うんですが、実際どうかは私は知りません。ここで言う配線というのはICチップの中の微小な配線です)
さらにいろいろなデータ形式のなかから「行列」を選んだ理由は、行列では複数の計算を一度に計算できて効率的で高速だからでしょう。

これは私の早合点かもしれません。私が勝手にそう思っただけで、文献とかを見て確認していませんのでご注意ください。
インターネットでいろいろ調べましたが、なんでハードウェアは行列を選んでいるのかわかりませんでした。
「行列を使うと高速だから」という説明は見当たらず、皆さん当たり前で行列を使っています。
実際はこういうことだよ、と業界の事情をご存知の方がいらっしゃるならメールでお知らせください。

業界では行列を使うことで高速にいろいろ実現されているようです。
自分もやってみたいと思って行列を始めて泣くほど無理だと思ったら「原理での3DCG」で、まずは3DCGの同じ結果をゲットして、それで安心できたら入門書の行列を使った3DCGにトライすれば泣くこともないと思います。




ページの上端へ (もくじ開く)