-
カテゴリー 日常 / プライベート
-
オフィス改造計画実行委員会活動!スチールラックの設置を行いました。
-
WebGL+HTML5で3DCGを勉強してみた! Part1
どうも。営業部の伊藤です。
前回のブログで書いたように、今更感は否めないですが…最近WebGLの勉強をしています。
まだ超々初心者レベルまでしか勉強できていないですが、これはなかなか頭の体操になります。
今まであまり経験した事のない思考が必要という事もあり、抜け毛の本数が増えそうではありますが…初心者レベルになれるまでは頑張ろうと思います。という事で、復習も兼ねてWebGLを利用した3Dグラフィックの描画方法について書こうと思います。
※3DCG、WebGLに着目しているため、実装で利用するHTML、JavaScript、数学的知識の基礎については省かせていただきます。3DCGのサンプルは以下をご覧ください。
※ピカピカしているのは描画遅延ではないです。チカチカしたらすいません。。。【補足情報】
利用しているオープンソースは以下2種類となります。
○Google webgl-utils.js
setIntervalやsetTimeoutだと色々と問題が多い(複数オブジェクトの描画に向いていない、処理の効率化ができない(GPUに優しくない))ので、上記再帰処理はwebgl-utilsを利用しています。○glMatrix.js
3DCGを表現するために必要となる行列計算用ライブラリとして利用しています。
後述しますが、STEP6の座標変換行列を生成&通知のところで利用します。勉強に利用しているサイトは以下になります。
○wgld.org
初心者向けに基礎から細かくレクチャーしてくれており、非常に分かりやすいです。オススメ!!
それではサンプルをもとに実装内容を書いていきます。
3DCG描画までのSTEPは以下のようになります。STEP1 canvasエレメントを取得
STEP2 canvasからWebGLコンテキストを取得
STEP3 シェーダを生成&コンパイル
STEP4 モデルデータを用意
STEP5 モデルデータからVBO(VertexBufferObject)を生成&バインド
STEP6 座標変換行列を生成&通知
STEP7 描画命令
STEP8 STEP6~7の再帰処理(canvasをレンダリング)それでは早速STEP1から処理の内容から確認していきます。
■STEP1(canvasエレメントを取得)
WebGLを利用した3DレンダリングはHTML5で策定されたcanvasタグ上で動きますので、まずはHTML内のcanvasエレメントを取得します。
今回の場合、以下部分でエレメントを取得しています。var canvas = document.getElementById(“cube”);■STEP2(canvasからWebGLコンテキストを取得)
次に、描画処理用オブジェクトとして、STEP1で取得したcanvasエレメントからWebGLコンテキストを取得します。
このコンテキストで各種描画処理や属性の設定をするため、ここでコンテキストが取得できなれけばWebGLを利用できないブラウザという事になります。
今回の場合、以下部分でコンテキストを取得しています。gl = c.getContext(“webgl”) || c.getContext(“experimental-webgl”);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);canvasに”webgl”という名前のコンテキストを要求しますが、仕様が策定中の時は失敗してしまうため”experimental-webgl”という名前のコンテキストも要求します。(失敗時用)
その後、コンテキスト内部を初期化(clearColor)し、黒色に塗りつぶしています。
3行目のenable(gl.DEPTH_TEST)はコンテキストに深度(DEPTH)を計算(TEST)しておけよ!という命令です。
近くにあるものが遠くにあるものを隠すためにも描画の前に深度を把握しておく必要があります。サクサク進みますね。
以外と簡単なのでは?と僕もここまでは今までの知識で問題なく吸収できたのですが、STEP3から急に難しくなり髪の毛が数本抜けたのを覚えています。。。■STEP3(シェーダを生成&コンパイル)
まず3D関係の知識が無い人はここで壁にぶるつかるでしょう。
「シェーダ」ってなに?
ウィキペディア先生には以下のように記載されています。主にライティング(光源計算)・シェーディング(陰影処理)とレンダリング(ピクセル化)を実行するためにグラフィックリソースに対して使用するソフトウェア命令の組み合わせである。
「shade」とは「次第に変化させる」「陰影・グラデーションを付ける」という意味で、「shader」は頂点色やピクセル色などを次々に変化させるもの(より具体的に、狭義の意味で言えば関数)を意味する。
※wikipedia内の文章を引用難しい………
超々初心者なりに要約します(間違えていたらすいません)。
ソース内のコメントにも記載しておりますが、シェーダには大きく以下2種類のシェーダ存在します。1. バーテックスシェーダ(頂点シェーダ)
2. フラグメントシェーダ(ピクセルシェーダ)【バーテックスシェーダ(頂点シェーダ)】
3次元描画はポリゴンで表示する事が多く、ポリゴンは一般的に三角形の面をつなげたものが使われます(四角形を利用する事もあります)。
三角形が利用されるポイントは、三角形は頂点の位置さえ決めれば面の位置やサイズが確定するという点です。
四角形、五角形、、、それ以上の多角形も頂点だけでは面の位置やサイズを決める事はできません。
この頂点の位置を計算するのがバーテックスシェーダ(頂点シェーダ)です。
※バーテックスシェーダは頂点の位置情報だけ管理する訳ではなく、頂点に関する”全ての情報(頂点の法線、テクスチャ座標、頂点色など)”を管理します。【フラグメントシェーダ(ピクセルシェーダ)】
バーテックスシェーダ(頂点シェーダ)で頂点の位置や形状を決め、その後ラスタライズ(着色前準備)まで終えると、次にオブジェクトへの「着色」が必要になります。
この着色内容を計算、決定する役割を担うのがフラグメントシェーダ(ピクセルシェーダー)です。
色の計算だけであれば簡単に思えますが、これがそうでもないんです。
3次元を表現するため、陰影、深度、光源、重複(オブジェクトやテクスチャの重なり)、乗算(透過時の後ろのテクスチャ色)などを考慮した着色計算が必要です。
よって、上記のような色を計算するため、バーテックスシェーダ(頂点シェーダ)の情報が不可欠になります。
このシェーダ連携についてはSTEP6で書かせていただきます。シェーダの生成&コンパイルは以下部分で実施しています。
var shader;
var shaderElement = document.getElementById(id);
if (!shaderElement) {
return null;
}switch(shaderElement.type){
case “x-shader/x-vertex”:
shader = gl.createShader(gl.VERTEX_SHADER);
break;
case “x-shader/x-fragment”:
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default:
return null;
}//子ノード取得
var source = “”;
var childNodes = shaderElement.firstChild;
while (childNodes) {
if (childNodes.nodeType == 3) {
source += childNodes.textContent;
}
childNodes = childNodes.nextSibling;
}
gl.shaderSource(shader, source); // ソース割り当て
gl.compileShader(shader); // コンパイル見慣れないエレメントタイプがありますね。
「x-shader/x-vertex」
「x-shader/x-fragment」JavaScriptファイルではなく、HTMLファイルのheader部分も見てみてください。
この2つのキーワードが上記で説明したバーテックスシェーダ(頂点シェーダ)とフラグメントシェーダ(ピクセルシェーダ)です。<script id=”vertex” type=”x-shader/x-vertex”>
attribute vec3 vertexPosition;
attribute vec2 vertexTexture;
uniform mat4 vertexMatrix1;
uniform mat4 vertexMatrix2;
varying vec2 textureCoord;void main(void) {
gl_Position = vertexMatrix1 * vertexMatrix2 * vec4(vertexPosition, 1.0);
textureCoord = vertexTexture;
}
</script>
<script id=”fragment” type=”x-shader/x-fragment”>
precision mediump float;
uniform sampler2D fragmentTexture;
varying vec2 textureCoord;void main(void) {
gl_FragColor = texture2D(fragmentTexture, vec2(textureCoord));
}
</script>中途半端にはなりますが…
ここから説明が一気に難しくなる&細かくなり始めるので今回のブログはここまでにします。ではでは。
カテゴリー 技術 / デザイン / 制作 -
引越し完了ー
カテゴリー 日常 / プライベート -
そうだ、アプリの研究しよう vol.0
-
人に何かを教える際に大切にしている10のこと
ブログのカテゴリの整理をしたら四年間で72記事を書いていることがわかった竹内です。こんにちは。
消えてしまっているものも含めれば100記事は書いていそうです。そしてこちらがちょうど4年前の記事。
はい。真面目ですね。
「あの頃~の未来に~僕らは立っているのかなぁ~♪」という感じです。さて、記事のタイトルの流行が過ぎてるのは知ってますが、今回はそんなテーマで。
立場も役割も変わってきて学ぶことと教えることの比重が変化しつつあるので自分への確認も含めたエントリになればと。
順番は特に意味は無いです。重要度でも順番でもありません。
思い出した順です。【1】何がわからないのかを分解する
イチから何かを教えなければならない場合は手とり足とりになりますが、そうでない場合はその人が「何がわからないのか」を一緒に考えます。
人に教えを請う場合の多くは、何がわからないのかがわかってなかったりします。【2】同じ目線になる
自分はただ単にそれを知っているだけで、相手は知らないだけです。
別に自分が偉いわけでもなんでもありません。
知らない人の目線や気持ちに立つ様にしています。【3】怒らない/イライラしない
教えているうちに「どうしてこれがわからないの!?」と怒り出す人がいます。
本末転倒と言うか、自分を棚に上げてと言うか…。
教える側だって教わる側になることもあるだろうし、わからないのは教える側の技量の問題もあるはずなのにそれを忘れてしまうんですね。【4】難しい言葉を使わない
これは相手の理解度にもよりますが、難しいことを難しく教えようとしてもなかなかうまくいかないと思います。
何かを教えようとした時に理解できない言葉が出てくると今度は先にそれを理解して貰わなければなりません。
そうなると本筋以外の事と混同して理解が進みません。【5】答えを教えない
もしかしたらこれが一番気をつけていることかもしれません。
魚を釣ってあげるのではなく、釣り方を教るという事を常に意識しています。
そうすれば似た様な問題や疑問が沸いた時に、次は自力で調べて解決できたり、訊きに来たとしても前回よりは少し進んだ質問になってると思います。
もし同じレベルで訊きに来たとしても、「あの時と同じだよね?」と本人に気がつかせる事で再度考えて貰う事が出来ます。
もちろん、もっと単純な事情で「答え」だけが知りたい人には「答え」だけを直ぐに教えます。【6】答えに辿り着いたら褒める
わざとらしくてもいいんです。
とにかく褒めます。答えが「バナナ」だとして、「バナ…」までヒントを出して最後に「ナ」と言えただけでもいいんです。
とにかく褒めます。
そういう時に褒められて嫌な人は殆どいないでしょう。【7】他の方法があることを示唆する
答えが一つしかない場合も多いですが、答えが複数ある場合もあります。
そして答えに繋がるアプローチが複数あることもたくさんあります。
最初は一つのアプローチとその答えが導き出せれば良いですが、そのうち複数のアプローチがある事を知る必要がありますし、それを自分で試す事が出来る能力も必要になってきます。
一つのアプローチとその答えを妄信して視野が狭くならない様に、「こんな方法もある」「こうしたらどうなると思う?」などと展開する事で、柔軟な考え方を養って貰える様にします。【8】理解できたか確認する
1回で100%理解できるとは思わない事が大切ですが、教えた内容や道筋自体を理解できているかを確認します。
「7×6=42」が理解できたか(覚えられたか)を確認するのではなく、7×6というのは「7が6個あることだから、7+7+7+7+7+7 とすればいい」という道筋にわからないことがなかったかを確認しています。
例えば「どうして + するの?」とか、「6が7個は駄目なの?」などといった疑問があれば解決しておきます。
別項目にするか迷ったんですが、「顔/目を見る」というのも大切ですね。【9】一緒に喜ぶ
優秀な人ほど同じ様な事で何度も教えなければならないという事は無いのですが、どうしても同じ様な事を訊かれたり繰り返してしまう人もいます。
そういった時は前回よりもヒントを少なくしたり、前回の道筋をもう一度辿って貰ったりして、「本人が気が付く」様にします。
そして答えに辿り着いた時は「やったじゃん!一人で解決できたじゃん!」と感嘆しながら一緒に喜びます。【10】反省する
自分が教える為にした全ての行動を反省します。
【1】~【8】が出来ているかどうかですね。
そしてもっと良い方法はなかったかを考えます。
教えてる方も教えられている(学んでいる)という意識を忘れないようにしています。以上。10個です。
まだまだあると言えばありますし、それぞれの項目も深堀りできるのですがそれは別の機会に。最後に…
こんなエントリを書いていますが、人に何かを教えるなんておこがましいと思ってます。
「教えている」のは確かに「教えている」のですが、もっと正確に言えば自分が理解している事や経験してきた事をただ共有しているだけに過ぎません。
そしてその知識だって自分はどこからか学んだり誰かからか教えて貰った事でしかないはずなのです。つまりこのエントリですら先人の受け売りでしかないのだと思います。
それでもどこかの誰かの役に立つのであれば、それは素晴らしいことだと思います。長文を読んで頂いてありがとうございました。
カテゴリー 日常 / プライベート