OpenGl 游戏编程笔记 第三章: OpenGL 状态和图元 (一)
OpenGl 游戏编程笔记 第三章: OpenGL 状态和图元 (三)

OpenGl 游戏编程笔记 第三章: OpenGL 状态和图元 (二)

皮贝贝 posted @ 2010年3月30日 07:25 in opengl with tags OpenGL game 游戏 , 7225 阅读

1 处理图元

在图元中, 点, 线, 面是最重要的, 几乎所有的游戏都可以用这三种图元来进行构建. 在介绍几种图元之前, 先来看看几个常用的相关函数. 第一个是 glBegin() :


void glBegin(GLenum mode);

glBegin() 会告诉 OpenGL 两件事: 1) 你已经准备好了绘图. 2) 你准备画的图元. 参数 mode 便是 指定要绘的图元, 可以是以下几种:


参数 描述
GL_POINTS 单独的一个点
GL_LINES 单独的一条有两个顶点构成的线段
GL_LINE_STRIP 一些连接的线段
GL_LINE_LOOP 许多连接的线段构成的闭合图形, 其中第一个顶点和最后一个顶点会自动连接成线段
GL_TRIANGLES 单独的一个三角形, 有三个顶点
GL_TRIANGLE_STRIP 一些连接起来的三角形
GL_TRIANGLE_FAN 一些三角形, 公用一个顶点
GL_QUADS 四边形, 有四个顶点
GL_QUADSTRIP 连接的四边形  
GL_POLYGON 任意顶点个数的凸边形. 通过 GLU 的相关函数可以创建凹边形

每一个 glBegin() 调用都要结对出现一个 glEnd():


void glEnd()

glEnd() 告诉 OpenGL 我已经绘完了我用 glBegin() 指定的图元.

注意, 并非所有函数都可以用在 glBegin()  glEnd() 中间. 事实上, 只有一部分, 下表列出. 使用其他的函数会产生一个 GL_INVALID_OPERATION 错误.


参数
glVertex*()
glColor*()
glSecondaryColor*()
glIndex*()
glNormal*()
glTexCoord*()
glMultiTexCoord*()
glFogCoord*()
glArrayElement()
glEvalCoord*()
glEvalPoint*()
glMaterial*()
glEdgeFlag*()
glCallList*()
glCallLists*()

 glVertex() 函数是下列形式的众多变体:


void glVertex{234}{dfis}(…);


void glVertex{234}{dfis}v(…);

你最常用的一个 glVertex() 版本是 glVertex3f(), 带有三个浮点型的顶点参数 x, y, z.

glVertex() 的一些使用例子:

 

glVertex2i(5, 29); // 在坐标 (5, 20) 处绘制一个顶点
glVertex3f(1.5, 0.5, 10.0);  //在坐标 (1.5, 0.5, 10.0) 处绘制一个顶点

GLfloat v[3] = {1.5, 0.5, 10.0};
glVertex3fv(v); // 同上. 只是形式上以数组形式来指定坐标


每次绘制一个顶点时, 会相应的应用当前 OpenGL 的状态.

1.1 在 3D 中绘制点

绘制一个点主要是下面代码:

 

glBegin(GL_POINTS);
        glVertex3f(0.0, 0.0, 0.0);
glEnd();


 

首先, 通过 GLPOINTS 传给 glBegin() 来告诉 OpenGL 你要开始绘制点. 第二行, 你告诉绘制的点在 什么位置. 最后, 用 glEnd() 告诉 OpenGL 你已经完成了绘制. 注意 glBegin() / glEnd()之间的 代码缩进不是必须的, 但是让代码更易读.

若要绘制第二个点怎么办? 你可以这样:

 

glBegin(GL_POINTS);
        glVertex3f(0.0, 0.0, 0.0);
glEnd();

glBegin(GL_POINTS);
        glVertex3f(0.0, 1.0, 0.0);
glEnd();


 

代码看起来有点乱. 仔细观察 GL_POINTS 是一个复数形式, 意味着 glBegin() / glEnd() 代码 块中可以绘制多个点:

 

glBegin(GL_POINTS);
        glVertex3f(0.0, 0.0, 0.0);
        glVertex3f(0.0, 1.0, 0.0);
glEnd();


 

这样更短, 也更快, 更好.

OpenGL 让你可以控制图元的绘制, 当然点也不例外. 你可以控制三类事情: 点的大小, 反走样, 点的尺寸和透明度效果

1.1.1 修改点的大小

欲更改点的大小, 使用:


void glPointSize(GLfloat size);

结果是在指定的点的坐标周围会画出一个 size * size 大小的正方形. 默认大小是 1.0. 如果反走样特性 被禁用(默认), 点的大小会调整到最接近的一个整数值上(最小为1). 你可以通过调用 glGet() 传入 GL_POINT_SIZE 来得到当前的大小. 下面是一个例子:

// 得到当前点的大小 GLfloat oldSize; glGetFloatv(GLPOINTSIZE);

// 如果点大小很小, 设为 5.0, 否则使用默认呢大小 1.0 if (oldSize < 1.0) glPointSize(5.0); else glPointSize(1.0);

1.1.2 反走样处理

若想使用反走样, 通过使用 glEnable() 传入 GL_POINT_SMOOTH 参数(同样地, 可以使用 glDisable() 传入 GL_POINT_SMOOTH 参数来关掉). 若想得知当前是否启用, 可以使用 GL_POINT_SMOOTH 调用 glGet() 来取得状态, 或者使用 glIsEnabled(GL_POINT_SMOOTH) . 下面是一个例子:

 

if (!glIsEnabled(GL_POINT_SMOOTH))
        glEnable(GL_POINT_SMOOTH);


 

反走样开启时, 支持的点大小范围是离散,不连续的. 一般是 1.0 为一个大小步伐. 如果使用不支持的大小, 会自动转化为接近的一个支持的值. 要想知道你的OpenGL 实现支持范围, 你可以通过调用 glGet() 的 GL_POINT_SIZE_RANGE. 还可以 通过 GL_POINT_SIZE_GRANULARITY 来获知大小步伐.

 

GLfloat sizes[2];
GLfloat granularity;

glGetFloatv(GL_POINT_SIZE_RANGE, sizes);
GLfloat minPointSize = sizes[0];
GLfloat maxPointSize = sizes[1];

glGetFloatv(GL_POINTS_SIZE_GRANULARITY, &granularity);


NOTE:
需要开启混色(Blending)效果来保证反走样正常工作.

1.1.3 距离效果

有时我们根据特定的应用需要点间看起来远一些, 可以通过 glPointParameter() 实现:


void glPointParameter{if}(enum pname, type param);
void glPointParameter{if}v(enum pname, const type *params);

参数pname 可以取下面的值:


参数
GL_POINT_SIZE_MIN
GL_POINT_SIZE_MAX
GL_POINT_DiSTANCE_ATTENUATION
GL_POINT_FADE_THRESHOLD

1.1.4 一个绘点例子

截取主要的代码部分:

 

float pointSize = 0.5;

// 绘制一行大小递增的点
for (float point = -4.0; point < 5.0; point += 0.5)
{
    // 设置点的大小
    glPointSize(pointSize);

    // 绘制点
    glBegin(GL_POINTS);
    glVertex3f(point, 0.0, 0.0);
    glEnd();

    // 设置下一个点的大小
    pointSize += 1.0;
}

./image/opengl3_point.jpg

 

在这个例子中, 我们沿着 x 轴绘制了一行点. 每个点的大小递增 0.5. 对每个点, 我们先设定点的大小 (glPointSize()), 然后用 glBegin(GL_POINTS) 来开始绘点, 通过 glVertex3f() 来具体绘制 , 最后 glEnd() 表示绘制完成.

1.2 3D 绘线

绘制 3D 的线, 同 2D 一样, 有两个点组成, 只要通过 GL_LINES 绘制两个点就可以了:

 

glBegin( GL_LINES);
        glVertex3f(-2.0, -1.0, 0.0);
        glVertex3f(3.0, 1.0, 0.0);
glEnd();


 

同绘点一样, 也可以在 glBegin() / glEnd() 代码块中绘制多条线段, 当然如果中间的点中最后一个点没有 配对的点, 那么 OpenGL 将忽略最后这个点.

OpenGL 可以允许你修改线的显示效果. 除了可以设定线的宽度, 开启反走样处理之外, 你还可以指定点画模式.

1.2.1 修改线宽

默认线宽是 1.0. 获得当前线宽通过 glGet() 传入 gl_LINE_WIDTH 参数. 更改线宽通过 glLineWidth():


void glLineWidth(GLfloat width);

使用举例:

 

// 获得当前设置
GLfloat oldWidth;
glGetFloatv(GL_LINE_WIDTH, &oldWidth);

// 若线宽太小, 设置大
if (oldWidth < 1.0)
    glLineWidth(5.0);

 

1.2.2 反走样

如同点的反走样, 你可以通过 glEnable()  glDisable() 的 GL_LINE_SMOOTH 来开启或关闭; 通过 glGet() 的 GL_LINE_SMOOTH 或 glIsDisable() 来获取当前状态. 默认, 反走样特性是关闭的.

启用反走样时, OpenGL 的默认设置线宽是 1.0. 要确定支持的 范围和规模的粒度, 可以使用 glGet() 的 GL_LINE_WIDTH_RANGE 和 GL_LINE_WIDTH_GRANDULARITY 参数来获得. 使用例程:

 

GLfloat sizes[2];
GLfloat granularity;

glGetFloatv(GL_LINE_WIDTH_RANGE, sizes);
GLfloat minLineWidth = sizes[0];
GLfloat maxLineWidth = sizes[1];

glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &granularity);


 

看起来是不是和点的反走样例程一样?

1.2.3 指定点画模式

在使用点画模式之前, 需要通过 glEnable() 的 GL_LINE_STIPPLE 来开启. 然后, 通过 glLineStipple() 来使用:


void glLineStipple(GLint factor, GLushort pattern);

参数 factor 默认是 1, 取值区间是 1~256, 指定了每个位的绘制 次数.

参数 pattern 指定了 16 位模式. 模式中的设定位会设定相应的像素; 否则他们不会被绘制出来. 值得注意的是, 整数的二进制表示是反序的, , 所以效果如下图:

下面是一个使用点画模式的例子:

 

glEnable(GL_LINE_STIPPLE);
GLushort stipplePattern = 0xFAFA; // 1111 1010 1111 1010

// 点画效果: 0101 1111 0101 1111
glLineStipple(2, stipplePattern);


 

要想知道当前的点画模式, 可以调用 glGet() 的 GL_LINE_STIPPLE_PATTERN 和 GL_LINE_STIPPLE_REPEAT. 下面是一个例子:

 

GLushort currentStipplePattern;
GLint currentStippleRepeat;

glGetShortv(GL_LINE_STIPPLE_PATTERN, &currentStipplePattern);
glGetIntv( GL_LINE_STIPPLE_REPEAT, &currentStippleRepeat);

 

1.2.4 绘线例程

你会看到有两列线, 左边一列是递增宽度的实线, 右边一列是设定点画模式 下的递增大小的线. 主要代码如下:

 

float lineWidth = 0.5;

// 绘制递增大小宽度的线
for (float line = 0.0; line < 7.0; line += 0.5)
{
    // 设定线宽
    glLineWidth(lineWidth);

    // 绘线
    glBegin(GL_LINES);
    glVertex3f(-5.0f, 0.0f, line-3.0);
    glVertex3f(-1.0f, 0.0f, line-3.0);
    glEnd();

    // 线宽递增
    lineWidth += 1.0;
}

 


接下来是点画模式的线:

 

// 重设线宽
lineWidth = 0.5;

// 开启点画模式
glEnable(GL_LINE_STIPPLE);

// 设置点画模式
// 0xAAAA = 1010 1010 1010 1010
short stipplePattern = 0xAAAA;
glLineStipple(2, stipplePattern);

// 绘制点画模式的线
for (float line = 0.0; line < 7.0; line += 0.5)
{
    // 设置线宽
    glLineWidth(lineWidth);

    // 绘线
    glBegin(GL_LINES);
    glVertex3f(1.0f, 0.0f, line-3.0);
    glVertex3f(5.0f, 0.0f, line-3.0);
    glEnd();

    // 递增线宽
    lineWidth += 1.0;
}



 

代码的不同之处在于开启了点画模式 (glEnable(GL_STIPPLE_PATTERN)), 然后设置(glLineStipple()).

 


Date: 2010-03-29 23:14:29

HTML generated by org-mode 6.30c in emacs 23

 


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter