3D 向量图形类






4.83/5 (4投票s)
3D 向量图形类。
引言
关于 3D 变换
在 2D 屏幕上显示 3D 对象时,我们需要将 3D 坐标转换为相应的 2D 坐标。我们称之为投影。为了创建投影,我们需要进行一些向量代数运算。
背景
基本上有很多类型的投影;在这里我们只涉及两种类型的投影。
- 正交投影
- 透视投影
- 单点透视。
- 两点透视。
- 三点透视。
在正交投影中,3D 点 A(x, y, z) 在投影 P(x', y') 上的计算如下。
x'=|Amag| *(Wx(.)A.Unitvector ());
y’=|Amag|*(Wy(.)A.UnitVector ());
其中 Wx= [1 0 0]
& Wy= [0 1 0]
& (.) 是向量的点积。
基本上 Wx &Wy 是单位向量。
透视投影有三种类型。
在这里我们只讨论单点透视。透视投影是通过矩阵乘法计算的。
X’=X/(rz+1);
Y’=Y/(rz+1);
其中 r= -1/Zc
& Zc 是从视点到 Z 方向的距离。
使用代码
编译并构建本文提供的代码。
class C3DCanvas : public CDC
{
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found in CMemDC
CDC* m_pDC; // Saves CDC passed in constructor
CRect m_rect; // Rectangle of drawing area.
BOOL m_bMemDC;
CPen m_penWhite;
CPen *m_oldPen;
C3DVector W_X;//(1,0,0);//Unit Vector for X Axis
C3DVector W_Y;//(0,1,0);//Unit Vector for Y Axis
C3DVector W_Z;//(0,0,1);//Unit Vector for Z Axis
C3DVector m_org;
int m_winOrgX;
int m_winOrgY;
BOOL m_bPerpective;
public:
C3DVector m_camera;
void SetCamera(double dx,double dy,double dz);
void SetWindowOrg(int x,int y);
void SetViewPortOrg(C3DVector v);
void TranslateView(C3DVector v);
void RotateView(float Angle,AXIS axis);
void Line3D(C3DVector P,C3DVector Q);
void EnablePerspectiveView(BOOL bSet=TRUE)
{
m_bPerpective=bSet;
}
C3DCanvas(CDC* pDC, const CRect* pRect = NULL,COLORREF m_clrBkColor=0xffffff)
:CDC(),
W_X(1,0,0),
W_Y(0,1,0),
W_Z(0,0,1),
m_org(0,0,0),
m_winOrgX(0),
m_winOrgY(0),
m_camera(0,0,1000)
{
m_bPerpective=FALSE;
ASSERT(pDC != NULL);
// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC = !pDC->IsPrinting();
// Get the rectangle to draw
if (pRect == NULL) {
pDC->GetClipBox(&m_rect);
} else {
m_rect = *pRect;
}
if (m_bMemDC) {
// Create a Memory DC
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = SelectObject(&m_bitmap);
SetMapMode(pDC->GetMapMode());
SetWindowExt(pDC->GetWindowExt());
SetViewportExt(pDC->GetViewportExt());
pDC->DPtoLP(&m_rect);
SetWindowOrg(m_rect.left, m_rect.top);
} else {
// Make a copy of the relevent parts of the current DC for printing
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}
// Fill background
FillSolidRect(m_rect, /*pDC->GetBkColor()*/m_clrBkColor);
m_penWhite.CreatePen(PS_SOLID,1,RGB(255,255,255));
m_oldPen=SelectObject(&m_penWhite);
SetTextColor(RGB(255,255,255));
}
~C3DCanvas()
{
if (m_bMemDC) {
// Copy the offscreen bitmap onto the screen.
m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
this, m_rect.left, m_rect.top, SRCCOPY);
//Swap back the original bitmap.
SelectObject(m_oldBitmap);
SelectObject(m_oldPen);
m_penWhite.DeleteObject();
} else {
// All we need to do is replace the DC with an illegal value,
// this keeps us from accidently deleting the handles associated with
// the CDC that was passed to the constructor.
m_hDC = m_hAttribDC = NULL;
}
}
C3DVector ToPerspectiveView(C3DVector v, double z);
};
//
void C3DCanvas::Line3D(C3DVector P, C3DVector Q)
{
int x1,y1,x2,y2;
P=P-m_org;
Q=Q-m_org;
double r=1;
if(m_bPerpective)
{
P=ToPerspectiveView(P,m_camera.Magnetude());
Q=ToPerspectiveView(Q,m_camera.Magnetude());
}
double dMag1=P.Magnetude();
x1=(int)( ( ( dMag1 ) * ( P.UnitVector() * W_X ) ) )-m_winOrgX;
y1=(int)( ( ( dMag1 ) * ( P.UnitVector() * W_Y ) ) )-m_winOrgY;
double dMag2=Q.Magnetude();
x2=(int)( ( ( dMag2 ) * ( Q.UnitVector() * W_X) ) )-m_winOrgX;
y2=(int)( ( ( dMag2 ) * ( Q.UnitVector() * W_Y) ) )-m_winOrgY;
MoveTo(x1,y1);
LineTo(x2,y2);
}
C3DVector C3DCanvas::ToPerspectiveView(C3DVector v, double z)
{
double r=0;
r=-1/z;
v.m_x=v.m_x/((r*v.m_z)+1);
v.m_y=v.m_y/((r*v.m_z)+1);
v.m_z=v.m_z/((r*v.m_z)+1);
return v;
}
void CMy3DTransformationsView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rectClient ;
GetClientRect(rectClient);
C3DCanvas canvas(&dc,&rectClient,RGB(0,0,0));
C3DVector v(-x1,-y1,-z1);
canvas.SetViewPortOrg(v);
canvas.EnablePerspectiveView(TRUE);
canvas.SetCamera(0,0,1650);
canvas.m_camera.Rotate(x,X_AXIS);
canvas.m_camera.Rotate(y,Y_AXIS);
canvas.m_camera.Rotate(z,Z_AXIS);
canvas.SetWindowOrg(-600,-300);
C3DVector v1(0,-300,0),v2(0,300,0);
CPen pen1(PS_DASHDOT,1,RGB(255,0,0));
CPen *oldPen=canvas.SelectObject(&pen1);
v1.Rotate(x,X_AXIS);
v1.Rotate(y,Y_AXIS);
v1.Rotate(z,Z_AXIS);
v2.Rotate(x,X_AXIS);
v2.Rotate(y,Y_AXIS);
v2.Rotate(z,Z_AXIS);
canvas.Line3D(v1,v2);
canvas.SelectObject(oldPen);
CPen pen2(PS_DASHDOT,1,RGB(0,255,0));
C3DVector v3(-300,0,0),v4(300,0,0);
v3.Rotate(x,X_AXIS);
v3.Rotate(y,Y_AXIS);
v3.Rotate(z,Z_AXIS);
v4.Rotate(x,X_AXIS);
v4.Rotate(y,Y_AXIS);
v4.Rotate(z,Z_AXIS);
oldPen=canvas.SelectObject(&pen2);
canvas.Line3D(v3,v4);
canvas.SelectObject(oldPen);
CPen pen3(PS_DASHDOT,1,RGB(0,0,255));
C3DVector v5(0,0,-300),v6(0,0,300);
v5.Rotate(x,X_AXIS);
v5.Rotate(y,Y_AXIS);
v5.Rotate(z,Z_AXIS);
v6.Rotate(x,X_AXIS);
v6.Rotate(y,Y_AXIS);
v6.Rotate(z,Z_AXIS);
oldPen=canvas.SelectObject(&pen3);
canvas.Line3D(v5,v6);
canvas.SelectObject(oldPen);
CPen pen4(PS_SOLID,1,RGB(122,255,255));
oldPen=canvas.SelectObject(&pen4);
C3DVector vect[8];
vect[0].m_x=50;
vect[0].m_y=50;
vect[0].m_z=50;
vect[1].m_x=250;
vect[1].m_y=50;
vect[1].m_z=50;
vect[2].m_x=50;
vect[2].m_y=250;
vect[2].m_z=50;
vect[3].m_x=250;
vect[3].m_y=250;
vect[3].m_z=50;
vect[4].m_x=250;
vect[4].m_y=250;
vect[4].m_z=250;
vect[5].m_x=50;
vect[5].m_y=250;
vect[5].m_z=250;
vect[6].m_x=250;
vect[6].m_y=50;
vect[6].m_z=250;
vect[7].m_x=50;
vect[7].m_y=50;
vect[7].m_z=250;
for(int i=0;i<8;i++)
{ vect[i].Rotate(x,X_AXIS);
vect[i].Rotate(y,Y_AXIS);
vect[i].Rotate(z,Z_AXIS);
}
canvas.Line3D(vect[0],vect[1]);
canvas.Line3D(vect[2],vect[0]);
canvas.Line3D(vect[3],vect[1]);
canvas.Line3D(vect[3],vect[2]);
canvas.Line3D(vect[3],vect[4]);
canvas.Line3D(vect[5],vect[4]);
canvas.Line3D(vect[5],vect[2]);
canvas.Line3D(vect[4],vect[6]);
canvas.Line3D(vect[7],vect[6]);
canvas.Line3D(vect[7],vect[0]);
canvas.Line3D(vect[7],vect[5]);
canvas.Line3D(vect[6],vect[1]);
canvas.SelectObject(oldPen);
}
享受 3D 向量图形。