3D 数学例程



Allegro 提供了一些3D函数用来控制向量, 构造并且使用变换矩阵, 而且提供了从

3 维世界到 2 维屏幕的透视投影变换功能. 它不是, 也永远不会是一个功能强劲的 3D 库 (我的目的是建立一个拥有基本功能的函数库,而不是一整套简陋的图象代码 :-) 但这些函数对用户开发自己的 3D 代码还是有用的.



每个 3D 函数都有两套版本: 一个使用定点算法, 一个使用浮点算法.两个版本的语 法基本相同,只是浮点函数和结构都有一个 '_f' 后缀.举例来说, 定点 corss_product() 函数的浮点版本是corss_product_f(). 如果用户使用C++, 则 Allegro 将重载这些函数以便配合与'fix'类使用.


3D 变换函数是提供变换矩阵来实现. 该矩阵是一个4x4的数组, 其中包含的数据可与 3D点相乘来产生一个不同的 3D 点. 正确设置该矩阵可以产生诸如偏移,旋转,缩放等 不同的操作. 更美妙的是可以相乘两个矩阵来产生第三个矩阵, 这和分别使用两个矩 阵来实现变换的效果是相同的.比如说, 如果用户旋转一个点后在偏移该点, 用户可以将旋转矩阵和偏移矩阵相乘来产生一个混合矩阵来一次完成这两种变换. 使用该方法可以产生一个任意复杂的变换矩阵. 只需要将每个点乘单个矩阵.






Allegro 在实现矩阵结构时使用了一些小技巧. 旋转和缩放矩阵可通过一个简单的 3x3的矩阵来完成. 但偏移和投影变换必须使用4x4的矩阵, 而且每个点的坐标也通过 加一个坐标量而扩展成 4维, w=1. 这样会降低效率, 但是幸运的是我们有优化的办法.


以下是一个4x4的矩阵:

   ( a, b, c, d )
   ( e, f, g, h )
   ( i, j, k, l )
   ( m, n, o, p )

这个模板可以分成几个部分来看. 左上方的 3x3 矩阵用来实现旋转和缩放.最右边一列的 d, h, 和l 用来实现偏移. 如果矩阵只用来作仿射变换, 则m, n 和 o 总是 0, p总是1. 如果你不知道什么是仿射, 请阅读 Foley & Van Damme: 一般来说它包含缩放 ,偏移, 旋转, 但不包含投影. 因为 Allegro 使用一个独立的函数来实现投影, 所以矩阵函数只需支持仿射变换, 也就是说无需在矩阵的最底下一行存储其他数据. Allegro将默认矩阵的最下一行为 (0, 0, 0, 1), 并相应的优化操作矩阵的函数.





矩阵存储在下列结构中.

typedef struct MATRIX            - 定点矩阵结构
{
   fixed v[3][3];                - 3x3的缩放和旋转组件
   fixed t[3];                   - x/y/z 偏移矩阵
} MATRIX;

typedef struct MATRIX_f - 浮点矩阵结构 { float v[3][3]; - 3x3缩放和旋转组件 float t[3]; - x/y/z 偏移矩阵 } MATRIX_f

extern MATRIX identity_matrix;
extern MATRIX_f identity_matrix_f;
一个表明 '什么也不做' 的单位矩阵. 一个矩阵与单位矩阵相乘得到原来的矩阵.


void get_translation_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void get_translation_matrix_f(MATRIX_f *m, float x, float y, float z);
构造一个偏移矩阵, 并将其存放在 m 中. 当该矩阵和向量(px, py, pz), 相乘后将得到一个 (px+x, py+y, pz+z) 向量. 也就是将原向量向某一方向平移.


void get_scaling_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void get_scaling_matrix_f(MATRIX_f *m, float x, float y, float z);
构造一个缩放矩阵, 将其存放在 m 中. 当该矩阵与向量 (px, py, pz) 相乘后得到一个(px*x, py*y, pz*z)向量. 即将原向量进行缩放.


void get_x_rotate_matrix(MATRIX *m, fixed r);
void get_x_rotate_matrix_f(MATRIX_f *m, float r);
构造一个绕 X 轴旋转的矩阵,将其存放在 m 中. 当该矩阵与某向量相乘后, 将使该向量按指定的角度绕X轴旋转(旋转角度用二进制表示,最多旋转256度).


void get_y_rotate_matrix(MATRIX *m, fixed r);
void get_y_rotate_matrix_f(MATRIX_f *m, float r);
构造一个绕Y轴旋转的矩阵,将其存放在m中.当该矩阵与某向量相乘后, 将使该向量按指定的角度绕 Y 轴旋转 (旋转角度用二进制表示,最多旋转256度).


void get_z_rotate_matrix(MATRIX *m, fixed r);
void get_z_rotate_matrix_f(MATRIX_f *m, float r);
构造一个绕Z轴旋转的矩阵, 将其存放在 m 中. 当该矩阵与某向量相乘后, 将使该向量按指定的角度绕 Z 轴旋转 (旋转角度用二进制表示, 最多旋转256度).


void get_rotation_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void get_rotation_matrix_f(MATRIX_f *m, float x, float y, float z);
构造一个绕任意坐标轴旋转的矩阵. 当该矩阵与某向量相乘后, 将使该向量按指定的角度绕任意坐标轴轴旋转 (旋转角度用二进制表示, 最多旋转 256 度).

void get_align_matrix(MATRIX *m, fixed xfront, yfront, zfront, fixed xup, fixed yup, fixed zup);
旋转一个矩阵使其与指定的坐标向量对齐 (它们不需被规格化或垂直, 但 up 和 front 必须相等). 一个(1, 0, 0)的 front 向量和一个 (0, 1, 0) 的 up 向量将返回一个单位矩阵.


void get_align_matrix_f(MATRIX *m, float xfront, yfront, zfront, float xup, yup, zup);
get_align_matrix() 的浮点版本.

void get_vector_rotation_matrix(MATRIX *m, fixed x, y, z, fixed a);
void get_vector_rotation_matrix_f(MATRIX_f *m, float x, y, z, float a);
构造一个按指定角度 (二进制表示,最多旋转256度) 围绕x, y, z 向量旋转的变换矩阵.



void get_transformation_matrix(MATRIX *m, fixed scale, fixed xrot, yrot, zrot, x, y, z);
构造一个按指定角度 (二进制表示,最多旋转256度) 围绕任意三个轴旋转, 将结果进行缩放 (将 scale 设置成1,如果不需缩放), 并且平移到 x,y,z 位置的矩阵.



void get_transformation_matrix_f(MATRIX_f *m, float scale, float xrot, yrot, zrot, x, y, z);
get_transformation_matrix() 的浮点版本.

void get_camera_matrix(MATRIX *m, fixed x, y, z, xfront, yfront, zfront, fixed xup, yup, zup, fov, aspect);
构造一个从世界坐标系变换到标准化的观察坐标系的变换矩阵, 并做好相应的投影变换. x,y,z参数指定观察点(或照像机)在世界坐标系中的位置, xfront, yfront, 和 zfront 参数为 'in front' 矢量, 指定观察点的方向, (这些向量可以是任意长短;不需标准化), xup, yup, 和 zup 是 'up' 方向向量. fov参数指明视角大小 (照相机的焦距宽度), 该参数是一个二进制值, 最多到 256度. 在一个一般的投影变换中, 视角一般在32度到48度之间. 最后, aspect 参数是用来将图象的 Y 方向尺寸相对其 X 方向尺寸进行缩放. (设置成 1 , 则不进行缩放).




void get_camera_matrix_f(MATRIX_f *m, float x, y, z, xfront, yfront, zfront, float xup, yup, zup, fov, aspect);
get_camera_matrix() 的浮点版本.

void qtranslate_matrix(MATRIX *m, fixed x, fixed y, fixed z);
void qtranslate_matrix_f(MATRIX_f *m, float x, float y, float z);
对一个以生成的矩阵进行偏移变换的优化版本: 该函数将自动在矩阵 m 中进入偏移量, 所以没必要将两个矩阵相乘来构造偏移矩阵.

void qscale_matrix(MATRIX *m, fixed scale);
void qscale_matrix_f(MATRIX_f *m, float scale);
对一个以生成的矩阵进行缩放变换的优化版本. 该函数将自动在矩阵 m 中进入缩放量, 所以没必要将两个矩阵相乘来构造缩放矩阵.


void matrix_mul(MATRIX *m1, MATRIX *m2, MATRIX *out);
void matrix_mul_f(MATRIX_f *m1, MATRIX_f *m2, MATRIX_f *out);
将两个矩阵相乘, 并将结果存放在 out 参数中 (该参数不能与其他两个参数所代表的矩阵相同). 结果矩阵和结合 m1 和 m2 的效果相同. 即: 设一点 p, (p * out) = ((p * m1) * m2). 可以以这个方法改变多次. 注意矩阵的乘法不遵守乘法交换律, 即: matrix_mul(m1, m2) != matrix_mul(m2, m1).

fixed vector_length(fixed x, fixed y, fixed z);
float vector_length_f(float x, float y, float z);
计算向量 (x,y,z) 的长度. 使用勾股定理.


void normalize_vector(fixed *x, fixed *y, fixed *z);
void normalize_vector_f(float *x, float *y, float *z);
将向量 (*x,*y,*z) 转换成单位矢量. 转换后的向量与原向量方向相同, 但长度为1.


fixed dot_product(fixed x1, y1, z1, x2, y2, z2);
float dot_product_f(float x1, y1, z1, x2, y2, z2);
点乘向量 (x1,y1,z1) 和 (x2,y2,z2), 并返回点乘结果.


void cross_product(fixed x1, y1, z1, x2, y2, z2, *xout, *yout, *zout);
void cross_product_f(float x1, y1, z1, x2, y2, z2, *xout, *yout, *zout);
计算向量 (x1,y1,z1) 和 (x2,y2,z2)的叉乘, 将结果存放在 (*xout, *yout, *zout). 叉乘产生一个垂直原来两个向量的向量,用该方法可以计算一个面的法向量.

fixed polygon_z_normal(V3D *v1, V3D *v2, V3D *v3);
float polygon_z_normal_f(V3D_f *v1, V3D_f *v2, V3D_f *v3);
该函数找到指定顶点法向量的 Z 分量 (所指定的顶点必须是凸多边形的顶点). 该函数通常用来实现背面裁剪. 一个封闭多边形的背面对观察者来说都是不可见的, 所以它们不需要显示.通过背面裁剪可以大约可以将一个场景中的多边形数目减少一半. 如果是负值, 多边形可以安全的被裁减. 如果是 0, 多边形垂直于屏幕.


void apply_matrix(MATRIX *m, fixed x, y, z, *xout, *yout, *zout);
void apply_matrix_f(MATRIX_f *m, float x, y, z, *xout, *yout, *zout);
将点(x,y,z) 与转换矩阵 m 相乘, 并把结果存放在 (*xout, *yout, *zout)中.


void set_projection_viewport(int x, int y, int w, int h);
设置用来缩放 persp_project() 函数输出的视口 (Viewport). 输入用户将要使用的屏幕尺寸, 一般为0, 0, SCREEN_W, SCREEN_H.


void persp_project(fixed x, y, z, *xout, *yout);
void persp_project_f(float x, y, z, *xout, *yout);
将 3维坐标中的点 (x, y, z) 投影变换到2维的屏幕空间, 将结果存放在 (*xout, *yout).该函数使用先前通过 set_projection_viewport() 函数设置的缩放参数. 该函数从一个标准化的视锥投影. 所谓标准化的视锥可以看成是放置在原点面朝 Z 轴正方向的一个照相机. X 轴从左到右,Y 轴从上到下,Z 轴指向屏幕里面. 该相机有 90 度的视角, 即在 x=z, -x=z 平面上的点会被投影到屏幕的左边界或右边界, y=z, -y=z屏幕上的点会被投影到屏幕的上边界或下边界. 如果用户想使用不同的视角或照相机位置, 则需通过一个适当的视见矩阵(Viewing Matrix) 将所有对象进行变换. 也就是说如果想产生一个照相机朝左旋转 10 度的效果, 则需将所有对象朝右旋转10度.




返回