研究现状

3d 图形学基础

三维图形及动画场景的显示,就是把所建立的三维空间模型,经过计算机的复杂 处理,最终在计算机二维屏幕上显示的过程。一般,设计三维图形软件要经过以 下步骤:

基于图元建立三维模型

建立三维模型,就是在三维坐标系中画三维场景。利用画点(Point)、画线 (Line)、画多边形(Polygon)等函数可以建立复杂的空间模型。在表示三维空 间时,一般用齐次坐标(Homogeneous Coordinate)。在实际应用时一般把一系 列顶点(Vertex )组织起来以构成物体或图元(Entity)。

设置观看物体的窗口和观看点(视点)

图形显示的区域称为显示窗口。流程顺序为:

  • 定义一个窗口一般由以下步骤完成:设置窗口模式,设置窗口位置、大小,初始化窗口,窗口颜色设置。
  • 清理窗口是指把窗口清成某种颜色。
  • 要观看场景,也需要一个窗口,即视口。通俗地讲,视口变大,场景被放大;视口变小,场景被缩小。

设定各物体的属性(如色彩、光照、纹理映射等)

要使物体具有真实感,就要对物体进行光照处理。在计算机图形学中,物体的真 实感应考虑两种因素:光源和物体材质。

光源具有以下的属性:

  • 颜色: 平常的光源是白色的,但在特殊的环境下,仍然会有不同的光线,一般光线的颜色及强度是以RGB三元色来表示的.
  • 光的种类:一般光可分为四种: 环境光(ambient light)、点光源(point source)、有向光(directional light 、聚光灯(spotlight).
  • 环境光: 最简单的一种光源,没有位置,对环境中的每个物体的各个方向都具有相同的强度.
  • 点光源: 点光源是射向四面八方的,它具有位置(position),但并不具有方向的属性(orientation).
  • 有向光: 这种光有方向,但没有位置,最好的例子是太阳光.这种光是平行穿过三度空间,通常是用来模拟无限远的光源.
  • 聚光灯:有方向有位置,并以圆锥形扩散.这种圆锥的形状是由阴影(umbra)及半影(penumbra)的角度来决定.

材质(texture)

每一面都可能会有其表面的材质纹路,如木制桌面或大理石地板等,通常是以2d静态图形来表示。

如果要物体动起来,还要进行图形变换(如几何变换、视窗变换和投影变换等)

三维动画,就是把三维物体通过各种三维图形变换,把它投影到视口中。OpenGL 提供双缓存来制作动画。计算机在显示前台缓存中的画面同时,在后台缓存中绘 制下一帧画面,需要显示后台缓存中的画面时,只要用函数把前后台缓存交换即 可:图形变换常用的函数,有:平移、旋转、放缩。

三维图形的二维化

三维图形是通过二维视口(屏幕)来观看的,因此,对三维图形要进行投影变换和透视变换。

屏幕三维坐标系

三维坐标系符合右手螺旋规则:X轴正方向沿屏幕向右, Y轴正方向沿屏幕向下, Z轴正方向垂直屏幕向里。

接口

数学函数

identity_matrix & identity_matrix_f

每个 3D 函数都有两套版本: 一个使用定点算法, 一个使用浮点算法.两个版本的语法基本相同,只是浮点函数和结构都有一个 '_f' 后缀. 举例来说, 定点 mg3dCorssProduct() 函数的浮点版本是mg3dCorssProductF().

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

矩阵存储在下列结构中.

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

一个表明 '什么也不做' 的单位矩阵. 一个矩阵与单位矩阵相乘得到原来的矩阵.

mg3dGetTranslationMatrix & mg3dGetTranslationMatrixF

void mg3dGetTranslationMatrix(MATRIX *m, fixed x, fixed y, fixed z);
void mg3dGetTranslationMatrixF(MATRIX_f *m, float x, float y, float z);

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

mg3dGetScalingMatrix & mg3dGetScalingMatrixF

void mg3dGetScalingMatrix(mg3dMatrix *m, fixed x, fixed y, fixed z);
void mg3dGetScalingMatrixF(mg3dMatrixf *m, float x, float y, float z);

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

mg3dGetXRotMatrix & mg3dGetXRotMatrixF

void mg3dGetXRotMatrix(mg3dMatrix *m, fixed r);
void mg3dGetXRotMatrixF(mg3dMatrixf *m, float r);

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

mg3dGetYRotMatrix & mg3dGetYRotMatrixF

void mg3dGetYRotMatrix(mg3dMatrix *m, fixed r);
void mg3dGetYRotMatrixF(mg3dMatrixf *m, float r);

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

mg3dGetZRotMatrix & mg3dGetZRotMatrixF

void mg3dGetZRotMatrix(mg3dMatrix *m, fixed r);
void mg3dGetZRotMatrixF(mg3dMatrixf *m, float r);

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

mg3dGetRotMatrix & mg3dGetRotMatrixF

void mg3dGetRotMatrix(mg3dMatrix *m, fixed x, fixed y, fixed z);
void mg3dGetRotMatrixF(mg3dMatrixf *m, float x, float y, float z);

构造一个绕任意坐标轴旋转的矩阵. 当该矩阵与某向量相乘后, 将使该向量按指定的角度绕任意坐标轴轴旋转 (旋转角度用二进制表示, 最多旋转 256 度).

mg3dgetAlignMatrix

void mg3dgetAlignMatrix(mg3dMatrix *m, fixed xfront, fixed yfront, fixed zfront, fixed xup, fixed yup, fixed zup);
void mg3dGetAlignMatrixF(mg3dMatrixf *m, float xfront, float yfront, float zfront, float xup, float yup, float zup);

旋转一个矩阵使其与指定的坐标向量对齐 (它们不需被规格化或垂直, 但 up 和 front 必须相等). 一个(1, 0, 0)的 front 向量和一个 (0, 1, 0) 的 up 向量将返回一个单位矩阵.

mg3dGetVectorRotMatrix & mg3dGetVectorRotMatrixF

void mg3dGetVectorRotMatrix(mg3dMatrix *m, fixed x, fixed y, fixed z, fixed a);
void mg3dGetVectorRotMatrixF(mg3dMatrixf *m, float x, float y, float z, float a);

构造一个按指定角度 (二进制表示,最多旋转256度) 围绕x, y, z 向量旋转的变换矩阵.

mg3dGetTransformMatrix & mg3dGetTransformMatrixF

void mg3dGetTransformMatrix(mg3dMatrix *m, fixed scale, fixed xrot, fixed yrot, fixed zrot, fixed x, fixed y, fixed z);
void mg3dGetTransformMatrixF(mg3dMatrixf *m, float scale, float xrot, float yrot, float zrot, float x, float y, float z);

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

mg3dGetCameraMatrix & mg3dGetCameraMatrixF

void mg3dGetCameraMatrix(mg3dMatrix *m, fixed x, fixed y, fixed z, fixed xfront, fixed yfront, fixed zfront, fixed xup, fixed yup, fixed zup, fixed fov, fixed aspect);
void mg3dGetCameraMatrixF(mg3dMatrixf *m, float x, float y, float z, float xfront, float yfront, float zfront, float xup, float yup, float zup, float fov, float aspect);

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

mg3dqTranslateMatrix & mg3dqTranslateMatrixF

void mg3dqTranslateMatrix(mg3dMatrix *m, fixed x, fixed y, fixed z);
void mg3dQtranslateMatrixF(mg3dMatrixf *m, float x, float y, float z);

对一个以生成的矩阵进行偏移变换的优化版本: 该函数将自动在矩阵 m 中进入偏移量, 所以没必要将两个矩阵相乘来构造偏移矩阵.

mg3dQscaleMatrix & mg3dQscaleMatrixF

void mg3dQscaleMatrix(mg3dMatrix *m, fixed scale);
void mg3dQscaleMatrixF(mg3dMatrixf *m, float scale);

对一个以生成的矩阵进行缩放变换的优化版本. 该函数将自动在矩阵 m 中进入缩放量, 所以没必要将两个矩阵相乘来构造缩放矩阵.

mg3dMatrixMul & mg3dMatrixMulF

void mg3dMatrixMul(const mg3dMatrix *m1, const mg3dMatrix *m2, mg3dMatrix *out);
void mg3dMatrixMulF(const mg3dMatrixf *m1, const mg3dMatrixf *m2, mg3dMatrixf *out);

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

mg3dVectorLength & mg3dVectorLengthF

fixed mg3dVectorLength(fixed x, fixed y, fixed z);
float mg3dVectorLengthF(float x, float y, float z);

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

mg3dNormalVector & mg3dNormalVectorF

void mg3dNormalVector(fixed *x, fixed *y, fixed *z);
void mg3dNormalVectorF(float *x, float *y, float *z);

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

mg3dDotProduct & mg3dDotProductF

fixed mg3dDotProduct(fixed x1, fixed y1, fixed z1, fixed x2, fixed y2, fixed z2);
float mg3dDotProductF(float x1, float y1, float z1, float x2, float y2, float z2);

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

mg3dCrossProduct & mg3dCrossProductF

void mg3dCrossProduct(fixed x1, fixed y1, fixed z1, fixed x2, fixed y2, fixed z2, fixed *xout, fixed *yout, fixed *zout);
void mg3dCrossProductF(float x1, float y1, float z1, float x2, float y2, float z2, float *xout, float *yout, float *zout);

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

** mg3dPolygonZNormal & mg3dPolygonZNormalF
fixed mg3dPolygonZNormal(V3D *v1, V3D *v2, V3D *v3);
float mg3dPolygonZNormalF(V3D_f *v1, V3D_f *v2, V3D_f *v3);

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

mg3dApplyMatrix & mg3dApplyMatrixF

void mg3dApplyMatrix(MATRIX *m, fixed x, y, z, *xout, *yout, *zout);
void mg3dApplyMatrixF(MATRIX_f *m, float x, y, z, *xout, *yout, *zout);

将点(x,y,z) 与转换矩阵 m 相乘, 并把结果存放在 (*xout, *yout, zout)中.

** mg3dSetProjectionViewport
void mg3dSetProjectionViewport(int x, int y, int w, int h);

设置用来缩放 mg3dPerspProject() 函数输出的视口 (Viewport). 输入用户将要使用的屏幕尺寸, 一般为0, 0, SCREEN_W, SCREEN_H.

mg3dPerspProject & mg3PerspProjectF

void mg3dPerspProject(fixed x, y, z, *xout, *yout);
void mg3dPerspProjectF(float x, y, z, *xout, *yout);

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

渲染函数

mg3dPolygon & mg3dPolygonF

void mg3dPolygon(HDC mem_dc, int type, HDC texture,
                       int vc, mg3dVp *vtx[]);
void mg3dPolygonF(HDC mem_dc, int type, HDC texture,
                        int vc, mg3dVpf *vtx[]);

向指定位图画 3D 多边形, 使用指定的渲染模式. 和常规的 Polygon() 函数不同, 这些函数不支持凹多边形或自交叉多边形. 贴图的宽度和高度必须是 2 的整数次方, 但可以不同, 比如:可以是 64x16 的贴图,但不能是 17x3 的. 顶点计数参数 (vc) 应当跟在一个包含有合适数量的顶点结构指针的数组之后: mg3dpolygon3d() 使用定点 mg3dVp结构指针, 而 mg3dPolygon3Df() 使用浮点 mg3dVpf 结构指针.

顶点数据的使用方法依赖于渲染模式,type 参数指定了多边形的渲染模式(参见宏定义)。

mg3dTriangle & mg3dTriangleF

void mg3dTriangle(HDC mem_dc, int type, HDC texture,
                  mg3dVp *v1, mg3dVp *v2, mg3dVp *v3);

void mg3dTriangleF(HDC mem_dc, int type, HDC texture,
                   mg3dVpf *v1, mg3dVpf *v2, mg3dVpf *v3);

画一个 3d 三角形, 使用定点或浮点的顶点结构.

mg3dQuad & mg3dQuadF

void mg3dQuad(HDC mem_dc, int type, HDC texture,
              mg3dVp *v1, mg3dVp *v2, mg3dVp*v3, mg3dVp *v4);

void mg3dQuadF(HDC mem_dc, int type, HDC texture,
               mg3dVpf *v1, mg3dVpf *v2, mg3dVpf *v3, mg3dVpf *v4);

画一个 3d 四边形, 使用顶点或浮点的顶点结构.

如果不使用贴图可将函数的texture可设为零,不使用zbuffer可将函数的zbuffer设为零。这些函数的重点和难点在于区域的剪切,渲染模式函数的内部实现。

mg3dClipF & mg3dClip

 int mg3dClipF(int type, float min_z, float max_z,
               int vc, mg3dVpf *vtx[], mg3dVpf *vout[],
               mg3dVpf *vtmp[], int out[]);

 int mg3dClip(int type, fixed min_z, fixed max_z,
            int vc, const mg3dVp *vtx[], mg3dVp *vout[],
            mg3dVp *vtmp[], int out[]);

裁剪vtx中的多边形。该多边形的顶点数是vc,裁剪结果 保存在vout中,vtmp和out则供该函数内部使用。

mg3dCreateZBuffer

 ZBUFFER mg3dCreateZBuffer(HDC mem_dc)

创建一个与mem_dc相同大小,颜色深度为32的ZBUFFER。

mg3dClearZBuffer

mg3dClearZBuffer(ZBUFFER zbuffer, float f)

用指定的 f 清空一个给定的zbuffer

Writes z into the given Z-buffer (0 means far away). This function should be used to initialize the Z-buffer before each frame. Actually, low-level routines compare depth of the current pixel with 1/z: for example, if you want to clip polygons farther than 10, you must call clear_zbuffer(zbuf, 0.1).

mg3dDeleteZBuffer

void mg3dDeleteZBuffer(ZBUFFER zbuffer)

释放一个zbuffer。

scene 函数

mg3dCreateScene

int mg3dCreateScene(int nedge, int npoly)

创建一场景,nedge和npoly分别表示要渲染的边数和多边形数。成功返回0,失败返回一个负值。

mg3dClearScene

void mg3dClearScene(HDC mem_dc)

初始化一场景,hMemDC是要渲染的DC。

mg3dDestroyScene

void mg3dDestroyScene(void)

销毁一场景。

mg3dDcenePolygon & mg3dScenePolygonF

int mg3dScenePolygon(int type, HDC hTextureDC, int vc, V3D *vtx[])
int mg3dScenePolygonF(int type, HDC hTextureDC, int vc, V3D_f *vtx[])

将一多边形加入渲染列表。此时不做任何渲染。该函数应在mg3dClearScene()和mg3dRenderScene()间使用。其中mg3dScenePolygon支持定点数操作,mg3dScenePolygonF支持浮点数操作。

mg3dRenderScene

void mg3dRenderScene(void)

渲染场景。完成对所有由mg3dScenePolygon()指定的多边形的渲染。一次处理一条扫描线。

links

This is the best tutorial that I have found for intro to openGL. It explains everything very well and provides good examples. Skip the part about creating a window, compiz does that part for you

http://www.falloutsoftware.com/tutorials/gl/gl0.htm

Some other tutorials/reference

http://www.paulsprojects.net/tutorials/tutorials.html http://steinsoft.net/index.php?site=...ippets/OpenGL/ http://www.codeproject.com/opengl/Ge...Primitives.asp http://gpwiki.org/index.php/Category...penGL_articles

Here are the online versions of the blue book and the red book. Blue is reference, red is a guide.

http://www.glprogramming.com/blue/ http://www.glprogramming.com/red/

Tutorials for reflection, shadows etc

http://nehe.gamedev.net/lesson.asp?index=06

SeeAlso

foot bar