2. On to OpenGL and 3D computer graphics
声明:该代码来自:Computer Graphics Through OpenGL From Theory to Experiments,仅用作学习参考
2.1 First Program
Square.cpp完整代码
cpp
///
// square.cpp
//
// OpenGL program to draw a square.
//
// Sumanta Guha.
///
#include <GL/glew.h>
#include <GL/freeglut.h>
// Drawing routine.
void drawScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 0.0);
// Draw a polygon with specified vertices.
//在glBegin(GL_POLYGON)和glEnd()之间绘制正方形
glBegin(GL_POLYGON);
glVertex3f(20.0, 20.0, 0.0);
glVertex3f(80.0, 20.0, 0.0);
glVertex3f(80.0, 80.0, 0.0);
glVertex3f(20.0, 80.0, 0.0);
glEnd();
glFlush();
}
// Initialization routine.
void setup(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0);
}
// OpenGL window reshape routine.
void resize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{
switch (key)
{
case 27:
exit(0);
break;
default:
break;
}
}
// Main routine.
// 主程序
int main(int argc, char **argv)
{
// 初始化GLUT库
glutInit(&argc, argv);
// 设置OpenGL上下文版本为4.3
glutInitContextVersion(4, 3);
// 设置OpenGL上下文配置为兼容配置
glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
// 设置显示模式为单缓冲和RGBA颜色模式
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
// 设置窗口大小为500x500像素
glutInitWindowSize(500, 500);
// 设置窗口位置为屏幕坐标(100, 100)
glutInitWindowPosition(100, 100);
// 创建窗口并命名为"square.cpp"
glutCreateWindow("square.cpp");
// 注册绘图回调函数
glutDisplayFunc(drawScene);
// 注册窗口重塑回调函数
glutReshapeFunc(resize);
// 注册键盘输入回调函数
glutKeyboardFunc(keyInput);
// 初始化GLEW库
glewExperimental = GL_TRUE;
glewInit();
// 执行初始化设置
setup();
// 进入GLUT事件处理循环
glutMainLoop();
}
我们通过修改参数来探索一下代码
Square.cpp运行结果
确定OpenGL window的坐标系朝向
如何确定该坐标系两个轴的方向?如何确定四个顶点代码和图像的对应关系?
我们可以通过修改坐标,看看顶点怎么移动来推测
先修改一下第一行的第一个坐标
通过修改我们知道第一个参数是控制水平方向的坐标且从左向右增大
通过修改第二个参数是控制竖直方向的坐标且从下向上增大
由上我们知道OpenGL window的坐标系为:原点在左下角,水平为x轴朝右,竖直为y轴朝上
知道了坐标系的朝向,如果我想画一个三角形该如何修改代码?
将 GLPOLYGON 修改为 GL_TRIANGLES,定义三个顶点
GL_TRIANGLES 是 OpenGL 中的一个绘图模式常量,用于指定绘制三角形。使用 glBegin(GL_TRIANGLES) 和 glEnd() 之间的顶点定义一系列独立的三角形。每三个顶点会组成一个独立的三角形。
什么是绘图模式常量?在OpenGL中,绘图模式常量用于指定绘制图元的类型
2.2 Orthographic Projection, Viewing Box and World Coordinates
glOrtho
本小节重点了解函数
OpenGL window定义决定了窗口的大小
cpp
// 设置窗口大小为500x500像素
glutInitWindowSize(500, 500); //(w,h)表示宽w个像素,高h个像素的窗口
由于OpenGL window定义决定了窗口的大小,所以小窗口的x坐标值20、大窗口的x坐标值20,大小是不一样的,故这里坐标值是没有单位的(例如cm、mm、pixels)不是绝对坐标,只是相对坐标
正交投影的视景体(Viewing Box)
视景体的各个坐标为该视景体在世界坐标系中的坐标
世界坐标系以屏幕中心为原点(0, 0, 0)??,在OpenGL中用来描述场景的坐标
虚拟三维场景中物体有实际大小的,那么世界坐标系的三个轴中一个单位大小代表的实际物理长度单位是多少?可以是inch、cm等
正交投影的代码
注意:始终设置 glOrtho(left, right, bottom, top, near, far) 的参数,以便 left < right, bottom < top, near < far。
正交投影的过程
将三维空间中的物体垂直投影(不考虑近大远小)到视景体的 front 面,为了能够让投影后的画面呈现在opengl窗口中,需要对front面进行适当缩放到窗口大小
这就是为什么二维坐标系轴上每个单元格是没有单位的,因为投影完为了适应窗口是需要对其进行缩放的,无所谓它的单位是inch、mm还是其他单位。
为了准确描述三维物体,三维坐标系轴上每个单元格是有实际单位的
例题:
世界坐标系坐标轴one unit 为1 cm,假设每个像素大小为0.2mm×0.2mm,窗口大小为500 pixel × 500 pixel
计算正方形的实际大小和渲染后在窗口中的位置
cpp
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0) //(left, right, bottom, top, near, far)
glVertex3f(20.0, 20.0, 0.0)//左下角的顶点坐标
已知 front 面大小为100,100,由于unit 单位为1cm,所以 front 面大小100cm×100cm、正方形四个顶点在 front 面上的坐标,根据坐标差得到正方形在三维空间中的实际大小为60cm×60cm、已知每个像素大小为0.2mm×0.2mm,窗口大小为500 pixel × 500 pixel,得到二维坐标系中窗口大小为500pixel×0.2mm/pixel=100mm,所以窗口大小为100mm×100mm
三维空间中front面大小为100cm×100cm,front缩放到与窗口一样大,二维空间中窗口大小为100mm×100mm,这里的比例为1/10,所以正方形的缩放比例也为1/10,由此得到二维坐标系中正方形的大小为60cm×1/10,即60mm×60mm
坐标系系统(可以用于表示世界坐标系和相机坐标系)
分为左手系和右手系
世界坐标系
观察框(相机视景体)和我们自己创建的对象(3D模型),都位于世界空间中,并在世界坐标中指定。尽管世界坐标系可以位于您喜欢的任何位置,但通常,想象一下 x 轴沿显示器底部向右,y 轴向上,而 z 轴朝向屏幕外侧,这通常很有帮助。
在单目相机中,我们通常选择拍摄第一张图像时的相机坐标系作为世界坐标系,(第一帧相机坐标系的相机外参矩阵是单位矩阵)即以拍摄第一张图像时相机的光心(小孔)作为原点,X轴为水平方向,Y轴为竖直方向,Z轴指向拍摄第一张图像时相机所观察的方向。选定后世界坐标系便不再发生变化,即不变且唯一 引用:相机模型中的世界坐标系究竟指什么?
在双目相机(A,B)中,与单目相机大同小异,我们可选取其中一个相机A拍摄第一张图像时的相机坐标系为世界坐标系,即以相机A拍摄第一张图像时相机的光心(小孔)作为原点,X轴为水平方向,Y轴为竖直方向,Z轴指向拍摄第一张图像时相机A所观察的方向。引用:相机模型中的世界坐标系究竟指什么?
更改一下观察框在世界空间中的位置
显示世界坐标系中x轴(-100,100)范围、y轴(-100,100)范围、z轴(-1,1)范围内的内容
更改前后对比图
例题:更改glOrtho参数,在运行程序前,手动计算更改参数后的正方形渲染位置
原理一样,我们仅计算第一个
cpp
glOrhto(0.0, 200.0, 0.0, 200.0, -1.0, 1.0)
//left=0.0, right=200.0, bottom=0.0, top=200.0, near=-1, far=1.0
window大小从(500,500)变为(500,250),y方向缩小一半,正方形由原来(60,60)像素大小,变为(60,30),60×30=180
viewing box 的宽/高=100/100=1/1, window的宽/高=500/250=2/1
注意:viewing box的纵横比 (= 宽度/高度) 应设置为 OpenGL 窗口的纵横比 (= 宽度/高度) ,否则场景将变形。
2.3 The OpenGL Window and Screen Coordinates
glutInitWindowSize、glutInitWindowPosition
glutInitWindowSize(w,h)命令,该命令将 OpenGL 窗口的大小设置为宽度 w 和高度 h(以像素为单位)
glutInitWindowPosition(x,y) 指定计算机屏幕上 OpenGL 窗口左上角的位置 (x,y)
电脑显示器屏幕的坐标系
显示器屏幕坐标系(黄色)、OpenGL window坐标系(红色)
2.4 Clipping
如果三维空间中的物体出现在了viewing box之外,那么会发生什么情况?
由于整个正方形在viewing box之外,所以在渲染之前被裁剪了,因而在window中没有显示出来
通过修改glOrtho参数,即修改视景体在世界坐标系中的位置,使得该正方形位于viewing box内部,最终将其显示出来
我们试着让三角形其中一部分在viewing box中,另外一部分在viewing box之外
我们试着将第一个顶点的z值进行调整,看看渲染效果会有什么变化
物体超出viewing box的部分被裁剪了
2.5 Color,OpenGL State Machine,and Interpolation
本小节探索函数 glColor3f(red, green, blue),每个部分的值在0到1范围内,每个值决定了红绿蓝的强度,该函数指定前景颜色
注意:每个颜色值都被限制在范围 [01] 内。这意味着,如果一个值恰好设置为大于 1,则将其视为 1;如果小于 0,则视为 0。
如果在红色前定义黑色(前景色是变量的集合之一,叫做状态变量,这决定了OpenGL的状态,状态变量state variables还包含point size, line width, line stipple, material properties, etc ),则最终显示前景色为红色,即当前状态(current state)或前景色状态为红色
OpenGL 将保持其当前状态并运行,直到做出更改状态变量的声明。因此,OpenGL 通常被称为状态机(state machine)
glClearColor、glClear
glClearColor(red, green, blue,alpha) 设置背景色
glClear(GL_COLOR_BUFFER_BIT) 清除颜色缓冲区,将其所有像素设置为之前通过glClearColor函数指定的颜色,以便为新的绘制操作提供一个干净的画布
如果将四个顶点指定为不同颜色,则正方形内部将会根据四个顶点颜色进行自动插值
cpp
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0); //原值
glOrtho(0.0, 100.0, 0.0, 100.0, 1.0, -1.0); //修改后的值,这里修改了viewing box的near和far面
我们发现near和far符号与原来相反后,渲染得到的图像并没有发生改变
因为OpenGL always projects up the z-axis, i.e., in its positive direction.
cpp
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0); //原值
glOrtho(100.0, 0.0, 0.0, 100.0, -1.0, 1.0); //修改后的值,这里修改了viewing box的left和right面
cpp
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0); //原值
glOrtho(0.0, 100.0, 100.0, 0.0, -1.0, 1.0); //修改后的值,这里修改了viewing box的bottom和top面
2.6 OpenGL Geometric Primitives
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
glPolygonMode(face, mode)用于控制多边形的绘制模式
face:GLFRONT,GLBACK or GL_FRONT_AND_BACK
• 顺时针绕序(Clockwise, CW):如果从观察者的视角看,顶点按顺时针顺序连接,则该多边形的面被认为是"前面"。
• 逆时针绕序(Counter-Clockwise, CCW):如果从观察者的视角看,顶点按逆时针顺序连接,则该多边形的面被认为是"前面"。
默认情况下,OpenGL将逆时针绕序的多边形面视为前面(正面),顺时针绕序的多边形面视为后面(背面)。你可以通过glFrontFace函数来改变这个默认设置。
顶点的定义顺序已经决定了三角形是顺时针绕序还是逆时针绕序
mode:GL_FILL,GL_LINE or GL_POINT
• GL_POINT:将多边形的每个顶点绘制为点。
• GL_LINE:将多边形的边绘制为线。
• GL_FILL:将多边形填充为实心。
第二个三角形中三个点定义顺序连接,则连接次序为顺时针
GL_TRIANGLE_STRIP
GL_TRAINGE_FAN
glRectf函数
2.7 Approximating Curved Objects
2 π 2\pi 2π/ numVertices \text{numVertices} numVertices将圆弧分为几等份,每等份对应的角度
cpp
/
// circle.cpp
//
// This program draws a line loop with vertices equally apart on
// a fixed circle. The larger the number of vertices the better
// the loop approximates the circle.
//
// Interaction:
// Press +/- to increase/decrease the number of vertices of the loop.
//
// Sumanta Guha.
/
#define _USE_MATH_DEFINES
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
// Globals.
static float R = 40.0; // Radius of circle.
static float X = 50.0; // X-coordinate of center of circle.
static float Y = 50.0; // Y-coordinate of center of circle.
static int numVertices = 5; // Number of vertices on circle.
// Drawing routine.
void drawScene(void)
{
float t = 0; // Angle parameter.
int i;
glClear(GL_COLOR_BUFFER_BIT);
// Draw a line loop with vertices at equal angles apart on a circle
// with center at (X, Y) and radius R, The vertices are colored randomly.
glBegin(GL_LINE_LOOP);
for (i = 0; i < numVertices; ++i)
{
glColor3f((float)rand() / (float)RAND_MAX,
(float)rand() / (float)RAND_MAX,
(float)rand() / (float)RAND_MAX);
glVertex3f(X + R * cos(t), Y + R * sin(t), 0.0);
t += 2 * M_PI / numVertices;
}
glEnd();
glFlush();
}
// Initialization routine.
void setup(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0);
}
// OpenGL window reshape routine.
void resize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{
switch (key)
{
case 27:
exit(0);
break;
case '+':
numVertices++;
glutPostRedisplay();
break;
case '-':
if (numVertices > 3) numVertices--;
glutPostRedisplay();
break;
default:
break;
}
}
// Routine to output interaction instructions to the C++ window.
void printInteraction(void)
{
std::cout << "Interaction:" << std::endl;
std::cout << "Press +/- to increase/decrease the number of vertices on the circle." << std::endl;
}
// Main routine.
int main(int argc, char** argv)
{
printInteraction();
glutInit(&argc, argv);
glutInitContextVersion(4, 3);
glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("circle.cpp");
glutDisplayFunc(drawScene);
glutReshapeFunc(resize);
glutKeyboardFunc(keyInput);
glewExperimental = GL_TRUE;
glewInit();
setup();
glutMainLoop();
}
键盘输入+增加点,输入'-'减少点
cpp
// Globals.
static float R = 20.0; // Radius of circle.
static float X = 50.0; // X-coordinate of center of circle.
static float Y = 50.0; // Y-coordinate of center of circle.
static int numVertices = 5; // Number of vertices on circle.
// Drawing routine.
void drawScene(void)
{
float t = 0; // Angle parameter.
int i;
glClear(GL_COLOR_BUFFER_BIT);
// Draw a line loop with vertices at equal angles apart on a circle
// with center at (X, Y) and radius R, The vertices are colored randomly.
glBegin(GL_LINE_STRIP);
for (i = 0; i < numVertices; ++i)
{
glColor3f((float)rand() / (float)RAND_MAX,
(float)rand() / (float)RAND_MAX,
(float)rand() / (float)RAND_MAX);
/*glVertex3f(X + R * cos(t), Y + R * sin(t), 0.0);
t += 2 * M_PI / numVertices;*/
glVertex3f(X + (R + i * R / numVertices) * cos(t), Y + (R + i * R / numVertices) * sin(t), 0.0);
t -= 6 * M_PI / numVertices; // Increment angle for next vertex -=顺时针 +=逆时针
//3 turns 6*pi
}
glEnd();
glFlush();
}
2.8 Three Dimensions,the Depth Buffer and Perspective Projection
cpp
void drawScene(void)
{
float angle;
int i;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the buffers including the depth buffer.
glPolygonMode(GL_FRONT, GL_FILL);
// Upper left circular annulus: the white disc overwrites the red disc.
glColor3f(1.0, 0.0, 0.0);
drawDisc(20.0, 25.0, 75.0, 0.0);
glColor3f(1.0, 1.0, 1.0);
drawDisc(10.0, 25.0, 75.0, 0.0);
// Upper right circular annulus: the white disc is in front of the red disc blocking it.
glEnable(GL_DEPTH_TEST); // Enable depth testing.
glColor3f(1.0, 0.0, 0.0);
drawDisc(20.0, 75.0, 75.0, 0.0);
glColor3f(1.0, 1.0, 1.0);
drawDisc(10.0, 75.0, 75.0, 0.5); // Compare this z-value with that of the red disc.
glDisable(GL_DEPTH_TEST); // Disable depth testing.
// Lower circular annulus: with a true hole.
if (isWire) glPolygonMode(GL_FRONT, GL_LINE);else glPolygonMode(GL_FRONT, GL_FILL);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLE_STRIP);
for (i = 0; i <= N; ++i)
{
angle = 2 * M_PI * i / N;
glVertex3f(50 + cos(angle) * 10.0, 30 + sin(angle) * 10.0, 0.0);
glVertex3f(50 + cos(angle) * 20.0, 30 + sin(angle) * 20.0, 0.0);
}
glEnd();
// Write labels.
glColor3f(0.0, 0.0, 0.0);
glRasterPos3f(15.0, 51.0, 0.0);
writeBitmapString((void*)font, "Overwritten");
glRasterPos3f(69.0, 51.0, 0.0);
writeBitmapString((void*)font, "Floating");
glRasterPos3f(38.0, 6.0, 0.0);
writeBitmapString((void*)font, "The real deal!");
glFlush();
}
2.8.1 A Vital 3D Utility:The Depth Buffer(z-buffer)
白色圆在红色圆的前面,也就是说投影后红色圆的一部分被白色圆遮挡住了,遮挡部分会通过深度测试去除掉
上图右侧:三个点 A、B 和 C,分别是红色、绿色和蓝色,共享相同的前两个坐标值,即 X = 30 和 Y =20。 A ( 30 , 20 , z 1 ) A(30,20,z_1) A(30,20,z1)、 B ( 30 , 20 , z 2 ) B(30,20,z_2) B(30,20,z2)、 C ( 30 , 20 , z 3 ) C(30,20,z_3) C(30,20,z3)、 因此,这三个点都沿直线 L 投影到观察面上的同一点 P。由于 A 具有三个坐标中最大的 z 坐标,因此它遮挡了其他两个坐标,因此 P 被绘制为红色
深度测试(Depth Test)
z-buffer 本身是 GPU 中的一个内存块,其中每个像素一个z 值。如果启用了深度测试,当处理基元以进行渲染时,其每个点的 z 值,或者更准确地说,将其每个像素的 z 值。与当前驻留在 z-buffer中具有相同 ( x , y ) (x,y) (x,y) 处的像素的 z 值进行比较。如果传入像素的 z 值大于驻留的 z 值,则其 RGB 属性和 z 值将替换当前像素的 RGB 属性和 z 值; 否则,将丢弃传入的 Pixel 数据。
在GPU的实际实现中,z-buffer中每像素的值在0到1之间,0对应于观察盒的近面,1对应于远面。发生的事情是,在将它们记录在z-buffer中之前,系统将(通过缩放,尽管不一定是线性的)世界空间的z值转换为 [ 0 , 1 ] [0,1] [0,1]范围,并带有一个ip符号,以便离观看面更远的像素具有更高的z值。因此,在这种转换之后,较低的值实际上赢得了在z-buffer中的竞争。然而,当我们编写OpenGL代码时,我们是在世界空间中操作的,在这个空间中,观察框中的较高z值更接近观察面,我们不需要担心这种执行的特殊性。
2.8.2 A Helix and Perspective Projection
透视投影和正交投影对比
透视投影符合近大远小、正交投影无深度信息
Perspective Projection(透视投影)
下图来自:视锥体剔除(Frustum Culling)算法详解
Viewing Frustum (视锥体)
glFrustum(left, right, bottom, top, near, far)
请注意 OpenGL 的一个小怪癖,即 near 和 far 值在符号上颠倒。
给函数glFrustum输入near,far为正数,实际上为 z = − near , z = − far \text{z}=-\text{near}, \text{z}=-\text{far} z=−near,z=−far
The rendering sequence in the case of perspective projection:
(1)The shooting step again consists of projecting objects within the viewing frustum onto the viewing face
(2)The second rendering step of printing where the viewing face is proportionately scaled to fit onto the OpenGL window
透视投影会造成近大远小
glFrustum
glFrustum(left, right, bottom, top, near, far)
请注意 OpenGL 的一个小怪癖,即 near 和 far 值在符号上颠倒。
(a)后移back face
(b)后移front face
(c)前移front face
(d)放大front face
在任何采用透视投影的观看系统中,三维物体的这种边缘失真都是不可避免的。
畸变这种情况在真实的相机中也会发生,但我们不太容易注意到,因为拍照时的视野通常很大,感兴趣的物体往往位于中心位置,而且弯曲的镜头也设计有补偿作用。
视场角
2.9 Drawing Objects
2.10 Approximating Curved Objects Once More
球面坐标
2.11 An OpenGL Program End to End
cpp
#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>
#include <GL/glew.h>
#include <GL/freeglut.h>
void drawScene(void) {
glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓冲区,并将背景设置为函数glClearColor指定的颜色
glColor3f(0.0, 0.0, 0.0);//设置前景颜色
glBegin(GL_POLYGON);//开始绘制多边形
//设置顶点坐标
glEnd();//结束绘制多边形
glFlush();//强制队列中的所有命令实际执行,清空或刷新命令缓冲区,这在本例中意味着多边形被绘制出来
}
void resize(int w, int h) {
glViewport(0, 0, w, h);//设置视口,视口是OpenGL窗口中的一个矩形区域,OpenGL在这个区域内绘制图形
//激活投影矩阵堆栈,将单位矩阵放在堆栈的顶部,然后将单位矩阵与对应于最终glOrtho()命令的矩阵相乘
glMatrixMode(GL_PROJECTION);//设置当前矩阵为投影矩阵
glLoadIdentity();//如果不重置矩阵,之前的变换会影响后续的变换。调用glLoadIdentity()可以确保从一个已知的、干净的状态开始进行新的变换。
//投影矩阵用于将相机坐标系转换到裁剪坐标系。它定义了视景体(Viewing Frustum),即相机能看到的空间区域
//投影矩阵可以是透视投影矩阵(glFrustum或gluPerspective)或正交投影矩阵(glOrtho)
//透视投影矩阵会模拟人眼的透视效果,使得远处的物体看起来更小。正交投影矩阵则不会有这种效果,所有物体的大小都是一致的
glFrustum(-5.0, 5.0, -5.0, 5.0, 5.0, 100.0);
//glOrtho(-5.0, 5.0, -5.0, 5.0, 5.0, 100.0);
//激活模型视图矩阵堆栈,并将单位矩阵放在堆栈的顶部,
//以准备在绘图例程中进行模型视图变换命令,这些命令用于移动对象
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();//如果不重置矩阵,之前的变换会影响后续的变换。调用glLoadIdentity()可以确保从一个已知的、干净的状态开始进行新的变换。
}
//处理ASCII键的回调函数是keyInput(unsigned char key, int x, int y)。
//当按下一个ASCII键时,该键会作为参数key传递给这个回调函数,同时鼠标的位置会作为参数x和y传递
void keyInput(unsigned char key, int x, int y) {
switch (key) {
case 27:
exit(0);
break;
}
}
void setup() {
glClearColor(1.0, 1.0, 1.0, 0.0);//设置背景颜色为白色
}
int main(int argc, char** argv)
{
/*初始化FreeGLUT库。FreeGLUT是一个用于管理窗口和监控鼠标、键盘输入的库
• 该函数需要传递命令行参数argc和argv,以便FreeGLUT可以处理命令行选项
*/
glutInit(&argc, argv);
/* 告诉FreeGLUT程序需要一个OpenGL 4.3的上下文
• OpenGL上下文是OpenGL实例与系统其余部分之间的接口
*/
glutInitContextVersion(4, 3);
glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
/* 设置OpenGL的显示模式为单缓冲区,并且每个像素包含红、绿、蓝和透明度四个通道
GLUT_SINGLE:单缓冲区模式意味着所有的绘图操作直接在屏幕上进行,而不是在后台缓冲区中进行
这种模式可能会导致绘图时出现闪烁,因为绘图操作是直接显示的
GLUT_RGBA:表示每个像素使用红色、绿色、蓝色和透明度(Alpha)四个通道
这意味着每个像素的颜色信息包括红、绿、蓝三种颜色以及透明度信息
*/
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
/*设置OpenGL窗口的初始大小为500x500像素
* 设置OpenGL窗口在屏幕上的初始位置,窗口左上角位于屏幕坐标(100, 100)
*/
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
/*创建一个窗口并将其命名为"×××.cpp"
*/
glutCreateWindow("hemisphere.cpp");
/*注册一个回调函数drawScene,该函数将在需要绘制OpenGL窗口时被调用
* 注册一个回调函数resize,该函数将在窗口大小改变时被调用
* 注册一个回调函数keyInput,该函数将在接收到键盘输入时被调用
*/
glutDisplayFunc(drawScene);
glutReshapeFunc(resize);
glutKeyboardFunc(keyInput);
/*设置GLEW(OpenGL Extension Wrangler Library)的实验性开关为真
这意味着即使在预发布驱动程序中实现的扩展也会被暴露出来
初始化GLEW库。GLEW库负责加载OpenGL扩展。
*/
glewExperimental = GL_TRUE;
glewInit();
/*调用初始化例程setup
这个函数通常用于设置OpenGL的初始状态,例如加载着色器、设置视口等
*/
setup();
/*开始事件处理循环。这个循环会不断调用注册的回调函数
(如drawScene、resize和keyInput),以响应窗口绘制、大小改变和键盘输入等事件
*/
glutMainLoop();
return 0;
}