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();
}
键盘输入+增加点,输入’-'减少点
// 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 声明:该代码来自:Computer Graphics Through OpenGL From Theory to Experiments,仅用作学习参考 2.1 First Program Square.cpp完整代码 /// // square.cpp // // OpenGL program to draw a squ…...
python实现http文件服务器访问下载
//1.py import http.server import socketserver import os import threading import sys# 获取当前脚本所在的目录 DIRECTORY os.path.dirname(os.path.abspath(__file__))# 设置服务器的端口 PORT 8000# 自定义Handler,将根目录设置为脚本所在目录 class MyHTT…...
Redis高阶5-布隆过滤器
Redis布隆过滤器 由一个初始值都为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素 目的减少内存占用方式不保存数据信息,只是在内存中做一个是否存在的标记flag 布隆过滤器(英语:Bloom Filter࿰…...
Hive关于数据库的语法,warehouse,metastore
关于数据库的语法 在default数据库下,查看其他数据库的表 in 打开控制台 字体大小的设置 Hive默认的库: default, 1/4说明一共有4个库,现在只展示了1个,单击>>所有架构 数据库的删除 方法一: 语法 删除有表的数据库,加cascade 方法二 当前连接的数据库 切换当前数据库…...
Kafka 深入服务端 — 时间轮
Kafka中存在大量的延迟操作,比如延时生产、延时拉取和延时删除等。Kafka基于时间轮概念自定义实现了一个用于延时功能的定时器,来完成这些延迟操作。 1 时间轮 Kafka没有使用基于JDK自带的Timer或DelayQueue来实现延迟功能,因为它们的插入和…...
ubuntu系统docker环境搭建
ubuntu系统docker环境搭建 docker引擎安装 高版本docker引擎安装时已经自带有docker compose 安装参考docker官网Install Docker Engine on Ubuntu 方式一: 在线安装 参考apt方式安装 1、Set up Docker’s apt repository. # Add Dockers official GPG key: …...
安宝特方案 | AR在供应链管理中的应用:提升效率与透明度
随着全球化的不断深入和市场需求的快速变化,企业对供应链管理的要求也日益提高。如何在复杂的供应链环境中提升效率、降低成本,并确保信息的透明度,成为了各大行业亟待解决的问题。而增强现实(AR)技术,特别…...
[ Spring ] Spring Cloud Alibaba Message Stream Binder for RocketMQ 2025
文章目录 IntroduceProject StructureDeclare Plugins and ModulesApply Plugins and Add DependenciesSender PropertiesSender ApplicationSender ControllerReceiver PropertiesReceiver ApplicationReceiver Message HandlerCongratulationsAutomatically Send Message By …...
再述 Dijkstra
再述 Dijkstra 学 Dijkstra 好久了,今天再学了一遍,感觉推翻了好多自己的知识…… 定义 一种用于求非负权值的图的单源最短路径的算法。 方法 已知:如果要求从起始点 s 到某一个点 x 的最短路径,显然只能从某一个已确认为最短…...
大语言模型之prompt工程
前言 随着人工智能的快速发展,我们正慢慢进入AIGC的新时代,其中对自然语言的处理成为了智能化的关键一环,在这个大背景下,“Prompt工程”由此产生,并且正逐渐成为有力的工具... LLM (Large Language Mode…...
JavaScript系列(43)--依赖注入系统实现详解
JavaScript依赖注入系统实现详解 💉 今天,让我们深入探讨JavaScript的依赖注入系统实现。依赖注入是一种设计模式,它通过将依赖关系的创建和管理从代码中分离出来,提高了代码的可维护性和可测试性。 依赖注入基础概念 …...
Mono里运行C#脚本36—加载C#类定义的成员变量和方法的数量
前面分析了加载类和基类的基本过程, 接着来分析一下加载成员变量和方法的数量。 因为我们知道C#语言定义一个类,主要就是定义成员变量,以及那些对此成员变量进行操作的方法, 所以需要使用一种方法来描述C#语言定义类的能力。 一般情况下,主要有两种类型: 普通的类,比如前…...
SWPU 2022 新生赛--web题
奇妙的MD5 进入靶场 然我们输入一个特殊的字符串,然后我到处翻了翻,发现有提示 在MD5中有两个特殊的字符串 0e215962017 //MD5加密后弱比较等于自身 ffifdyop //MD5加密后变成万能密码 这里明显就是万能密码了 输入之后就来到了这个页…...
Windows 靶机常见服务、端口及枚举工具与方法全解析:SMB、LDAP、NFS、RDP、WinRM、DNS
在渗透测试中,Windows 靶机通常会运行多种服务,每种服务都有其默认端口和常见的枚举工具及方法。以下是 Windows 靶机常见的服务、端口、枚举工具和方法的详细说明: 1. SMB(Server Message Block) 端口 445/TCP&…...
记一次Linux共享内存段排除Bug:key值为0x0000000的共享内存段删除不了
本文目录 一、问题情况二、解决方法2.1 通过kill命令删除2.2 通过程序删除 一、问题情况 今天查看共享内存段发现好多共享内存段,而且命令ipcrm -m <shmid>删除不了。 回想了一下,应该是有一些程序跑while循环,或者死循环,…...
RV1126画面质量四:GOP改善画质
一. 什么是 GOP GOP 实际上就是两个 I 帧的间隔,比方说分辨率是 1920 * 1080 50 帧,假设 GOP 为 5,那就是大概 2s 插入一个 I 帧。我们再 回顾下,H264/H265 的帧结构。H264/H265 分别分为三种帧类型:I 帧、…...
手机app如何跳过无障碍权限实现弹框自动点击-ADB连接专题
手机app如何跳过无障碍权限实现弹框自动点击 --ADB连接专题 一、前言 我们在前期的时候,在双SIM卡进行协同外呼和SIM卡切换时,对如何在手机中“执行批处理脚本做自动点击”的内容进行预研,力图使用事件触发和坐标点击等方式来实现手机安装…...
kafka-保姆级配置说明(consumer)
bootstrap.servers #deserializer应该与producer保持对应 #key.deserializer #value.deserializer ##fetch请求返回时,至少获取的字节数,默认值为1 ##当数据量不足时,客户端请求将会阻塞 ##此值越大,客户端请求阻塞的时间越长&…...
c语言中的数组(上)
数组的概念 数组是⼀组相同类型元素的集合; 数组中存放的是1个或者多个数据,但是数组元素个数不能为0。 数组中存放的多个数据,类型是相同的。 数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。 数组创建 在C语言…...
20250122-正则表达式
1. 正则标记 表示一位字符:\\ 表示指定的一位字符:x 表示任意的一位字符:. 表示任意一位数字:\d 表示任意一位非数字:\D 表示任意一个字母:[a-zA-Z](大写或小写) 表示任意一个…...
(回溯法 子集)leetcode78
#include<iostream> #include<string> #include<vector> //只有子集需要在每个结点收集结果,其余在叶子结点收集结果 using namespace std; vector<vector<int>>ans; vector<int>combine; void backtracking(int index,vector&…...
Pyecharts之图表组合与布局优化
在数据可视化中,我们经常需要将多个图表组合在一起,以展示不同维度的数据或者进行对比分析。同时,合理的布局能够提升图表的可读性和用户体验。Pyecharts 提供了强大的组件和方法,让我们可以轻松实现图表的组合和布局优化。本篇将…...
代码随想录训练营第五十六天| 108.冗余连接 109.冗余连接II
108.冗余连接 题目链接:卡码网题目链接(ACM模式) (opens new window) 讲解链接:代码随想录 并查集可以解决什么问题:两个节点是否在一个集合,也可以将两个节点添加到一个集合中。 引自代码随想录ÿ…...
私有包上传maven私有仓库nexus-2.9.2
一、上传 二、获取相应文件 三、最后修改自己的pom文件...
二叉搜索树中的搜索(力扣700)
首先介绍一下什么是二叉搜索树。 二叉搜索树是一个有序树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉…...
社区养老服务平台的设计与实现(代码+数据库+LW)
摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱,出错率高,信息安全性差&#…...
高速光模块中的并行光学和WDM波分光学技术
随着AI大模型训练和推理对计算能力的需求呈指数级增长,AI数据中心的网络带宽需求大幅提升,推动了高速光模块的发展。光模块作为数据中心和高性能计算系统中的关键器件,主要用于提供高速和大容量的数据传输服务。 光模块提升带宽的方法有两种…...
python生成图片和pdf,快速
1、下载安装 pip install imgkit pip install pdfkit2、wkhtmltopdf工具包,下载安装 下载地址:https://wkhtmltopdf.org/downloads.html 3、生成图片 import imgkit path_wkimg rD:\app\wkhtmltopdf\bin\wkhtmltoimage.exe # 工具路径,安…...
浅谈在AI时代GIS的发展方向和建议
在AI时代,GIS(地理信息系统)的发展正经历着深刻的变革,随着人工智能技术的进步,GIS不再仅仅是传统的地图和空间数据处理工具,而是向更加智能化、自动化、精准化的方向发展。作为一名GIS开发工程师ÿ…...
【25考研】中科院软件考研复试难度分析!
中科院软件复试不需要上机!且对专业综合能力要求较高!提醒同学一定要认真复习! 一、复试内容 二、参考书目 官方并未明确给出,建议同学参考初试书目: 1)《数据结构(C语言版)》严蔚…...
【2024年华为OD机试】 (A卷,200分)- 计算网络信号、信号强度(JavaScriptJava PythonC/C++)
一、问题描述 题目解析 问题描述 我们有一个 m x n 的二维网格地图,每个格子可能是以下几种情况之一: 0:表示该位置是空旷的。x(正整数):表示该位置是信号源,信号强度为 x。-1:表示该位置是阻隔物,信号无法直接穿透。信号源只有一个,阻隔物可能有多个。信号在传播…...
SpringBoot整合Swagger UI 用于提供接口可视化界面
目录 一、引入相关依赖 二、添加配置文件 三、测试 四、Swagger 相关注解 一、引入相关依赖 图像化依赖 Swagger UI 用于提供可视化界面: <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactI…...
C语言:数据的存储
本文重点: 1. 数据类型详细介绍 2. 整形在内存中的存储:原码、反码、补码 3. 大小端字节序介绍及判断 4. 浮点型在内存中的存储解析 数据类型结构的介绍: 类型的基本归类: 整型家族 浮点家族 构造类型: 指针类型&…...
OpenFGA
1.什么是OpenFGA Fine-Grained Authorization 细粒度关系型授权 2.什么是细粒度授权 细粒度授权 (FGA) 意味着能够授予特定用户在特定资源中执行特定操作的权限。 精心设计的 FGA 系统允许您管理数百万个对象和用户的权限。随着系统不断添加对象并更新用户的访问权限&#…...
Kafka 入门与应用实战:吞吐量优化与与 RabbitMQ、RocketMQ 的对比
前言 在现代微服务架构和分布式系统中,消息队列作为解耦组件,承担着重要的职责。它不仅提供了异步处理的能力,还能确保系统的高可用性、容错性和扩展性。常见的消息队列包括 Kafka、RabbitMQ 和 RocketMQ,其中 Kafka 因其高吞吐量…...
单链表算法实战:解锁数据结构核心谜题——链表的回文结构
题目如下: 解题过程如下: 回文结构举例: 回文数字:12521、12321、1221…… 回文字符串:“abcba”、“abba”…… 并不是所有的循环嵌套的时间复杂度都是O(n^2) 可以用C写C程序: C里可以直接使用ListNode…...
【2024年 CSDN博客之星】我的2024年创作之旅:从C语言到人工智能,个人成长与突破的全景回顾
我的2024年创作之旅:从C语言到人工智能,个人成长与突破的全景回顾 引言 回望2024年,我不仅收获了技术上的成长,更收获了来自CSDN平台上无数粉丝、朋友以及网友们的支持与鼓励。在这条创作之路上,CSDN不仅是我展示技术成…...
Qt Enter和HoverEnter事件
介绍 做PC开发的过程中或多或少都会接触到鼠标的悬停事件,Qt中处理鼠标悬停有Enter和HoverEnter两种事件 相同点 QEvent::Enter对应QEnterEvent,描述的是鼠标进入控件坐标范围之内的行为,QEnterEvent可以抓取鼠标的位置;QEvent…...
Python:元组构造式和字典推导式
(Python 元组构造式和字典推导式整理笔记) 1. 元组构造式 1.1 创建元组 使用圆括号: tuple1 (1, 2.5, (three, four), [True, 5], False) print(tuple1) # 输出: (1, 2.5, (three, four), [True, 5], False) 省略圆括号: tup…...
科普篇 | “机架、塔式、刀片”三类服务器对比
一、引言 在互联网的世界里,服务器就像是默默运转的超级大脑,支撑着我们日常使用的各种网络服务。今天,咱们来聊聊服务器家族中的三位 “明星成员”:机架式服务器、塔式服务器和刀片式服务器。如果把互联网比作一座庞大的城市&…...
数据结构——概念与时间空间复杂度
目录 前言 一相关概念 1什么是数据结构 2什么是算法 二算法效率 1如何衡量算法效率的好坏 2算法的复杂度 三时间复杂度 1时间复杂度表示 2计算时间复杂度 2.1题一 2.2题二 2.3题三 2.4题四 2.5题五 2.6题六 2.7题七 2.8题八 四空间复杂度 1题一 2题二 3…...
centos7 配置国内镜像源安装 docker
使用国内镜像源:由于 Docker 的官方源在国内访问可能不稳定,你可以使用国内的镜像源,如阿里云的镜像源。手动创建 /etc/yum.repos.d/docker-ce.repo 文件,并添加以下内容: [docker-ce-stable] nameDocker CE Stable -…...
PyCharm配置Python环境
1、打开PyCharm项目 可以从File-->Open-->选择你的项目路径-->OK,或者直接点击Open,找到项目路径-->OK,如图所示(点击Ok后可能有下面的弹窗,选择“Trust Project”即可,然后选择“New Window”打开项目) …...
Linux(Centos、Ubuntu) 系统安装jenkins服务
该文章手把手演示在Linux系统下如何安装jenkins服务、并自定义jenkins数据文件位置、以及jenkins如何设置国内镜像源加速,解决插件下载失败问题 安装方式:war包安装 阿里云提供的war下载源地址:https://mirrors.aliyun.com/jenkins/war/?s…...
本地大模型编程实战(02)语义检索(1)
文章目录 准备加载文档分割文档嵌入矢量存储查询矢量库检索返回评分先嵌入查询文本再检索 检索器总结代码 我们在百度、必应、谷歌等搜索引擎中使用的检索都是基于字符串的:用户输入字符串后,搜索引擎先对搜索内容进行分词,然后在已经进行了倒…...
使用 Redis 实现分布式锁的基本思路
使用 Redis 实现分布式锁的基本思路 在分布式系统中,多个进程或服务可能会同时访问共享资源(如数据库、缓存、文件等),这可能会导致数据不一致或并发冲突。Redis 由于其高性能和单线程模型,是实现分布式锁的一个常见选…...
SQL-leetcode—1193. 每月交易 I
1193. 每月交易 I 表:Transactions ---------------------- | Column Name | Type | ---------------------- | id | int | | country | varchar | | state | enum | | amount | int | | trans_date | date | ---------------------- id 是这个表的主键。 该表包含…...
【MYSQL】mysql 常用命令
文章目录 1. 数据库管理命令2. 表管理命令3. 数据操作命令4. 数据查询进阶5. 用户与权限管理6. 使用脚本操作数据库 1. 数据库管理命令 -- 查看所有数据库 SHOW DATABASES;-- 创建数据库 CREATE DATABASE 数据库名;-- 选择数据库 USE 数据库名;-- 删除数据库 DROP DATABASE 数…...
linux 内核学习方向以及职位
### 学习路径 1. 基础阶段: - C语言高级特性 - 指针和内存管理 - 数据结构实现 - 位操作 - 多线程编程 - Linux系统编程 - 文件I/O操作 - 进程管理 - 信号处理 - IPC机制 - Socket编程 - 必备知识 - 操作系统原理 - 计算机体系结构 - …...
DeepSeek火爆,参数量、激活参数 和 预训练 token 量 是什么?
最近DeepSeek火爆,爆出了几个关键参数,分别是参数量、激活参数 和 预训练 token 量。 这里用通俗的语言给大家解释一下~ 首先要知道1B 是 Billion(十亿)的缩写 参数量:671B(6710 亿) 想象你…...