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(VS或MinGW)
本文使用了EGE
Easyx/EGE的使用可参见:这个
如何获得看到的图像
假设我们的屏幕是棱锥的底面
我们的眼睛看到的物体位置可以转化为
连接眼睛与物体的直线与屏幕的交点
原理
将顶点投影到屏幕上,这个过程我们称之为透视投影。
如果垂直于x轴看,可以发现
△
A
B
C
\triangle ABC
△ABC与
△
A
B
′
C
′
\triangle AB'C'
△AB′C′相似
注意我们的屏幕处于
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=AB′B′C′→P′.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′.x∣≤2Wand∣P′.y∣≤2Hotherwise
注意: 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归一化.x∗W像素⌋P像素.y=⌊(1−P归一化.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;
}
非常简单?不是的
还是需要了解的更多些