Opengl 最初是由 кремний 图形公司开发的底层图形库规范。你的系统中准确实现这个规范的部分 通常被称为 通常被称为 opengl 驱动 , 它允许你使用几何集合 (点 , 线 多边形 , 图像等等) 来描述你希望表现的场景。让肉眼观察起来较为舒适的中等规模场景 , 通常在毫秒级的速度上实现 这意味着该库文件有足够的能力来支持你创建一个生机勃勃的虚拟世界。
OpenGL 驱动一般以二进制库文件的形式提供。它能够动态的连接到你的程序中。在 Windows 平台上 它将是成为 dll 的形式 (在你的系统目录下检查 opengl.dll) 。自从 delphi 能够使用任何 dll 开始 它对 它对 opengl 3d 编程的能力就像其他任何语言一样容易了。本文将帮助你获得在 delphi 中进行 opengl 开发的有效知识。
数学基础
Opengl 拥有强大的数学基础 , (译者注 : 没有做不到 , 只有想不到) 。对于理解那些公理和引理 , 更好的是让我们立刻认识一个简单的 3d 坐标系统 , 3d 编程中惯用的坐标系统。如下 :
你应该如何理解你的屏幕 (蓝色的方块) 在场景中的放置位置呢?发出四条射线并形成屏幕的那个点 , 是该想象空间中的视点 (точка зрения) 。opengl 让你调用两个简单的函数来定义这个场景
glmatrixmode (gl_procection);
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);
Гленд;
这是个简单的三角形。它距离你的视点有 5 个单位 , 1 个单位 , 宽 2 个单位。
这是屏幕截图 :
即使它看起来不象 3d 图形 , 但它是我们的初始块。在下面你可以看到这个例子的源代码。
在你开始钻研代码前 , 还有些话要说。每次 opengl 编程 都包含一些初始化输出设备的 都包含一些初始化输出设备的 OS 设定 (OS-специфический) 代码。如果你使用 win32 , 你将需要设置像素格式以及建立显示上下文环境脱离 Windows 设备上下文环境。如果 Windows 系统级编程你并不很在行 , 你可以把如下的代码作为模版使用。formcreate 中被调用函数的详细信息可以参考帮助文档。
Файл: tri.pas
единица три;
интерфейс
Использование
OpenGL, Windows, сообщения, Sysutils, классы, графика, управления, формы, диалоги,
STDCTRLS, EXTCTRLS, COMCTRLS;
тип
Tform1 = class (tform)
Процедура FormCreate (отправитель: Tobject);
Процедура Formpaint (отправитель: tobject);
частный
Процедура рисовать; // рисует сцену OpenGL по запросу
публичный
конец;
вар
Форма1: tform1;
выполнение
{$ R *.dfm}
Процедура setuppixelformat (DC: HDC);
констант
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; // нет альфа -буфера
Caccumbits: 0;
Caccumredbits: 0; // нет буфера накопления,
Caccumgreenbits: 0; // Накапливание (игнорируется)
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_procection);
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 Out Out из DC
WglmakeCurrent (DC, RC); // делает активным окном OpenGL
Глиний; // Инициализировать OpenGL
конец;
Процедура tform1.draw;
const s = 1,0; D = 5,0;
начинать
glclear (gl_color_buffer_bit или gl_depth_buffer_bit);
Glluctidentity;
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
Подпись = 'Базовая программа OpenGL'
ClientHeight = 318
Клиент -Whidth = 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 // вершины
A1: tglarrayf3 = ( -d, 0, -h1); // bootom остался
A2: tglarrayf3 = (D, 0, -H1); // Bootom Right
A3: tglarrayf3 = (0, 0, H2); // bootom обратно
A4: tglarrayf3 = (0, hy, 0); //вершина
начинать
glclear (gl_color_buffer_bit или gl_depth_buffer_bit);
Glluctidentity;
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 中光源模式有两部分 : (颜色 , 强度等等) 和对象材质。材质 , 依次包括颜色 , 一些物理参数 (比如不透明性光泽性) 以及纹理。深入其中 , 这会是一个巨大的世界 , 我们将一步步地接近。
定义一个光源相当容易。
Процедура Glinit;
констант
Light0_position: tglarrayf4 = (-8,0, 8,0, -16,0, 0,0);
Амбиент: tglarrayf4 = (0,3, 0,3, 0,3, 0,3);
начинать
// установить проекцию просмотра
glmatrixmode (gl_procection);
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 // вершины
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);
Glluctidentity;
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 提供的的东西。在窗体上放一个 Таймер , 指定一个类成员 «Угол: одинокий» 并在每次 Таймер 触发时让他增加 1 :
Процедура tform1.timer1timer (отправитель: tobject);
начинать
Угол: = Угол+1,0;
Рисовать;
конец;
离一个充满生气的 Opengl 仅差条线 :
Glrotatef (угол, 0,0, 1,0, 0,0);
把它放在 glbegin () 内三角开始绘制前的位置上 这样你的阴影部分就可以旋转了 这样你的阴影部分就可以旋转了 至此 至此 一切结束。 一切结束。