DXLIBでちょっとリッチな見下ろし疑似3Dを描画してみる

勤務も2年目の佳境に入りました、笑也知郎です。やっぱり必要なことは実地でないと分からないと言うのが今の感想です。

それはさておき、今年はカレンダーの方に参加させていただこうと思います。DrawModiGraphを用いた擬似3D描画を紹介します。

**つくるもの**

さて、擬似3DというとスペースハリアーなどのようなラスタスクロールだったりDooMのようなものだったりを指しますが、本件ではそこからさらに分岐した真上見下ろし型を扱います。
DXライブラリでは4辺を指定して四角形を描画するDrawModiGraphという機能があるため、こちらを活用してそれっぽく作っていきましょう。
(DXライブラリ特有の機能！みたいな紹介ではありません、申し訳ないです)

**基本描画**

まずはベースとなるソースをサンプルプロジェクトと[過去サンプル]( https://qiita.com/emiya_ubw/items/38919d76eb67086eaf8a )を元に作りました。処理上の原点(0,0)からxy方向に16ずつ正方形を敷き詰めています。座標の持ち方としてはVECTORのxyをそれぞれのタイル縦横にあてがっています。素材はサンプルプロジェクトから拝借しました。

<details><summary>ベース</summary>

Windows版 VisualStudio( C++ )用のVisualStudio_2017用を使用し、言語をC++17に指定
```cpp:test.cpp
#include "DxLib.h"
#include<memory>
#include<algorithm>

static const int draw_x = 960;			//ウィンドウサイズX
static const int draw_y = 720;			//ウィンドウサイズY

static const int tile_size = 64;		//表示上のタイルサイズ
static const int tile_pic_size = 32;	//画像のタイルサイズ

//いろいろまとめるクラス
class DXDraw {
public:
	//コンストラクタ
	DXDraw() {
		SetOutApplicationLogValidFlag(FALSE);           /*log*/
		SetMainWindowText("game title");                /*タイトル*/
		ChangeWindowMode(TRUE);                         /*窓表示*/
		SetUseDirect3DVersion(DX_DIRECT3D_11);          /*directX ver*/
		SetGraphMode(draw_x, draw_y, 32);               /*解像度*/
		SetUseDirectInputFlag(TRUE);                    /*DirectInput使用*/
		SetDirectInputMouseMode(TRUE);                  /*DirectInputマウス使用*/
		SetWindowSizeChangeEnableFlag(FALSE, FALSE);    /*ウインドウサイズを手動変更不可、ウインドウサイズに合わせて拡大もしないようにする*/
		SetUsePixelLighting(TRUE);                      /*ピクセルライティングの使用*/
		SetFullSceneAntiAliasingMode(4, 2);             /*アンチエイリアス*/
		SetEnableXAudioFlag(TRUE);                      /*XAudioを用いるか*/
		Set3DSoundOneMetre(1.0f);                       /*3Dオーディオの基準距離指定*/
		SetWaitVSyncFlag(FALSE);                         /*垂直同期*/
		DxLib_Init();                                   /*ＤＸライブラリ初期化処理*/
		SetSysCommandOffFlag(TRUE);                     /*タスクスイッチを有効にするかどうかを設定する*/
		SetAlwaysRunFlag(TRUE);                         /*background*/
		SetUseZBuffer3D(TRUE);                          /*zbufuse*/
		SetWriteZBuffer3D(TRUE);                        /*zbufwrite*/
		MV1SetLoadModelPhysicsWorldGravity(-9.8f);      /*重力*/
	}
	//デストラクタ
	~DXDraw() {
		DxLib_End();
	}
};
// プログラムは WinMain から始まります
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
	auto draw = std::make_unique<DXDraw>();
	/*初期化処理*/
	int FloorHandles[4];
	LoadDivGraph("Tex3.bmp", 4, 2, 2, tile_pic_size, tile_pic_size, FloorHandles);//床テクスチャ
	VECTOR EyePos;	EyePos = VGet(0.f, 0.f, 64.f);	//視点の場所
	/*メインループ開始*/
	LONGLONG m_Wait = GetNowHiPerformanceCount();//開始時の経過秒数を得る
	while (ProcessMessage() == 0) {
		{
			/*ループ処理*/
			float speed = 5.f;
			if (CheckHitKey(KEY_INPUT_UP) != 0) {
				EyePos.y += -speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_DOWN) != 0) {
				EyePos.y += speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_LEFT) != 0) {
				EyePos.x += -speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_RIGHT) != 0) {
				EyePos.x += speed * 60.f / GetFPS();
			}

			SetDrawScreen(DX_SCREEN_BACK);
			ClearDrawScreen();
			{
				for (int x = 0; x < 16; x++) {
					for (int y = 0; y < 16; y++) {
						//床の座標
						float x_f = (float)(tile_size * x) + (float)(draw_x / 2) - EyePos.x;
						float y_f = (float)(tile_size * y) + (float)(draw_y / 2) - EyePos.y;
						//
						DrawRotaGraph((int)x_f, (int)y_f, (double)tile_size / (double)tile_pic_size, 0.0, FloorHandles[0], FALSE);
					}
				}
			}
			ScreenFlip();
		}
		//次ループに行く直前に待機
		while ((GetNowHiPerformanceCount() - m_Wait) <= (1000 * 1000 / 60)) {}//16.66msループ
		m_Wait = GetNowHiPerformanceCount();//現時点の経過秒数を得る
	}
	return 0;// ソフトの終了 
}
```

</details>

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/5b36d6c5-81c1-d976-d9dd-17a0d5f23fc8.png">

こちらに高さを導入します。端に行くほど高さぶんのサイズ調整をするような形です。

<details><summary>クリックして展開</summary>

```cpp:test.cpp変更部分
/*初期化部分に追加*/
	int HeightData[16][16] = {
		8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,3,0,0,1,0,0,0,0,0,8,
		8,0,0,0,0,0,3,0,2,0,0,0,0,0,0,8,
		8,0,0,0,0,0,4,3,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,4,4,3,3,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
	};//高さデータ
/*描画に高さを付与するように修正*/
			SetDrawScreen(DX_SCREEN_BACK);
			ClearDrawScreen();
			{
				for (int x = 0; x < 16; x++) {
					for (int y = 0; y < 16; y++) {
						//床の座標
						float x_f = (float)(tile_size * x) + (float)(draw_x / 2) - EyePos.x;
						float y_f = (float)(tile_size * y) + (float)(draw_y / 2) - EyePos.y;
						//タイルの表示倍率
						float tile_scale = EyePos.z / (EyePos.z - (float)(HeightData[x][y]));
						//高さ込みの座標
						float x_f2 = (float)(draw_x / 2) + (x_f - (float)(draw_x / 2)) * tile_scale;
						float y_f2 = (float)(draw_y / 2) + (y_f - (float)(draw_y / 2)) * tile_scale;

						DrawRotaGraph((int)(x_f2), (int)(y_f2), (double)(tile_size / tile_pic_size * tile_scale), 0.0, FloorHandles[0], FALSE);
					}
				}
			}
			ScreenFlip();
```

</details>

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/045910c5-8d7e-94cb-5926-0c4e1d272b46.png">

描画順がおかしい為、タイルの描画順を端→中央になるように修正します。

<details><summary>クリックして展開</summary>

```cpp:test.cpp修正部分
			//高さ込みの座標
			auto Get2DPos = [&](int x, int y) {
				x = std::clamp(x, 0, 16 - 1);
				y = std::clamp(y, 0, 16 - 1);
				float x_f = (float)(tile_size * x) + (float)(draw_x / 2) - EyePos.x;
				float y_f = (float)(tile_size * y) + (float)(draw_y / 2) - EyePos.y;
				float tile_scale = EyePos.z / (EyePos.z - (float)(HeightData[16 - 1 - x][16 - 1 - y]));//タイルの表示倍率
				return VGet((float)(draw_x / 2) + (x_f - (float)(draw_x / 2)) * tile_scale, (float)(draw_y / 2) + (y_f - (float)(draw_y / 2)) * tile_scale, tile_scale);
			};
			SetDrawScreen(DX_SCREEN_BACK);
			ClearDrawScreen();
			{
				auto DrawFloor=[&](int x, int y) {
					auto pos_11 = Get2DPos(x, y);
					DrawRotaGraph((int)(pos_11.x), (int)(pos_11.y), (double)(tile_size / tile_pic_size * pos_11.z), 0.0, FloorHandles[0], FALSE);
				};
				for (int x = 0; x < 16; x++) {
					float x_f = (float)(tile_size * x) - EyePos.x;
					if (x_f > -tile_size/2) { continue; }
					//左上
					for (int y = 0; y < 16; y++) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f > -tile_size / 2) { continue; }
						DrawFloor(x, y);
					}
					//左下
					for (int y = 16 - 1; y >= 0; y--) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f < tile_size / 2) { continue; }
						DrawFloor(x, y);
					}
				}
				for (int x = 16 - 1; x >= 0; x--) {
					float x_f = (float)(tile_size * x) - EyePos.x;
					if (x_f < tile_size / 2) { continue; }
					//右上
					for (int y = 0; y < 16; y++) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f > -tile_size / 2) { continue; }
						DrawFloor(x, y);
					}
					//右下
					for (int y = 16 - 1; y >= 0; y--) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f < tile_size / 2) { continue; }
						DrawFloor(x, y);
					}
				}
				//被っているところ
				for (int x = 0; x < 16; x++) {
					float x_f = (float)(tile_size * x) - EyePos.x;
					if (x_f <= -tile_size / 2 || tile_size / 2 <= x_f) { continue; }
					//上
					for (int y = 0; y < 16; y++) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f > -tile_size / 2) { continue; }
						DrawFloor(x, y);
					}
					//下
					for (int y = 16 - 1; y >= 0; y--) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f < tile_size / 2) { continue; }
						DrawFloor(x, y);
					}
				}
				for (int y = 0; y < 16; y++) {
					float y_f = (float)(tile_size * y) - EyePos.y;
					if (y_f <= -tile_size / 2 || tile_size / 2 <= y_f) { continue; }
					//上
					for (int x = 0; x < 16; x++) {
						float x_f = (float)(tile_size * x) - EyePos.x;
						if (x_f > 0) { continue; }
						DrawFloor(x, y);
					}
					//下
					for (int x = 16 - 1; x >= 0; x--) {
						float x_f = (float)(tile_size * x) - EyePos.x;
						if (x_f < 0) { continue; }
						DrawFloor(x, y);
					}
				}
			}
			ScreenFlip();
```

</details>

ここから低いところと高いところをDrawModiGraphでつなげることで壁が出来上がります。

<details><summary>クリックして展開</summary>

```cpp:test.cpp修正部分
				auto DrawFloor=[&](int x, int y) {
					auto pos_01 = Get2DPos(x-1, y);
					auto pos_10 = Get2DPos(x, y-1);
					auto pos_11 = Get2DPos(x, y);
					auto pos_21 = Get2DPos(x + 1, y);
					auto pos_12 = Get2DPos(x, y + 1);
					{
						//左上、右上、左下、右下

						//左
						DrawModiGraph(
							(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
							(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
							(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f), (int)(pos_01.y + (float)tile_size * pos_01.z / 2.f),
							(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f), (int)(pos_01.y - (float)tile_size * pos_01.z / 2.f),
							WallHandles[0], FALSE);
						//上
						DrawModiGraph(
							(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
							(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
							(int)(pos_10.x - (float)tile_size * pos_10.z / 2.f), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f),
							(int)(pos_10.x + (float)tile_size * pos_10.z / 2.f), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f),
							WallHandles[0], FALSE);
						//右
						DrawModiGraph(
							(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
							(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
							(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f), (int)(pos_21.y - (float)tile_size * pos_21.z / 2.f),
							(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f), (int)(pos_21.y + (float)tile_size * pos_21.z / 2.f),
							WallHandles[0], FALSE);
						//下
						DrawModiGraph(
							(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
							(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
							(int)(pos_12.x + (float)tile_size * pos_12.z / 2.f), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f),
							(int)(pos_12.x - (float)tile_size * pos_12.z / 2.f), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f),
							WallHandles[0], FALSE);
					}
					DrawRotaGraph((int)(pos_11.x), (int)(pos_11.y), (double)(tile_size / tile_pic_size * pos_11.z), 0.0, FloorHandles[0], FALSE);
				};
```

</details>

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/31e9c84a-6266-d28c-062e-0890551a153a.png">


基本はこれで完了です。

**影などの効果を追加**

ではより立体感を出すため、シェーディングや平行影、スポットライト影、地面の鏡面反射を実装してみましょう。
シェーディングはライトの角度に応じて上に黒をブレンドします。

<details><summary>クリックして展開</summary>

```cpp:test.cpp変更部分
/*初期化部分に追加*/
	int BlackScreen = MakeScreen(tile_pic_size, tile_pic_size);
	int WallBuffer = MakeScreen(tile_pic_size, tile_pic_size);
	SetDrawScreen(BlackScreen);
	{
		DrawBox(0, 0, tile_pic_size, tile_pic_size, GetColor(0, 0, 0), TRUE);
	}
	float lightrad = 45.f * DX_PI_F / 180.f;		//平行光の角度
/*ブレンドに対応*/
				//床だけ
				auto DrawFloor=[&](int x, int y) {
					auto pos_11 = Get2DPos(x, y);
					DrawRotaGraph((int)(pos_11.x), (int)(pos_11.y), (double)(tile_size / tile_pic_size * pos_11.z), 0.0, FloorHandles[0], FALSE);
				};
				//壁だけ
				auto DrawWall = [&](int x, int y) {
					auto pos_01 = Get2DPos(x - 1, y);
					auto pos_10 = Get2DPos(x, y - 1);
					auto pos_11 = Get2DPos(x, y);
					auto pos_21 = Get2DPos(x + 1, y);
					auto pos_12 = Get2DPos(x, y + 1);
					{
						//左上、右上、左下、右下
						//下
						if (pos_11.z > pos_12.z) {
							GraphBlendBlt(WallHandles[0], BlackScreen, WallBuffer, 128 + (int)(127.f*std::cos(lightrad - 0.f * DX_PI_F / 180.f)), DX_GRAPH_BLEND_NORMAL);
							DrawModiGraph(
								(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
								(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
								(int)(pos_12.x + (float)tile_size * pos_12.z / 2.f), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f),
								(int)(pos_12.x - (float)tile_size * pos_12.z / 2.f), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f),
								WallBuffer, FALSE);
						}
						//左
						if (pos_11.z > pos_01.z) {
							GraphBlendBlt(WallHandles[0], BlackScreen, WallBuffer, 128 + (int)(127.f*std::cos(lightrad - 90.f * DX_PI_F / 180.f)), DX_GRAPH_BLEND_NORMAL);
							DrawModiGraph(
								(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
								(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
								(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f), (int)(pos_01.y + (float)tile_size * pos_01.z / 2.f),
								(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f), (int)(pos_01.y - (float)tile_size * pos_01.z / 2.f),
								WallBuffer, FALSE);
						}
						//上
						if (pos_11.z > pos_10.z) {
							GraphBlendBlt(WallHandles[0], BlackScreen, WallBuffer, 128 + (int)(127.f*std::cos(lightrad - 180.f * DX_PI_F / 180.f)), DX_GRAPH_BLEND_NORMAL);
							DrawModiGraph(
								(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
								(int)(pos_11.x - (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
								(int)(pos_10.x - (float)tile_size * pos_10.z / 2.f), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f),
								(int)(pos_10.x + (float)tile_size * pos_10.z / 2.f), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f),
								WallBuffer, FALSE);
						}
						//右
						if (pos_11.z > pos_21.z) {
							GraphBlendBlt(WallHandles[0], BlackScreen, WallBuffer, 128 + (int)(127.f*std::cos(lightrad - 270.f * DX_PI_F / 180.f)), DX_GRAPH_BLEND_NORMAL);
							DrawModiGraph(
								(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f),
								(int)(pos_11.x + (float)tile_size * pos_11.z / 2.f), (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f),
								(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f), (int)(pos_21.y - (float)tile_size * pos_21.z / 2.f),
								(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f), (int)(pos_21.y + (float)tile_size * pos_21.z / 2.f),
								WallBuffer, FALSE);
						}
					}
				};
				//まとめる
				auto DrawAny = [&](int x, int y) {
					DrawWall(x,y);
					DrawFloor(x, y);
				};
				//全てを描画
				auto DrawAll = [&](std::function<void(int, int)> Func) {
					for (int x = 0; x < 16; x++) {
						float x_f = (float)(tile_size * x) - EyePos.x;
						if (x_f > -tile_size / 2) { continue; }
						//左上
						for (int y = 0; y < 16; y++) {
							float y_f = (float)(tile_size * y) - EyePos.y;
							if (y_f > -tile_size / 2) { continue; }
							Func(x, y);
						}
						//左下
						for (int y = 16 - 1; y >= 0; y--) {
							float y_f = (float)(tile_size * y) - EyePos.y;
							if (y_f < tile_size / 2) { continue; }
							Func(x, y);
						}
					}
					for (int x = 16 - 1; x >= 0; x--) {
						float x_f = (float)(tile_size * x) - EyePos.x;
						if (x_f < tile_size / 2) { continue; }
						//右上
						for (int y = 0; y < 16; y++) {
							float y_f = (float)(tile_size * y) - EyePos.y;
							if (y_f > -tile_size / 2) { continue; }
							Func(x, y);
						}
						//右下
						for (int y = 16 - 1; y >= 0; y--) {
							float y_f = (float)(tile_size * y) - EyePos.y;
							if (y_f < tile_size / 2) { continue; }
							Func(x, y);
						}
					}
					//被っているところ
					for (int x = 0; x < 16; x++) {
						float x_f = (float)(tile_size * x) - EyePos.x;
						if (x_f <= -tile_size / 2 || tile_size / 2 <= x_f) { continue; }
						//上
						for (int y = 0; y < 16; y++) {
							float y_f = (float)(tile_size * y) - EyePos.y;
							if (y_f > -tile_size / 2) { continue; }
							Func(x, y);
						}
						//下
						for (int y = 16 - 1; y >= 0; y--) {
							float y_f = (float)(tile_size * y) - EyePos.y;
							if (y_f < tile_size / 2) { continue; }
							Func(x, y);
						}
					}
					for (int y = 0; y < 16; y++) {
						float y_f = (float)(tile_size * y) - EyePos.y;
						if (y_f <= -tile_size / 2 || tile_size / 2 <= y_f) { continue; }
						//上
						for (int x = 0; x < 16; x++) {
							float x_f = (float)(tile_size * x) - EyePos.x;
							if (x_f > 0) { continue; }
							Func(x, y);
						}
						//下
						for (int x = 16 - 1; x >= 0; x--) {
							float x_f = (float)(tile_size * x) - EyePos.x;
							if (x_f < 0) { continue; }
							Func(x, y);
						}
					}
				};

				DrawAll(DrawAny);
```

</details>

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/300f3eec-6f36-bb32-44a3-9849b6a6c6d0.png">

平行影も先ほどのライトに合わせて高さで適当に演算します。それをいったんウィンドウ大のスクリーンバッファに書き、底の床だけにブレンドしましょう。

<details><summary>クリックして展開</summary>

```cpp:test.cpp変更部分

/*初期化部分に追加*/
	int ShadowBuffer = MakeScreen(draw_x, draw_y, TRUE);//影のスクリーンバッファ
/*影バッファの書き込み*/
			SetDrawScreen(ShadowBuffer);
			ClearDrawScreen();
			{
				//影
				auto DrawShadow = [&](int x, int y) {
					auto pos_01 = Get2DPos(x - 1, y);
					auto pos_10 = Get2DPos(x, y - 1);
					auto pos_11 = Get2DPos(x, y);
					auto pos_21 = Get2DPos(x + 1, y);
					auto pos_12 = Get2DPos(x, y + 1);
					{	//左上、右上、左下、右下
						//下
						if (pos_11.z > pos_12.z) {
							if (std::cos(lightrad - 0.f * DX_PI_F / 180.f) > 0.f) {
								float xadd = std::sin(lightrad)*(pos_11.z - pos_12.z)*120.f;
								float yadd = std::cos(lightrad)*(pos_11.z - pos_12.z)*120.f;
								DrawModiGraph(
									(int)(pos_12.x - (float)tile_size * pos_12.z / 2.f + xadd), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f + yadd),
									(int)(pos_12.x + (float)tile_size * pos_12.z / 2.f + xadd), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f + yadd),
									(int)(pos_12.x + (float)tile_size * pos_12.z / 2.f), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f),
									(int)(pos_12.x - (float)tile_size * pos_12.z / 2.f), (int)(pos_12.y - (float)tile_size * pos_12.z / 2.f),
									BlackScreen, FALSE);
							}
						}
						//左
						if (pos_11.z > pos_01.z) {
							if (std::cos(lightrad - 270.f * DX_PI_F / 180.f) > 0.f) {
								float xadd = std::sin(lightrad)*(pos_11.z - pos_01.z)*120.f;
								float yadd = std::cos(lightrad)*(pos_11.z - pos_01.z)*120.f;
								DrawModiGraph(
									(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f + xadd), (int)(pos_01.y - (float)tile_size * pos_01.z / 2.f + yadd),
									(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f + xadd), (int)(pos_01.y + (float)tile_size * pos_01.z / 2.f + yadd),
									(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f), (int)(pos_01.y + (float)tile_size * pos_01.z / 2.f),
									(int)(pos_01.x + (float)tile_size * pos_01.z / 2.f), (int)(pos_01.y - (float)tile_size * pos_01.z / 2.f),
									BlackScreen, FALSE);
							}
						}
						//上
						if (pos_11.z > pos_10.z) {
							if (std::cos(lightrad - 180.f * DX_PI_F / 180.f) > 0.f) {
								float xadd = std::sin(lightrad)*(pos_11.z - pos_10.z)*120.f;
								float yadd = std::cos(lightrad)*(pos_11.z - pos_10.z)*120.f;
								DrawModiGraph(
									(int)(pos_10.x + (float)tile_size * pos_10.z / 2.f + xadd), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f + yadd),
									(int)(pos_10.x - (float)tile_size * pos_10.z / 2.f + xadd), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f + yadd),
									(int)(pos_10.x - (float)tile_size * pos_10.z / 2.f), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f),
									(int)(pos_10.x + (float)tile_size * pos_10.z / 2.f), (int)(pos_10.y + (float)tile_size * pos_10.z / 2.f),
									BlackScreen, FALSE);
							}
						}
						//右
						if (pos_11.z > pos_21.z) {
							if (std::cos(lightrad - 90.f * DX_PI_F / 180.f) > 0.f) {
								float xadd = std::sin(lightrad)*(pos_11.z - pos_21.z)*120.f;
								float yadd = std::cos(lightrad)*(pos_11.z - pos_21.z)*120.f;
								DrawModiGraph(
									(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f + xadd), (int)(pos_21.y + (float)tile_size * pos_21.z / 2.f + yadd),
									(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f + xadd), (int)(pos_21.y - (float)tile_size * pos_21.z / 2.f + yadd),
									(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f), (int)(pos_21.y - (float)tile_size * pos_21.z / 2.f),
									(int)(pos_21.x - (float)tile_size * pos_21.z / 2.f), (int)(pos_21.y + (float)tile_size * pos_21.z / 2.f),
									BlackScreen, FALSE);
							}
						}
					}
				};
				for (int x = 0; x < 16; x++) {
					//左上
					for (int y = 0; y < 16; y++) {
						DrawShadow(x, y);
					}
				}
			}
/*底床にブレンド*/
				//床だけ
				auto DrawFloor=[&](int x, int y) {
					auto pos_11 = Get2DPos(x, y);

					GraphBlendRectBlt2(
						FloorHandles[0],
						ShadowBuffer,
						WallBuffer,
						0, 0,
						tile_pic_size, tile_pic_size,
						std::max(0, (int)(pos_11.x - (float)tile_size * pos_11.z / 2.f)), std::max(0, (int)(pos_11.y - (float)tile_size * pos_11.z / 2.f)),
						std::min(draw_x, (int)(pos_11.x + (float)tile_size * pos_11.z / 2.f)), std::min(draw_y, (int)(pos_11.y + (float)tile_size * pos_11.z / 2.f)),
						0, 0,
						(pos_11.z <= 1.f) ? 128 : 0, DX_GRAPH_BLEND_NORMAL);

					DrawRotaGraph((int)(pos_11.x), (int)(pos_11.y), (double)(tile_size / tile_pic_size * pos_11.z), 0.0, WallBuffer, FALSE);
				};
```

</details>

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/3193faa3-ee08-d810-03d8-d94b06051f8d.png">

平行影ができたらスポットライトはもうすぐ。ライトのある点からの座標で同じように描きます。頂点ごとにライトとのベクトルと比較すると正確です。

<details><summary>クリックして展開</summary>

```cpp:test.cpp変更部分
/*特定のポイントから影を作成*/
				auto DrawPointShadow = [&](int x, int y, float lightx, float lighty) {
					float length = 480.f;
					auto pos_11 = Gettile2DPos(x, y);
					auto Draw = [&](const VECTOR& basepos, const VECTOR& postmp1, const VECTOR& postmp2, const VECTOR& lightpos, float radsub) {
						if (pos_11.z > basepos.z) {
							auto lightvec = VSub(basepos, lightpos);
							float lightrad_t = std::atan2f(lightvec.x, lightvec.y);
							if (std::cos(lightrad_t - radsub) > 0.f) {
								auto lighttmp1 = VSub(postmp1, lightpos);
								float lightrad_t1 = std::atan2f(lighttmp1.x, lighttmp1.y);
								auto lighttmp2 = VSub(postmp2, lightpos);
								float lightrad_t2 = std::atan2f(lighttmp2.x, lighttmp2.y);
								DrawModiGraph(
									(int)(postmp1.x + std::sin(lightrad_t1)*(pos_11.z - basepos.z)*length), (int)(postmp1.y + std::cos(lightrad_t1)*(pos_11.z - basepos.z)*length),
									(int)(postmp2.x + std::sin(lightrad_t2)*(pos_11.z - basepos.z)*length), (int)(postmp2.y + std::cos(lightrad_t2)*(pos_11.z - basepos.z)*length),
									(int)(postmp2.x), (int)(postmp2.y),
									(int)(postmp1.x), (int)(postmp1.y),
									BlackScreen, FALSE);
							}
						}
					};
					//下
					{
						auto basepos = Gettile2DPos(x, y + 1);
						auto postmp1 = VGet(basepos.x - (float)tile_size * basepos.z / 2.f, basepos.y - (float)tile_size * basepos.z / 2.f, 0.f);
						auto postmp2 = VGet(basepos.x + (float)tile_size * basepos.z / 2.f, basepos.y - (float)tile_size * basepos.z / 2.f, 0.f);
						Draw(basepos, postmp1, postmp2, Get2DPos(lightx, lighty, x, y + 1), 0.f * DX_PI_F / 180.f);
					}
					//左
					{
						auto basepos = Gettile2DPos(x - 1, y);
						auto postmp1 = VGet(basepos.x + (float)tile_size * basepos.z / 2.f, basepos.y - (float)tile_size * basepos.z / 2.f, 0.f);
						auto postmp2 = VGet(basepos.x + (float)tile_size * basepos.z / 2.f, basepos.y + (float)tile_size * basepos.z / 2.f, 0.f);
						Draw(basepos, postmp1, postmp2, Get2DPos(lightx, lighty, x - 1, y), 270.f * DX_PI_F / 180.f);
					}
					//上
					{
						auto basepos = Gettile2DPos(x, y - 1);
						auto postmp1 = VGet(basepos.x + (float)tile_size * basepos.z / 2.f, basepos.y + (float)tile_size * basepos.z / 2.f, 0.f);
						auto postmp2 = VGet(basepos.x - (float)tile_size * basepos.z / 2.f, basepos.y + (float)tile_size * basepos.z / 2.f, 0.f);
						Draw(basepos, postmp1, postmp2, Get2DPos(lightx, lighty, x, y - 1), 180.f * DX_PI_F / 180.f);
					}
					//右
					{
						auto basepos = Gettile2DPos(x + 1, y);
						auto postmp1 = VGet(basepos.x - (float)tile_size * basepos.z / 2.f, basepos.y + (float)tile_size * basepos.z / 2.f, 0.f);
						auto postmp2 = VGet(basepos.x - (float)tile_size * basepos.z / 2.f, basepos.y - (float)tile_size * basepos.z / 2.f, 0.f);
						Draw(basepos, postmp1, postmp2, Get2DPos(lightx, lighty, x + 1, y), 90.f * DX_PI_F / 180.f);
					}
				};

				for (int x = 0; x < 16; x++) {
					for (int y = 0; y < 16; y++) {
						DrawPointShadow(x, y, 6.5f,4.f);
					}
				}

```

</details>

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/ff4ab428-b21e-93ae-4762-1cfd31076d58.png">

最後は床に反射する壁です。壁描画を反対にするだけなので最後の完成物から確認していただければと思います

<img width="320" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1581549/de840dd4-23a5-daf0-277c-3a74afb267e9.png">

そして各種をちょちょっと調整してできたのがこちら


<details><summary>完成品です</summary>

```cpp:test.cpp
#define NOMINMAX

#include "DxLib.h"

#include <vector>
#include <memory>
#include <algorithm>
#include <functional>

static const int draw_x = 960;			//ウィンドウサイズX
static const int draw_y = 720;			//ウィンドウサイズY

static const int tile_size = 64;		//表示上のタイルサイズ
static const int tile_pic_size = 32;	//画像のタイルサイズ
//ベクトルのxy間の角度を取得
static const auto GetRadVec2Vec(const VECTOR& vec1, const VECTOR& vec2) { return std::atan2f(vec1.x - vec2.x, vec1.y - vec2.y); }
//角度をラジアンに変換
static const auto Deg2Rad(float value) { return value * DX_PI_F / 180.f; }

//いろいろまとめるクラス
class DXDraw {
public:
	//コンストラクタ
	DXDraw(void) noexcept {
		SetOutApplicationLogValidFlag(FALSE);           /*log*/
		SetMainWindowText("game title");                /*タイトル*/
		ChangeWindowMode(TRUE);                         /*窓表示*/
		SetUseDirect3DVersion(DX_DIRECT3D_11);          /*directX ver*/
		SetGraphMode(draw_x, draw_y, 32);               /*解像度*/
		SetUseDirectInputFlag(TRUE);                    /*DirectInput使用*/
		SetDirectInputMouseMode(TRUE);                  /*DirectInputマウス使用*/
		SetWindowSizeChangeEnableFlag(FALSE, FALSE);    /*ウインドウサイズを手動変更不可、ウインドウサイズに合わせて拡大もしないようにする*/
		SetUsePixelLighting(TRUE);                      /*ピクセルライティングの使用*/
		SetFullSceneAntiAliasingMode(4, 2);             /*アンチエイリアス*/
		SetWaitVSyncFlag(TRUE);                         /*垂直同期*/
		DxLib_Init();                                   /*ＤＸライブラリ初期化処理*/
		SetSysCommandOffFlag(TRUE);                     /*タスクスイッチを有効にするかどうかを設定する*/
		SetAlwaysRunFlag(TRUE);                         /*background*/
	}
	//デストラクタ
	~DXDraw(void) noexcept {
		DxLib_End();
	}
};

//タイルデータ
struct TileData {
	int Height{ 0 };
	int WallID{ 0 };
	int FloorID{ 0 };
};

//描画クラス
class DrawControl {
private:
	VECTOR					m_EyePos;

	int						ShadowBuffer{ -1 };				//影のスクリーンバッファ
	int						MirrorBuffer{ -1 };				//地面反射のスクリーンバッファ
	int						LastDrawBuffer{ -1 };			//最終描画結果を描画するスクリーンバッファ

	int						m_BlackScreen{ -1 };
	int						m_WallBuffer{ -1 };

	int*					m_FloorHandlePtr{ nullptr };
	int*					m_WallHandlePtr{ nullptr };

	float					m_AmbientLightRad{ 0 };
	std::vector<VECTOR>		m_PointLightPos;

	std::vector<TileData>	m_TileData;
	int						m_MapXsize{ 0 };
	int						m_MapYsize{ 0 };
private:
	//タイルごとのデータを取得
	const auto GetTileData(int x, int y) const noexcept { return m_TileData.at(std::clamp(x, 0, m_MapXsize - 1) * m_MapYsize + std::clamp(y, 0, m_MapYsize - 1)); }
	//座標変換
	const auto Get2DPos(float x, float y, float z) const noexcept {
		float tile_scale = m_EyePos.z*(float)tile_size / (m_EyePos.z * (float)tile_size - z) * (float)tile_size;//タイルの表示倍率
		return VGet((float)(draw_x / 2) + (x - m_EyePos.x) * tile_scale, (float)(draw_y / 2) + (y - m_EyePos.y) * tile_scale, tile_scale);
	}
	//タイルの最小最大座標を取得
	const auto GetTileMin(const VECTOR& value) const noexcept { return VGet(value.x - value.z / 2.f, value.y - value.z / 2.f, 0.f); }
	const auto GetTileMax(const VECTOR& value) const noexcept { return VGet(value.x + value.z / 2.f, value.y + value.z / 2.f, 0.f); }
	//タイルごとの座標
	const auto Gettile2DPos(int x, int y, bool ismnus) const noexcept { return Get2DPos((float)x, (float)y, (float)(GetTileData(x, y).Height * (ismnus ? -1 : 1))); }
	//環境影を描画
	void DrawAmbientShadow(int x, int y) const noexcept {
		auto Draw = [&](const VECTOR& sidePos, const VECTOR& postmp1, const VECTOR& postmp2, float radsub) {
			auto centerZ = (Gettile2DPos(x, y, false).z - sidePos.z) / (float)tile_size;
			if (centerZ > 0 && std::cos(m_AmbientLightRad - radsub) > 0.f) {
				auto length = 120.f * centerZ;
				DrawModiGraph(
					(int)(postmp1.x + std::sin(m_AmbientLightRad)*length), (int)(postmp1.y + std::cos(m_AmbientLightRad)*length),
					(int)(postmp2.x + std::sin(m_AmbientLightRad)*length), (int)(postmp2.y + std::cos(m_AmbientLightRad)*length),
					(int)postmp2.x, (int)postmp2.y,
					(int)postmp1.x, (int)postmp1.y,
					m_BlackScreen, FALSE);
			}
		};
		//下
		{
			auto sidePos = Gettile2DPos(x, y + 1, false);
			Draw(sidePos, VGet(GetTileMin(sidePos).x, GetTileMin(sidePos).y, 0.f), VGet(GetTileMax(sidePos).x, GetTileMin(sidePos).y, 0.f), Deg2Rad(0.f));
		}
		//左
		{
			auto sidePos = Gettile2DPos(x - 1, y, false);
			Draw(sidePos, VGet(GetTileMax(sidePos).x, GetTileMin(sidePos).y, 0.f), VGet(GetTileMax(sidePos).x, GetTileMax(sidePos).y, 0.f), Deg2Rad(270.f));
		}
		//上
		{
			auto sidePos = Gettile2DPos(x, y - 1, false);
			Draw(sidePos, VGet(GetTileMax(sidePos).x, GetTileMax(sidePos).y, 0.f), VGet(GetTileMin(sidePos).x, GetTileMax(sidePos).y, 0.f), Deg2Rad(180.f));
		}
		//右
		{
			auto sidePos = Gettile2DPos(x + 1, y, false);
			Draw(sidePos, VGet(GetTileMin(sidePos).x, GetTileMax(sidePos).y, 0.f), VGet(GetTileMin(sidePos).x, GetTileMin(sidePos).y, 0.f), Deg2Rad(90.f));
		}
	}
	//ポイント影を描画
	void DrawPointShadow(int x, int y, const VECTOR& light2D) const noexcept {
		auto Draw = [&](const VECTOR& sidePos, const VECTOR& postmp1, const VECTOR& postmp2, const VECTOR& lightpos, float radsub) {
			auto centerZ = (Gettile2DPos(x, y, false).z - sidePos.z) / (float)tile_size;
			if (centerZ > 0 && std::cos(GetRadVec2Vec(sidePos, lightpos) - radsub) > 0.f) {
				auto length = 480.f * centerZ;
				float lightrad1 = GetRadVec2Vec(postmp1, lightpos);
				float lightrad2 = GetRadVec2Vec(postmp2, lightpos);
				DrawModiGraph(
					(int)(postmp1.x + std::sin(lightrad1)*length), (int)(postmp1.y + std::cos(lightrad1)*length),
					(int)(postmp2.x + std::sin(lightrad2)*length), (int)(postmp2.y + std::cos(lightrad2)*length),
					(int)postmp2.x, (int)postmp2.y,
					(int)postmp1.x, (int)postmp1.y,
					m_BlackScreen, FALSE);
			}
		};
		//下
		{
			auto sidePos = Gettile2DPos(x, y + 1, false);
			Draw(sidePos, VGet(GetTileMin(sidePos).x, GetTileMin(sidePos).y, 0.f), VGet(GetTileMax(sidePos).x, GetTileMin(sidePos).y, 0.f), Get2DPos(light2D.x, light2D.y, (float)GetTileData(x, y + 1).Height), Deg2Rad(0.f));
		}
		//左
		{
			auto sidePos = Gettile2DPos(x - 1, y, false);
			Draw(sidePos, VGet(GetTileMax(sidePos).x, GetTileMin(sidePos).y, 0.f), VGet(GetTileMax(sidePos).x, GetTileMax(sidePos).y, 0.f), Get2DPos(light2D.x, light2D.y, (float)GetTileData(x - 1, y).Height), Deg2Rad(270.f));
		}
		//上
		{
			auto sidePos = Gettile2DPos(x, y - 1, false);
			Draw(sidePos, VGet(GetTileMax(sidePos).x, GetTileMax(sidePos).y, 0.f), VGet(GetTileMin(sidePos).x, GetTileMax(sidePos).y, 0.f), Get2DPos(light2D.x, light2D.y, (float)GetTileData(x, y - 1).Height), Deg2Rad(180.f));
		}
		//右
		{
			auto sidePos = Gettile2DPos(x + 1, y, false);
			Draw(sidePos, VGet(GetTileMin(sidePos).x, GetTileMax(sidePos).y, 0.f), VGet(GetTileMin(sidePos).x, GetTileMin(sidePos).y, 0.f), Get2DPos(light2D.x, light2D.y, (float)GetTileData(x + 1, y).Height), Deg2Rad(90.f));
		}
	}
	//床を描画
	void DrawFloor(int x, int y) const noexcept {
		auto centerPos = Gettile2DPos(x, y, false);
		//鏡面をブレンド
		GraphBlendRectBlt2(
			m_FloorHandlePtr[GetTileData(x, y).FloorID], MirrorBuffer, m_WallBuffer,
			0, 0,
			tile_pic_size, tile_pic_size,
			std::max(0, (int)GetTileMin(centerPos).x), std::max(0, (int)GetTileMin(centerPos).y),
			std::min(draw_x, (int)GetTileMax(centerPos).x), std::min(draw_y, (int)GetTileMax(centerPos).y),
			0, 0,
			(centerPos.z <= (float)tile_size) ? 128 : 0, DX_GRAPH_BLEND_NORMAL);
		//影をブレンド
		GraphBlendRectBlt2(
			m_WallBuffer, ShadowBuffer, m_WallBuffer,
			0, 0,
			tile_pic_size, tile_pic_size,
			std::max(0, (int)GetTileMin(centerPos).x), std::max(0, (int)GetTileMin(centerPos).y),
			std::min(draw_x, (int)GetTileMax(centerPos).x), std::min(draw_y, (int)GetTileMax(centerPos).y),
			0, 0,
			(centerPos.z <= (float)tile_size) ? 128 : 0, DX_GRAPH_BLEND_NORMAL);
		//最終描画
		DrawRotaGraph((int)centerPos.x, (int)centerPos.y, (double)(centerPos.z / tile_pic_size), 0.0, m_WallBuffer, FALSE);
	}
	//壁を描画
	void DrawWall(int x, int y, bool ismnus) const noexcept {
		auto centerPos_P = Gettile2DPos(x, y, false);		//マイナスを考慮しない高さを取得
		auto centerPos = Gettile2DPos(x, y, ismnus);		//マイナスを考慮する高さを取得
		//下
		{
			auto radsub = Deg2Rad(0.f);
			auto sidePos = (!ismnus) ? Gettile2DPos(x, y + 1, false) : Get2DPos(x, y + 1, 0);
			if ((GetTileData(x, y).Height > GetTileData(x, y + 1).Height) && (y + 1 < m_MapYsize) && (GetTileMax(centerPos_P).y < GetTileMin(sidePos).y)) {
				GraphBlendBlt(m_WallHandlePtr[GetTileData(x, y).WallID], m_BlackScreen, m_WallBuffer, 128 + (int)(127.f*std::cos(m_AmbientLightRad - radsub)), DX_GRAPH_BLEND_NORMAL);
				DrawModiGraph(
					(int)GetTileMin(centerPos).x, (int)GetTileMax(centerPos).y,
					(int)GetTileMax(centerPos).x, (int)GetTileMax(centerPos).y,
					(int)GetTileMax(sidePos).x, (int)GetTileMin(sidePos).y,
					(int)GetTileMin(sidePos).x, (int)GetTileMin(sidePos).y,
					m_WallBuffer, FALSE);
			}
		}
		//左
		{
			auto radsub = Deg2Rad(270.f);
			auto sidePos = (!ismnus) ? Gettile2DPos(x - 1, y, false) : Get2DPos(x - 1, y, 0);
			if ((GetTileData(x, y).Height > GetTileData(x - 1, y).Height) && (x - 1 >= 0) && (GetTileMin(centerPos_P).x > GetTileMax(sidePos).x)) {
				GraphBlendBlt(m_WallHandlePtr[GetTileData(x, y).WallID], m_BlackScreen, m_WallBuffer, 128 + (int)(127.f*std::cos(m_AmbientLightRad - radsub)), DX_GRAPH_BLEND_NORMAL);
				DrawModiGraph(
					(int)GetTileMin(centerPos).x, (int)GetTileMin(centerPos).y,
					(int)GetTileMin(centerPos).x, (int)GetTileMax(centerPos).y,
					(int)GetTileMax(sidePos).x, (int)GetTileMax(sidePos).y,
					(int)GetTileMax(sidePos).x, (int)GetTileMin(sidePos).y,
					m_WallBuffer, FALSE);
			}
		}
		//上
		{
			auto radsub = Deg2Rad(180.f);
			auto sidePos = (!ismnus) ? Gettile2DPos(x, y - 1, false) : Get2DPos(x, y - 1, 0);
			if ((GetTileData(x, y).Height > GetTileData(x, y - 1).Height) && (y - 1 >= 0) && (GetTileMin(centerPos_P).y > GetTileMax(sidePos).y)) {
				GraphBlendBlt(m_WallHandlePtr[GetTileData(x, y).WallID], m_BlackScreen, m_WallBuffer, 128 + (int)(127.f*std::cos(m_AmbientLightRad - radsub)), DX_GRAPH_BLEND_NORMAL);
				DrawModiGraph(
					(int)GetTileMax(centerPos).x, (int)GetTileMin(centerPos).y,
					(int)GetTileMin(centerPos).x, (int)GetTileMin(centerPos).y,
					(int)GetTileMin(sidePos).x, (int)GetTileMax(sidePos).y,
					(int)GetTileMax(sidePos).x, (int)GetTileMax(sidePos).y,
					m_WallBuffer, FALSE);
			}
		}
		//右
		{
			auto radsub = Deg2Rad(90.f);
			auto sidePos = (!ismnus) ? Gettile2DPos(x + 1, y, false) : Get2DPos(x + 1, y, 0);
			if ((GetTileData(x, y).Height > GetTileData(x + 1, y).Height) && (x + 1 < m_MapXsize) && (GetTileMax(centerPos_P).x < GetTileMin(sidePos).x)) {
				GraphBlendBlt(m_WallHandlePtr[GetTileData(x, y).WallID], m_BlackScreen, m_WallBuffer, 128 + (int)(127.f*std::cos(m_AmbientLightRad - radsub)), DX_GRAPH_BLEND_NORMAL);
				DrawModiGraph(
					(int)GetTileMax(centerPos).x, (int)GetTileMax(centerPos).y,
					(int)GetTileMax(centerPos).x, (int)GetTileMin(centerPos).y,
					(int)GetTileMin(sidePos).x, (int)GetTileMin(sidePos).y,
					(int)GetTileMin(sidePos).x, (int)GetTileMax(sidePos).y,
					m_WallBuffer, FALSE);
			}
		}
	}
	//描画順を考慮しつつ描画
	void DrawAll(std::function<void(int, int)> Func) const noexcept {
		for (int x = 0; x < m_MapXsize; x++) {
			if ((float)x - m_EyePos.x > -1.f / 2.f) { continue; }
			//左上
			for (int y = 0; y < m_MapYsize; y++) {
				if ((float)y - m_EyePos.y > -1.f / 2.f) { continue; }
				Func(x, y);
			}
			//左下
			for (int y = m_MapYsize - 1; y >= 0; y--) {
				if ((float)y - m_EyePos.y < 1.f / 2.f) { continue; }
				Func(x, y);
			}
		}
		for (int x = m_MapXsize - 1; x >= 0; x--) {
			if ((float)x - m_EyePos.x < 1.f / 2.f) { continue; }
			//右上
			for (int y = 0; y < m_MapYsize; y++) {
				if ((float)y - m_EyePos.y > -1.f / 2.f) { continue; }
				Func(x, y);
			}
			//右下
			for (int y = m_MapYsize - 1; y >= 0; y--) {
				if ((float)y - m_EyePos.y < 1.f / 2.f) { continue; }
				Func(x, y);
			}
		}
		//被っているところ
		for (int x = 0; x < m_MapXsize; x++) {
			float x_f = (float)x - m_EyePos.x;
			if (x_f <= -1.f / 2.f || 1.f / 2.f <= x_f) { continue; }
			//上
			for (int y = 0; y < m_MapYsize; y++) {
				if ((float)y - m_EyePos.y > -1.f / 2.f) { continue; }
				Func(x, y);
			}
			//下
			for (int y = m_MapYsize - 1; y >= 0; y--) {
				if ((float)y - m_EyePos.y < 1.f / 2.f) { continue; }
				Func(x, y);
			}
		}
		for (int y = 0; y < m_MapYsize; y++) {
			float y_f = (float)y - m_EyePos.y;
			if (y_f <= -1.f / 2.f || 1.f / 2.f <= y_f) { continue; }
			//上
			for (int x = 0; x < m_MapXsize; x++) {
				if ((float)x - m_EyePos.x > 0) { continue; }
				Func(x, y);
			}
			//下
			for (int x = m_MapXsize - 1; x >= 0; x--) {
				if ((float)x - m_EyePos.x < 0) { continue; }
				Func(x, y);
			}
		}
	}
	//画像ハンドルをリセット
	void InitGraphHandle(void) noexcept {
		if (m_FloorHandlePtr) {
			DeleteSharingGraph(m_FloorHandlePtr[0]);
			delete[] m_FloorHandlePtr;
			m_FloorHandlePtr = nullptr;
		}
		if (m_WallHandlePtr) {
			DeleteSharingGraph(m_WallHandlePtr[0]);
			delete[] m_WallHandlePtr;
			m_WallHandlePtr = nullptr;
		}
	}
public://ゲッター
	//環境ライトの角度指定
	void SetAmbientLightRad(float rad) noexcept { m_AmbientLightRad = rad; }
	//カメラの座標指定
	void SetCameraPos(const VECTOR& EyePos) noexcept { m_EyePos = VScale(EyePos, 1.f / (float)tile_size); }
	//カメラの座標取得
	const auto& GetCameraPos(void) const noexcept { return VScale(m_EyePos, (float)tile_size); }
	//ポイントライトの追加
	const auto AddPointLight(const VECTOR& LightPos) noexcept {
		m_PointLightPos.emplace_back(LightPos);
		return m_PointLightPos.size() - 1;
	}
	//該当IDのポイントライト座標を指定
	void SetPointLight(size_t ID, const VECTOR& LightPos) noexcept { m_PointLightPos.at(ID) = LightPos; }
	//該当IDのポイントライト座標を取得
	const auto& GetPointLight(size_t ID) const noexcept { return m_PointLightPos.at(ID); }
	//画像ハンドルを設定
	void SetPicture(const char* floorTexName, const char* WallTexName) noexcept {
		InitGraphHandle();
		m_FloorHandlePtr = new int[1];
		m_WallHandlePtr = new int[1];
		LoadDivGraph(floorTexName, 1, 1, 1, tile_pic_size, tile_pic_size, m_FloorHandlePtr);//床テクスチャ
		LoadDivGraph(WallTexName, 1, 1, 1, tile_pic_size, tile_pic_size, m_WallHandlePtr);//壁テクスチャ
	}
	//マップデータのサイズを設定
	void SetMapSize(int x, int y) noexcept {
		m_MapXsize = x;
		m_MapYsize = y;
		m_TileData.resize(m_MapXsize*m_MapYsize);
	}
	//タイルごとのデータポインタを取得
	auto* SetTileData(int x, int y) noexcept { return &m_TileData.at(std::clamp(x, 0, m_MapXsize - 1) * m_MapYsize + std::clamp(y, 0, m_MapYsize - 1)); }
public:
	//コンストラクタ
	DrawControl(void) noexcept {
		m_BlackScreen = MakeScreen(tile_pic_size, tile_pic_size);
		m_WallBuffer = MakeScreen(tile_pic_size, tile_pic_size);
		SetDrawScreen(m_BlackScreen);
		{
			DrawBox(0, 0, tile_pic_size, tile_pic_size, GetColor(0, 0, 0), TRUE);
		}
		ShadowBuffer = MakeScreen(draw_x, draw_y, TRUE);
		MirrorBuffer = MakeScreen(draw_x, draw_y, TRUE);
		LastDrawBuffer = MakeScreen(draw_x, draw_y, TRUE);
	}
	//デストラクタ
	~DrawControl(void) noexcept {
		//画像ハンドルの削除
		DeleteGraph(ShadowBuffer);
		DeleteGraph(MirrorBuffer);
		DeleteGraph(LastDrawBuffer);
		DeleteGraph(m_BlackScreen);
		DeleteGraph(m_WallBuffer);
		//タイルデータのクリア
		m_TileData.clear();
		//画像ハンドルの消去
		InitGraphHandle();
	}
	//更新
	void Execute(void) noexcept {
		//ミラーバッファに描画
		SetDrawScreen(MirrorBuffer);
		ClearDrawScreen();
		{
			DrawAll([&](int x, int y) {
				DrawWall(x, y, true);						//壁を反対向きの高さで描画
			});
		}
		//シャドウバッファに描画
		SetDrawScreen(ShadowBuffer);
		ClearDrawScreen();
		{
			SetDrawBlendMode(DX_BLENDMODE_ALPHA, 192);
			for (int x = 0; x < m_MapXsize; x++) {
				for (int y = 0; y < m_MapYsize; y++) {
					DrawAmbientShadow(x, y);				//環境影
					for (auto& l : m_PointLightPos) {
						DrawPointShadow(x, y, l);		//ポイント影
					}
				}
			}
			SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 255);
		}
		//最終描画
		SetDrawScreen(LastDrawBuffer);
		ClearDrawScreen();
		{
			DrawAll([&](int x, int y) {
				DrawWall(x, y, false);						//壁
				DrawFloor(x, y);							//床
			});
			//ポイントライトのデバッグ描画
			for (auto& l : m_PointLightPos) {
				auto light_t = Get2DPos(l.x, l.y, (float)GetTileData((int)l.x, (int)l.y).Height);
				DrawCircle((int)light_t.x, (int)light_t.y, 16, GetColor(255, 255, 0));
			}
		}
	}
	//描画
	void Draw(void) const noexcept {
		DrawGraph(0, 0, LastDrawBuffer, TRUE);
	}
};

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
	auto maincontrol = std::make_unique<DXDraw>();
	auto drawcontrol = std::make_unique<DrawControl>();
	//設定
	drawcontrol->SetAmbientLightRad(45.f * DX_PI_F / 180.f);	//環境光の角度指定
	drawcontrol->SetCameraPos(VGet(0.f, 0.f, 12.f));			//視点の場所(x,y,高さ)
	auto PointLightID = drawcontrol->AddPointLight(VGet(6.5f, 4.f, 0.f));	//ポイントライトの追加
	//マップデータ作成
	int HeightData[16][16] = {
		8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,3,0,0,1,0,0,0,0,0,8,
		8,0,0,0,0,0,3,0,2,0,0,0,0,0,0,8,
		8,0,0,0,0,0,4,3,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,4,4,3,3,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,
		8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
	};//高さデータ
	drawcontrol->SetPicture("Tex3.bmp", "KabeTex.bmp");
	drawcontrol->SetMapSize(16, 16);
	for (int x = 0; x < 16; x++) {
		for (int y = 0; y < 16; y++) {
			drawcontrol->SetTileData(x, y)->Height = HeightData[16 - 1 - x][16 - 1 - y];
			drawcontrol->SetTileData(x, y)->FloorID = 0;
			drawcontrol->SetTileData(x, y)->WallID = 0;
		}
	}
	//メインループ開始
	while (ProcessMessage() == 0) {
		LONGLONG WaitTime = GetNowHiPerformanceCount();//現時点の経過秒数を得る
		//カメラの位置を設定
		{
			VECTOR EyePos = drawcontrol->GetCameraPos();
			float speed = 5.f;
			if (CheckHitKey(KEY_INPUT_UP) != 0) {
				EyePos.y += -speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_DOWN) != 0) {
				EyePos.y += speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_LEFT) != 0) {
				EyePos.x += -speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_RIGHT) != 0) {
				EyePos.x += speed * 60.f / GetFPS();
			}
			drawcontrol->SetCameraPos(EyePos);
		}
		//ポイントライト場所の更新
		{
			VECTOR LightPos = drawcontrol->GetPointLight(PointLightID);
			float speed = 0.1f;
			if (CheckHitKey(KEY_INPUT_W) != 0) {
				LightPos.y += -speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_S) != 0) {
				LightPos.y += speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_A) != 0) {
				LightPos.x += -speed * 60.f / GetFPS();
			}
			if (CheckHitKey(KEY_INPUT_D) != 0) {
				LightPos.x += speed * 60.f / GetFPS();
			}
			drawcontrol->SetPointLight(PointLightID, LightPos);
		}
		//
		drawcontrol->Execute();
		//描画
		SetDrawScreen(DX_SCREEN_BACK);
		ClearDrawScreen();
		{
			drawcontrol->Draw();
			//デバッグ表示
			clsDx();
			printfDx("FPS:%4.1f\n", GetFPS());
			printfDx("処理時間:%5.2fms\n", (float)(GetNowHiPerformanceCount() - WaitTime) / 1000.f);
		}
		ScreenFlip();
	}
	return 0;// ソフトの終了 
}
```

</details>

**おわり**

こんな感じでほぼほぼサンプルプロジェクトそのまま動かせる疑似3D表現ができました。ゲーム制作におけるまた違った表現方法として取り入れてみるのはいかがでしょうか？