视频加载失败

课程

3327 字
约 10 分钟

1.1 OpenGL 点的绘制

计算机图形学labs/lab01-opengl-drawing·更新于 2026-09-15

1.1 OpenGL 点的绘制

1. 任务描述

根据下面要求,在右侧修改代码空间,绘制出预期输出的图片。平台会对你编写的代码进行测试。

本关任务

  1. 熟悉编程环境。
  2. 了解光栅图形显示器的特点。
  3. 了解计算机绘图的特点。
  4. 进行编程,以 OpenGL 为开发平台设计程序,以能够在屏幕上生成三个坐标、颜色和尺寸一定的点。

预期输出

实验图片

具体要求

  1. 背景色为黑色,用 glClearColor() 来完成。
  2. 渲染的点的直径设置为 3
  3. 选用 GL_POINTS 作为图形类型。
  4. 三个点的颜色分别为 (1.0f, 0.0f, 0.0f), (0.0f, 1.0f, 0.0f), (0.0f, 0.0f, 1.0f)
  5. 三个点对应的顶点坐标分别为 (-0.4f, -0.4f), (0.0f, 0.0f), (0.4f, 0.4f)

相关知识

为了完成本关任务,你需要掌握:Freeglut 库的基本用法与虚拟机代码的使用方法。

1. FreeGlut 简介

先介绍下 GLUT 库。GLUT 最初由 Mark Kilgard 编写,从 OpenGL Redbook(红宝书)第二版起就用来作为示例程序的支持环境,直到第八版为止(注:第九版开始改为 GLFW)。从那时起,GLUT 因为其简单、可用性广、可移植性强,被广泛应用于各种 OpenGL 实际应用中。Glut 最新版本为 3.7 版,大致在 1998 年 8 月停止维护和更新,同时其代码也没有开源。

Freeglut 是 Glut 库(OpenGL Utility Toolkit,OpenGL 实用工具包)的免费开源替代品。它是由 Pawel W. Olszta 在 1999 年 12 月创建,最新版本为 2015 年 3 月的 3.0 版本。

2. 基本语法

常用的程序设计语言,如 C, C++, Pascal, Fortran 和 Java 等,都支持 OpenGL 的开发。这里只讨论 C 版本下 OpenGL 的语法。

  • OpenGL 基本函数均使用 gl 作为函数名的前缀,如 glClearColor()
  • 实用函数则使用 glu 作为函数名的前缀,如 gluSphere()
  • OpenGL 基本常量的名字以 GL_ 开头,如 GL_LINE_LOOP
  • 实用常量的名字以 GLU_ 开头,如 GLU_FILL
  • 一些函数如 glColor*()(定义颜色值),函数名后可以接不同的后缀以支持不同的数据类型和格式。例如 glColor3b(...), glColor3d(...), glColor3f(...)glColor3bv(...) 等,这几个函数在功能上是相似的,只是适用于不同的数据类型和格式。其中 3 表示该函数带有三个参数,b, d, f 分别表示参数的类型是字节型、双精度浮点型和单精度浮点型,v 则表示这些参数是以向量形式出现的。

为便于移植,OpenGL 定义了一些自己的数据类型,如 GLfloat, GLvoid,它们其实就是 C 语言中的 floatvoid。在 gl.h 文件中可以看到以下定义:

typedef float GLfloat;
typedef void GLvoid;

3. freeglut 库的基本函数

  • (1) 初始化函数
    glutInit(int *argc, char **argv)
    • argc:一个指针,指向从 main() 函数传递过来的没更改的 argc 变量。
    • argv:一个指针,指向从 main() 函数传递过来的没更改的 argv 变量。
  • (2) 初始化窗口位置
    glutInitWindowPosition(int x, int y)
  • (3) 初始化窗口大小
    glutInitWindowSize(int x, int y)
  • (4) 创建窗口
    glutCreateWindow(char *title)
    • title:窗口标题。
  • (5) 绘图函数
    display(void)
  • (6) 窗口重绘函数
    glutDisplayFunc(&display)
    • display:调用的函数名称。
  • (7) 指定刷新颜色缓冲区时所用的颜色
    glClearColor(float r, float g, float b, float a)
  • (8) 刷新颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT)
  • (9) 指定栅格化点的直径
    glPointSize(int size)
  • (10) 指定绘制图形的类型
    glBegin(GLenum mode)
    • mode:图形类型。
  • (11) 设置颜色
    glColor3f(float r, float g, float b)
  • (12) 设置顶点坐标
    glVertex2f(float x, float y)
  • (13) 指定的类型结束(与 glBegin 相对)
    glEnd()
  • (14) 清空缓冲区
    glFlush()
  • (15) 所有的与“事件”有关的函数调用无限循环
    glutMainLoop()

4. 状态机制

OpenGL 的工作方式是一种状态机制,它可以进行各种状态或模式设置,这些状态或模式在重新改变它们之前一直有效。例如,当前颜色就是一个状态变量,在这个状态改变之前,绘制的每个像素都将使用该颜色,直到当前颜色被设置为其他颜色为止。

OpenGL 中大量使用了这种状态机制,如颜色模式、投影模式、单双显示缓存区的设置、背景色的设置、光源的位置和特性等。许多状态变量可以通过 glEnable(), glDisable() 这两个函数来设置成有效或无效状态,如是否设置光照、是否进行深度检测等;在被设置成有效状态之后,绝大部分状态变量都有一个默认值。

通常情况下,可以用下列四个函数来获取某个状态变量的值:glGetBooleanv, glGetDouble, glGetFloatvglGetIntegerv。究竟选择哪个函数应该根据所要获得的返回值的数据类型来决定。还有些状态变量有特殊的查询函数,如 glGetLight*, glGetErrorglPolygonStipple 等。另外,使用 glPushAttribglPopAttrib 函数空间,可以存储和恢复最近的状态变量的值。只要有可能,都应该使用这些函数,因为它们比其他查询函数的效率更高。

5. OpenGL 的坐标系统

如下图所示,OpenGL 坐标与绘图区坐标关系如下:

  • 绘图区的中心点:(0.0, 0.0, 0.0)
  • 绘图区的右上角点:(1.0, 1.0, 0.0)
  • 绘图区的左下角点:(-1.0, -1.0, 0.0)

图1 OpenGL绘图区对应坐标


开始你的任务吧,祝评测通过!

2. 我的回答

文件 step1/test1.cpp:

// 提示:写完代码请保存之后再进行评测
#include <GL/freeglut.h>
#include <stdio.h>

// 评测代码所用头文件-开始
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
// 评测代码所用头文件-结束

void myDisplay(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glPointSize(3.0f);

    glBegin(GL_POINTS);
    
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex2f(-0.4f, -0.4f);

        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex2f(0.0f, 0.0f);

        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex2f(0.4f, 0.4f);
        
    glEnd();

   /********** End **********/

 	glFlush();
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(400, 400);
	glutCreateWindow("Hello Point!");
	glutDisplayFunc(&myDisplay);
    glutMainLoopEvent();     
     
     
    /*************以下为评测代码,与本次实验内容无关,请勿修改**************/
	GLubyte* pPixelData = (GLubyte*)malloc(400 * 400 * 3);//分配内存
    GLint viewport[4] = {0};    
    glReadBuffer(GL_FRONT);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glGetIntegerv(GL_VIEWPORT, viewport);
    glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3], GL_RGB, GL_UNSIGNED_BYTE, pPixelData);

	cv::Mat img;
    std::vector<cv::Mat> imgPlanes;
    img.create(400, 400, CV_8UC3);
    cv::split(img, imgPlanes);
 
    for(int i = 0; i < 400; i ++) {
        unsigned char* plane0Ptr = imgPlanes[0].ptr<unsigned char>(i);
        unsigned char* plane1Ptr = imgPlanes[1].ptr<unsigned char>(i);
        unsigned char* plane2Ptr = imgPlanes[2].ptr<unsigned char>(i);
        for(int j = 0; j < 400; j ++) {
            int k = 3 * (i * 400 + j);
            plane2Ptr[j] = pPixelData[k];
            plane1Ptr[j] = pPixelData[k+1];
            plane0Ptr[j] = pPixelData[k+2];
        }
    }
    cv::merge(imgPlanes, img);
    cv::flip(img, img ,0); 
    cv::namedWindow("openglGrab");
    cv::imshow("openglGrab", img);
    //cv::waitKey();
    cv::imwrite("../img_step1/test.jpg", img);
	return 0;
}
Profile Image of the Author
Sonder
好想要技术
这是公告标题
这只是一个公告
分类
标签
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.16.7
文章许可
CC BY-NC-SA 4.0
文章目录