计算机图形:图形软件
图形软件分类
图形软件有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;
}