Небольшое введение в OpenGL — Архив WASM.RU
Я думаю, вы, как и я, смотрели на эти OpenGL'ные демки, как двигаются по экрану полигоны, меняются различные эффекты и так далее. Также, вполне вероятно, вы не очень сильны в математике и не хотите самостоятельно выводить все эти математические синусоидальные процедуры. OpenGL - это классная библиотека, которая позволит вам создать 3D-вселенную очень быстро, двигать ее и наложить серию спецэффектов, используя простую концепцию API.
В наши дни есть два основных вида программирования под OpenGL, в зависимости от операционной системы, которую вы используете. Обычный путь состоит в использовании glut-библиотеки, чтобы задавать анимацию, которая совместима с linux, win32, sgx-станциями и т.д... Другой путь - эт чистый win32. В последнем случае используются обратный вызов win32-клинта, чтобы переключиться на следующее изображение. Как бы то ни было, результат одинаков. Мы будем рассматривать программирование под win32. Поэтому для использования OpenGL вам сначала нужно инициализровать процедуру окна.
1 Процедура окна
Процедура окна обычно обрабатывает все системные события, которые направляются текущему (отображаемому или нет) окну. Этот принцип очень хорошо работает для GUI и уменьшает количество потребляемых машиной ресурсов.
1.1 Класс окна
Для инициализации процедуры окна вам требуется определить класс, небольшую структуры, в которой задается различная информация, например адрес процедуры окна. Это очень легко сделать:
Код (Text):
mov edx,esp ; сохраняем стек push offset classname push 0 ; меню окна push 0 ; цвет бэкграунда (черный) push 0 ; наш курсор push 0 ; наша иконка push edx ; сохраняем стек push 0 call GetModuleHandleA ; загружаем текущую программу pop edx push eax ; равен hinstance push 0 ; дополнительно резервирующаяся память push 0 ; дополнительный размер структуры push offset Window_proc ; адрес процедуры окна push 0 ; we don't care of style mov eax,esp push edx push eax call RegisterClassA ; регистрируем класс pop esp ; восстанавливаем стекКак только вы зарегистрировали класс, вам необходимо создать свою процедуру окна
1.2 Создание окна
Это просто: один вызов функции API
Код (Text):
push 0 call GetModuleHandleA push eax push 0 push 0 push 480 push 640 push 0 push 0 push 090000000h ; WS_VISIBLE push offset windowname push offset classname push 0400000h ; WS_EX_CLIENTEDGE call CreateWindowExAЭто правильно, но если вы вызовите его таким образом, приложение может повиснуть, почему? Потому что мы еще не создали процедуру окна.
1.3 Процедура окна
Процедура окна получает 4 аргумента. Первый - это хэндл окна. Второй - это сообщение. Третий - это нижний параметр, а четвертый - это верхний параметр.
Так как Windows работает в C-подобной среде, аргументы лежат на стеке. Поэтому к ним легко получить доступ. Также вам потребуется позаботиться о некоторых регистра, не модифицировать ebp, например, или ваша программа будет закрыта.
Ваша процедура окна должна перехватывать некоторые сообщения, которые заданы как win32-константы. Давайте взглянем, на что похожа процедура окна.
Код (Text):
lParam equ 16 wParam equ 12 uMsg equ 8 hWnd equ 4 window_proc: cmp dword ptr [esp+uMsg], 0Fh ; WM_PAINT jne not_event1 ... здесь какой-то код по обработке WM_PAINT not_event1: ... здесь вы можете поместить обработку других событий not_event99: cmp dword ptr [esp+uMsg], WM_CLOSE jne not_quiting push dword ptr [esp+lParam] push dword ptr [esp+4+wParam] push dword ptr [esp+4+4+uMsg] push dword ptr [esp+4+4+4+hWnd] call DefWindowProc ret 161.4 Обработка события
В силу определенных причин некоторые сообщения принимаются, а некоторые посылаются. Поэтому вам нужно, чтобы текущить тред принимал сообщения, что стоит довольно мало ресурсов. Поэтому не паникуйте. Этот код достаточно общий, он может меняться от программиста к программиста, но делает он примерно одно и то же.
Код (Text):
mov edx,esp sub esp,44 mov ebx,esp push ebx push eax loopit: push ebx push 0 push 0 push eax push ebx call GetMessageA pop ebx cmp eax,0 je goodbye push ebx push ebx call TranslateMessage call DispatchMessageA pop eax pop ebx push ebx push eax jmp loopit goodbye: program_error: add esp,44+4+4 push 0 call ExitProcessКак только ваша процедура окна инициализирована, вам требуется синхронизировать ее с OpenGL.
2 OpenGL
Сейчас у нас есть зарегистрированный класс, созданное окно и маленькая оконная процедура. Но теперь нам нужна помощь OpenGL, чтобы инициализировать систему, графику и множество других вещей. Нам необходимо будет делать их в два момента времени: при создании окна и при обновлении экрана.
2.1 При создании окна
Во-первых, вам нужно инициализировать пиксельный формат, который будет использоваться как двойной графический буфер. Для этого вам необходимо использовать PIXELFORMATDESCRIPTOR, это длинная и скучная структура, в которой нужно заполнить тольконесколько полей
Код (Text):
mov ecx,40 sub esp,ecx mov edi,esp push edi xor eax,eax repz stosb pop edi mov word ptr [edi], 40 ; размер структуры mov word ptr [edi+2], 1 ; заполняем версию mov dword ptr [edi+4], 37 ; PFD_SUPPORT_OPENGL ; PFD_DRAW_TO_WINDOW ; PFD_DOUBLE_BUFFER mov byte ptr [edi+9], 16 ; биты цвета mov byte ptr [edi+17], 16 ; биты глубины push edi push dword ptr [currentDC] ; загружаем DC call ChoosePixelFormatЕсли результат равен 0, вызов API не удавлся, и пользователь не может отобразить разрешение. В противном случае вы можете напрямую установить пиксельный формат.
Код (Text):
push edi push eax push dword ptr [currentDC] ; загружаем DC call SetPixelFormatтогда это равно пиксельному формату. Получить текущий DC очень просто:
Код (Text):
push dword ptr [esp+hWnd] call GetDCи eax равен текущему DC. Нам также требуется создать контекст OpenGL из этого DC.
Код (Text):
push eax call wglCreateContextЕсли eax == 0, то вызов не удался и лучшее, что мы можем сделать - это послать WM_CLOSE, чтобы благополучно закрыть приложение.
Далее мы синхронизируем оба контекста:
Код (Text):
push eax ; контекст OpenGL push ebx ; текущий DC call wglMakeCurrentСделано, OpenGL был проинициализирован, теперь вы можете наложить ряд эффектов, но прежде, чем сделать это, вы должны из разрешить. Делается это так:
Код (Text):
push 0B57h ; CL_COLOR_MATERIAL call glEnable ; damn easy isn't ?Вы также можете попробовать другие значения: GL_DEPTH, G_LIGHTING.
2.2 Как только был изменен размер окна
Когда Windows закончила с инициализацией, она шлет сообщение WM_SIZE, которое озанчает, что настало время пофиксить все, связанное с графикой. Разрешение экрана посылается в lParam. Поэтому сначала устанавливаем порт просмотра.
Код (Text):
mov ecx,dword ptr [esp+lParam] movzx edx,cx shr ecx,16 push ecx push edx ; длина push 0 push 0 ; начало call glViewport ; определяем порт просмотраПосле этого нам нужно определить модель матрицы, я не буду давать урок математики о матрицах и трансформациях, но как обычно это очень легко:
Код (Text):
push 1701h ; GL_PROJECTION call glMatrixModeПосле этого мы вызываем функцию glLoadIndentity:
Код (Text):
call glLoadIdentityЗатем мы вызываем библиотеку GL-эффектов, чтобы добавить перспективу порту просмотра. Эта функция принимает только числа двойной точности. Компилер может инициализировать такое число так:
Код (Text):
double1 dq 1.0fА вот макрос, облегчающий загонку таких чисел в стек:
Код (Text):
pushdl macro double1 fld qword ptr [&double1&] sub esp,8 fstp qword ptr [esp] endmТеперь передадим необходимые параметры порту просмотра.
Код (Text):
pushdl farclip pushdl nearclip pushdl XYration pushdl FovAngle call gluPerpectiveПерспектива - это необходимый аспект нашего 3D-мира. Перспектива бывает разных видов, например как при обзоре через камеру наблюдения или обзор сцены в формате 16/9.
И, опционально, вы можете установить модель теней для сложной 3D-сцены, установите ее в GL_F:AT, это может ускорить рендеринг изображения.
Код (Text):
push 01D01h call glShadeModelВот и все с этим.
3 Пришло время показать ваши умения демокодера
Windows говорит процедуре окна, что настало время обновить текущее изображение с помощью сообщения WM_PAINT.
Первое, что нужно сделать - это обновить текущее изображение с помощью сообщения WM_PAINT.
Код (Text):
push 04100h ; GL_COLOR_BUFFER_BIT или call glClear ; GL_DEPTH_BUFFER_BIT push GL_MODEL_VIEW call glMatrixMode call glLoadIdentityТеперь мы можем установить порт просмотра в виде камеры. У этой камеры есть 9 операндов.
Код (Text):
3 первых - это начало камеры 3 следующих - это назанчение камеры 3 последних - это координаты верхнего вектора камеры, ее вращениеЭти операнды также двойной точности, поэтому используйте pushdl, чтобы использовать эту функцию.
Код (Text):
pushf... pushf... call gluLookAtА теперь надо отрисовать окружение. Можно сделать много вещей: установить свет, тени, прозрачность, искажения и так далее.
Давайте сделаем такую простую вещь:
Код (Text):
push GL_POINTS call glBegin push 0.5 push 0.5 push 0.5 call glVertex3f call glEndglColor3d и glVertex3i существуют во многих форматах. Последняя буква задает тип переменной, который вы хотите использовать. glVertex3d означается, что вы используете аргумент двойной точности.
Когда окружение построено, вы можете закончить это так:
Код (Text):
push DC call SwapBuffersИ буфер окажется на экране.
4 Небольшое приложение
Часто люди удивляются, почему их продукция отображается на экране очень странно. Причина проста: OpenGL не интересуется другими элементами, расположенными на экране, поэтому н может перерисовать их... Вы можете установить тест глубины во время создания окна:
Код (Text):
push 0B71h ; DEPTH_TEST call glEnable ;Некоторые люди хотят полноэкранное разрешение, это можно сделать в два шага, меняя разрешение дисплея:
Код (Text):
mov ecx,148 mov esp,ecx mov edi,esp xor eax,eax repz stosb mov dword ptr [esp+36],148 ; dmSize mov dword ptr [esp+104],16 ; dmBitsPerPixel mov dword ptr [esp+108],640 ; dmWidth mov dword ptr [esp+112], 480 ; dmHeight mov dword ptr [esp+40], 1C0000h ; DM_BITSPERPEL DM_PELSWIDTH DM_PELSHEIGHT mov edx,esp push 4 ; CDS_FULLSCREEN push edx ; dmScreenSettings call ChangeDisplay add esp,148Не правда ли, это очень легко? © Star0, пер. Aquila
Небольшое введение в OpenGL
Дата публикации 27 июн 2002