当前位置: 首页 > news >正文

On to OpenGL and 3D computer graphics

2. On to OpenGL and 3D computer graphics

声明:该代码来自:Computer Graphics Through OpenGL From Theory to Experiments,仅用作学习参考

2.1 First Program

Square.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.3glutInitContextVersion(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定义决定了窗口的大小

// 设置窗口大小为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
计算正方形的实际大小和渲染后在窗口中的位置

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参数,在运行程序前,手动计算更改参数后的正方形渲染位置

原理一样,我们仅计算第一个

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函数指定的颜色,以便为新的绘制操作提供一个干净的画布


如果将四个顶点指定为不同颜色,则正方形内部将会根据四个顶点颜色进行自动插值

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.

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面

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将圆弧分为几等份,每等份对应的角度

/         
// 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();
}


键盘输入+增加点,输入’-'减少点
1



// 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

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

#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;
}

相关文章:

On to OpenGL and 3D computer graphics

2. On to OpenGL and 3D computer graphics 声明&#xff1a;该代码来自&#xff1a;Computer Graphics Through OpenGL From Theory to Experiments&#xff0c;仅用作学习参考 2.1 First Program Square.cpp完整代码 /// // square.cpp // // OpenGL program to draw a squ…...

从曾国藩的经历看如何打破成长中的瓶颈

《曾国藩传》是一部充满智慧与人生哲理的传记&#xff0c;而曾国藩本人更是一个从“最笨”到“最智慧”的奇人。看他的成长与蜕变&#xff0c;不仅能感受到他如何超越自己的局限&#xff0c;也能从中获得关于人性、社会和历史的重要启示。曾国藩的一生让人深思&#xff0c;正是…...

JavaWeb学习-SpringBotWeb开发入门(HTTP协议)

(一)SpringBotWeb开发步骤 (1)创建springboot工程,并勾选开发相关依赖 (2)定义HelloController类,添加方法hello,并添加注解 (3)运行测试 (二)HTTP入门概述 创建请求页面 package com.itheima.demo3; /*请求处理类,加上注解标识为请求处理类*/import org.spr…...

数据库用户管理

数据库用户管理 1.创建用户 MySQL在安装是&#xff0c;会默认创建一个名位root的用户&#xff0c;该用户拥有超级权限&#xff0c;可以控制整个MySQL服务器。 在对MySQL的日常管理和操作中&#xff0c;通常创建一些具有适当权限的用户&#xff0c;尽可能的不用或少用root登录…...

BGP边界网关协议(Border Gateway Protocol)路由聚合详解

一、路由聚合 1、意义 在大规模的网络中&#xff0c;BGP路由表十分庞大&#xff0c;给设备造成了很大的负担&#xff0c;同时使发生路由振荡的几率也大大增加&#xff0c;影响网络的稳定性。 路由聚合是将多条路由合并的机制&#xff0c;它通过只向对等体发送聚合后的路由而…...

ASP.NET Core WebAPI的异步及返回值

目录 Action方法的异步 Action方法参数 捕捉URL占位符 捕捉QueryString的值 JSON报文体 其他方式 Action方法的异步 Action方法既可以同步也可以异步。异步Action方法的名字一般不需要以Async结尾。Web API中Action方法的返回值如果是普通数据类型&#xff0c;那么返回值…...

「 机器人 」仿生扑翼飞行器中的“被动旋转机制”概述

前言 在仿生扑翼飞行器的机翼设计中,模仿昆虫翼的被动旋转机制是一项关键技术。其核心思想在于:机翼旋转角度(攻角)并非完全通过主动伺服来控制,而是利用空气动力和惯性力的作用,自然地实现被动调节。以下对这种设计的背景、原理与优势进行详细说明。 1. 背景:昆虫的被动…...

「 机器人 」扑翼飞行器的数据驱动建模核心方法

前言 数据驱动建模可充分利用扑翼飞行器的已有运行数据,改进动力学模型与控制策略,并对未建模动态做出更精确的预测。在复杂的非线性飞行环境中,该方法能有效弥补传统解析建模的不足,具有较高的研究与应用价值。以下针对主要研究方向和实现步骤进行整理与阐述。 1. 数据驱动…...

个人网站搭建

搭建 LNMP环境搭建&#xff1a; LNMP环境指&#xff1a;Linux Nginx MySQL/MariaDB PHP&#xff0c;在debian上安装整体需要300MB的磁盘空间。MariaDB 是 MySQL 的一个分支&#xff0c;由 MySQL 的原开发者维护&#xff0c;通常在性能和优化上有所改进。由于其轻量化和与M…...

飞书项目流程入门指导手册

飞书项目流程入门指导手册 参考资料准备工作新建空间国际化配置新建工作项字段管理新建字段对接标识授权角色 流程管理基础说明流程节点配置流程节点的布局配置页面上布局按钮布局配置 流程节点驳回流程图展示自动化字段修改 局限性 参考资料 飞书官方参考文档&#xff1a;飞书…...

xss靶场

xss-labs下载地址&#xff1a;GitHub - do0dl3/xss-labs: xss 跨站漏洞平台 xss常见触发标签&#xff1a;XSS跨站脚本攻击实例与防御策略-CSDN博客 level-1 首先查看网页的源代码发现get传参的name的值test插入了html里头&#xff0c;还回显了payload的长度。 <!DOCTYPE …...

XML实体注入漏洞攻与防

JAVA中的XXE攻防 回显型 无回显型 cve-2014-3574...

switch组件的功能与用法

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了PageView这个Widget,本章回中将介绍Switch Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在这里介绍的Switch是指左右滑动的开关&#xff0c;常用来表示某项设置是打开还是关闭。Fl…...

cursor重构谷粒商城05——docker容器化技术快速入门【番外篇】

前言&#xff1a;这个系列将使用最前沿的cursor作为辅助编程工具&#xff0c;来快速开发一些基础的编程项目。目的是为了在真实项目中&#xff0c;帮助初级程序员快速进阶&#xff0c;以最快的速度&#xff0c;效率&#xff0c;快速进阶到中高阶程序员。 本项目将基于谷粒商城…...

高等数学学习笔记 ☞ 微分方程

1. 微分方程的基本概念 1. 微分方程的基本概念&#xff1a; &#xff08;1&#xff09;微分方程&#xff1a;含有未知函数及其导数或微分的方程。 举例说明微分方程&#xff1a;&#xff1b;。 &#xff08;2&#xff09;微分方程的阶&#xff1a;指微分方程中未知函数的导数…...

【探索 Kali Linux】渗透测试与网络安全的终极操作系统

探索 Kali Linux&#xff1a;渗透测试与网络安全的终极操作系统 在网络安全领域&#xff0c;Kali Linux 无疑是最受欢迎的操作系统之一。无论是专业的渗透测试人员、安全研究人员&#xff0c;还是对网络安全感兴趣的初学者&#xff0c;Kali Linux 都提供了强大的工具和灵活的环…...

四方连续贴图是什么意思

上下左右四个方向都是连续的图案&#xff0c;是由一个纹样或几个纹样组成一个单位&#xff0c;向四周重复地连续和延伸扩展而成的图案形式...

RKNN_C++版本-YOLOV5

1.背景 为了实现低延时&#xff0c;所以开始看看C版本的rknn的使用&#xff0c;确实有不足的地方&#xff0c;请指正&#xff08;代码借鉴了rk官方的仓库文件&#xff09;。 2.基本的操作流程 1.读取模型初始化 // 设置基本信息 // 在postprocess.h文件中定义&#xff0c;详见…...

k8s优雅重启

理论上处于terminating状态的pod&#xff0c;k8s 就会把它从service中移除了&#xff0c;只用配置一个优雅停机时长就行了。kubectl get endpoints 验证 因此&#xff0c;优雅重新的核心问题&#xff0c;是怎么让空闲长连接关闭&#xff0c;再等待处理中的请求执行完。 一些底…...

三高“高性能、高并发、高可靠”系统架构设计系列文章

目录 高并发系统的艺术&#xff1a;如何在流量洪峰中游刃有余 《数据密集型应用系统设计》读后感与高并发高性能实践案例 系统稳定性与高可用保障的几种思路 软件系统限流的底层原理解析 技术解决方案调研 延迟队列调研 重试调研 异步回调调研 分库分表调研 分布式事…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...