用c++实现3d引擎-超详细(附源码)

upd:2024/1 重写全部 b l o g blog blog

前言

这篇文章介绍如何用最简单的c++代码(只有100行)实现3d线框渲染
思路主要来自SPixel 3d教程网站(英文)
重要:关于前置数学(如矩阵、线性代数)请参见 这个 \boxed{这个} 这个
下一篇:渲染贴图

效果图

实现了第一人称视角移动等等
在这里插入图片描述

C++如何输出图形?

需要ege库,但是easyx也可(graphics.h)
或者不用图形库,输出到svg文件也可,需要自行更改代码

可以下载 小熊猫 c + + ( 直接带有 e a s y x ) \boxed{小熊猫c++(直接带有easyx)} 小熊猫c++(直接带有easyx)或下载 e a s y x ( V S 或 M i n G W ) \boxed{easyx(VS或MinGW)} easyx(VSMinGW)

本文使用了EGE

Easyx/EGE的使用可参见:这个

如何获得看到的图像

假设我们的屏幕是棱锥的底面
在这里插入图片描述
我们的眼睛看到的物体位置可以转化为
连接眼睛与物体的直线与屏幕的交点
在这里插入图片描述

原理

将顶点投影到屏幕上,这个过程我们称之为透视投影。
在这里插入图片描述

如果垂直于x轴看,可以发现 △ A B C \triangle ABC ABC △ A B ′ C ′ \triangle AB'C' ABC相似
在这里插入图片描述

注意我们的屏幕处于 z z z的负半轴,并且假定焦距 A B ′ = 1 AB'=1 AB=1
于是有 B C A B = B ′ C ′ A B ′ → P ′ . y = P . y − P . z \dfrac{BC}{AB}=\dfrac{B^{\prime}C^{\prime}}{AB^{\prime}}\to P^{\prime}.y=\dfrac{P.y}{-P.z} ABBC=ABBCP.y=P.zP.y
以上是透视投影的原理,你可以略过下面的内容(下面的内容是具体实现)

现在我们需要注意屏幕的大小(显示屏),应该根据假想屏幕(如下图)变化到真实屏幕里
在这里插入图片描述

现在我们的投影后坐标会在这个假定的屏幕里,即应该满足 x , y ∈ [ − 1 , 1 ] x,y\in[-1,1] x,y[1,1],当然屏幕外的点应该舍去不显示
[ − 1 , 1 ] → [ 0 , 长 ] 和 [ 0 , 宽 ] [-1,1]\to[0,长]和[0,宽] [1,1][0,][0,], 这里引入NDC空间,即,将点从它们最初所在的范围转换为范围 [ 0 , 1 ] [0,1] [0,1]
在这里插入图片描述

我们可以采用 x ′ = ( 1 + x ) / 2 × 长, y ′ = ( 1 + y ) / 2 × 宽 x'=(1+x)/2\times长,y'=(1+y)/2\times宽 x=(1+x)/2×长,y=(1+y)/2×

可见性

如何处理立方体哪个面可见,哪个面不可见,就是可见性问题
我们可以按照点的 z z z值从大到小(从后到前)进行计算,以避免此问题

透视投影的具体计算

第一步

将物体从世界坐标系变换到相机坐标系(因为相机会移动和旋转)
在这里插入图片描述
为了计算,我们需要引入矩阵(看不懂就看代码)

现在假设相机坐标系为 M 相机 M_{相机} M相机,这是一个局部坐标系, 于是我们只要用世界坐标乘以相机坐标系即可得到相机坐标
这一步相当于 P 世界 × M 世界 − 相机 = P 相机 P_{世界}\times M_{世界-相机}=P_{相机} P世界×M世界相机=P相机
相反的,我们要求的 P 世界 = P 相机 / M 相机 P_{世界}=P_{相机}/ M_{相机} P世界=P相机/M相机

在这里插入图片描述

所以将物体从世界坐标系变换到相机坐标系的结果 P ′ = P M 相机 P'=\dfrac P{M_{相机}} P=M相机P

第二步

现在,我们可以使用相机空间中的点坐标,通过使用透视投影方程 P ′ . y = P . y − P . z P^{\prime}.y=\dfrac{P.y}{-P.z} P.y=P.zP.y来计算其在屏幕上的坐标。

即:将 P ′ P' P变换到屏幕坐标系 P ′ ′ P'' P′′ 但是要注意 P ′ ′ P'' P′′的可见性
{ 可见 ∣ P ′ . x ∣ ≤ W 2 and ∣ P ′ . y ∣ ≤ H 2 不可见 otherwise \begin{cases}可见&|P'.x|\leq\frac{W}{2}\text{and}|P'.y|\leq\frac{H}{2}\\不可见&\text{otherwise}\end{cases} {可见不可见P.x2WandP.y2Hotherwise
注意: P ′ ′ P'' P′′在以后仍记为 P P P

第三步

注意显示屏为 [ 0 , W ] × [ 0 , H ] [0,W]\times[0,H] [0,W]×[0,H],而现在的 P P P点在 [ − W / 2 , W / 2 ] × [ − H / 2 , H / 2 ] [-W/2,W/2]\times[-H/2,H/2] [W/2,W/2]×[H/2,H/2]
所以要将 P P P变换到显示屏坐标系

(1)对 P P P归一化(NDC标准空间),即将 x , y x,y x,y相对于长宽,化为 [ 0 , 1 ] [0,1] [0,1]
P 归一化 . x = P . x + W / 2 W P 归一化 . y = P . y + H / 2 H \begin{aligned}P_{归一化}.x&=\frac{P.x+W/2}{W}\\P_{归一化}.y&=\frac{P.y+H/2}{H}\end{aligned} P归一化.xP归一化.y=WP.x+W/2=HP.y+H/2
(2)注意显示屏的 y y y向下,于是有 P 栅格 . x = ⌊ P 归一化 . x ∗ W 像素 ⌋ P 像素 . y = ⌊ ( 1 − P 归一化 . y ) ∗ H 栅格 ⌋ \begin{array}{l}P_{栅格}.x=\lfloor P_{归一化}.x*W_{像素}\rfloor\\P_{像素}.y=\lfloor(1-P_{归一化}.y)*H_{栅格}\rfloor\end{array} P栅格.x=P归一化.xW像素P像素.y=⌊(1P归一化.y)H栅格

具体代码怎么写?

在这里插入图片描述

#include<graphics.h>
#include<bits/stdc++.h>
float pi = 3.14159265;
struct Vec2i{int x, y;};
struct Vec2f{float x, y;};
template<typename T>
class Vec3
{
public:
	Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
	Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
	Vec3 operator + (Vec3 &v) {return Vec3(x + v.x, y + v.y, z + v.z);}
	Vec3 operator - (Vec3 &v){return Vec3(x - v.x, y - v.y, z - v.z);}
	Vec3 operator * (T &r){return Vec3(x * r, y * r, z * r);}
	Vec3 operator * (Vec3 &v){return Vec3(x * v.x, y * v.y, z * v.z);}
	/*绕x旋转*/void rotate_point3d_x(float R) {T new_y = y * cos(R) - z * sin(R);T new_z = y * sin(R) + z * cos(R);y = new_y;z = new_z;}
	/*绕y旋转*/void rotate_point3d_y(float R) {T new_x = x * cos(R) + z * sin(R);T new_z = -x * sin(R) + z * cos(R);x = new_x;z = new_z;}
	/*点乘*/T dotProduct(Vec3<T> &v){return x * v.x + y * v.y + z * v.z;}
	Vec3& operator *= (T &r){x *= r, y *= r, z *= r;return *this;}
	/*叉乘*/Vec3 crossProduct(Vec3<T> &v){return Vec3<T>(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);}
	/*模长平方*/T norm(){return x * x + y * y + z * z;}
	/*模长*/T length(){return sqrt(norm());}
	/*下标访问*/const T& operator [] (uint8_t i) const{return (&x)[i];}
	T& operator [] (uint8_t i){return (&x)[i];}
	/*归一化*/Vec3& normalize(){T n = norm();if (n > 0){T factor = 1 / sqrt(n);x *= factor, y *= factor, z *= factor;}return *this;}
	friend Vec3 operator * (const T &r, const Vec3 &v){return Vec3<T>(v.x * r, v.y * r, v.z * r);}
	friend Vec3 operator / (const T &r, const Vec3 &v){return Vec3<T>(r / v.x, r / v.y, r / v.z);}
	friend std::ostream& operator << (std::ostream &s, const Vec3<T> &v){return s << '[' << v.x << ' ' << v.y << ' ' << v.z << ']';}
	T x, y, z;
};
typedef Vec3<float> Vec3f;
typedef Vec3<int> Vec3i;
template<typename T>
class Matrix44
{
public:
	T x[4][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
	Matrix44() {}
	Matrix44 (T a, T b, T c, T d, T e, T f, T g, T h,T i, T j, T k, T l, T m, T n, T o, T p){x[0][0] = a;x[0][1] = b;x[0][2] = c;x[0][3] = d;x[1][0] = e;x[1][1] = f;x[1][2] = g;x[1][3] = h;x[2][0] = i;x[2][1] = j;x[2][2] = k;x[2][3] = l;x[3][0] = m;x[3][1] = n;x[3][2] = o;x[3][3] = p;}
	const T* operator [] (uint8_t i) const{return x[i];}
	T* operator [] (uint8_t i){return x[i];}
	Matrix44 operator * (const Matrix44& v) const{Matrix44 tmp;multiply (*this, v, tmp);return tmp;}
	/*矩阵乘法*/static void multiply(const Matrix44<T> &a, const Matrix44& b, Matrix44 &c){const T * __restrict ap = &a.x[0][0];const T * __restrict bp = &b.x[0][0];T * __restrict cp = &c.x[0][0];T a0, a1, a2, a3;a0 = ap[0];a1 = ap[1];a2 = ap[2];a3 = ap[3];cp[0]  = a0 * bp[0]  + a1 * bp[4]  + a2 * bp[8]  + a3 * bp[12];cp[1]  = a0 * bp[1]  + a1 * bp[5]  + a2 * bp[9]  + a3 * bp[13];cp[2]  = a0 * bp[2]  + a1 * bp[6]  + a2 * bp[10] + a3 * bp[14];cp[3]  = a0 * bp[3]  + a1 * bp[7]  + a2 * bp[11] + a3 * bp[15];a0 = ap[4];a1 = ap[5];a2 = ap[6];a3 = ap[7];cp[4]  = a0 * bp[0]  + a1 * bp[4]  + a2 * bp[8]  + a3 * bp[12];cp[5]  = a0 * bp[1]  + a1 * bp[5]  + a2 * bp[9]  + a3 * bp[13];cp[6]  = a0 * bp[2]  + a1 * bp[6]  + a2 * bp[10] + a3 * bp[14];cp[7]  = a0 * bp[3]  + a1 * bp[7]  + a2 * bp[11] + a3 * bp[15];a0 = ap[8];a1 = ap[9];a2 = ap[10];a3 = ap[11];cp[8]  = a0 * bp[0]  + a1 * bp[4]  + a2 * bp[8]  + a3 * bp[12];cp[9]  = a0 * bp[1]  + a1 * bp[5]  + a2 * bp[9]  + a3 * bp[13];cp[10] = a0 * bp[2]  + a1 * bp[6]  + a2 * bp[10] + a3 * bp[14];cp[11] = a0 * bp[3]  + a1 * bp[7]  + a2 * bp[11] + a3 * bp[15];a0 = ap[12];a1 = ap[13];a2 = ap[14];a3 = ap[15];cp[12] = a0 * bp[0]  + a1 * bp[4]  + a2 * bp[8]  + a3 * bp[12];cp[13] = a0 * bp[1]  + a1 * bp[5]  + a2 * bp[9]  + a3 * bp[13];cp[14] = a0 * bp[2]  + a1 * bp[6]  + a2 * bp[10] + a3 * bp[14];cp[15] = a0 * bp[3]  + a1 * bp[7]  + a2 * bp[11] + a3 * bp[15];}
	/*转置矩阵*/Matrix44 transposed()const{return Matrix44(x[0][0],x[1][0],x[2][0],x[3][0],x[0][1],x[1][1],x[2][1],x[3][1],x[0][2],x[1][2],x[2][2],x[3][2],x[0][3],x[1][3],x[2][3],x[3][3]);}
	template<typename S>
	/*矩阵乘向量*/void multVecMatrix(const Vec3<S> &src, Vec3<S> &dst) const{S a, b, c, w;a = src[0] * x[0][0] + src[1] * x[1][0] + src[2] * x[2][0] + x[3][0];b = src[0] * x[0][1] + src[1] * x[1][1] + src[2] * x[2][1] + x[3][1];c = src[0] * x[0][2] + src[1] * x[1][2] + src[2] * x[2][2] + x[3][2];w = src[0] * x[0][3] + src[1] * x[1][3] + src[2] * x[2][3] + x[3][3];dst.x = a / w;dst.y = b / w;dst.z = c / w;}
	template<typename S>
	void multDirMatrix(const Vec3<S> &src, Vec3<S> &dst) const{S a, b, c;a = src[0] * x[0][0] + src[1] * x[1][0] + src[2] * x[2][0];b = src[0] * x[0][1] + src[1] * x[1][1] + src[2] * x[2][1];c = src[0] * x[0][2] + src[1] * x[1][2] + src[2] * x[2][2];dst.x = a;dst.y = b;dst.z = c;}
	/*矩阵的逆*/Matrix44 inverse() const{int i, j, k;Matrix44 s;Matrix44 t (*this);for (i = 0; i < 3 ; i++){int pivot = i;T pivotsize = t[i][i];if (pivotsize < 0)pivotsize = -pivotsize;for (j = i + 1; j < 4; j++){T tmp = t[j][i];if (tmp < 0)tmp = -tmp;if (tmp > pivotsize){pivot = j;pivotsize = tmp;}}if (pivotsize == 0){return Matrix44();}if (pivot != i){for (j = 0; j < 4; j++){T tmp;tmp = t[i][j];t[i][j] = t[pivot][j];t[pivot][j] = tmp;tmp = s[i][j];s[i][j] = s[pivot][j];s[pivot][j] = tmp;}}for (j = i + 1; j < 4; j++){T f = t[j][i] / t[i][i];for (k = 0; k < 4; k++){t[j][k] -= f * t[i][k];s[j][k] -= f * s[i][k];}}}for (i = 3; i >= 0; --i){T f;if ((f = t[i][i]) == 0){return Matrix44();}for (j = 0; j < 4; j++){t[i][j] /= f;s[i][j] /= f;}for (j = 0; j < i; j++){f = t[j][i];for (k = 0; k < 4; k++){t[j][k] -= f * t[i][k];s[j][k] -= f * s[i][k];}}}return s;}
};
typedef Matrix44<float> Matrix44f;
using namespace std;
Vec3f verts[146]={{0,39.034,0},{0.76212,36.843,0},{3,36.604,0},{1,35.604,0},{2.0162,33.382,0},{0,34.541,0},{-2.0162,33.382,0},{-1,35.604,0},{-3,36.604,0},{-0.76212,36.843,0},{-0.040181,34.31,0},{3.2778,30.464,0},{-0.040181,30.464,0},{-0.028749,30.464,0},{3.2778,30.464,0},{1.2722,29.197,0},{1.2722,29.197,0},{-0.028703,29.197,0},{1.2722,29.197,0},{5.2778,25.398,0},{-0.02865,25.398,0},{1.2722,29.197,0},{5.2778,25.398,0},{3.3322,24.099,0},{-0.028683,24.099,0},{7.1957,20.299,0},{-0.02861,20.299,0},{5.2778,19.065,0},{-0.028663,18.984,0},{9.2778,15.265,0},{-0.028571,15.185,0},{9.2778,15.265,0},{7.3772,13.999,0},{-0.028625,13.901,0},{9.2778,15.265,0},{12.278,8.9323,0},{-0.028771,8.9742,0},{12.278,8.9323,0},{10.278,7.6657,0},{-0.028592,7.6552,0},{15.278,2.5994,0},{-0.028775,2.6077,0},{15.278,2.5994,0},{13.278,1.3329,0},{-0.028727,1.2617,0},{18.278,-3.7334,0},{18.278,-3.7334,0},{2.2722,-1.2003,0},{-0.028727,-1.3098,0},{4.2722,-5,0},{4.2722,-5,0},{-0.028727,-5,0},{-3.3582,30.464,0},{-3.3582,30.464,0},{-1.3526,29.197,0},{-1.3526,29.197,0},{-1.3526,29.197,0},{-5.3582,25.398,0},{-1.3526,29.197,0},{-5.3582,25.398,0},{-3.4126,24.099,0},{-7.276,20.299,0},{-5.3582,19.065,0},{-9.3582,15.265,0},{-9.3582,15.265,0},{-7.4575,13.999,0},{-9.3582,15.265,0},{-12.358,8.9323,0},{-12.358,8.9323,0},{-10.358,7.6657,0},{-15.358,2.5994,0},{-15.358,2.5994,0},{-13.358,1.3329,0},{-18.358,-3.7334,0},{-18.358,-3.7334,0},{-2.3526,-1.2003,0},{-4.3526,-5,0},{-4.3526,-5,0},{0,34.31,0.040181},{0,30.464,-3.2778},{0,30.464,0.040181},{0,30.464,0.028749},{0,30.464,-3.2778},{0,29.197,-1.2722},{0,29.197,-1.2722},{0,29.197,0.028703},{0,29.197,-1.2722},{0,25.398,-5.2778},{0,25.398,0.02865},{0,29.197,-1.2722},{0,25.398,-5.2778},{0,24.099,-3.3322},{0,24.099,0.028683},{0,20.299,-7.1957},{0,20.299,0.02861},{0,19.065,-5.2778},{0,18.984,0.028663},{0,15.265,-9.2778},{0,15.185,0.028571},{0,15.265,-9.2778},{0,13.999,-7.3772},{0,13.901,0.028625},{0,15.265,-9.2778},{0,8.9323,-12.278},{0,8.9742,0.028771},{0,8.9323,-12.278},{0,7.6657,-10.278},{0,7.6552,0.028592},{0,2.5994,-15.278},{0,2.6077,0.028775},{0,2.5994,-15.278},{0,1.3329,-13.278},{0,1.2617,0.028727},{0,-3.7334,-18.278},{0,-3.7334,-18.278},{0,-1.2003,-2.2722},{0,-1.3098,0.028727},{0,-5,-4.2722},{0,-5,-4.2722},{0,-5,0.028727},{0,30.464,3.3582},{0,30.464,3.3582},{0,29.197,1.3526},{0,29.197,1.3526},{0,29.197,1.3526},{0,25.398,5.3582},{0,29.197,1.3526},{0,25.398,5.3582},{0,24.099,3.4126},{0,20.299,7.276},{0,19.065,5.3582},{0,15.265,9.3582},{0,15.265,9.3582},{0,13.999,7.4575},{0,15.265,9.3582},{0,8.9323,12.358},{0,8.9323,12.358},{0,7.6657,10.358},{0,2.5994,15.358},{0,2.5994,15.358},{0,1.3329,13.358},{0,-3.7334,18.358},{0,-3.7334,18.358},{0,-1.2003,2.3526},{0,-5,4.3526},{0,-5,4.3526}};
const int numTris = 128;
//tris[n*3],tris[n*3+1],tris[n*3+2]分别为第n个三角形的三个顶点在verts[]的编号
int tris[numTris * 3] ={8,   7,   9,   6,   5,   7,   4,   3,   5,   2,   1,   3,   0,   9,   1,5,   3,   7,   7,   3,   9,   9,   3,   1,  10,  12,  11,  13,  15,  14,15,  13,  16,  13,  17,  16,  18,  20,  19,  17,  20,  21,  20,  23,  22,20,  24,  23,  23,  26,  25,  24,  26,  23,  26,  27,  25,  26,  28,  27,27,  30,  29,  28,  30,  27,  30,  32,  31,  30,  33,  32,  27,  30,  34,32,  36,  35,  33,  36,  32,  36,  38,  37,  36,  39,  38,  38,  41,  40,39,  41,  38,  41,  43,  42,  41,  44,  43,  44,  45,  43,  44,  47,  46,44,  48,  47,  48,  49,  47,  48,  51,  50,  10,  52,  12,  13,  53,  54,55,  17,  54,  13,  54,  17,  56,  57,  20,  17,  58,  20,  20,  59,  60,20,  60,  24,  60,  61,  26,  24,  60,  26,  26,  61,  62,  26,  62,  28,62,  63,  30,  28,  62,  30,  30,  64,  65,  30,  65,  33,  62,  66,  30,65,  67,  36,  33,  65,  36,  36,  68,  69,  36,  69,  39,  69,  70,  41,39,  69,  41,  41,  71,  72,  41,  72,  44,  44,  72,  73,  44,  74,  75,44,  75,  48,  48,  75,  76,  48,  77,  51,  78,  80,  79,  81,  83,  82,83,  81,  84,  81,  85,  84,  86,  88,  87,  85,  88,  89,  88,  91,  90,88,  92,  91,  91,  94,  93,  92,  94,  91,  94,  95,  93,  94,  96,  95,95,  98,  97,  96,  98,  95,  98, 100,  99,  98, 101, 100,  95,  98, 102,100, 104, 103, 101, 104, 100, 104, 106, 105, 104, 107, 106, 106, 109, 108,107, 109, 106, 109, 111, 110, 109, 112, 111, 112, 113, 111, 112, 115, 114,112, 116, 115, 116, 117, 115, 116, 119, 118,  78, 120,  80,  81, 121, 122,123,  85, 122,  81, 122,  85, 124, 125,  88,  85, 126,  88,  88, 127, 128,88, 128,  92, 128, 129,  94,  92, 128,  94,  94, 129, 130,  94, 130,  96,130, 131,  98,  96, 130,  98,  98, 132, 133,  98, 133, 101, 130, 134,  98,133, 135, 104, 101, 133, 104, 104, 136, 137, 104, 137, 107, 137, 138, 109,107, 137, 109, 109, 139, 140, 109, 140, 112, 112, 140, 141, 112, 142, 143,112, 143, 116, 116, 143, 144, 116, 145, 119};
//计算在世界空间中定义的点的二维像素坐标。
bool computePixelCoordinates(const Vec3f pWorld, Vec2i &pRaster, const Matrix44f &worldToCamera, const float &canvasWidth, const float &canvasHeight, const int &imageWidth, const int &imageHeight)
{
	Vec3f pCamera;
	worldToCamera.multVecMatrix(pWorld, pCamera);
	if(pCamera.z>0)return 0;
	Vec2f pScreen;
	pScreen.x = pCamera.x / -pCamera.z;
	pScreen.y = pCamera.y / -pCamera.z;
	Vec2f pNDC;
	pNDC.x = (pScreen.x + canvasWidth * 0.5) / canvasWidth;
	pNDC.y = (pScreen.y + canvasHeight * 0.5) / canvasHeight;
	pRaster.x = (int)(pNDC.x * imageWidth);
	pRaster.y = (int)((1 - pNDC.y) * imageHeight);
	return 1;
}
Vec3f camera(14.77747,29.36194,27.99346),X0(1,0,0),Y0(0,1,0),Z0(0,0,1);
float Rotate_x,Rotate_y;
int main(int argc, char **argv)
{
	initgraph(512,512);
	float canvasWidth = 2, canvasHeight = 2;
	int imageWidth = 512, imageHeight = 512;
	while(1)
	{
		VECTOR3D RX(X0.x,X0.y,X0.z),RY(Y0.x,Y0.y,Y0.z),RZ(Z0.x,Z0.y,Z0.z);
		Vec3f X=X0,Y=Y0,Z=Z0,FORWARD,RIGHT;
		X.rotate_point3d_y(Rotate_y);Y.rotate_point3d_y(Rotate_y);Z.rotate_point3d_y(Rotate_y);
		X.rotate_point3d_x(Rotate_x);Y.rotate_point3d_x(Rotate_x);Z.rotate_point3d_x(Rotate_x);
		Matrix44f cameraToWorld(X.x, Y.x, Z.x, 0, X.y, Y.y, Z.y, 0, X.z, Y.z, Z.z, 0, camera.x, camera.y, camera.z, 1);
		Matrix44f worldToCamera = cameraToWorld.inverse();
		cleardevice();
		for (int i = 0; i < numTris; ++i)
		{
			const Vec3f &v0World = verts[tris[i * 3]];
			const Vec3f &v1World = verts[tris[i * 3 + 1]];
			const Vec3f &v2World = verts[tris[i * 3 + 2]];
			Vec2i v0Raster, v1Raster, v2Raster;
			bool show0=computePixelCoordinates(v0World, v0Raster, worldToCamera, canvasWidth, canvasHeight, imageWidth, imageHeight);
			bool show1=computePixelCoordinates(v1World, v1Raster, worldToCamera, canvasWidth, canvasHeight, imageWidth, imageHeight);
			bool show2=computePixelCoordinates(v2World, v2Raster, worldToCamera, canvasWidth, canvasHeight, imageWidth, imageHeight);
			if(show0&&show1)line(v0Raster.x,v0Raster.y,v1Raster.x,v1Raster.y);
			if(show0&&show2)line(v0Raster.x,v0Raster.y,v2Raster.x,v2Raster.y);
			if(show2&&show1)line(v2Raster.x,v2Raster.y,v1Raster.x,v1Raster.y);
		}
		Vec3f S(worldToCamera[0][2],0.0,worldToCamera[2][2]);
		Vec3f D(worldToCamera[0][0],0.0,worldToCamera[2][0]);
		S.normalize();D.normalize();
		line(400,400,400-S.x*50,400-S.z*50);
		while(kbhit())
		{
			int t=getch();
			if(t=='w')camera=camera-S;
			if(t=='s')camera=camera+S;
			if(t=='a')camera=camera-D;
			if(t=='d')camera=camera+D;
		}
		while(mousemsg())
		{
			mouse_msg m=getmouse();
			if(m.is_move())
			{
				Rotate_x=m.y*pi/imageWidth-pi/2;
				Rotate_y=m.x*pi*2/imageHeight-pi;
			}
		}
		delay_ms(100);
	}
	return 0;
}

非常简单?不是的
还是需要了解的更多些

### 回答1: 设计模式是软件开发中常用的一种解决方案,它们是一些经过实践验证的可复用设计思想。设计模式允许开发人员在类和对象的结构上灵活地更改,并提供了一种优雅的解决方案来应对各种软件开发问题。 GOF(Gang of Four)是指Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位软件工程师,他们在《设计模式:可复用面向对象软件的基础》一书中总结了23种常见的设计模式,这本书因此而获得了“设计模式圣经”的称号。 这本书以案例为基础,深入浅出地讲解了每个设计模式的原理和应用场景,并提供了C++实现源码。 其中,创建型设计模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。这些模式都提供了一种方式来创建对象,使得程序在实例化对象时更加灵活和可扩展。 结构型设计模式包括适配器模式、装饰器模式、代理模式、组合模式、享元模式和外观模式。这些模式关注如何通过类和对象的组合来创建更大的结构,并提供了一种优雅的方式来简化系统的复杂性。 行为型设计模式包括策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式和中介者模式。这些模式关注对象之间的通信和交互,它们提供了一种优雅的方式来实现松耦合和可维护的代码。 总之,设计模式是软件开发中非常重要的一部分,它们提供了一种通用的解决方案来处理常见的设计问题。通过学习和应用设计模式,开发人员可以提高代码的可复用性、可扩展性和可维护性,并加快开发进度。 ### 回答2: 设计模式是软件开发中常用的解决问题的一种思维方式或者说是一种已被证实有效的解决问题的方法。GOF 23种设计模式是由四位著名的软件工程师提出并总结出的一套经典的设计模式。 GOF 23种设计模式分别是创建型模式、结构型模式和行为型模式。创建型模式包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。结构型模式包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。行为型模式包括策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式和解释器模式。 GOF 23种设计模式具有不同的应用场景和优势。通过学习和理解这些设计模式,开发者可以更加灵活地应对各种软件开发中的问题。同时,掌握这些设计模式也有助于提高代码的可读性、可维护性和可扩展性。 带C语言实现源码是一种更加具体的学习和理解设计模式的方法。通过查看实现源码,可以更加直观地看到设计模式在实践中的应用。这些源码可以作为学习的参考,帮助开发者更好地理解设计模式的思想和使用方式。 总之,设计模式是软件开发中非常重要的一部分,通过学习GOF 23种设计模式并理解其应用场景和优势,可以提高软件开发的效率和质量。带C语言实现源码能够更加具体地帮助开发者理解设计模式的实际应用。 ### 回答3: 设计模式是软件工程中常用的一种设计思想或模板,可以用于解决特定的问题和提供可重用的解决方案。GOF(Gang of Four)提出了23种设计模式,并在书籍《设计模式:可复用面向对象软件的基础》中进行了详细的解析和说明。 这本书详细地介绍了23种设计模式,包括创建型模式、结构型模式和行为型模式。通过阅读这本书,读者可以了解每种设计模式的特点、适用场景和实现方法。另外,书中还通过示例代码的方式演示了每种设计模式的具体实现,并提供了带的C语言实现源码。 这本书对于理解设计模式的概念和思想非常有帮助。它不仅提供了23种设计模式的名字和简介,还详细解释了每种模式的适用场景和应用案例。读者可以通过学习这些设计模式,了解如何将它们应用于自己的软件开发工作中,提高代码的可重用性和可维护性。 书中的C语言实现源码是帮助读者理解和实践设计模式的重要资源。通过阅读源码,读者可以更加深入地理解每种设计模式的具体实现细节,并进一步提升自己的编程能力。 总之,通过学习《设计模式:可复用面向对象软件的基础》这本书,读者可以全面了解设计模式的概念、分类和实现方法,并通过阅读带的C语言实现源码来加深对设计模式的理解和应用。这将对提升软件设计和开发的能力和水平非常有帮助。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值