LuaJITでお手軽3Dプログラミング (5) (2014/03/31, 2017/12/24)
LjES ver.2.01 について
「LuaJITでお手軽3Dプログラミング (1)、(2)、(3)、(4)」で使っている Raspberry Pi 専用の3DライブラリLjES ですが、機能を追加して LjES ver.2.01 として公開します。前バージョン(ljes-1.00)では、球とか立方体といった形状以外は扱いにくかったので、Blender で作成して Collada 形式でエクスポートした3Dモデルを読み込む機能を追加しました。また、3Dモデルの内部に骨(ボーン)を埋め込んで変形する機能 (Skeletal Animation、Bone Animation) もサポートしました。これまでの「LuaJITでお手軽3Dプログラミング (1)、(2)、(3)、(4)」のサンプルは修正の必要はありません。
LjES ver.2.00 は Raspbian Jessie以前用です
Blender(ブレンダー)は、3次元コンピュータグラフィックスを作成する非常に高機能のソフトウェアで、モデリング(3Dモデルの作成)、レンダリング(画像の生成)、アニメーション、ゲームエンジンとしての機能も備える無料(オープンソース)で使用できます。サポートされているプラットフォームは Windows、Linux, MacOSX などでほとんどのコンピュータで使用できます。
LjESは、LuaJIT という非常に高速なスクリプト言語を使って、 3Dアニメーションのプログラミング経験がなくても3Dプログラミングを手軽に楽しめることを目的としています。 Raspberry Pi の公式ディスクイメージの Raspbian には最初から LuaJIT が入っているため、新たに何もインストールする必要もなくプログラミングを始められます。 サンプルプログラムを試すには LjESを解凍したディレクトリでデモのスクリプトを実行するだけです。
hand.lua
demo_spheres.lua
LjES のダウンロードと使い方
LjES はいくつかの画像ファイルとテキストだけが含まれています。テキストの多くはLua言語のソースプログラムで、Raspberry Piでそのまま実行できます。
ljes-2.01.tar.gz (514KB)
GitHub (https://github.com/jun-mizutani/ljes.git )
適当なディレクトリで次のコマンドを実行すると、LjESのダウンロードと展開、デモの実行まで行います。
curl -O https://www.mztn.org/rpi/ljes-2.01.tar.gz tar zxf ljes-2.01.tar.gz cd ljes-2.01 cd examples luajit hand.lua
examples ディレクトリのサンプルは [q] キーを押すか、2分程経過すると終了します。キーを押した場合の動作を下に示します。
| キー | 動作 |
|---|---|
| h | ヘルプ |
| q | 終了 |
| w | ノードの回転 +X |
| s | ノードの回転 -X |
| a | ノードの回転 +Y |
| d | ノードの回転 -Y |
| z | ノードの回転 +Z |
| x | ノードの回転 -Z |
| e | ボーンの回転 +X |
| r | ボーンの回転 -X |
| y | ボーンの回転 +Y |
| u | ボーンの回転 -Y |
| c | ボーンの回転 +Z |
| v | ボーンの回転 -Z |
| o | メッシュを隠す |
| i | メッシュを表示 |
| m | メッシュの選択 (+1) |
| n | メッシュの選択 (-1) |
| j | ボーンの選択 (+1) |
| k | ボーンの選択 (-1) |
| @ | アニメーションの再生 |
| 1 | 指で 1 を示す |
| 2 | 指で 2 を示す |
| 3 | 指で 3 を示す |
| 4 | 指で 4 を示す |
| 5 | 手を開く |
| 6 | 手を握る |
| 9 | ボーンを表示する |
| 0 | ボーンを隠す |
| p | スクリーンショットを PNG で取得 |
| t | 描画領域を右上に |
| b | 描画領域を右下に |
| g | 描画領域のサイズを戻す |
| f | フルスクリーン |
LjES-2.01 の構成
LjES は大体下の図に示すような構成になっています。 図の青い部分の demo.lua はLjESの全体を使うための簡単なフレームワークになっています。グリーンの部分のbcm.lua、egl.lua、 png.lua、 gles.lua、 termios.lua は、どんな Linux のディストリビューションでも持っているシステム用の共有ライブラリ(図中の赤い部分)を LuaJIT から使うための低レベルなコードです。残りの黄色の部分は LjES のクラスライブラリ群です。 OpenGL ES2で必要となる行列演算、GPUへの転送、シェーダプログラミングといった機能や物体の形状や動作の指定、画面への文字表示といった部分を担当しています。ライトブルーのモジュールは COLLADA ファイルの読み込みとアニメーションを担当します。
クラスモジュールの依存関係とクラスの継承による階層構造です。
Colladaのアニメーションサンプル
次のコードは LjES-2 を使ってCollada形式のアニメーションを表示します。 Collada 形式は非常に自由度が高いため、LjES-2 では Blenderから Collada 形式でエクスポートしたファイルのみを対象としています。Blender 上でのメッシュは LjES の Shape に変換され、1つの Shape は最大40本のボーンを埋め込むことができます。Blender 上でメッシュとボーン(Armature)の原点(0, 0, 0)は一致させる必要があります。Rest Positionでエクスポートして下さい。
package.path = "../LjES/?.lua;" .. package.path
local demo = require("demo")
require("ColladaShape")
if arg[1] ~= nil then
collada_file = arg[1]
else
print("usage: luajit dae_anim0.lua your_collada.dae")
return
end
demo.screen(0, 0)
local aSpace = demo.getSpace()
demo.backgroundColor(0.2, 0.2, 0.4)
-- load collada
local collada = ColladaShape:new()
local collada_text = util.readFile(collada_file)
collada:parse(collada_text ,true)
local shapes = collada:makeShapes(true, true)
-- set object
local node = aSpace:addNode(nil, "node")
for i = 1, #shapes do
shapes[i]:shaderParameter("color", {0.8, 0.7, 0.6, 1.0})
node:addShape(shapes[i])
end
-- set eye position
local eye = aSpace:addNode(nil, "eye")
local size = demo.getShapeSize(shapes)
eye:setPosition(size.centerx, size.centery, size.max*2)
-- setup animation
node.shapes[1].anim:start()
-- call back function
function draw()
demo.aText:writeAt(2, 24, "[q] : quit")
node.shapes[1].anim:play() -- play one frame of animation
aSpace:draw(eye)
end
-- main loop
demo.loop(draw, true)
demo.exit()
Shape
回転するドーナッツを表示するアプリケーションは次に示すように 20行ほどのコードで作成することができます。 コードの赤い部分を変更すると、物体の形状や動きを変更することができます。 前バージョン(ljes-1.00)からの変更はありません。
package.path = "../LjES/?.lua;" .. package.path
local demo = require("demo")
demo.screen(0, 0)
local aSpace = demo.getSpace()
local eye = aSpace:addNode(nil, "eye")
eye:setPosition(0, 0, 30)
demo.backgroundColor(0.2, 0.2, 0.4)
local shape = Shape:new()
shape:donut(8, 3, 16, 16)
shape:endShape()
shape:shaderParameter("color", {0.5, 0.3, 0.0, 1.0})
local node = aSpace:addNode(nil, "node1")
node:setPosition(0, 0, 0)
node:addShape(shape)
function draw()
node:rotateX(0.1)
aSpace:draw(eye)
end
demo.loop(draw, true)
demo.exit()
この例は、[q] キーを押すか、14 分ほど経過すると終了します。 [p] キーを押すとカレントディレクトリに "ss_date_time.png" というファイル名でスクリーンショットを保存します。
コードの詳細は こちらを参照して下さい。