はじめに

 hgimg4では、うまく作れば1つのウィンドウに複数のカメラの映像を表示させることができます。 マリカーの画面分割のように複数人プレイ出来るように画面を分割したり、後方映像を出してみたり、真上の映像を平行投影して簡易的なマップ表示にしたりといろいろな使い方ができそうですね。

 画面分割を利用するには、大きく分けて2つの機能を使用する必要があります。

  • 2台目のカメラを準備する。
  • レンダリングバッファを使用する。

 関連したhgimg4の同梱サンプルには、 buffer.hsp や feedback.hsp がありますが、いつものアヒルを使ってサンプルを作ってみました。

サンプル

 3Dモデルはいつものアヒルを使うのでHSP3に同梱されたsampleのresフォルダをコピーしておいてください。 今回のサンプルは、2画面以外にも少し欲張って色々やっています。

サンプル実行結果


; 2台のカメラ映像を1つの画面上に表示するサンプル
#include "hgimg4.as"

title "HGIMG4 Test"

gpreset
setcls CLSMODE_SOLID, $404040		; 画面クリア設定

; メインバッファ
widMain = 0			; ウィンドウID

; レンダリングバッファ
widBuffer = 1		; ウィンドウID
pxBWidth  = 900		; ウィンドウ横幅
pxBHeight = 480		; ウィンドウ高さ
buffer widBuffer, pxBWidth, pxBHeight, screen_offscreen
;logmes "" + screen_offscreen


gpload id_model,"res/duck"			; モデル読み込み
gpfloor id_floor, 8,8, $00ffff		; 床ノードを追加

;	デフォルトのカメラ(1台目)を設定
setpos   GPOBJ_CAMERA, 0,2,5		; カメラ位置を設定
gplookat GPOBJ_CAMERA, 0,0.3,0		; カメラから指定した座標を見る

;	2台目のカメラを作成して設定
; ヌルノードにカメラを割り当てて、カメラノードを作成します。
gpnull   id_camera
gpcamera id_camera, , double(pxBWidth) / pxBHeight	; カメラとして設定する
setpos   id_camera, 0,4,0.1				; カメラ位置を設定する
gplookat id_camera, 0,0.3,0

;	レンダリンググループ
; カメラと同じグループのオブジェクトだけ描画されます。
; アヒル    2
; 床      1
; カメラ0 1|2 = 床とアヒル
; カメラ1   2 = アヒルのみ
setobjrender id_model, 2
setobjrender GPOBJ_CAMERA, 1|2
setobjrender id_camera, 2


gsel widMain

*main
	stick key,15
	if key&128 : end

	; オブジェクトを回転
	addang id_model,0,0.02

	;-----------------------------
	;	レンダリングバッファ
	;-----------------------------
	; 2台目のカメラ映像を使用します。
	gsel widBuffer
	gpusecamera id_camera
	gpdraw
	
	;-----------------------------
	;	メインバッファ
	;-----------------------------
	; 1台目のカメラ映像の上に、レンダリングバッファの画像を貼り付けます。
	gsel widMain
	redraw 0			; 描画開始
	gpusecamera GPOBJ_CAMERA
	gpdraw
	gmode 0
	pos 10, 380
	gmode 0 ; 通常のコピー
	; gmode 1	; アヒルが切り抜かれる?!
	; gmode 2	; アヒルが切り抜かれる?!
	; gmode 6	; 赤い画像?!
	celput widBuffer, 0, 0.5, 0.5

	; celput以外を使用する例
	; gzoom pxBWidth/2,pxBHeight/2, widBuffer, 0,0, pxBWidth,pxBHeight
	; gcopy widBuffer,0,0,pxBWidth, pxBHeight
	
	; 左右反転表示の例
	; pos 640/2+10, 380
	; celput widBuffer,0,-0.5, 0.5
	
	; 描画時のフレームレート
	getreq fps, SYSREQ_FPS
	color 255,255,255
	pos 8,8
	mes "" + fps + " fps"
	
	redraw 1			; 描画終了

	;-----------------------------
	;	ループ終了処理
	;-----------------------------
	await 1000/60			; 待ち時間
	goto *main

レンダリングバッファ

 カメラを追加する前に、カメラの投影先について少し説明します。

 基本的に、1台のカメラ映像は1つのウィドウにしか投影できません。 2台のカメラを同時に使うには、2つのウィンドウが必要です。

 1つのウィンドウに複数のカメラ映像を投影するには、次のような方法を取ります。 まずbuffer命令で見えないウィンドウを作成して、2台目のカメラ映像を投影します。 次にメインのウィンドウに1台目のカメラ映像をレンダリングし、buffer命令で作成した見えないウィンドウの画像をメインウィンドウ上に縮小コピーして表示させます。 標準命令でもbuffer命令は使用されるので、イメージはしやすいかなと思います。

 hgimg4で実装する場合、レンダリングバッファという機能を使用します。 作り方は標準命令同様にbuffer命令を使用します。


buffer widBuffer, pxBWidth, pxBHeight, screen_offscreen

 ウィンドウIDは0以外(メインウィンドウ以外)を指定します。 ウィンドウサイズは、カメラの設定でも使うので変数に入れています。後でginfo_winx, ginfo_winyで取得してもいいですね。 初期化する画面モードは、screen_offscreenに設定しておく必要があるようです。

 作成した見えないウィンドウの使い方は、標準命令での描画と同様でgsel命令で描画先を選択してからgpdrawします。

カメラを追加

 1台目のカメラ(GPOBJ_CAMERA)はデフォルトで作成されます。 2台目以降が必要な場合は、自分で作成する必要があります。作り方は簡単です。


gpnull   id_camera
gpcamera id_camera, , double(pxBWidth) / pxBHeight	; カメラとして設定する

 ヌルノード(空のノード)を作成して、作成したヌルノードをカメラとして設定します。 double(pxBWidth) / pxBHeightと指定しているアスペクト比は、レンダリングバッファとしてbuffer命令で準備したウィンドウの縦横比です。 ここをbuffer命令で指定した値と合わせておかないと、縦横比が歪んだ画像になってしまいます。

 これで2台目のカメラができたので、通常のカメラ(GPOBJ_CAMERA)同様に位置と向きの設定を行っています。 これでカメラを使う準備ができました。

 メインループ内で2台目のカメラ映像を使いたい場合は、gpusecamera命令でカメラを切り替えて使用します。

gpusecamera id_camera

 1台目にカメラを戻す際も、gpusecamera命令を使用します。

gpusecamera GPOBJ_CAMERA

レンダリング

 2台のカメラ映像を2つのウィンドウに投影して、2つの画像をメインウィンドウに表示してみます。

 まずは、レンダリングバッファ(見えないウィンドウ)からレンダリング。


;-----------------------------
;	レンダリングバッファ
;-----------------------------
; 2台目のカメラ映像を使用します。
gsel widBuffer
gpusecamera id_camera
gpdraw

 gsel命令でレンダリングバッファを選択して、gpusecamera命令で2台目のカメラに切り替えて、結果をgpdraw命令でレンダリングしています。 見えないウィンドウなのでredraw 1で画面更新する必要はないようです。

 次はメインウィンドウをレンダリングします。


;-----------------------------
;	メインバッファ
;-----------------------------
; 1台目のカメラ映像の上に、レンダリングバッファの画像を貼り付けます。
gsel widMain
redraw 0			; 描画開始
gpusecamera GPOBJ_CAMERA
gpdraw
gmode 0
pos 10, 380
gmode 0 ; 通常のコピー
celput widBuffer, 0, 0.5, 0.5

 gsel命令でメインウィンドウに切り替えて、カメラをデフォルト(GPOBJ_CAMERA)に切り替えて、レンダリング。 この後、celput命令を使ってレンダリングバッファの画像をメインウィンドウにコピーしています。

 celput以外にもgzoomgcopyが使用できるようです。 レンダリングバッファは、このような単純なコピーだけでなく、うまく使えばテクスチャとして使ったり出来るようです。(標準のサンプルpronama3.hspを参照)

レンダリンググループ

 サンプルでは、2台目のカメラ映像に水面(水色の平面)がレンダリングされていません。 これには、レンダリンググループという機能を使用しています。

 レンダリンググループは、同じグループ番号に属するカメラとノードオブジェクトだけを映像投影する仕組みです。 何も設定しなければグループ番号はカメラもオブジェクトも全てに「1」が設定されるため、カメラ映像には全てのオブジェクト表示されます。 レンダリンググループを設定すると、特定のカメラには(にしか)表示されないオブジェクトを作ることもできます。


setobjrender id_model, 2
setobjrender GPOBJ_CAMERA, 1|2
setobjrender id_camera, 2

 レンダリンググループは、setobjrender命令で変更します。複数のグループを設定する場合は、+|でつなげて使います。 サンプルでは、次のようにレンダリンググループを設定しています。

ノードオブジェクト レンダリンググループ 備考
床(水面) 1デフォルト値
アヒル 2
カメラ0(GPOBJ_CAMERA)1|2床とアヒルを表示
カメラ1(id_camera) 2アヒルのみ表示

 全てのカメラとノードオブジェクトのレンダリンググループ初期値には 1 が設定されているため、床は何もしなくても 1 となっています。

gmode

 今回のサンプルを作っている際に面白い(?)現象に遭遇しました。少し紹介しておきます。

 通常はcelput命令でバッファをコピーする前に、gmode命令でコピーモードを通常のコピー(gmode 0)に設定する必要があります。 マニュアルやサンプルではそのように説明されています。

 では他のモードだった場合の動作はどうなるだろうと試しに gmode 1gmode 2を設定してみると…アヒルのノードオブジェクト部分が透明になってしまいました。 HDLの説明では、01は基本的には同じ動作なので謎な挙動です。

サンプル実行結果

 これはこれで工夫すれば何かに使えそうな感じがするのですが、ただのバグだった場合は将来修正されてしまう可能性があるので手が出せません。

 また、gmode 6に設定すると赤い画像が。引数を適当に変えてみても反応がありません。色減算合成コピーとは異なる動作っぽいですね。