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 语言编程。每个块都应该被เริ่มต้น包含着, 更为确切的说是 glbegin () 和 glend () 。如同下面的例子:::
const s = 1.0; d = 5.0;
-
glbegin (gl_triangles);
glvertex3f (-s, 0, d); glvertex3f (s, 0, d); glvertex3f (0, s, d);
Glend;
这是个简单的三角形。它距离你的视点有 5 个单位, 自身高 1 个单位, 宽 2 个单位。
这是屏幕截图:
即使它看起来不象 3d 图形, 但它是我们的初始块。在下面你可以看到这个例子的源代码。
在你开始钻研代码前, 还有些话要说。每次 OpenGl 编程, 都包含一些初始化输出设备的 OS 设定 (เฉพาะระบบปฏิบัติการ) 代码。如果你使用 Win32, 你将需要设置像素格式以及建立显示上下文你将需要设置像素格式以及建立显示上下文环境脱离 windows 设备上下文环境。如果 windows 系统级编程你并不很在行, 你可以把如下的代码作为模版使用。formcreate中被调用函数的详细信息可以参考帮助文档。
ไฟล์: tri.pas
หน่วย tri;
ส่วนต่อประสาน
ใช้
OpenGL, Windows, ข้อความ, sysutils, คลาส, กราฟิก, การควบคุม, รูปแบบ, กล่องโต้ตอบ
stdctrls, extctrls, comctrls;
พิมพ์
tform1 = คลาส (tform)
โพรซีเดอร์แบบฟอร์ม (ผู้ส่ง: tobject);
รูปแบบขั้นตอน (ผู้ส่ง: tobject);
ส่วนตัว
การวาดขั้นตอน; // วาดฉาก OpenGL ตามคำขอ
สาธารณะ
จบ;
วาจา
Form1: TFORM1;
การดำเนินการ
{$ r *.dfm}
ขั้นตอน setuppixelformat (DC: HDC);
const
pfd: tpixelformatdescriptor = (
NSize: sizeof (tpixelformatdescriptor); // ขนาด
Nversion: 1; // เวอร์ชัน
dwflags: pfd_support_opengl หรือ pfd_draw_to_window หรือ
pfd_doublebuffer; // รองรับการบัฟเฟอร์สองครั้ง
ipixeltype: pfd_type_rgba; // ประเภทสี
Ccolorbits: 24; // ความลึกสีที่ต้องการ
เครดิต: 0; Credshift: 0; // บิตสี (ละเว้น)
CGREENBITS: 0; CGREENSHIFT: 0;
cbluebits: 0; cblueshift: 0;
Calphabits: 0; Calphashift: 0; // ไม่มีบัฟเฟอร์อัลฟ่า
cacbumbits: 0;
Caccumredbits: 0; // ไม่มีบัฟเฟอร์สะสม
Caccumgreenbits: 0; // บิต ACCUM (ละเว้น)
caccumbluebits: 0;
Caccumalphabits: 0;
cdepthbits: 16; // บัฟเฟอร์ความลึก
Cstencilbits: 0; // ไม่มีบัฟเฟอร์ลายฉลุ
Cauxbuffers: 0; // ไม่มีบัฟเฟอร์เสริม
ilayertype: pfd_main_plane; // ชั้นหลัก
Breserved: 0;
dwlayermask: 0;
dwvisiblemask: 0;
dwdamagemask: 0; // ไม่มีเลเยอร์ที่มองเห็นได้มาสก์ความเสียหาย
-
var pixelformat: จำนวนเต็ม;
เริ่ม
PixelFormat: = choosePixelFormat (DC, @PFD);
if (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);
// position viewer
glmatrixmode (gl_modelview);
glenable (gl_depth_test);
จบ;
ขั้นตอน TFORM1.FormCreate (ผู้ส่ง: TOBJECT);
VAR DC: HDC;
RC: HGLRC;
ฉัน: จำนวนเต็ม;
เริ่ม
DC: = getDC (มือจับ); // จริง ๆ แล้วคุณสามารถใช้การควบคุมหน้าต่างได้ที่นี่
setuppixelformat (DC);
RC: = WGlCreateContext (DC); // ทำให้หน้าต่าง OpenGL ออกจาก DC
WglMakecurrent (DC, RC); // ทำให้หน้าต่าง OpenGL ทำงาน
glinit; // เริ่มต้น OpenGL
จบ;
ขั้นตอน tform1.draw;
const s = 1.0; d = 5.0;
เริ่ม
glclear (gl_color_buffer_bit หรือ gl_depth_buffer_bit);
Glloadidentity;
gltranslatef (0.0, 0.0, -12.0);
glbegin (gl_triangles);
glvertex3f (-s, 0, d); glvertex3f (s, 0, d); glvertex3f (0, s, d);
Glend;
swapbuffers (wglgetCurrentDC);
จบ;
ขั้นตอน TFORM1.FormPaint (ผู้ส่ง: TOBJECT);
เริ่ม
วาด;
จบ;
จบ.
ไฟล์: tri.dfm
Object Form1: tform1
BorderStyle = BSDialog
คำบรรยายภาพ = 'โปรแกรม OpenGL พื้นฐาน'
clientHeight = 318
clientWidth = 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 // vertexes
A1: tglArrayf3 = ( -d, 0, -H1); // บูทซ้าย
A2: tglArrayf3 = (d, 0, -H1); // บูทขวา
A3: tglArrayf3 = (0, 0, H2); // bootom กลับมา
A4: tglArrayf3 = (0, hy, 0); //สูงสุด
เริ่ม
glclear (gl_color_buffer_bit หรือ gl_depth_buffer_bit);
Glloadidentity;
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);
Glend;
swapbuffers (wglgetCurrentDC);
จบ;
虽然看起来有点复杂, 不过当你面对下面这张图时, 它就很容易理解了。
我们定义顶点 A1 - A4 同时依据 4 个顶点位置建立指定的三角形。当你定义自己的三角形 (或者其他的多边形), 请使用如下的规则: 始终按照逆时针顺序排列定点序号, 就像你正在外部就像你正在外部观看侧面一样。通过这个规则, 我们可以指定指定 A1-A2-A4, A1-A3-A2 (仰视), A2-A3-A4 和 A3-A1-A4 。 A2-A3-A3
现在就替换 tri.pas 中 tform1.darw () 部分, 程序运行的效果不会体现出过多的变化。它看起来仍然不象三维图形。这是因为我们还没有设定任何光源。
ไฟ กล้อง! Opengl
在 opengl 中光源模式有两部分: 光源自身(颜色, 强度等等)),, 依次包括颜色, 一些物理参数 (比如不透明性光泽性) 以及纹理。深入其中, 这会是一个巨大的这会是一个巨大的这会是一个巨大的世界, 我们将一步步地接近。
定义一个光源相当容易。
ขั้นตอน Glinit;
const
Light0_Position: tglArrayf4 = (-8.0, 8.0, -16.0, 0.0);
โดยรอบ: 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 需要知道你指定的每个多边形的“ ปกติ” 以便进行光线计算 (ปกติ是一个与表面正交的向量) 。如果你没有自己的向量函数库, 可以使用以下方法计算三角形中三个顶点的ปกติ。这个函数是以定点逆时针排列为基础的, ปกติ是一个向量的叉积, 如果你不遵守该规则, 会使会使向量指向四面体内部。
ฟังก์ชั่น 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 // vertexes
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; // normals
เริ่ม
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);
Glloadidentity;
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);
Glend;
swapbuffers (wglgetCurrentDC);
จบ;
这便是以上代码的效果:
现在, 使用一点 delphi vcl 提供的的东西。在窗体上放一个ตัวจับเวลา, 指定一个类成员“ มุม: เดี่ยว” 并在每次ตัวจับเวลา触发时让他增加 1:
ขั้นตอน tform1.timer1timer (ผู้ส่ง: tobject);
เริ่ม
มุม: = มุม+1.0;
วาด;
จบ;
离一个充满生气的 OpenGl 仅差条线:
glrotatef (มุม, 0.0, 1.0, 0.0);
把它放在 glbegin () 内三角开始绘制前的位置上, 这样你的阴影部分就可以旋转了, 至此, 一切结束。一切结束。