OpenGL学习进程(6)第四课:点、边和图形(一)点

本节是OpenGL学习的第四个课时,下面介绍OpenGL点的相关知识:

    (1)点的概念:

    数学上的点,只有位置,没有大小。但在计算机中,无论计算精度如何提高,始终不能表示一个无穷小的点。一般情况下,OpenGL中的点将被画成单个的像素,虽然它可能足够小,但并不会是无穷小。同一像素上,OpenGL可以绘制许多坐标只有稍微不同的点,但该像素的具体颜色将取决于OpenGL的实现。

点是OpenGL中绘制一切的基础。

    (2)如何绘制点:

1)OpenGL为点的绘制提供了一系列函数:

glVertex2d
glVertex2f
glVertex3f
glVertex3fv
//数字表示参数的个数,字母表示参数的类型:
s:表示16位整数(OpenGL中将这个类型定义为GLshort),
i:表示32位整数(OpenGL中将这个类型定义为GLint和GLsizei),
f:表示32位浮点数(OpenGL中将这个类型定义为GLfloat和GLclampf),
d:表示64位浮点数(OpenGL中将这个类型定义为GLdouble和GLclampd)。
v:表示传递的几个参数将使用指针的方式。//OpenGL的很多函数都是采用这样的形式,一个相同的前缀再加上参数说明标记。同时用glVertex*来表示这一系列函数。

上面这些函数虽然函数名和参数都不相同,但功能都类似:

//下面5段代码是等效的:
(一)glVertex2i(1, 3);
(二)glVertex2f(1.0f, 3.0f);
(三)glVertex3f(1.0f, 3.0f, 0.0f);
(四)glVertex4f(1.0f, 3.0f, 0.0f, 1.0f);
(五)GLfloat VertexArr3[] = {1.0f, 3.0f, 0.0f};glVertex3fv(VertexArr3);

2)绘制不同大小的点:

点的大小默认为1个像素,也可以用glPointSize()函数改变:

#include <GL/glut.h>
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void paint(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(1,1,1,1);
    glViewport(0, 0, 400, 100);

    glViewport(0, 0, 100, 100);
    glPointSize(10.0f); glColor3f(1,0,0);
    glBegin(GL_POINTS);
    glVertex2f(0.0f,0.0f);
    glEnd();

    glViewport(100, 0, 100, 100);
    glPointSize(20.0f); glColor3f(0, 1, 0);
    glBegin(GL_POINTS);
    glVertex2f(0.0f, 0.0f);
    glEnd();

    glViewport(200, 0, 100, 100);
    glPointSize(30.0f); glColor3f(0, 0, 1);
    glBegin(GL_POINTS);
    glVertex2f(0.0f, 0.0f);
    glEnd();

    glViewport(300, 0, 100, 100);
    glPointSize(40.0f); glColor3f(1, 1, 0);
    glBegin(GL_POINTS);
    glVertex2f(0.0f, 0.0f);
    glEnd();

    glFlush();
}

int main(int argc,char ** argv)
{
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
    glutInitWindowSize(400,100);
    glutInitWindowPosition(500,300);
    glutCreateWindow("绘制不同大小的点");
    glutDisplayFunc(paint);
    glutMainLoop();
    return 0;
}

需要注意的点:

1:给所画图形指定颜色,用glColor3b()不能达到效果,要用glColor3f();

2.glVertex(float x,float y)中x,y为百分比。范围均为(-1,1)表示当前区域位置。(0,0)表示中点,(1,1)表示右上角。

3.glViewport()函数的后两个参数表示区域的宽度和高度,不代表坐标值。

4.在画图完毕不要忘了glFlush();

    (3)3D空间中点的动态绘制及动态查看:

#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#define GL_PI  3.1416f
// 定义旋转角度,在按键控制中使用
static GLfloat  xRot = 0.0f;
static GLfloat  yRot = 0.0f;

// 设置渲染状态
void SetupRC()
{
    glClearColor(0.4f, 0.4f, 0.4f, 1.0f);
    glColor3f(0.0f, 1.0f, 1.0f);
}

// 绘制场景(显示回调函数)
void RenderScene()
{
    //存储坐标和角度
    GLfloat x, y, z, angle;
    glClear(GL_COLOR_BUFFER_BIT);

    // 保存矩阵状态并旋转
    glPushMatrix();
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);//绕x轴旋转
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);//绕y轴旋转
    // 开始绘制顶点图元
    glBegin(GL_POINTS);
    // 设置Z轴的起始位置
    z = -50.0f;
    for (angle = 0.0f; angle <= (2.0f * GL_PI * 3.0f); angle += 0.1f) {
        //计算X、Y坐标
        x = 50.0f * sin(angle);//相当于在xy平面画正弦图像,并且在z轴上有纵深变化。
        y = 50.0f * cos(angle);
        //指定顶点位置
        glVertex3f(x, y, z);
        //对Z坐标值稍作修改,在下一个顶点使用
        z += 0.5f;
    }
    glEnd();
    // 恢复矩阵状态
    glPopMatrix();
    // 刷新绘图命令,此时所有未执行的OpenGL命令被执行
    glutSwapBuffers();
}

// 当窗口大小改变时由GLUT函数库调用
void ChangeSize(GLsizei w, GLsizei h)
{
    // 范围
    GLfloat nRange = 100.0f;
    // 纵横比
    GLfloat aspectRatio;
    // 防止被0所除
    if (0 == h){
        h = 1;
    }
    // 根据窗口大小设置视口
    glViewport(0, 0, w, h);
    // 选择投影矩阵,并重置坐标系统
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    // 计算窗口的纵横比(像素比)
    aspectRatio = (GLfloat)w / (GLfloat)h;
    // 定义裁剪区域(根据窗口的纵横比,并使用正投影)
    if (w <= h) {//宽 < 高
        glOrtho(-nRange, nRange, -nRange / aspectRatio, nRange / aspectRatio, -nRange, nRange);
    }
    else {//宽 > 高
        glOrtho(-nRange * aspectRatio, nRange *aspectRatio, -nRange, nRange, -nRange, nRange);
    }
    // 选择模型视图矩阵,并重置坐标系统
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

// 按键响应函数
void SpecialKeys(int key, int x, int y)
{
    if (key == GLUT_KEY_UP)
        xRot -= 5.0f;

    if (key == GLUT_KEY_DOWN)
        xRot += 5.0f;

    if (key == GLUT_KEY_LEFT)
        yRot -= 5.0f;

    if (key == GLUT_KEY_RIGHT)
        yRot += 5.0f;

    if (key> 356.0f)
        xRot = 0.0f;

    if (key< -1.0f)
        xRot = 355.0f;

    if (key> 356.0f)
        yRot = 0.0f;

    if (key< -1.0f)
        yRot = 355.0f;

    // 使用新的坐标重新绘制场景
    glutPostRedisplay();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(480, 320);
    glutCreateWindow("3D空间中点的绘制与查看");
    // 设置显示回调函数
    glutDisplayFunc(RenderScene);
    // 设置当窗口的大小发生变化时的回调函数
    glutReshapeFunc(ChangeSize);
    // 设置按键响应函数(特殊键)
    glutSpecialFunc(SpecialKeys);
    // 设置渲染状态
    SetupRC();
    // 启动GLUT框架的运行,一经调用便不再返回,直到程序终止
    glutMainLoop();

    return 0;
}

1)代码解释:

1.void glutSpecialFunc(void (*func)(int key, int x, int y));

glutSpecialFunc sets the special keyboard callback for the current window.The special keyboard callback is triggered when keyboard function or directional keys are pressed. The key callback parameter is a GLUT_KEY_* constant for the special key pressed. The x and y callback parameters indicate the mouse in window relative coordinates when the key was pressed. When a new window is created, no special callback is initially registered and special key strokes in the window are ignored. Passing NULL to glutSpecialFunc disables the generation of special callbacks.

  During a special callback, glutGetModifiers may be called to determine the state of modifier keys when the keystroke generating the callback occurred. An implementation should do its best to provide ways to generate all the GLUT_KEY_* special keys.

  The available GLUT_KEY_* values are:

GLUT_KEY_F1:F1 function key.
GLUT_KEY_F2:F2 function key.
......
GLUT_KEY_F12:F12 function key.
GLUT_KEY_LEFT:Left directional key.
GLUT_KEY_UP:Up directional key.
GLUT_KEY_RIGHT:Right directional key.
GLUT_KEY_DOWN:Down directional key.
GLUT_KEY_PAGE_UP:Page up directional key.
GLUT_KEY_PAGE_DOWN:Page down directional key.
GLUT_KEY_HOME:Home directional key.
GLUT_KEY_END:End directional key.
GLUT_KEY_INSERT:Inset directional key.
Note that the escape, backspace, and delete keys are generated as an ASCII character.

设置当前窗口的特定键的回调函数。x,y:当按下键时鼠标的坐标,相对于窗口左上角,以像素为单位。

注意:ESC,回车和delete键由ASCII码产生,即可以用glutKeyboardFunc()处理. 当在键盘上敲击上述按键时调用该函数.注意与glutKeyboardFunc()的区别。

2.自定义键盘监听函数(注册)

一个经常的用法是当按下ESCAPE键时退出应用程序。

  注意,我们提到过,glutMainLoop函数产生的是一个永无止境的循环。唯一的跳出循环的方法就是调用系统exit函数。这就是我们函数要做的,当按下ESCAPE键调用exit函数终止应用程序(同时要记住在源代码包含头文件stdlib.h)。下面就是这个函数的代码:

void processNormalKeys(unsigned char key,int x,int y)
{
      if(key==27)
            Exit(0);
} 

我们还可以控制特殊键的按键消息:

//当在绘图中动态指定绘图颜色时:
void processSpecialKeys(int key, int x, int y) {
         switch(key) {
                 case GLUT_KEY_F1 :
                                   red = 1.0;
                                   green = 0.0;
                                   blue = 0.0; break;
                 case GLUT_KEY_F2 :
                                   red = 0.0;
                                   green = 1.0;
                                   blue = 0.0; break;
                 case GLUT_KEY_F3 :
                                   red = 0.0;
                                   green = 0.0;
                                   blue = 1.0; break;
         }
} 

3.glPushMatrix()、glPopMatrix()

相当于栈里的入栈和出栈。glPushMatrix其实就是把当前状态做一个副本放入堆栈之中;glPopMatrix()从栈中取出一个副本,恢复成副本的状态。

glLoadIdentity();
glTranslatef(1,0,0);//向右移动(1,0,0)
glPushMatrix();//保存当前位置
glTranslatef(0,1,0);//现在是(1,1,0)了
glPopMatrix();//这样,现在又回到(1,0,0)了

4.void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)

  模型变换函数中的旋转。angle表示旋转的角度(注意单位不是弧度),(x,y,z)表示转轴。

glRotatef(45.0, 0.0, 0.0, 1.0);//表示模型沿着(0,0,1)这个轴旋转45°。

5.glFlush和glutSwapBuffers[void glFlush(void void);void glutSwapBuffers(void)]

glFlush 是强制马上输出命令执行的结果,而不是存储在缓冲区中,继续等待其他OpenGL命令。

  当执行双缓冲交换的时候,使用glutSwapBuffers。但是在有 glutSwapBuffers 的情况下,不需要 glFlush 就可以达到同样的效果,因为我们执行双缓冲交换的时候,就隐形的执行了一次刷新操作。

6.void glutPostRedisplay(void)与Void glutIdleFunc(void(*f)(void))

glutIdleFunc()空闲回调函数标识了这样一个函数:当事件队列中没有事件需要处理时(即事件队列为空时),它才有机会得到执行。

这两个函数经常这样用:

glutIdleFunc(myidle);
void myidle()
{
   glutPostRedisplay();
}

glutPostRedisplay标记当前窗口需要重新绘制。通过glutMainLoop下一次循环时,窗口显示将被回调以重新显示窗口的正常面板。多次调用glutPostRedisplay,在下一个显示回调只产生单一的重新显示回调。

2)相关知识:

1.双缓冲技术:

glutSwapBuffers函数是OpenGL中GLUT工具包中用于实现双缓冲技术的一个重要函数。该函数的功能是交换两个缓冲区指针。当我们进行复杂的绘图操作时,画面便可能有明显的闪烁。解决这个问题的关键在于使绘制的东西同时出现在屏幕上。

  双缓冲技术,是指使用两个缓冲区: 前台缓冲和后台缓冲。前台缓冲即我们看到的屏幕,后台缓冲则在内存当中,对我们来说是不可见的。每次的所有绘图操作都在后台缓冲中进行,当绘制完成时, 把绘制的最终结果复制到屏幕上,这样,我们看到所有GDI元素同时出现在屏幕上,从而解决了频繁刷新导致的画面闪烁问题。

   在OpenGL中实现双缓冲技术的一种简单方法:

1.在调用glutInitDisplayMode函数时, 开启GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);。这里将我们惯用的GLUT_SINGLE替换为GLUT_DOUBLE,意为要使用双缓冲而非单缓冲。
2. 调用glutDisplayFunc(display)注册回调函数时, 在回调函数中所有绘制操作完成后调用glutSwapBuffers()交换两个缓冲区指针。
3. 调用glutIdleFunc注册一个空闲时绘制操作函数, 注册的这个函数再调用display函数。

2.单一的重新显示回调:

glutPostRedisplay()所执行的功能类似于直接调用显示回调函数display(),但该函数允许实现在对何时真正需要调用显示回调函数而作出决策时,变得更加“智能化”。

  在GLUT遍历整个事件循环时,必然会检索到许多要求窗口重绘的事件。如果每次都去直接调用显示回调函数,窗口必然会被多次绘制。而使用glutPostRedisplay()之后,就使得在遍历消息队列的整个过程中,只对窗口重绘一次。

(4)绘制正弦线上的点:并改变点的大小:

#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

#define GL_PI  3.1416f
const GLfloat factor = 0.1f;

void SetupRC(void)
{
    glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
}
void paint(void)
{
    GLfloat x;
    glColor3f(1.0f, 1.0f, 1.0f);
    glPointSize(4.0f);//应在glBegin之前
    glBegin(GL_POINTS);
    for (x = -10; x<10; x += 0.1)
    {
        glVertex2f(x*factor, sin(x)*factor*5);
    }
    glEnd();
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex2f(-1.0f, 0.0f);
    glVertex2f(1.0f, 0.0f);
    glVertex2f(0.0f, -1.0f);
    glVertex2f(0.0f, 1.0f);
    glEnd();
    glFlush();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);//当没有键盘监听时不需要用双缓冲模式
    glutInitWindowSize(400, 400);
    glutCreateWindow("正弦点图");
    glutDisplayFunc(paint);
    SetupRC();
    glutMainLoop();

    return 0;
}

时间: 2024-10-16 19:55:12

OpenGL学习进程(6)第四课:点、边和图形(一)点的相关文章

OpenGL教程翻译 第十四课 相机控制(一)

OpenGL教程翻译 第十四课 相机控制(一) 原文地址:http://ogldev.atspace.co.uk/(源码请从原文主页下载) Background 在之前的教程中我们学习了如何在三维场景中的任何地方放置相机.那么我们下一步就应该学着去控制这个相机.相机可以向任何方向自由移动.我们可以用鼠标和键盘控制相机--鼠标控制视口方向,键盘控制我们的位置.这些都和第一人称视角相似.这一章我们主要来学习鼠标和键盘的控制. 我们仍然使用上下左右四个方向键.记住,我们的相机的变换取决于位置.targ

OpenGL学习之路(四)

1 引子 上次读书笔记主要是学习了应用三维坐标变换矩阵对二维的图形进行变换,并附带介绍了GLSL语言的编译.链接相关的知识,之后介绍了GLSL中变量的修饰符,着重介绍了uniform修饰符,来向着色器程序传入输入参数. 这次读书笔记的内容相对有趣一些,主要是和园友们分享讨论三维坐标变换矩阵在三维几何体上的应用,以及介绍一下如何实现三维图形与用户操作的交互.这一次笔记在三维编程中也是非常重要的——我们最后开发的三维程序最终就是要和目标用户进行交互的. 之前一直没有在博客上放过gif格式的动画图片,

OpenGL学习进程(11)第八课:颜色绘制的详解

    本节是OpenGL学习的第八个课时,下面将详细介绍OpenGL的颜色模式,颜色混合以及抗锯齿.     (1)颜色模式: OpenGL支持两种颜色模式:一种是RGBA,一种是颜色索引模式. RGBA模式与索引模式的区别: 计算机必须为每个像素保存一些数据,在RGBA模式中数据就代表了颜色:而颜色索引模式中数据代表了一个索引,要获取真正的颜色值还需要查索引表.数据的数量是由帧缓存中的位面决定的.一个位面为一个像素的一个位的数据.假如是8位面的颜色,每个像素就有8个颜色位,因此就有2的8次方

OpenGL学习进程(10)第七课:四边形绘制与动画基础

    本节是OpenGL学习的第七个课时,下面以四边形为例介绍绘制OpenGL动画的相关知识:     (1)绘制几种不同的四边形: 1)四边形(GL_QUADS) OpenGL的GL_QUADS图元用于绘制四边形,它根据每四个顶点绘制一个四边形. 注意:在使用四边形时必需记住四边形的四个角必须位于同一个平面中(不存在弯曲的四边形). 2)四边形带(GL_QUAD_STRIP) 该图元指定一个连接的四边形带.它们都保持相同方向的环绕. 3)通用多边形GL_POLYGON 我们可以用它绘制任意数

OpenGL学习进程(3)第一课:初始化窗体

    本节是OpenGL学习的第一个课时,下面介绍如何初始化一个窗体:     (1)显示一个有蓝色背景的窗体: #include <GL/glut.h> #include <stdlib.h> void display(void) { /* clear all pixels */ glClear (GL_COLOR_BUFFER_BIT); glFlush (); } int main(int argc, char** argv) { glutInit(&argc, a

OpenGL学习进程(5)第三课:视口与裁剪区域

本节是OpenGL学习的第三个课时,下面介绍如何运用显示窗体的视口和裁剪区域:     (1)知识点引入:     1)问题现象: 当在窗体中绘制图形后,拉伸窗体图形形状会发生变化: #include <GL/glut.h> #include <math.h> const float Pi = 3.1415926f; const int n = 1000; const float R = 0.8f; void init(void) { glClearColor(0.0,0.0,0.

OpenGl学习进程(7)第五课:点、边和图形(二)边

本节是OpenGL学习的第五个课时,下面介绍OpenGL边的相关知识: (1)边的概念: 数学上的直线没有宽度,但OpenGL的直线则是有宽度的.同时,OpenGL的直线必须是有限长度,而不是像数学概念那样是无限的.可以认为,OpenGL的“直线”概念与数学上的“线段”接近,它可以由两个端点来确定.     (2)如何绘制边: 1)OpenGL支持绘制三种类型的边: GL_LINES :指定两个顶点,在它们之间绘制一条直线.如果为GL_LINES指定了奇数个顶点,那么最后一个顶点会被忽略. GL

OpenGL学习进程(4)第二课:绘制图形

本节是OpenGL学习的第二个课时,下面介绍如何用点和线来绘制图形:     (1)用点的坐标来绘制矩形: #include <GL/glut.h> void display(void) { // clear all pixels glClear(GL_COLOR_BUFFER_BIT); // draw yellow polygon (rectangle) with corners at glColor3f(1.0, 1.0, 0.0); glBegin(GL_POLYGON); //绘制开

OpenGl学习进程(8)第六课:点、边和图形(三)绘制图形

本节是OpenGL学习的第六个课时,下面介绍OpenGL图形的相关知识:     (1)多边形的概念: 多边形是由多条线段首尾相连而形成的闭合区域.OpenGL规定,一个多边形必须是一个“凸多边形”.通过点.直线和多边形,就可以组合成各种几何图形.一段弧可以看成是是很多短的直线段相连,这些直线段足够短,以至于其长度小于一个像素的宽度.通过位于不同平面的相连的小多边形,还可以组成一个“曲面”. 什么是凸边形: 凸边形:多边形内任意两点所确定的线段都在多边形内,由此也可以推导出,凸多边形不能是空心的