コード長いですが、HGIMG4 で鏡もどきを作ってみました。
hsp3dish.ini で
hsp3dish.ini
wx=512
wy=512
に設定する必要があります。
鏡を含む平面に対して主カメラを鏡転した位置に鏡カメラを配置してそこからバッファに描画し
その後、鏡モデルのための必要な部分だけUV指定で取り出す方法です。
この方法では鏡の後ろに何かオブジェクトがあっても鏡の中に描画されますが鏡モデルの頂点を鏡カメラから見たときのZ軸でnearを前へ補正すると少しましになるかもしれません。
pseudomirror.hsp
#include "hgimg4.as"
#packopt name pseudomirror
#packopt xsize 512
#packopt ysize 512
#module
#deffunc _add array out, array a, array b, double ka, double kb
repeat 3
out.cnt = a.cnt * ka + b.cnt * kb
loop
return
#deffunc _sub array out, array a, array b
repeat 3
out.cnt = a.cnt - b.cnt
loop
return
#defcfunc _dot array a, array b
return a.0 * b.0 + a.1 * b.1 + a.2 * b.2
#deffunc _ref array out, array dir, array n
// dir は入射ベクトル、n は反射する平面の法線
ip = _dot(dir, n)
_add out, dir, n, 1.0, -2.0 * ip
return
#deffunc _qxp array out, double x, double y, double z, double w
out.0 = (w * w + x * x - y * y - z * z)
out.1 = 2.0 * (x * y + w * z)
out.2 = 2.0 * (x * z - w * y)
return
#deffunc _qyp array out, double x, double y, double z, double w
out.0 = 2.0 * (x * y - w * z)
out.1 = (w * w - x * x + y * y - z * z)
out.2 = 2.0 * (y * z + w * x)
return
#deffunc _qzp array out, double x, double y, double z, double w
// (0,0,1) を q で回す
out.0 = 2.0 * (x * z + w * y)
out.1 = 2.0 * (y * z - w * x)
out.2 = (w * w - x * x - y * y + z * z)
return
#defcfunc _sign double a
if a == 0.0 {
return 0.0
}
if a > b {
return 1.0
}
return -1.0
#deffunc _rot2q array out, array xb, array yb, array zb
// 回転行列 xb, yb, zb からクォータニオン
w = xb.0 + yb.1 + zb.2 + 1.0
x = xb.0 - yb.1 - zb.2 + 1.0
y = - xb.0 + yb.1 - zb.2 + 1.0
z = - xb.0 - yb.1 + zb.2 + 1.0
if w < 0.0 {
w = 0.0
}
if x < 0.0 {
x = 0.0
}
if y < 0.0 {
y = 0.0
}
if z < 0.0 {
z = 0.0
}
x = sqrt(x * 0.25)
y = sqrt(y * 0.25)
z = sqrt(z * 0.25)
w = sqrt(w * 0.25)
if (w >= x) && (w >= y) && (w >= z) {
out.0 = _sign(yb.2 - zb.1) * x
out.1 = _sign(zb.0 - xb.2) * y
out.2 = _sign(xb.1 - yb.0) * z
out.3 = w
return
}
if (x >= w) && (x >= y) && (x >= z) {
out.0 = x
out.1 = _sign(xb.1 + yb.0) * y
out.2 = _sign(zb.0 + xb.2) * z
out.3 = _sign(yb.2 - zb.1) * w
return
}
if (y >= w) && (y >= x) && (y >= z) {
out.0 = _sign(xb.1 + yb.0) * x
out.1 = y
out.2 = _sign(yb.2 + zb.1) * z
out.3 = _sign(zb.0 - xb.2) * w
return
}
if 1 {
out.0 = _sign(xb.2 + zb.0) * x
out.1 = _sign(yb.2 + zb.1) * y
out.2 = z
out.3 = _sign(xb.1 - yb.0) * w
}
return
#deffunc _refpoint array out, array p, array path, array n
// 平面を挟んで面対象の点を求める
// p は位置、path は平面の通過点、n は平面の法線で正規化が必要
ddim v0, 4
_sub v0, p, path
ip = _dot(v0, n)
_add out, p, n, 1.0, -2.0 * ip
return
#global
gpreset
setreq SYSREQ_LOGWRITE,1 ; 終了時にログを出力
setcls CLSMODE_SOLID, $404040
gpload id_model,"res/tamane2"
setang id_model, 0.0, M_PI * 1.0, 0.0
setscale id_model, 0.005,0.005,0.005
gpaddanim id_model,"WALK00_F" ; アニメーションクリップを設定
gpact id_model,"WALK00_F" ; アニメーションクリップを再生
mirror_buf = 3
buffer mirror_buf, 1024, 1024, screen_offscreen
setobjrender GPOBJ_CAMERA, -1
// 動かす箱
gpbox id_box, 0.4, 0xffff00
// 床
gptexmat tex_mtl, dirinfo(5) + "\\bg10.jpg"
gpfloor id_floor, 8,8, 0xffffff, tex_mtl ; 床ノードを追加
// 鏡のためのカメラ
gpnull id_mirrorcam
gpcamera id_mirrorcam, 45.0, 1.0, 0.5, 768
setobjrender id_mirrorcam, 0xff0f
// 鏡のテクスチャのための材質
gpscrmat mirror_mtl, mirror_buf, GPOBJ_MATOPT_NOLIGHT|GPOBJ_MATOPT_NOMIPMAP
// 鏡の位置のためのオブジェクト
gpplate id_mirror, 4.0, 4.0, 0x0, mirror_mtl
setpos id_mirror, -1.0, 0.0, -4.0
setobjrender id_mirror, 0 // 不可視にする
camx=0.0:camy=3.0:camz=3.0
counter = 0
gsel 0
*main
getreq fps, SYSREQ_FPS
counter ++
topo = double(counter) * 0.01
bx = sin(topo)
setpos id_box, bx, 0.2, -2.0
; タッチでカメラ位置を動かす
if dragmd { ; ドラッグ中
getkey a,1
if a=1 {
camx=0.05*(mousex-dragx)+cx
camy=0.05*(mousey-dragy)+cz
} else {
dragmd=0
}
} else { ; ドラッグなし
getkey a,1
if a {
cx=camx:cz=camz
dragx=mousex:dragy=mousey
dragmd=1
}
}
; カーソルキーでカメラ位置を動かす
if key&1 : camx -=0.2
if key&4 : camx +=0.2
if key&8 : camz +=0.2
if key&2 : camz -=0.2
if key&0x4000 : camx -= 0.1 // A key
if key&0x10000 : camx += 0.1 // D key
if key&0x8000 : camy += 0.1 // W key
if key&0x20000 : camy -= 0.1 // S key
gosub *render
await 1000/60 ; 待ち時間
goto *main
*render
stick key,15+64+0x3C000
if key&128 : end
gpusecamera GPOBJ_CAMERA ; 使用するカメラを選択する
setpos GPOBJ_CAMERA, camx,camy,camz ; カメラ位置を設定
gplookat GPOBJ_CAMERA, 0,1.8,0 ; カメラから指定した座標を見る
//// 鏡もどき開始
// 鏡の中心位置
getpos id_mirror, x,y,z
ddim mpos, 4
mpos.0 = x, y, z
// 鏡の法線
ddim mn, 4
mn.0 = 0.0, 0.0, 1.0
// 対象カメラの位置と回転
target_camera = GPOBJ_CAMERA
getpos target_camera, x,y,z
ddim cp, 4
fvset cp, x,y,z
getquat target_camera, qx,qy,qz,qw
ddim q,4
q.0 = qx
q.1 = qy
q.2 = qz
q.3 = qw
ddim cxb, 4
ddim cyb, 4
ddim czb, 4
// カメラの基底
_qxp cxb, qx, qy, qz, qw
_qyp cyb, qx, qy, qz, qw
_qzp czb, qx, qy, qz, qw
ddim mxb, 4
ddim myb, 4
ddim mzb, 4
// ミラーの基底
_ref mzb, czb, mn
_ref myb, cyb, mn
mxb.0 = myb.0, myb.1, myb.2
fvouter mxb, mzb.0, mzb.1, mzb.2
ddim mq, 4
ddim check, 4
_rot2q mq, mxb, myb, mzb
// 鏡のためのカメラの位置を算出する
ddim mcp, 4
_refpoint mcp, cp, mpos, mn
// 鏡の新モデル
gpusecamera id_mirrorcam
// ミラーカメラと位置と回転
setpos id_mirrorcam, mcp.0, mcp.1, mcp.2
setquat id_mirrorcam, mq.0, mq.1, mq.2, mq.3
// ミラーカメラのUVを考慮したミラーモデル
mwhalf = 4.0 * 0.5
mhhalf = 4.0 * 0.5
ddim vts, 4,4
vts.0.0 = -mwhalf, mhhalf, 0.0
vts.0.1 = mwhalf, mhhalf, 0.0
vts.0.2 = -mwhalf, -mhhalf, 0.0
vts.0.3 = mwhalf, -mhhalf, 0.0
gpmeshclear
num = 4
repeat num+1
i = cnt
repeat num+1
j = cnt
s = double(j) / double(num)
t = double(i) / double(num)
x = vts.0.0 * s + vts.0.1 * (1.0 - s) + mpos.0
y = vts.1.0 * t + vts.1.2 * (1.0 - t) + mpos.1
z = vts.2.0 + mpos.2
nx = mn.0
ny = mn.1
nz = mn.2
gpcnvaxis cx,cy,cz, x,y,z, 1
u = cx
v = 1.0 - cy
gpmeshadd id, x,y,z, nx,ny,nz, u,v
loop
loop
repeat num
i = cnt
repeat num
j = cnt
vi0 = j + i * (num+1)
vi1 = vi0 + 1
vi2 = vi0 + (num+1)
vi3 = vi2 + 1
gpmeshpolygon vi0, vi2, vi3
gpmeshpolygon vi0, vi3, vi1
loop
loop
gpmesh id_newmirror, 0x0, mirror_mtl
setobjrender id_newmirror, 0xff0f
// ミラーカメラで描画
gsel mirror_buf
gpusecamera id_mirrorcam
redraw 0
gmode 6
gpdraw
redraw 1
// 最終メインカメラ
gpusecamera GPOBJ_CAMERA
gsel 0
redraw 0
gpdraw
color 255,255,255
pos 8,8:mes "" + fps + " [fps]"
redraw 1
// ミラーモデルを毎回作っているので消す
delobj id_newmirror
return
実行結果 |
---|
ASDWキーでカメラ移動できます |