Автор Тема: VC++ & OpenGL  (Прочитано 6356 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн r57shell

  • Пользователь
  • Сообщений: 1402
    • Просмотр профиля
VC++ & OpenGL
« : 14 Март 2010, 00:48:01 »
Для тех кому нехватает возможностей/производительности в каких-то движках например GameMaker...
Для начала вводим в MSDN "OpenGL" или в гугле "OpenGL site:msdn.microsoft.com" (без ковычек). Я буду описывать всё по ссылкам на online версии msdn.
Сначала попадаем на OpenGL Start Page
И смотрим примеры:
1) Портабельный исходник:
2) Исходник портированный под Win32

Оба выглядят ужасно (для тех кто видел только консольные приложения на С++, или вообще не видел код на С++). Работать будем со вторым. Почему? Потому что я с первым не работал (зачем я буду обучать тому что не знаю?).

Для начала как заставить пример работать? Microsoft Visual Studio 2005 например или VC6.0
File->New->Project->Visual C++ -> Win32->Win32 Project
Пишем имя (например: OpenGL Test)
Ok, Next, windows application empty project
Finish
Затем в папке Source Files создаём исполняемый файл (LMOUSE_BUTTON->Add->New Item->Visual C++ -> Code -> C++ File (.cpp)
Пишем имя (например: main.cpp)
Вставляем исходник примера (второго)
Сохраняем файл, и пытаемся скомпилировать (Build->Build Solution или просто F7)
« Последнее редактирование: 15 Март 2010, 20:42:23 от r57shell »

Оффлайн r57shell

  • Пользователь
  • Сообщений: 1402
    • Просмотр профиля
Re: VC++ & OpenGL
« Ответ #1 : 14 Март 2010, 01:20:29 »
У меня выдало:

CHAR - это тип символов простых (байтовых) а WCHAR - это тип UNICODE символов. Аналогично STR и WSTR - только не символы а строки. Такие ошибки чаще всего из-за несоответствия: UNICODE программа, а строки простые или наоборот.
Можно устранить несколькими способами:
1) Сделать в настройках проэкта тип программы не UNICODE и всё станет гладко (Project->Project Settings->Configuration Properties->Character Set поставить "Use Multi-Byte Character Set")
2) Поправить строки первым способом: вручную изменить типы CHAR на WCHAR и STR на WSTR и похожие на них, и функции связанные с ними.
3) Поправить строки вторым способом (я так сделал в этот раз): подключить библиотеку <tchar.h>.

Теперь по подробнее как я сделал:
1) Подключил библиотеку #include <tchar.h> после cтрочки #include <GL/glu.h>.
2)заменил
CHAR szAppName[]="Win OpenGL";
на
TCHAR szAppName[]=_T("Win OpenGL");

TCHAR это такой гибкий тип, который если программа UNICODE то тоже самое что WCHAR а если программа Multi-Byte то просто CHAR.
А _T(текст) гибкий макрос, который если использовать UNICODE то преобразует строку "asd" (эта строка Multi-Byte) в строку L"asd" где L означает что строка UNICODE. А если используется Multi-Byte то ничего не делает со строкой.

3)Аналогично "Generic OpenGL Sample" заменил на _T("Generic OpenGL Sample") И остальные строки "ChoosePixelFormat failed"...

Сохранил. Снова компилирую (F7).
Образно сказать "не находит OpenGL в проэкте". На самом деле не находит экспортированные функции OpenGL в проэкте. Нужно просто подключить библиотеки.
Project->Project Properties->Configuration Properties->Linker->Input->Additional Dependencies
Пишем ручками "OpenGL32.lib Glu32.lib"
Ok
Cнова Build (F7)
Тада! Успешно (succeeded).
Запускаем (F5 на отладку).
Видим окно с безумно вертящейся картинкой. Это первое что исправим - скорость.


Заметим что latitude += latinc; выполняется в каждый вызов функции drawScene. А функция drawScene вызывается в
Вывод какой? То что drawScene() вызывается в любой момент когда делать нечего. А делать нечего может быть очень долго, а комп может быть очень шустрым, вот и скорость вращения бешенная.
Сразу приходит три варьянта как можно поправить (в зависимости чего хотим). Рассмотрим все

1) Просто после отрисовки сказать пусть "отдохнёт".
   Делается простым добавлением Sleep(10) (10 милисекунд) после drawScene. Но тогда и сообщения будет отражать с таймаутом 10 милисекунд, что мало заметно, и отрисовка будет не быстрее 100 фпс, но а вдруг комп медленный, тогда будет ужасно лагать, а если не спать может бы и не лагало. Не тру решение.
   
2) Ограничить FPS. Опишу как сделать не точное ограничение FPS.
Есть такая функция GetTickCount() возвращяет кол-во милисекунд с запуска системы. Заведём переменную, которая будет означать последнее время отрисовки, назовём lastdraw. Теперь будем отрисовывать только в том случае если текущее время больше чем lastdraw+10, а обновлять lastdraw только если действительно отрисовываем. Время отрисовки тоже может быть солидное, поэтому будем обновлять lastdraw значение перед отрисовкой.
Итого теперь вместо
drawScene();
будет
static int lastdraw=0; //static чтобы переменная сохраняла значение даже после выхода из функции.
if (lastdraw+10<=GetTickCount()) //если текущее время больше либо равно чем время старой отрисовки + 10 милисекунд
{
   lastdraw=GetTickCount(); //обновим время старой отрисовки
   drawScene(); //отрисуем
}


Вроде всё хорошо, только есть незаметные грабли. Если посмотреть какие значения может выдавать GetTickCount() то обнаружится что не до милисекунд, а с каким-то шагом примерно равным 15..20. Поэтому таким таймером не обеспечить допустим 60 FPS когда надо чётко 16.666... ждать.

3) Вместо ограничений на отрисовку, использовать масштабирование пройденного времени. То есть прибавлять не latinc а latinc*time где time это пройденное время с последней отрисовки, а latinc инкремент в одну милисекунду.
Тогда это будет выгледить так:
а) GLvoid drawScene(GLvoid) будет теперь GLvoid drawScene(GLdouble delay)
б) Вместо latitude += latinc; будет теперь latitude += latinc * delay / 10; (поделить на 10 чтобы как 100 фпс было)
в) Вместо longitude += longinc; будет теперь longitude += longinc * delay / 10; (поделить на 10 чтобы как 100 фпс было)

Я сделал второй варьянт покачто.

Добавлено позже:
Следуйщее с чем охото разобраться - в сэмпле сделаны выборы цветов, а всё белое. Не хочу разбираться с палитрами, сделаю всё в RGB.
Для этого пиксель формат надо RGBA то есть
ppfd->iPixelType = PFD_TYPE_COLORINDEX;
ppfd->cColorBits = 8;
заменить на
ppfd->iPixelType = PFD_TYPE_RGBA;
ppfd->cColorBits = 32;

Теперь у нас нету индексов в палитре, так что и цвет которым заполнять тоже надо указать не через индекс.
glClearIndex( (GLfloat)BLACK_INDEX);
заменить на
glClearColor(0,0,0,1);

Цвета объектов:
glIndexi(RED_INDEX);
glIndexi(BLUE_INDEX);
glIndexi(GREEN_INDEX);
теперь
glColor3f(1,0,0);
glColor3f(0,0,1);
glColor3f(0,1,0);

С цветами разобрались, теперь охото чтобы сферы, пирамиды, были обьёмные (то есть с затемнениями на поверхности)

Оффлайн HoRRoR

  • Пользователь
  • Сообщений: 983
  • Пол: Мужской
  • Ромхакер
    • Просмотр профиля
Re: VC++ & OpenGL
« Ответ #2 : 14 Март 2010, 15:01:34 »
Не могу понять суть темы...

Цитата
С цветами разобрались, теперь охото чтобы сферы, пирамиды, были обьёмные (то есть с затемнениями на поверхности)
Используй освещение.

Оффлайн r57shell

  • Пользователь
  • Сообщений: 1402
    • Просмотр профиля
Re: VC++ & OpenGL
« Ответ #3 : 15 Март 2010, 21:44:35 »
Чтобы затенять объекты, нужно указать освещение, и матерьял объектам:
после строки:
glEnable(GL_DEPTH_TEST);

вставим:
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);

То есть включим освещение, и первый источник света - они нумеруются с нуля. Если теперь запустить и посмотреть, будет серая сфера, цилиндр, конус, это потому что не настроили матерьял.

Для того чтобы цвет матерьяла AMBIENT и DIFFUSE брались из текущего цвета включим эту опцию, и настроим, дописав после включения освещения следуйщее:
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);

Меня например задолбало что всё постоянно вращается, хочу сам управлять. Для этого отключим старый контроль вращения. Убить функцию вращения
Вводим в поиске "polarView" для устранения любых напоминаний о ней, и смотрим, теперь чёрный экран, видимо вид неправильно настроен )). Из удалённой функции следует что она отодвигает всё на radius, и всяко вращает. Значит надо отодвинуть самим теперь.
После
longitude += longinc;
вставим
glTranslated(0.0, 0.0, -radius);

Во, теперь всё стоит на месте, и всё видно :) Прогресс. Теперь надо удалить остатки управления старого вращения это всё что связанно с longinc, longitude, latitude, latinc.
То есть

Выгледит уныло когда всё стоит, и никакой динамики, но это покачто то, на чём остановлюсь и выложу промежуточный исходник. А пока отвлекусь.
Теперь немного о том, как работает видеокарта вообще. Конечно сейчас появились новые технологии которые я не знаю, но надеюсь то, что я здесь написал, ещё актуально.

Видео карта работает как мастерский художник, то есть она рисует одно, поверх другого, или добавляет некоторые штрихи, причём в том порядке как вы ей скажете. То есть, если вы скажете сначала нарисовать человека стоящего за деревом (вы человека не видите из-за дерева), а затем скажете рисовать дерево, то видеокарта нарисует поверх человека дерево. А если на оборот сначала дерево, а потом человека, то видеокарта поймёт, что человека не надо уже рисовать, так как его невидно (эти проверки настраиваются). На этом основана одна из техник – сортировка объектов спереди назад, чтобы потом не рисовать то, что невидно. Просто есть народ который думает что видеокарта производит Raytrace в сцене ))).

Видеокарта не знает ни какого трёхмерного измерения, всё, что видеокарта знает, это большое количество алгебраических операций, и может выставлять пиксель в нужный цвет, ещё, ей вбита по default-у последовательность алгебраических манипуляций чтобы отрисовать на экране polygon. Сначала матрицы WORLD,VIEW,PROJECTION перемножаются в соответствующем порядке, а потом умножаются на все вершины полигона, после чего идёт процесс расторизации, и проверок для каждого пикселя нужно или нет рисовать. Эту последовательность можно настраивать, флагами, а почти полностью менять её позволяют шейдеры.

Ещё хочу отметить: текст видеокарта рисует как текстуру на квадрате (два треугольника), поэтому от текста зависит скорость прорисовки. Конечно можно блокировать буфер в котором лежит вся картинка, которая потом будет выведена на экран, и рисовать там текст но это скорее всего будет дольше. Аналогично лучше текст первым выводить, чтобы потом нерисовать то что за ним.