DLGOPENGL-04理论知识

什么是OPENGL?

我们需要知道OpenGL是“开放图形库”的缩写。OpenGL是一种API,它允许我们相对容易地在屏幕上输出图形,而不必确切地知道背景中发生了什么。整个过程在某种程度上与Windows API相当。我们知道如何创建一个窗口,但我们不知道后台到底发生了什么。

OpenGL属于跨平台,您可以在任何操作系统下使用OpenGL,并且您的程序可以在任何硬件上运行。然而,这有一个缺点,即某些功能(如窗口管理、键盘或鼠标控制)被排除在库之外,因为这些功能可能因操作系统而异。所以我们必须自己处理这些事情。幸运的是,Delphi的VCL(可视化组件库)为我们提供了一个强大的工具来应对这些挑战。

Opengl只有点,线,三角形,学过三维建模的同学应该知道,模型的最基本元素就是点线三角形。

OpenGL是一个巨大的状态机。根据您切换各个状态的方式,屏幕上输出的图像看起来可能完全不同。例如,已绘制的具有灯光的立方体看起来与没有灯光的相同立方体不同。如果更改状态(如颜色),则在再次更改颜色之前,绘制的所有图元都将使用此颜色。

简单的说就是,靠状态切换来改变图像。就像画家调了色,必须画上去才能让别人看到。

 

 

 这个函数的基本框架是glColor。所有这些函数的行为相同,仅在参数传输方面不同。对于glColor3i,需要3个Integer类型的参数。另一方面,glColor4b需要4字节的参数。所有OpenGL函数都有前缀“gl”,后跟命令干。某些函数具有反映传递的参数类型或数量的后缀。(另见本声明)OpenGL常量始终以大写字母标识,并以前缀“GL_”开头。尽管Delphi在大小写之间没有区别,但我建议保留这种拼写,因为它极大地增加了整体性。

着色器的解释:GLCOLOR后面跟什么类型,那就需要传什么类型的值。

TglColor3b = procedure(red: GLbyte; green: GLbyte; blue: GLbyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3bv = procedure(const v: PGLbyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3d = procedure(red: GLdouble; green: GLdouble; blue: GLdouble); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3dv = procedure(const v: PGLdouble); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3f = procedure(red: GLfloat; green: GLfloat; blue: GLfloat); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3fv = procedure(const v: PGLfloat); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3i = procedure(red: GLint; green: GLint; blue: GLint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3iv = procedure(const v: PGLint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3s = procedure(red: GLshort; green: GLshort; blue: GLshort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3sv = procedure(const v: PGLshort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3ub = procedure(red: GLubyte; green: GLubyte; blue: GLubyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3ubv = procedure(const v: PGLubyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3ui = procedure(red: GLuint; green: GLuint; blue: GLuint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3uiv = procedure(const v: PGLuint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3us = procedure(red: GLushort; green: GLushort; blue: GLushort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor3usv = procedure(const v: PGLushort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4b = procedure(red: GLbyte; green: GLbyte; blue: GLbyte; alpha: GLbyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4bv = procedure(const v: PGLbyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4d = procedure(red: GLdouble; green: GLdouble; blue: GLdouble; alpha: GLdouble); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4dv = procedure(const v: PGLdouble); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4f = procedure(red: GLfloat; green: GLfloat; blue: GLfloat; alpha: GLfloat); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4fv = procedure(const v: PGLfloat); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4i = procedure(red: GLint; green: GLint; blue: GLint; alpha: GLint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4iv = procedure(const v: PGLint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4s = procedure(red: GLshort; green: GLshort; blue: GLshort; alpha: GLshort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4sv = procedure(const v: PGLshort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4ub = procedure(red: GLubyte; green: GLubyte; blue: GLubyte; alpha: GLubyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4ubv = procedure(const v: PGLubyte); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4ui = procedure(red: GLuint; green: GLuint; blue: GLuint; alpha: GLuint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4uiv = procedure(const v: PGLuint); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4us = procedure(red: GLushort; green: GLushort; blue: GLushort; alpha: GLushort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}
TglColor4usv = procedure(const v: PGLushort); {$IFDEF DGL_WIN}stdcall; {$ELSE}cdecl; {$ENDIF}

 

GLenum = Cardinal;
GLboolean = BYTEBOOL;
GLbitfield = Cardinal;
GLbyte = Shortint;
GLshort = c;
GLint = Integer;
GLsizei = Integer;
GLubyte = Byte;
GLushort = Word;
GLuint = Cardinal;
GLfloat = Single;
GLclampf = Single;
GLdouble = Double;
GLclampd = Double;
GLvoid = Pointer;
GLint64 = Int64;
GLuint64 = {$IFDEF DELPHI6_AND_DOWN} Int64 {$ELSE} UInt64 {$ENDIF};

要渲染三角形,只需绘制并输出一次图像即可。然而,你们中的大多数人不会满足于三角形,至少希望在程序中使用移动和动画。这个过程既简单又巧妙:为了实现移动,需要多次绘制和输出图像。想象一下,你想在屏幕上从左向右移动一个三角形!你在最左边画一个三角形,然后花掉它。即使在人眼有足够的时间将此图像解释为单个图像之前,由于当今PC的高速,您已经将三角形向右移动了几个像素并再次绘制。如果你的电脑足够快,那么在观看者看来,这种运动是完全流畅的,而几个像素的实际跳跃是无法识别的。人眼可以分辨大约每秒25-30帧,因此您应该始终尝试每秒绘制超过30帧(FPS)。

最晚,你应该意识到为什么当你的电脑太慢时,现代游戏有时会结巴。
理论就到此为止。但你如何实现这个过程?我们画了一个循环。现在问题来了,如何优雅地解决这个问题。例如,您可以编写自己的渲染主程序循环。然而,在本例中,还必须处理操作系统的消息。
在我看来,一个更优雅的解决方案是使用应用程序的OnIdle事件。如果将参数“Done”设置为“False”,则将尽可能频繁地执行此消息。OnIdle事件在应用程序的主线程中调用,这具有积极的副作用,即操作系统的消息由VCL处理,您可以充分利用VCL。
所提出的方法的缺点是应用程序永远不会停止,因为循环一直在进行。这通常会将处理器利用率提高到100%。如果出于某些原因不想在真实循环中渲染,也可以使用计时器。

大意就是想要搞出动画,那就是需要循环绘制,或者利用程序自己的OnIdle事件。

如果您想立即开始,我可以向您保证,因为使用我们的头文件初始化OpenGL非常容易。在使用OpenGL之前,只需在表单的OnCreate事件中调用函数“InitOpenGL”,然后在use子句中包含单元“dglOpenGL”。
为了让OpenGL也知道在哪里绘制,我们必须告诉她。为此,我们使用“GetDC”函数和表单句柄来确定表单的设备上下文。
然后我们使用“CreateRenderingContext”函数创建角色上下文。
在我们创建了它之后,我们仍然需要激活它。这是通过函数“ActiveRenderingContext”完成的。整个事情看起来像这样:

var
  DC, RC:HDC;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  DC:=GetDC(Handle);
  RC:=CreateRenderingContext(DC,          //Device Contest
                             [opDoubleBuffered], //Optionen
                             32,          //ColorBits
                             24,          //ZBits
                             0,           //StencilBits
                             0,           //AccumBits
                             0,           //AuxBuffers
                             0);          //Layer
  ActivateRenderingContext(DC, RC);
end;

CreateRenderingContext函数的参数解释。作为初学者可以不用仔细研究,我直接略过。

DC是渲染上下文挂起的设备上下文。例如,窗体或面板的上下文。这取决于你以后想在哪里画。
选项是提示,例如:opDoubleBuffered。此选项指定应使用双缓冲。TODO请询问更多选项。链接到可以阅读的位置。
ColorBits(此处为32)表示颜色深度。32表示1Byte=8Bit可用于四个颜色通道(红色、绿色、蓝色、alpha(透明度))。这意味着每个通道可以使用256个渐变。总共256^4=该死的多!
ZBits,这里是24,表示为深度缓冲器保留了多少位。24位意味着从0到2^24=1670万的条目是可能的。值越高,深度测试越精细/越准确。模具测试需要模具位。(屏蔽屏幕部分)AccumulBits指示累积缓冲器中可以存储多少位。
AuxBuffer指定辅助缓冲区中可以存储多少位。
图层指定图层数。(我想不出OpenGL在这里有什么用处。如果有人有什么想法,请发到论坛。)----难道可以多层绘图?有前辈懂的可以指点下。

SwapBuffers

原则上,你现在就可以开始。如果您现在在OnIdle事件中绘制图片,您将很快注意到您没有看到任何内容。这也很正常,因为您还必须输出图像。这是在函数“SwapBuffers”的帮助下完成的。
由于我们想要干净地编程,所以当我们结束程序或不再需要OpenGL时,我们必须再次释放渲染上下文和设备上下文。ReleaseDC和DestroyRenderingContext函数执行这些任务。
现在我们只需要定义要渲染的图像的大小。使用命令glViewport定义艺术板。与Windows不同,OpenGL的原点位于左下角。由于艺术板的大小应该与窗口的大小相对应,因此我们在表单的OnResize事件中调用此命令:

glViewport(0, 0, ClientWidth, ClientHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
gluPerspective(60, ClientWidth/ClientHeight, 0.1, 100);
glMatrixMode(GL_MODELVIEW);


 

 

posted @ 2022-11-20 17:01  下雨天不爱打伞  阅读(60)  评论(0编辑  收藏  举报