はじめに
本連載では、9月16日に株式会社カールよりプレスリリースされた『リッチクライアントCurlで開発した3Dアプリケーション「Curl 3D Gallery」の提供を開始』などでも使用されているCurlでの3次元表現の基礎的な部分について、3回にわたって解説しようと思います(参照:リッチクライアントCurlで開発した3Dアプリケーション「Curl 3D Gallery」の提供を開始・PDF)。

Curlは、標準で3次元グラフィック機能が実装されていますが、上記のようなアプリケーションを構築する場合、3次元グラフィックスに関する知識が必要となるケースも多々あります。紙面の都合上、すべてに関する解説を行うことはできませんが、他にも使えそうなトピック的な部分に絞って連載していく予定です。
Curlの3Dグラフィックス
まずはCurlの3次元グラフィックスについて、簡単に触れておきたいと思います。Curlの3次元表現には3D グラフィックス シーン グラフ パッケージを用います。このパッケージで使用されるクラスの関係をCurlのHelpドキュメントを元にまとめてみますと、
SceneGraphic
Sceneを含むGUIウィジェットです。Sceneの表示に使用されるGraphicのサブクラスであり、画面上には本Graphicを配置します。
Scene
Cameraを内包しており、SceneObjectの管理を行います。
Camera
CameraはSceneの表示方法を決定するクラスで、視点位置や視線方向、投影法、視野などを設定します。
SceneObject
SceneObjectを作成して、Sceneに配置します。SceneObjectには次のようなオブジェクトが用意されています。
- PolygonSet
- Quad
- Triangle
- SceneGroup
複数の面、線または点を描くオブジェクト。多角形から複雑なオブジェクトをモデル化するために使用します。
塗りつぶされた平面四角形。
塗りつぶされた三角形。
SceneObjectをグルーピングするためのオブジェクト。オブジェクトのグループを1つの単位として変換する際に、SceneGroupを使用します。
ではさっそく簡単な例を示したいと思います。以下の図のように、X、Y、Z軸の座標軸を表示するものです。また、SceneGraphicは左ボタンドラッグで回転動作、右ボタンドラッグで拡大・縮小動作が行えるようになっています。本サンプルでは「Reset Camera」ボタンを押下するとカメラを初期状態に戻せるようにしています。

以下が上記サンプルのソースコードになります。最初ということもあり、各行についてコメント形式で補足を書いてあります。
{curl 6.0 applet} || Applet指定
{curl-file-attributes character-encoding = "shift-jis"} || 文字コード指定
{import * from CURL.GRAPHICS.SCENE} || Sceneパッケージのインポート
{define-proc public {make-axis-object scale:Distance}:PolygonSet || 座標軸オブジェクト生成処理
let constant scale-f:FloatDistance = scale asa FloatDistance || 引数の座標軸長さの型変換
let object:PolygonSet = {PolygonSet} || PolygonSetの生成
set object.primitive-type = PrimitiveType.lines || レンダリング方法は線分列指定
set object.vertices = || 座標軸の座標を指定
{new {Array-of FloatDistance3d},
{FloatDistance3d 0f(m), 0f(m), 0f(m)}, || X軸
{FloatDistance3d scale-f, 0f(m), 0f(m)},
{FloatDistance3d 0f(m), 0f(m), 0f(m)}, || Y軸
{FloatDistance3d 0f(m), scale-f, 0f(m)},
{FloatDistance3d 0f(m), 0f(m), 0f(m)}, || Z軸
{FloatDistance3d 0f(m), 0f(m), scale-f}
}
set object.colors = || 座標軸の始点、終点の色指定
{new {Array-of Pixel},
{Pixel.create 1, 0, 0}, || X軸
{Pixel.create 1, 0, 0},
{Pixel.create 0, 1, 0}, || Y軸
{Pixel.create 0, 1, 0},
{Pixel.create 0, 0, 1}, || Z軸
{Pixel.create 0, 0, 1}
}
set object.opaque-to-intersection? = false || 交点イベントを処理しない
{return object}
}
{value
let scene:Scene = {Scene} || Sceneの作成
let camera:Camera = scene.camera || SceneのCameraを取得
let camera-target:Distance3d = {Distance3d 0ft, 0ft, 0ft} || 注視点位置を設定
let camera-position:Distance3d = {Distance3d 5ft, -3ft, 5ft} || 視点位置を設定
let camera-direction:Direction3d = || 視線方向ベクトルを算出
{(camera-target - camera-position).direction}
let up:Direction3d = {Direction3d 0, 0, 1} || 視線-上方向ベクトルを設定
{camera.set-orientation-and-position || Cameraに視線情報を設定
camera-direction, up, position = camera-position}
set camera.projection = Projection.perspective || Cameraを透視投影法に設定
set camera.near-clipping-plane = 1in || Cameraの近クリップ面を設定
set camera.far-clipping-plane = 20ft || Cameraの遠クリップ面を設定
set camera.field-of-view = 90degrees || Cameraの視野角を設定
{scene.add-object {make-axis-object 1m}} || 座標軸オブジェクトを追加
let scene-graphic:SceneGraphic = || SceneGraphicを生成
{SceneGraphic
scene,
width=4in, || SceneGraphicの幅を設定
height=4in, || SceneGraphicの高さを設定
background = {FillPattern.get-black} || SceneGraphicの背景を設定
}
let reset-camera:CommandButton = || 「Reset Camera」ボタンを生成
{CommandButton
label = "Reset Camera",
{on Action at b:CommandButton do || ボタン押下時のイベント処理
{camera.set-orientation-and-position || 押下時、Camera情報を元に戻す
camera-direction, up, position = camera-position}
{scene-graphic.update-drawable} || SceneGraphicを再表示
}
}
{VBox || 画面上に、
scene-graphic, || SceneGraphicと
reset-camera || ボタンを配置
}
}
Curlにおける3次元モデルの生成は、以下の3ステップで行います。
- Scene情報を生成し、属性を設定する。
- Sceneに配置するSceneObjectを定義し、Sceneに登録する。
- SceneをSceneGraphicに設定する。
非常に簡単なサンプルではありますが、Curlでの3次元表現の実装イメージがご理解いただけるのでないかと思います。基本はこの3ステップだけであり、ソースコードもほとんどがパラメータの設定関係となっています。
いかがでしょうか? OpenGLなどから比較すると直観的なソースコードになっていると思いませんか?
霧の表現
前述の「Curl 3D Gallery」にも組み込まれていますが、ここからは遠景を霧で霞ませる表現の実装方法について、簡単なサンプルを用いて説明していきます。サンプルは少し複雑になりますが、霧表現自体の実相は驚くほど簡単なものになっています。
まずは、画面のイメージから見ていきましょう(奥の図形は、霧効果により霞んで見えています)。

まず、本サンプルの操作説明を行います。
- 通常Sceneの操作と同じく、左ボタンドラッグによる回転、右ボタンドラッグによる拡大・縮小が行えます。
- 「シーン霧効果」チェックボックスにより、全要素の霧効果属性を、有効、もしくは、無効にできます。
- 三角形をクリックすることにより、個別に霧効果の切り替えが可能です。
今回は、霧効果に関する部分のみを説明していきます。
Scene、SceneObjectの描画は、Renderer3dを用いて行われており、霧効果はRenderer3dクラスのfog-enabled?パラメータで有効/無効の指定を行います。他の霧効果に関するパラメータには、次のような項目が用意されています。
| fog-mode | 霧のかかり方で、今回、FogMode.linearという指定により距離に比例して霞むように指定しています。 |
| fog-color | 霧の最遠部の色コードです。 |
| fog-start | 霧効果を開始するカメラからの距離を指定します。 |
| fog-end | 霧効果を終了するカメラからの距離を指定します。この距離より遠い図形は、最遠部の色コードで表示されます。 |
霧効果を表示するには、Scene、もしくはSceneObjectのpaintメソッドをオーバーライドし、本来の表示処理の前に霧属性を引数で渡されるRenderer3dに対して設定します。このように、数項目のパラメータを指定するだけで霧効果を実現できるのです。
ここで気をつけないといけない事は、Renderer3dのインスタンスはSceneやSceneObjectの描画時に引数として渡されますが、同一のインスタンスが渡されるという点です。そのため、今回のサンプルでは個々の図形の再表示処理(paintメソッド)にて、毎回属性値を設定するようにしています。図形個別の設定ではなくScene全体ということであれば、Sceneのpaintメソッドのみに霧効果による処理を追加することで実現できます。
応用次第で目立たせたいオブジェクトのみを霧効果無効にすることも可能ですが、Render3dの属性値はpaintメソッド内で必要時のみ変更し、変更後は元に戻しておくなどといった工夫が必要になります(サンプルでは、Sceneの霧効果処理はコメントアウトしてあります)。
今回は最初ということもあり、Curlでの3次元描画の基礎的な部分を説明しました。次回はテクスチャマッピングについてのお話をしようと思います。

