计算机图形:图形软件

发布时间 2023-10-23 09:10:00作者: 明明1109

图形软件有2类:专用软件包、通用编程软件包。

  • 专用软件包:为非程序员设计,在某些应用中能用来生成图形、表格,而不必关心图形函数。接口通常是一组菜单,用户通过菜单与程序通信。e.g. 绘画程序,各种建筑、商务、医学及工程CAD。

  • 通用编程软件包:提供一个可用于C、C++、Java等高级语言程序设计的图形函数库。典型的基本函数用来描述图元(直线、多边形、球面和其他对象)、设定函数、选择观察的场景、进行旋转或其他变换等。e.g. GL、OpenGL、VRML(Virtual-Reality Modeling Language,虚拟现实建模语言)、Java2D、Java3D等。

图形函数库跟APP、硬件之间的软件接口,称为计算机图形应用编程接口(CG API)。

坐标

生成图形时,如何描述对象的位置、形状?
e.g. 立方体由顶点位置描述,球由中心位置+半径描述。通用图形软件包要求在标准笛卡尔坐标下,给出图形位置描述。

建模过程中,各个物体各自的坐标系称为 建模坐标系(modeling coordinate),也成局部坐标系(local coordinate),或主坐标系(master coordinate)。一旦指定物体形状,可将其放到世界坐标系(world coordinate)中。过程涉及 局部坐标系 => 世界坐标系 的转换。

显示设备的坐标系称为设备坐标系(device coordinate) ,对显示器来说是屏幕坐标系(screen coordinate)。


图形功能

通用图形软件包提供子程序,为用户提供建立和管理图形的各种功能。这些子程序可按照它们是否处理输入、输出、属性、变换、观察、图形分割或一般的控制而进行分类。

图形的基本构造块称为图形输出图元(graphics output primitive),包括字符串、集合成分,如点、直线、曲线、填充区域(通常为多边形)以及由彩色阵列定义的形状。

属性(attribute)是输出图元的特性,描述图元如何显示,包括颜色设定、线型、文本格式及区域填充图案等。

可用几何变换(geometric transformation)改变场景中一个对象的大小(size)、位置(position)、方向(rotation)。有些图形包提供一组函数实现建模变换(modeling transformation),将建模坐标系中对象描述组织成场景。通常将复杂对象描述为树形结构。

对象形状、属性的描述函数构造场景后,图形软件包将选定视图投影到输出设备。观察变换(viewing transformation)用来指定将要显示的视图、使用的投影类型,及在输出显示区域出现的范围。还有些函数通过指定位置、大小、结构,来管理屏幕显示范围;对于三维场景,还要判定可见对象、应用光照条件。

交互式图形APP有多种输入设备:鼠标、数据板、操纵杆等。输入函数(input function)用来控制和处理这些交互设备的数据流。


软件标准

标准化图形软件主要目标:可移植性。

  • 图形软件标准

第一个:1984年 2D图形核心系统(Graphical Kernel System,GKS)。

第二个:分层结构交互图形标准(PHIGS),对GKS扩充,提供层次式对象建模、颜色设定、表面绘制、图形管理等功能。

第三个:GL(Graphics Library)函数集,图形界广泛,事实图形标准。快速、实时绘制。

第四个:90年代 OpenGL,是GL与硬件无关的版本,由若干图形公司和OpenGL委员会维护、更新。OpenGL为高效处理3D应用而设计。


OpenGL简介

OpenGL函数库用来描述图元、属性、几何变换、观察变换,及其他许多的操作。OpenGL与硬件无关,因此不包括输入、输出函数等,但OpenGL辅助库支持。

OpenGL语法

函数名gl前缀,单词第一个字母大写,如:

glBegin, glClear, glCopyPixels, glPolygonMode

常量/宏,均采用大写(以GL_开头):

GL_2D, GL_RGB, GL_CCW, GL_POLYGON, GL_AMBIENT_AND_DIFFUSE

数据类型,为屏蔽不同机器差异,OpenGL采用内置数据类型(以GL开头,其余小写):

GLbyte, GLshort, GLint, GLfloat, GLdouble, GLboolean

相关库

OpenGL基本库,也称OpenGL核心库。
其他库:

  • GLU(OpenGL Utility,OpenGL实用函数)可设置观察和投影矩阵,利用线条、多边形近似法描述复杂对象,使用线性近似法显示二次曲线、样条曲线,处理表面绘制操作,完成其他复杂任务。每个OpenGL实现都包括GLU库,前缀glu。
  • Open Inventor 面向对象工具包,为交互式三维应用提供函数和预定义的对象形状。
  • GLX(OpenGL Extension to the X Window System,,OpenGL的X窗口系统扩充)用于创建显示窗口,前缀glx,。
  • Apple GL(AGL),Apple可用来进行窗口管理操作,前缀agl。
  • WGL,Windows到OpenGL的接口,前缀wgl。
  • GLUT(OpenGL Utility Toolkit,OpenGL实用函数工具包),提供与任意屏幕窗口系统进行交互的函数库,也包含描述与绘制二次和样条曲线及曲面的方法,前缀glut。

头文件

Windows下,引入OpenGL基本库:

#include <windows.h> // for WGL
#include <GL/gl.h>
#include <GL/glu.h> // 许多系统都需要GLU, 包含引入窗口系统的头文件

注意:Windows平台要求在gl.h或glu.h之前包含windows.h,因为Windows版本下gl.h和glu.h内部宏是在windows.h中定义的。

如果用GLUT处理窗口,就不需要引入gl.h, glu.h。

#include <GL/glut.h>

Apple OS X下,引入OpenGL基本库:

#include <GLUT/glut.h>

GLUT窗口管理

  • 使用OpenGL库步骤

1)初始化GLUT

glutInit(&argc, argv);

2)创建窗口并设定标题

glutCreateWindow("An Example OpenGL Program");

3)用OpenGL创建一个图并将其定义传给GLUT glutDisplayFunc(将图赋给显示窗口)

假设已有OpenGL描述程序的线段函数lineSegment

glutDisplayFunc(lineSegment); // 显示线段

4)此时屏幕并未显示线段,还需执行窗口主循环
显示初始图形,并使得程序进入检查鼠标和键盘灯设备输入的循环。

glutMainLoop();

后面程序,都是在1)~4)步骤框架下修改OpenGL程序,进行交互处理。

  • 设置窗口位置、尺寸

设置窗口左上角坐标(50, 100):

glutInitWindowPosition(50, 100);

设置窗口大小(400, 300):

glutInitWindowSize(400, 300);
  • 设置窗口缓存、颜色模型

使用单个缓存,由RGB三元素颜色模型:

glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
// GLUT_SINGLE 单缓存
// GLUT_RGB 颜色模型为RGB
// 上面2个选项都是默认选项

一个典型的OpenGL程序

下面代码绘制一条直线段:

#include <iostream>
#include <windows.h>
#include <gl/glut.h> // Or others, depending on the system in use
void init(void)
{
       // 将窗口背景色设为白色
       glClearColor(1.0, 1.0, 1.0, 0.0); // Set display-window color to white
       // 设置投影类型(模式)和其他观察参数
       glMatrixMode(GL_PROJECTION);                      // Set projection parameters
       gluOrtho2D(0.0, 200.0, 0.0, 150.0);
}

void lineSegment(void)
{
       // 用glClearColor指定的值来设定窗口颜色
       glClear(GL_COLOR_BUFFER_BIT);       // Clear display window
       // 设置将要显示的场景中对象颜色
       // 3f表示3个参数都是浮点数 GLfloat
       glColor3f(0.0, 0.4, 0.2); // Set line segment color to green
       // 建立线段
       glBegin(GL_LINES);
       glVertex2i(180, 15);                // Specify line-segment geometry
       glVertex2i(10, 145);
       glEnd();
       // 强制执行由计算机系统存放在缓存中不同位置的OpenGL函数
       glFlush(); // Process all OpenGL routines as quickly as possible
}

int main(int argc, char* argv[])
{
       glutInit(&argc, argv);                            // Initial GLUT
       glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // Set display mode
       glutInitWindowPosition(50, 100);     // Set top-left display-window position
       glutInitWindowSize(400, 300);        // Set display-window width and height
       glutCreateWindow("An Example OpenGL Program");      // Create display window
       init();                                   // Execute initialization procedure
       glutDisplayFunc(lineSegment);             // Send graphics to display window
       glutMainLoop();                           // Display everything and wait
       return 0;
}

lineSegment()用于描绘图形,作为显示回调函数(display callback function),通过glutDisplayFunc注册。

OpenGL出错处理

OpenGL和GLU记录错误方法较简单:当OpenGL发现对gl或glu子程序的调用有错误时,会在内部记录一个出错编码,而出错子程序被忽略(不影响OpenGL内部状态、帧缓存)。

缺点:OpenGL每次只记录第一个出错编码,在明确查询出错状态前,不会再记录另外的。

// 查询OpenGL出错状态
GLenum code;

// GL_NO_ERROR代表没有错误
code = glGetError(); // 调用该查询前 不会再记录其他错误

常见OpenGL出错编码:

符号常数 含义
GL_INVALID GLenum的参数超出范围
GL_INVALID_VALUE 数值常数超出范围
GL_INVALID_OPERATION 当前OpenGL状态中有一个操作非法
GL_STACK_OVERFLOW 该命令将引起栈向上溢出
GL_STACK_UNDERFLOW 该命令将引起栈向下溢出
GL_OUT_OF_MEMORY 没有足够的存储空间可用于执行命令

直接打印符号常数,并不提供特别信息,可利用gluErrorString函数将其转换为字符串。

#include <cstdio>

GLenum code;
const GLubyte* str;

code = glGetError();
str = gluErrorString(code);
fprintf(stderr, "OpenGL error: %s\n", str);

gluErrorString返回值指向GLU库内部一个字符串,非动态分配,因此不能由我们的程序管理其内存。

综上,可以对错误检查过程进行封装:

#include <cstdio>

GLenum errorCheck()
{
    GLenum code;
    const GLubyte* str;
    code = glGetError();
    if (code != GL_NO_ERROR) {
        str = gluErrorString(code);
        fprintf(stderr, "OpenGL error: %s\n", str);
    }
    return code;
}