HSP3.6 HGIMG4 で疑似鏡

コード長いですが、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
```
|実行結果|
|:---:|
|![mir01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/4992e8ce-b98a-2a86-f255-79f471135e3f.png)|
|ASDWキーでカメラ移動できます|