OpenGl 最初是由 silicon 图形公司开发的底层图形库规范。你的系统中准确实现这个规范的部分 , 通常被称为 opengl 驱动 , 它允许你使用几何集合 它允许你使用几何集合 (点 , 线 , 多边形 , 图像等等) 来描述你希望表现的场景。让肉眼观察起来较为舒适的中等规模场景 , 通常在毫秒级的速度上实现 , 这意味着该库文件有足够的能力来支持你创建一个生机勃勃的虚拟世界。
OpenGL 驱动一般以二进制库文件的形式提供。它能够动态的连接到你的程序中。在 Windows 平台上 , 它将是成为 dll 的形式 (在你的系统目录下检查 opengl.dll) 。自从 delphi 能够使用任何 dll 开始 , 它对 opengl 3d 编程的能力就像其他任何语言一样容易了。本文将帮助你获得在 delphi 中进行 opengl 开发的有效知识。
数学基础
OpenGL 拥有强大的数学基础 , 因此对它功能的限制完全取决于你的想象能力 (: : 没有做不到 , 只有想不到) 。对于理解那些公理和引理 , 更好的是让我们立刻认识一个简单的 3D 坐标系统 , 它是 3D 编程中惯用的坐标系统。如下 :
你应该如何理解你的屏幕 (蓝色的方块) 在场景中的放置位置呢?发出四条射线并形成屏幕的那个点 , 是该想象空间中的视点 (وجهة النظر) 。opengl 让你调用两个简单的函数来定义这个场景
GlmatrixMode (gl_projection) ؛
GLFRUSTUM (-0.1 ، 0.1 ، -0.1 ، 0.1 ، 0.3 ، 25.0) ؛
在这个调用的过程中的 -0.1،0.1 ، -0.1،0.1 定义了这个可视屏幕的左上角和右下角坐标; 0.3 指定视点到屏幕的距离 (就好象 "近剪贴板" (بالقرب من مستوى القطع )) 同时 25.0 指定 "远剪贴板" (طائرة قصاصات بعيدة) 。任何近剪贴板前面的物体以及远剪贴板后面的物体都将不可见。当然 , 你能够任意摆弄这些数字 , 以使他们适合你需要的场景。
从基本元素 (بدائية) 到对象
: : 对象。opengl : : 点 , 线和多边形。没有表面或者更高级的图形 (比如球状图形) 能被作为基本图形元素绘制。但是它们能够用多边形完美的模仿出来。随意看看现代 3D 游戏 , 你会发现它们完全由三角形建立。因此 , 我们不会被此限制所约束。
对象的绘制非常类似 pascal 语言编程。每个块都应该被 ending 包含着 , 更为确切的说是 更为确切的说是 glbegin () 和 Glend () 。如同下面的例子 :
const s = 1.0 ؛ D = 5.0 ؛
...
glbegin (gl_triangles) ؛
glvertex3f (-s ، 0 ، d) ؛ glvertex3f (s ، 0 ، d) ؛ glvertex3f (0 ، s ، d) ؛
لذيذة
这是个简单的三角形。它距离你的视点有 5 个单位 , 自身高 1 个单位 , 宽 2 个单位。
: :
即使它看起来不象 3D 图形 , 但它是我们的初始块。在下面你可以看到这个例子的源代码。
在你开始钻研代码前 , 还有些话要说。每次 opengl 编程 , 都包含一些初始化输出设备的 os 设定 (OS خاص) 代码。如果你使用 win32 , 你将需要设置像素格式以及建立显示上下文windows windows windows 系统级编程你并不很在行 , 你可以把如下的代码作为模版使用。formcreate 中被调用函数的详细信息可以参考帮助文档。
ملف: tri.pas
وحدة TRI ؛
واجهة
يستخدم
OpenGL ، Windows ، الرسائل ، sysutils ، الفئات ، الرسومات ، عناصر التحكم ، النماذج ، الحوار ،
stdctrls ، extctrls ، comctrls ؛
يكتب
tform1 = فئة (tform)
Formcreate الإجراء (المرسل: TOBJECT) ؛
Formpaint الإجراء (المرسل: tooject) ؛
خاص
رسم الإجراء ؛ // يرسم مشهد OpenGL عند الطلب
عام
نهاية؛
var
Form1: Tform1 ؛
تطبيق
{$ r *.dfm}
الإجراء setuppixelformat (DC: HDC) ؛
كونست
PFD: tpixelformatdescriptor = (
nsize: sizeof (tpixelformatdescriptor) ؛ // مقاس
NVERVER: 1 ؛ // إصدار
dwflags: pfd_support_opengl أو pfd_draw_to_window أو
pfd_doubleBuffer ؛ // دعم التعقيد المزدوج
ipixeltype: pfd_type_rgba ؛ // نوع اللون
ccolorbits: 24 ؛ // عمق اللون المفضل
credbits: 0 ؛ CredShift: 0 ؛ // بتات اللون (تجاهل)
cgreenbits: 0 ؛ cgreenshift: 0 ؛
cbluebits: 0 ؛ cblueshift: 0 ؛
Calphabits: 0 ؛ Calphashift: 0 ؛ // لا عازلة ألفا
caccumbits: 0 ؛
caccumredbits: 0 ؛ // لا يوجد مخزن مؤقت للتراكم ،
caccumgreenbits: 0 ؛ // أجهزة البتات (تجاهلها)
caccumbluebits: 0 ؛
caccumalphabits: 0 ؛
cdepthbits: 16 ؛ // عمق العازلة
CSTANCLBITS: 0 ؛ // لا مخزن مؤقت للاستنسل
Cauxbuffers: 0 ؛ // لا توجد مخازن المؤقتة المساعدة
ilayertype: pfd_main_plane ؛ // الطبقة الرئيسية
breserved: 0 ؛
dwlayermask: 0 ؛
dwvisiblemask: 0 ؛
dwdamagemask: 0 ؛ // لا طبقة ، أقنعة مرئية ، أضرار
) ؛
var pixelformat: عدد صحيح ؛
يبدأ
pixelformat: = chooSepixelformat (DC ، pfd) ؛
إذا (pixelformat = 0) ثم
مخرج؛
if (setPixelformat (DC ، pixelformat ، pfd) <> true) ثم
مخرج؛
نهاية؛
الإجراء glinit.
يبدأ
// تعيين عرض العرض
GlmatrixMode (gl_projection) ؛
GLFRUSTUM (-0.1 ، 0.1 ، -0.1 ، 0.1 ، 0.3 ، 25.0) ؛
// عارض الموضع
GlmatrixMode (gl_modelview) ؛
glenable (gl_depth_test) ؛
نهاية؛
الإجراء tform1.formcreate (المرسل: tobject) ؛
var DC: HDC ؛
RC: HGLRC ؛
أنا: عدد صحيح.
يبدأ
DC: = GETDC (مقبض) ؛ // في الواقع ، يمكنك استخدام أي عنصر تحكم نافذة هنا
setuppixelformat (DC) ؛
RC: = wglcreateContext (DC) ؛ // يجعل نافذة OpenGL من العاصمة
wglmakeCurrent (DC ، RC) ؛ // يجعل نافذة OpenGL نشطة
glinit // تهيئة OpenGL
نهاية؛
الإجراء tform1.draw ؛
const s = 1.0 ؛ D = 5.0 ؛
يبدأ
glClear (gl_color_buffer_bit أو gl_depth_buffer_bit) ؛
Glovingidentity.
gltranslatef (0.0 ، 0.0 ، -12.0) ؛
glbegin (gl_triangles) ؛
glvertex3f (-s ، 0 ، d) ؛ glvertex3f (s ، 0 ، d) ؛ glvertex3f (0 ، s ، d) ؛
لذيذة
swapbuffers (wglgetCurrentDC) ؛
نهاية؛
الإجراء tform1.formpaint (المرسل: tobject) ؛
يبدأ
يرسم؛
نهاية؛
نهاية.
ملف: tri.dfm
نموذج الكائن 1: tform1
BorderStyle = BSDialog
Caption = 'Basic OpenGL Program "
ClientHeight = 318
عميل العميل = 373
oncreate = formcreate
onpaint = formpaint
نهاية
3D 历险
好了 , 让我们开始真正的 3D 吧。将先前的代码作为框架 , 我们增加一些画线的代码建立一个带阴影面的四面体。应该如何用基本图形元素来构建呢?我们使用四个三角形。 : : : :
الإجراء tform1.draw ؛
const d = 1.5 ؛
H1 = D/1.732 ؛
H2 = D*1.732-H1 ؛ // d/h = tg (30) = 1/sqrt (3)
hy = 3.0 ؛
const // pertexes
A1: tglarrayf3 = ( -d ، 0 ، -H1) ؛ // bootom اليسار
A2: tglarrayf3 = (d ، 0 ، -H1) ؛ // bootom الحق
A3: tglarrayf3 = (0 ، 0 ، H2) ؛ // bootom الظهر
A4: tglarrayf3 = (0 ، hy ، 0) ؛ //قمة
يبدأ
glClear (gl_color_buffer_bit أو gl_depth_buffer_bit) ؛
Glovingidentity.
gltranslatef (0.0 ، 0.0 ، -12.0) ؛
glbegin (gl_triangles) ؛
glvertex3fv (@a1) ؛ glvertex3fv (@a3) ؛ glvertex3fv (@a2) ؛
glvertex3fv (@a1) ؛ glvertex3fv (@a2) ؛ glvertex3fv (@a4) ؛
glvertex3fv (@a2) ؛ glvertex3fv (@a3) ؛ glvertex3fv (@a4) ؛
glvertex3fv (@a3) ؛ glvertex3fv (@a1) ؛ glvertex3fv (@a4) ؛
لذيذة
swapbuffers (wglgetCurrentDC) ؛
نهاية؛
虽然看起来有点复杂 , 不过当你面对下面这张图时 , 它就很容易理解了。
我们定义顶点 A1 - A4 同时依据 4 个顶点位置建立指定的三角形。当你定义自己的三角形 (或者其他的多边形) , : : 始终按照逆时针顺序排列定点序号 , 就像你正在外部观看侧面一样。通过这个规则 , 我们可以指定指定 A1-A2-A4 , A1-A3-A2 (仰视) , A2-A3-A4 和 A3-A1-A4。
现在就替换 tri.pas 中 tform1.darw () 部分 , 程序运行的效果不会体现出过多的变化。它看起来仍然不象三维图形。这是因为我们还没有设定任何光源。
أضواء! آلة تصوير! OpenGL!
在 OpenGL : : 光源自身 (颜色 , 强度等等 和对象材质。材质 和对象材质。材质 , 依次包括颜色 , 一些物理参数 (比如不透明性光泽性) 以及纹理。深入其中 , 这会是一个巨大的世界 , 我们将一步步地接近。
定义一个光源相当容易。
الإجراء glinit.
كونست
Light0_position: tglarrayf4 = (-8.0 ، 8.0 ، -16.0 ، 0.0) ؛
Ambient: tglarrayf4 = (0.3 ، 0.3 ، 0.3 ، 0.3) ؛
يبدأ
// تعيين عرض العرض
GlmatrixMode (gl_projection) ؛
GLFRUSTUM (-0.1 ، 0.1 ، -0.1 ، 0.1 ، 0.3 ، 25.0) ؛
// عارض الموضع */
GlmatrixMode (gl_modelview) ؛
glenable (gl_depth_test) ؛
// ضبط الأضواء
glenable (gl_lighting) ؛
gllightfv (gl_light0 ، gl_position ، @light0_position) ؛
gllightfv (gl_light0 ، gl_ambient ، @ambient) ؛
glenable (gl_light0) ؛
نهاية؛
代码内的两个常量是必须的。一个定义光源位置 (位于视点的后面的左上角) , 另外一个定义环境光线。这将产生少量的散乱光线 , 使你能够看到完全位于阴影中的某些物体。
虽然你可以使用光照设定光源 , 可是物体仍然没有绘制阴影。这是因为 OpenGL 需要知道你指定的每个多边形的 "Normal" 以便进行光线计算 (Normal 是一个与表面正交的向量) 。如果你没有自己的向量函数库 , 可以使用以下方法计算三角形中三个顶点的 عادي 。这个函数是以定点逆时针排列为基础的 , 因为 عادي 是一个向量的叉积 , 如果你不遵守该规则 , 会使向量指向四面体内部。
وظيفة getNormal (p1 ، p2 ، p3: tglarrayf3): tglarrayf3 ؛
var a ، b: tglarrayf3 ؛
يبدأ
// اصنع متجهين
A [0]: = P2 [0] -P1 [0] ؛ A [1]: = P2 [1] -P1 [1] ؛ A [2]: = P2 [2] -P1 [2] ؛
B [0]: = P3 [0] -P1 [0] ؛ B [1]: = P3 [1] -P1 [1] ؛ B [2]: = P3 [2] -P1 [2] ؛
// حساب المنتجات المتقاطعة
النتيجة [0]: = A [1]*B [2] -a [2]*B [1] ؛
النتيجة [1]: = A [2]*B [0] -a [0]*B [2] ؛
النتيجة [2]: = A [0]*B [1] -a [1]*B [0] ؛
نهاية؛
: : : :
الإجراء tform1.draw ؛
const d = 1.5 ؛
H1 = D/1.732 ؛
H2 = D*1.732-H1 ؛ // d/h = tg (30) = 1/sqrt (3)
hy = 3.0 ؛
const // pertexes
A1: tglarrayf3 = ( -d ، 0 ، -H1) ؛
A2: tglarrayf3 = (d ، 0 ، -H1) ؛
A3: tglarrayf3 = (0 ، 0 ، H2) ؛
A4: tglarrayf3 = (0 ، hy ، 0) ؛
var n1 ، n2 ، n3 ، n4: tglarrayf3 ؛ // القواعد الطبيعية
يبدأ
N1: = getNormal (A1 ، A3 ، A2) ؛
N2: = getNormal (A1 ، A2 ، A4) ؛
N3: = getNormal (A2 ، A3 ، A4) ؛
N4: = getNormal (A3 ، A1 ، A4) ؛
glClear (gl_color_buffer_bit أو gl_depth_buffer_bit) ؛
glenable (gl_normalize) ؛
glshademodel (gl_flat) ؛
glcullface (gl_back) ؛
Glovingidentity.
gltranslatef (0.0 ، 0.0 ، -12.0) ؛
glbegin (gl_triangles) ؛
glnormal3fv (@n1) ؛
glvertex3fv (@a1) ؛ glvertex3fv (@a2) ؛ glvertex3fv (@a3) ؛
glnormal3fv (@n2) ؛
glvertex3fv (@a1) ؛ glvertex3fv (@a2) ؛ glvertex3fv (@a4) ؛
glnormal3fv (@n3) ؛
glvertex3fv (@a2) ؛ glvertex3fv (@a3) ؛ glvertex3fv (@a4) ؛
glnormal3fv (@n4) ؛
glvertex3fv (@a3) ؛ glvertex3fv (@a1) ؛ glvertex3fv (@a4) ؛
لذيذة
swapbuffers (wglgetCurrentDC) ؛
نهاية؛
: :
现在 , 使用一点 delphi vcl 提供的的东西。在窗体上放一个 timer , 指定一个类成员 指定一个类成员 "الزاوية: واحد" timer 并在每次 1 :
الإجراء tform1.timer1timer (المرسل: tobject) ؛
يبدأ
زاوية: = زاوية+1.0 ؛
يرسم؛
نهاية؛
离一个充满生气的 OpenGL : :
glrotatef (زاوية ، 0.0 ، 1.0 ، 0.0) ؛
把它放在 glbegin () 内三角开始绘制前的位置上 , 这样你的阴影部分就可以旋转了 , 至此 , 一切结束。