边界表示法建模
立方体-顶表
立方体-面表
变换矩阵
正交投影
算法设计
代码
// 22-三维几何变换算法
// 参考 https://www.bilibili.com/video/BV1Pd4y127iq/
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"22-三维几何变换算法"
#define ROUND(d) int(floor(d)+0.5) // 四舍五入
#define PI 3.1415926
struct Point2 // 二维点
{
double x;
double y;
double w; // 齐次坐标
Point2() :x(0), y(0),w(0) {}
Point2(double x, double y) :x(x), y(y), w(0) {}
};
struct Point3 // 三维点
{
double x;
double y;
double z;
double w; // 齐次坐标
Point3() :x(0), y(0), z(0), w(0) {}
Point3(double x, double y ,double z) :x(x), y(y), z(z), w(0) {}
};
struct Face // 面
{
int ptIndex[4] = {0,0,0,0}; // 面顶点索引
Face() {};
~Face() {};
};
class Transform3
{
public:
Point3* _P; // 顶点数组
int _ptNumber; // 顶点数量
double _M[4][4]; // 变换矩阵
public:
Transform3():_P(NULL), _ptNumber(0)
{
memset(_M,0,sizeof(_M));
};
~Transform3() {};
void MultiplyMatrix() //矩阵相乘
{
Point3* PTemp = new Point3[_ptNumber];
for (int i = 0; i < _ptNumber; i++)
{
PTemp[i] = _P[i];
}
for (int i = 0; i < _ptNumber; i++)
{
_P[i].x = _M[0][0] * PTemp[i].x + _M[0][1] * PTemp[i].y + _M[0][2] * PTemp[i].z + _M[3][0]; // * PTemp[i].w
_P[i].y = _M[1][0] * PTemp[i].x + _M[1][1] * PTemp[i].y + _M[1][2] * PTemp[i].z + _M[3][1]; // * PTemp[i].w
_P[i].z = _M[2][0] * PTemp[i].x + _M[2][1] * PTemp[i].y + _M[2][2] * PTemp[i].z + _M[3][2]; // * PTemp[i].w
_P[i].w = _M[3][0] * PTemp[i].x + _M[3][1] * PTemp[i].y + _M[3][2] * PTemp[i].z + _M[3][3]; // * PTemp[i].w
}
delete[]PTemp;
}
void SetMatrix(Point3* P, int ptNumber) //顶点数组初始化
{
_P = P;
_ptNumber = ptNumber;
}
void Identity()
{
_M[0][0] = 1.0, _M[0][1] = 0.0, _M[0][2] = 0.0, _M[0][3] = 0.0;
_M[1][0] = 0.0, _M[1][1] = 1.0, _M[1][2] = 0.0, _M[1][3] = 0.0;
_M[2][0] = 0.0, _M[2][1] = 0.0, _M[2][2] = 1.0, _M[2][3] = 0.0;
_M[3][0] = 0.0, _M[3][1] = 0.0, _M[3][2] = 0.0, _M[3][3] = 1.0;
}
void Translate(double tx, double ty, double tz)//平移变换
{
Identity();
_M[3][0] = tx;
_M[3][1] = ty;
_M[3][2] = tz;
MultiplyMatrix();
}
void Scale(double sx, double sy, double sz)//缩放变换
{
Identity();
_M[0][0] = sx;
_M[1][1] = sy;
_M[2][2] = sz;
MultiplyMatrix();
}
void Scale(double sx, double sy, double sz, Point3 p)//相对于任意点的缩放变换
{
Translate(-p.x, -p.y, -p.z);
Scale(sx, sy, sz);
Translate(p.x, p.y, p.z);
}
void Scale(double s)//整体缩放变换
{
Identity();
_M[0][0] = s, _M[1][1] = s, _M[2][2] = s;
MultiplyMatrix();
}
void Scale(double s, Point3 p)//相对于任意点的整体缩放变换
{
Translate(-p.x, -p.y, -p.z);
Scale(s);
Translate(p.x, p.y, p.z);
}
void RotateX(double beta)//绕X轴旋转变换
{
Identity();
beta = beta * PI / 180;
_M[1][1] = cos(beta), _M[1][2] = -sin(beta);
_M[2][1] = sin(beta), _M[2][2] = cos(beta);
MultiplyMatrix();
}
void RotateY(double beta)//绕Y轴旋转变换
{
Identity();
beta = beta * PI / 180;
_M[0][0] = cos(beta), _M[0][2] = sin(beta);
_M[2][0] = -sin(beta), _M[2][2] = cos(beta);
MultiplyMatrix();
}
void RotateZ(double beta)//绕Z轴旋转变换
{
Identity();
beta = beta * PI / 180;
_M[0][0] = cos(beta), _M[0][1] = -sin(beta);
_M[1][0] = sin(beta), _M[1][1] = cos(beta);
MultiplyMatrix();
}
void RotateX(double beta, Point3 p)//相对于任意点的X轴旋转变换
{
Translate(-p.x, -p.y, -p.z);
RotateX(beta);
Translate(p.x, p.y, p.z);
}
void RotateY(double beta, Point3 p)//相对于任意点的Y轴旋转变换
{
Translate(-p.x, -p.y, -p.z);
RotateY(beta);
Translate(p.x, p.y, p.z);
}
void RotateZ(double beta, Point3 p)//相对于任意点的Z轴旋转变换
{
Translate(-p.x, -p.y, -p.z);
RotateZ(beta);
Translate(p.x, p.y, p.z);
}
void ReflectX(void)//X轴反射变换
{
Identity();
_M[1][1] = -1, _M[2][2] = -1;
MultiplyMatrix();
}
void ReflectY(void)//Y轴反射变换
{
Identity();
_M[0][0] = -1, _M[2][2] = -1;
MultiplyMatrix();
}
void ReflectZ(void)//Z轴反射变换
{
Identity();
_M[0][0] = -1, _M[1][1] = -1;
MultiplyMatrix();
}
void ReflectXOY(void)//XOY面反射变换
{
Identity();
_M[2][2] = -1;
MultiplyMatrix();
}
void ReflectYOZ(void)//YOZ面反射变换
{
Identity();
_M[0][0] = -1;
MultiplyMatrix();
}
void ReflectZOX(void)//XOZ面反射变换
{
Identity();
_M[1][1] = -1;
MultiplyMatrix();
}
void ShearX(double b, double c)//X方向错切变换
{
Identity();
_M[0][1] = b, _M[0][2] = c;
MultiplyMatrix();
}
void ShearY(double d, double f)//Y方向错切变换
{
Identity();
_M[1][0] = d, _M[1][2] = f;
MultiplyMatrix();
}
void ShearZ(double g, double h)//Z方向错切变换
{
Identity();
_M[2][0] = g; _M[2][1] = h;
MultiplyMatrix();
}
};
class Cube // 立方体
{
public:
Point3 _V[8]; // 点表
Face _F[6]; // 面表
public:
Cube() {};
~Cube() {};
void ReadVertex() // 读取点表
{
_V[0].x = 0; _V[0].y = 0; _V[0].z = 0;
_V[1].x = 1; _V[1].y = 0; _V[1].z = 0;
_V[2].x = 1; _V[2].y = 1; _V[2].z = 0;
_V[3].x = 0; _V[3].y = 1; _V[3].z = 0;
_V[4].x = 0; _V[4].y = 0; _V[4].z = 1;
_V[5].x = 1; _V[5].y = 0; _V[5].z = 1;
_V[6].x = 1; _V[6].y = 1; _V[6].z = 1;
_V[7].x = 0; _V[7].y = 1; _V[7].z = 1;
}
void ReadFace() // 读取面表
{
_F[0].ptIndex[0] = 0; _F[0].ptIndex[1] = 4; _F[0].ptIndex[2] = 7;_F[0].ptIndex[3] = 3; // 左
_F[1].ptIndex[0] = 1; _F[1].ptIndex[1] = 2; _F[1].ptIndex[2] = 6;_F[1].ptIndex[3] = 5; // 右
_F[2].ptIndex[0] = 0; _F[2].ptIndex[1] = 1; _F[2].ptIndex[2] = 5;_F[2].ptIndex[3] = 4; // 底
_F[3].ptIndex[0] = 2; _F[3].ptIndex[1] = 3; _F[3].ptIndex[2] = 7;_F[3].ptIndex[3] = 6; // 顶
_F[4].ptIndex[0] = 0; _F[4].ptIndex[1] = 3; _F[4].ptIndex[2] = 2;_F[4].ptIndex[3] = 1; // 后
_F[5].ptIndex[0] = 4; _F[5].ptIndex[1] = 5; _F[5].ptIndex[2] = 6;_F[5].ptIndex[3] = 7; // 前
}
void Draw(HDC hdc)
{
Point2 screenPoint[4]; // 二维点
for (int nFace = 0; nFace < 6; nFace++) // 面循环
{
for (int nVertex = 0; nVertex < 4; nVertex++) // 点循环
{
int vertex = _F[nFace].ptIndex[nVertex];
screenPoint[nVertex].x = _V[vertex].x;
screenPoint[nVertex].y = _V[vertex].y;
}
MoveToEx(hdc, ROUND(screenPoint[0].x), ROUND(screenPoint[0].y), NULL); // 绘制多边形
//LineTo(hdc, ROUND(screenPoint[0].x), ROUND(screenPoint[0].y));
LineTo(hdc, ROUND(screenPoint[1].x), ROUND(screenPoint[1].y));
LineTo(hdc, ROUND(screenPoint[2].x), ROUND(screenPoint[2].y));
LineTo(hdc, ROUND(screenPoint[3].x), ROUND(screenPoint[3].y));
LineTo(hdc, ROUND(screenPoint[0].x), ROUND(screenPoint[0].y));
}
}
};
static bool m_Play = false;
static wchar_t* m_str = L"左键 - 旋转 | 暂停";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rc;
static double alpha;
static double beta;
switch (uMsg)
{
case WM_TIMER:
alpha += 1;
beta += 1;
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_LBUTTONDOWN:
m_Play = !m_Play;
if (m_Play)
{
SetTimer(hwnd,0,0,NULL);
}
else
{
KillTimer(hwnd,0);
}
return 0;
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rc);
// 内存DC
HDC memDC = CreateCompatibleDC(hdc); // 创建兼容DC 画板
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); // 创建画布
HGDIOBJ oldBitmap = SelectObject(memDC, newBitmap); // 将画布选入画板
FillRect(memDC, &rc, (HBRUSH)(COLOR_WINDOW + 1));
DrawText(memDC, m_str, wcslen(m_str), &rc, NULL);
SetMapMode(memDC, MM_ANISOTROPIC);
SetWindowExtEx(memDC, rc.right, -rc.bottom, NULL);
SetViewportExtEx(memDC, rc.right, -rc.bottom, NULL);
SetViewportOrgEx(memDC, rc.right / 2, rc.bottom / 2, NULL);
// 绘制图形
{
static Cube cube;
static Transform3 transform;
cube.ReadVertex();
cube.ReadFace();
transform.SetMatrix(cube._V,8); // 设置顶点
double scale = 300;
transform.Scale(scale); // 放大
transform.Translate(-scale / 2 , -scale / 2, -scale / 2); // 平移
transform.RotateX(alpha);
transform.RotateY(beta);
cube.Draw(memDC);
}
// 内存dc复制到设备
BitBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, memDC, ROUND(-rc.right / 2), ROUND(-rc.bottom / 2), SRCCOPY);
SelectObject(memDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"CAG";
WNDCLASS wc = { };
wc.style = CS_HREDRAW | CS_VREDRAW; // 重新绘制整个工作区
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
WINDOW_TEXT, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}