- Компьютерная графика для начинающих: как создать компьютерную графику?
- Типы и виды графики в играх
- Виды графики в играх: двухмерная графика и ее типы
- Виды графики в играх: трехмерная графика
- Компьютерная 3D-графика для начинающих
- Компьютерная графика для начинающих: инструменты
- Заключение
- Краткий курс компьютерной графики: пишем упрощённый OpenGL своими руками, статья 1 из 6
- Улучшение кода
- Official translation (with a bit of polishing) is available here.
- Постановка задачи
- Алгоритм Брезенхэма
- Проволочный рендер.
- Краткий курс компьютерной графики: пишем упрощённый OpenGL своими руками, статья 2 из 6
- Улучшение кода
- Official translation (with a bit of polishing) is available here.
- Update:
- Рисуем заполненный треугольник
- Рисуем модель
- Плоская тонировка
Компьютерная графика для начинающих: как создать компьютерную графику?
Очень часто, когда употребляют словосочетание «компьютерная графика», подразумевают графику, используемую в компьютерных играх, потому что именно там она имеет важное значение.
Компьютерная графика в играх для начинающих покажется достаточно сложным элементом, но, зная определенные основы, а также типы и виды графики в играх, можно перестать ее боятся.
При этом компьютерная графика — это не только игры, но и:
эффекты для кино;
и многое другое.
Сегодня мы поговорим о компьютерной графике, затрагивая тему создания графики для игр.
Типы и виды графики в играх
Компьютерная графика в современных играх бывает 2-х видов: 2D и 3D или двухмерная и трехмерная графика. Каждый из этих видов разделяется на собственные виды и типы, в зависимости от способ а ее реализации.
Виды графики в играх: двухмерная графика и ее типы
История графики в играх начинается еще с 40-х годов, когда были придуманы первые игровые автоматы, а в качестве экрана выступало полотно из обычных электрических лампочек. Это и графикой назвать сложно, но начиналось все именно так. Потом шло долгое развитие графики в играх и постепенное ее усовершенствование. 2D-графика в играх впервые появилась уже в 80-х годах, но широкое распространение получила во второй половине 90-х.
На сегодняшний день 2D-графика до сих пор применяется в компьютерных играх, однако не так активно , как 3D. Но пару слов о двухмерной графике сказать нужно.
Различают следующие виды двухмерной графики в играх и не только:
Векторная графика. Это набор и з простых геометрических элементов, например: точка, прямая, окружность, прямоугольник и т. д. В основном такой вид 2Д-графики применяется в качестве иконок и логотипов для веб-сайтов. В играх применяется реже. Отличительная особенность такой графики — эт о способность к масштабированию и деформации без потери качества.
Растровая графика. За основу такой графики берется матрица пикселей, где у каждого пикселя есть какое-то значение или комбинация значений цвета, света, прозрачности и т. д. Главный недостаток такой графики — это более высокий «вес», а также потери качества при масштабировании. Однако именно такой тип графики чаще всего применяется в играх.
Как ни крути, но двухмерная графика постепенно изживает себя. Это не значит, что она полностью исчезнет, но придет время, когда она будет использоваться в минимальных количествах, а ее место уверенно займет 3D.
Виды графики в играх: трехмерная графика
Трехмерная графика — это тренд современной компьютерной графики. Она активно используется везде, начиная от небольших объектов на веб-сайтах и заканчивая компьютерными играми и ли кино. Невозможно коротко описать все процессы , связанные с созданием трехмерной графики в играх. Хотя бы потому , что они достаточно разнообразны, а чтобы стать специалистом в 3Д-области , нужно достаточно долго обучаться и очень много практиковаться.
Но приоткрыть завесу и рассказать все максимально просто можно.
Компьютерная 3D-графика для начинающих
Неважно , для чего вы будете создавать трехмерный объект, вам нужно знать некоторые понятия, свойства и тер м ины:
Моделирование. Это одна из самых популярных технологий создания 3Д-объектов, где каждый объект описывается большим количество вершин и гранями, которые их соединяют. Если простыми словами, то любой ваш объект будет геометрической фигурой, состоящей из «сетки». Эту «сетку» вы будете деформировать до тех пор, пока она не примет форму нужного вам объекта.
Текстурирование. На объект, который вы смоделировали , теперь нужно будет наложить текстуры, чтобы он выглядел максимально реалистично. Сюда входит придание цвета, прозрачности, матовости и т . д. вашему объекту.
Свет объекта. Чтобы придать максимальной реалистичности объекту , мало просто добавить необходимой текстуры. Очень важно, чтобы был правильно настроен свет на нем. Для этого есть точечные и глобальные источники света, которые можно применять в зависимости от ситуации.
Анимация объекта. Создать реалистичный объект с правильным освещением — это тоже не все. Для чего он нужен, если он не будет двигаться? Поэтому любому объекту нужна будет анимация. Для этого объекту создают «скелет» и контролируют процесс изменения его внешнего вида в зависимости от его передвижения.
Композ. Создать объект и придать ему анимацию — это уже достижение, но когда объектов несколько, как их объединить? Композ — это и есть процесс, при котором происхо дит объединение нескольких анимированных объектов в один кадр.
Симуляция частиц. Созда ть несколько объектов и помести ть их в единый кадр — этого может оказаться мало для полноценной игровой сцены. Когда нужно придать больше визуальных эффектов , на помощь приходит симуляция частиц. По сути , это является системой свободных точек в пространстве, которые можно использовать по своему усмотрению. Из них можно сделать огонь, воду, песок, эффек т взрывов или волшебства и т. д.
Компьютерная графика для начинающих: инструменты
Не нужно верить т ем, кто говорит, что для работы в 3Д нужно знать основы и иметь под рукой хороший инструмент для работы. Нужно для начала теоретически подготовит ь ся к работе с компьютерной графикой. Для этого можно пройти специализированные курсы, почитать книги или найти себе хорошего наставника, а дальше можно переходить уже к поиску инструментов.
Что касается инструментов для создания компьютерной графики, то тут огромное поле для выбора. Именитые компании , типа Pixar и Disney , используют дорогой софт или софт личной разработки. Среди крутых и дорогих программ можно отметить:
Однако начинающему специалисту вряд ли захочется тратить большие деньги на специализированный софт, поэтому многие останавливаются на бесплатном Blender. Он хоть и бесплатный, но по своим возможностям не уступает многим платным «собратьям».
Заключение
Чтобы быть специалистом компьютерной графики в игра х ( или для чего-нибудь другого), нужно в первую очередь выучит ь ся, а не просто понимать ее виды и знать , какими инструментами можно с ней работать. Благо сейчас возможность обучиться ничем не ограничена, кроме вашего желания.
Мы будем очень благодарны
если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.
Источник
Краткий курс компьютерной графики: пишем упрощённый OpenGL своими руками, статья 1 из 6
Улучшение кода
Official translation (with a bit of polishing) is available here.
Постановка задачи
Цель этого цикла статей — показать, как работает OpenGL, написав его (сильно упрощённый!) клон самостоятельно. На удивление часто сталкиваюсь с людьми, которые не могут преодолеть первоначальный барьер обучения OpenGL/DirectX. Таким образом, я подготовил краткий цикл из шести лекций, после которого мои студенты выдают неплохие рендеры.
Итак, задача ставится следующим образом: не используя никаких сторонних библиотек (особенно графических) получить примерно такие картинки:
Внимание, это обучающий материал, который в целом повторит структуру библиотеки OpenGL. Это будет софтверный рендер, я не ставлю целью показать, как писать приложения под OpenGL. Я ставлю целью показать, как сам OpenGL устроен. По моему глубокому убеждению, без понимания этого написание эффективных приложений с использованием 3D библиотек невозможно.
Я постараюсь не перевалить за 500 строк в конечном коде. Моим студентам требуется от 10 до 20 часов программирования, чтобы начать выдавать подобные рендеры. На вход получаем текстовый файл с полигональной сеткой + картинки с текстурами, на выход отрендеренную модель. Никакого графического интерфейса, запускаемая программа просто генерирует файл с картинкой.
Поскольку целью является минимизация внешних зависимостей, то я даю своим студентам только один класс, позволяющий работать с TGA файлами. Это один из простейших форматов, поддерживающий картинки в формате RGB/RGBA/чёрно-белые. То есть, в качестве отправной точки мы получаем простой способ работы с картинками. Заметьте, единственная функциональность, доступная в самом начале (помимо загрузки и сохранения изображения), это возможность установить цвет одного пикселя.
Никаких функций отрисовки отрезков-треугольников, это всё придётся писать вручную.
Я даю свой исходный код, который пишу параллельно со студентами, но не рекомендую его использовать, в этом просто нет смысла.
Весь код доступен на гитхабе, здесь находится начальный код, который я даю своим студентам.
output.tga должен выглядеть примерно так:
Алгоритм Брезенхэма
Цель первой лекции — сделать рендер в проволочной сетке, для этого нужно научиться рисовать отрезки.
Можно просто пойти и почитать, что такое алгоритм Брезенхэма, но большинство моих студентов буксуют, читая сразу целочисленную версию, поэтому давайте напишем код сами.
Как выглядит простейший код, рисующий отрезок между двумя точками (x0, y0) и (x1, y1)?
Видимо, как-то так:
Снапшот кода доступен на гитхабе.
Проблема этого кода (помимо эффективности) это выбор константы, которую я взял равной .01.
Если вдруг мы возьмём её равной .1, то наш отрезок будет выглядеть вот так:
Мы легко можем найти нужный шаг: это просто количество пикселей, которые нужно нарисовать.
Простейший (с ошибками!) код выглядит примерно так:
Осторожно: наипервейший источник ошибок в подобном коде у моих студентов — это целочисленное деление типа (x-x0)/(x1-x0).
Далее, если мы попробуем этим кодом нарисовать вот такие линии:
То выяснится, что одна линия хороша, вторая с дырками, а третьей вовсе нет.
Обратите внимание, что первая и вторая строчки (в коде) дают одну и ту же линию разного цвета. Белую мы уже видели, она хорошо отрисовывается. Я надеялся перекрасить белую в красный цвет, не получилось. Это тест на симметричность: результат отрисовки сегмента не должен зависеть от порядка точек: сегмент (a,b) дожен быть ровно таким же, как и сегмент (b,a).
Дырки в одном из сегментов из-за того, что его высота больше ширины.
Мои студенты часто мне предлагают такой фикс: if (dx>dy)
Ну ёлки!
Этот код работает прекрасно. Именно подобной сложности код я хочу видеть в финальной версии нашего рендера.
Разумеется, он неэффективен (множественные деления и тому подобное), но он короткий и читаемый.
Заметьте, в нём нет ассертов, в нём нет проверок на выход за границы, это плохо.
Но я стараюсь не загромождать именно этот код, так как его много читают, при этом я систематически напоминаю про необходимости проверок.
Итак, предыдущий код прекрасно работает, но он может быть оптимизирован.
Оптимизация — опасная вещь, нужно чётко представлять, на какой платформе будет работать код.
Оптимизировать код под графическую карту или просто под центральный процессор — совсем разные вещи.
Перед и во время любой оптимизации код нужно профилировать.
Попробуйте угадать, какая операция здесь наиболее ресурсоёмкая?
Для тестов я рисую 1000000 раз 3 отрезка, которые мы рисовали перед этим. Мой процессор: is Intel® Core(TM) i5-3450 CPU @ 3.10GHz.
Этот код для каждого пикселя вызывает конструктор копирования TGAColor.
А это 1000000 * 3 отрезка * примерно 50 пикслей на отрезок. Немало вызовов.
Где начнём оптимизацию?
Профилировщик нам скажет.
Я откомпилировал код с ключами g++ -ggdb -g3 -pg -O0; затем запустил gprof:
10% рабочего времени — это копирование цвета.
Но ещё 70% проводятся в вызове line()! Тут и будем оптимизировать.
Заметим, что каждое деление имеет один и тот же делитель, давайте его вынесем за пределы цикла.
Переменная error даёт нам дистанцию до идеальной прямой от нашего текущего пикселя (x, y).
Каждый раз, как error превышает один пиксель, мы увеличиваем (уменьшаем) y на единицу, и на единицу же уменьшаем ошибку.
А зачем нам нужны плавающие точки? Едиственная причина — это одно деление на dx и сравнение с .5 в теле цикла.
Мы можем избавиться от плавающей точки, заменив переменную error другой, назовём её error2, она равна error*dx*2.
Вот эквивалентный код:
Другой разговор, теперь достаточно убрать ненужные копии при вызове функции, передвая цвет по ссылке (или просто включив флаг компиляции -O3) и всё готово. Ни единого умножения, ни единого деления в коде.
Время работы снизилось с 2.95 секунды до 0.64.
Проволочный рендер.
Теперь всё готово для создания проволочного рендера. Снимок кода и тестовая модель находятся здесь.
Я использовал wavefront obj формат файла для хранения модели. Всё, что нам нужно для рендера, это прочитать из файла массив вершин вида
v 0.608654 -0.568839 -0.416318
[. ]
это координаты x,y,z, одна вершина на строку файла
и граней
f 1193/1240/1193 1180/1227/1180 1179/1226/1179
[. ]
Тут нас интересуют первое число после каждого пробела, это номер вершины в массиве, который мы прочитали ранее. Таким образом эта строчка говорит, что вершины 1193, 1180 и 1179 образуют треугольник.
Файл model.cpp содержит простейший парсер.
Пишем такой цикл в наш main.cpp и вуаля, наш проволочный рендер готов.
В следующий раз будем рисовать 2D треугольники и подправлять наш рендер.
Источник
Краткий курс компьютерной графики: пишем упрощённый OpenGL своими руками, статья 2 из 6
Улучшение кода
Official translation (with a bit of polishing) is available here.
Update:
Внимание, статья 4в даёт новую, более простую версию растеризатора.
Давайте знакомиться, это я.
То есть, модель моей башки, отрендеренная в программе, которую мы сделаем за ближайшие час-два.
В прошлый раз мы нарисовали проволочную сетку трёхмерной модели, в этот раз мы зальём полигоны. Точнее, треугольники, так как OpenGL практически любой полигон триангулирует, поэтому ни к чему разбирать сложный случай. Напоминаю, что этот цикл статей создан для самостоятельного программирования. Время, которое я здесь привожу — это не время чтения моего кода. Это время написания вашего кода с нуля. Мой код здесь только для того, чтобы сравнить ваш (рабочий) код с моим. Я совсем не являюсь хорошим программистом, поэтому ваш код может быть существенно лучше моего. Любая критика приветствуется, любым вопросам рад.
Пожалуйста, если вы следуете этому туториалу и пишете свой код, выкладывайте его на github.com/code.google.com и им подобные и давайте ссылки в комментариях! Это может хорошо помочь как и вам (другие люди могут чего посоветовать), так и будущим читателям.
Рисуем заполненный треугольник
Итак, тема на сегодня (примерно на два часа для плохо программирующих, но мотивированных студентов): отрисовка двумерных треугольников. В прошлый раз мы разобрали алгоритм Брезенхэма для растеризации отрезка, теперь задача нарисовать заполненный треугольник. Вы будете смеяться, но это нетривиальная задача. Я не знаю почему, но я знаю, что это так. Большинство моих студентов без подсказок проводят над этой задачей существенно больше пары часов. Давайте определимся с методом, а затем будем программировать.
В самом начале давайте рассмотрим вот такой псевдокод:
Я очень люблю этот метод. Он простой и рабочий. Найти описывающий прямоугольник крайне просто, проверить принадлежность точки двумерному треугольнику (да и любому выпуклому полигону) тоже просто.
Оффтоп: если мне нужно будет написать код, который будет крутиться на, скажем, самолёте, и этот код должен будет проверять принадлежность точки полигону, я никогда не сяду на этот самолёт. Это на удивление сложная проблема, если мы хотим её решить надёжно.
Почему я люблю этот код? Да потому, что, увидев такое, совсем новичок в программировании его воспримет с энтузиазмом, человек, немного знакомый с программированием, только самодовольно хмыкнет, мол, вот идиот писал. А эксперт в программировании компьютерной графики просто пожмёт плечами, мол, ну да, так оно и работает в реальной жизни. Массивно-параллельные вычисления в тысячах маленьких графических процессоров (я говорю про обычные потребительские компьютеры) творят чудеса. Но мы будем писать код под центральный процессор, поэтому этот метод использовать не будем. Да и какая разница, как оно там в кремнии, нашей абстракции вполне хватит для понимания принципа работы.
Окей, начальная заглушка будет выглядеть следующим образом:
Как обычно, на гитхабе доступен отпечаток кода. В этом коде всё просто: я даю три треугольника для начальной отладки вашего кода; если внутри функции triangle просто сделать вызов line(), то получим контур треугольника. Как нарисовать заполненный треугольник?
Хороший метод отрисовки треугольника должен обладать следующими свойствами:
- Он должен быть (сюрприз) простым и быстрым
- Он должен быть симметричным: картинка не должна зависеть от порядка вершин, переданных в функцию отрисовки
- Если два треугольника имеют две общие вершины, между ними не должно быть дырок из-за округлений растеризации.
Требований можно добавлять гораздо больше, но мы довольствуемся этими тремя.
Традиционно используется line sweeping (заметание отрезком?):
- Сортируем вершины треугольника по их y-координате
- Растеризуем параллельно левую и правую границы треугольника
- Отрисовываем горзонтальный отрезок между левой и правой точкой границы
Тут мои студенты начинают теряться, кто левый, кто правый, да и вообще, в треугольнике три отрезка…
В этот момент я оставляю своих студентов примерно на час, чтение моего кода куда как менее ценно, нежели сравнение своего (выстраданного!) кода с моим.
Как рисую я? Ещё раз, если у вас есть лучший метод, то я его с огромным удовольствием возьму на вооружение. Давайте предположим, что у нас есть три точки треугольника, t0,t1,t2, они отсортированы по возрастанию y-координаты.
Тогда граница А будет между t0 и t2, граница Б будет между t0 и t1, а затем между t1 и t2.
Здесь у нас граница А нарисована красным, а граница Б зелёным.
Граница Б, к сожалению, составная. Давайте отрисуем нижнюю половину треугольника, разрезав его по горизонтали в точке излома границы Б.
Заметьте, что в этот раз у меня получились разрывные отрезки. В отличие от прошлого раза (где мы рисовали прямые) я не заморочился поворотом изображения на 90°. Почему? Это оказывается не всем очевидным моментом. Просто если мы соединим горизонтальными линиями соответствующие пары точек, то пробелы пропадут:
Теперь осталось отрисовать вторую половину треугольника. Это можно сделать, добавив второй цикл:
На этом можно было бы успокоиться, но у меня случается несварение, когда я дважды вижу один и тот же код, да ещё так рядом. Поэтому сделаем его чуть менее читаемым, зато более простым для модификаций.
Отпечаток кода для отрисовки 2d треугольников.
Рисуем модель
Мы умеем уже отрисовывать модель с пустыми треугольниками, давайте их зальём случайным цветом, это поможет нам проверить, насколько хорошо мы закодировали заполнение треугольников. Вот код.
Всё просто: как и раньше, пробегаем по всем треугольникам, превращаем мировые координаты в экранные и рисуем треугольники. Подробное описание разных систем координат в последущих статьях. Должно получиться нечто вроде этого:
Плоская тонировка
Давайте теперь убирать эти клоунские цвета и освещать нашу модель.
Капитан Очевидность: «При одной и той же итенсивности света полигон освещён максимально ярко, если свет ему перпендикулярен».
Нулевую освещённость мы получим, если полигон параллелен вектору света.
Перефразируем: интенсивность освещённости равна скалярному произведению вектора света и нормали к данному треугольнику.
Нормаль к треугольнику может быть посчитана просто как векторное произведение двух его рёбер.
Но ведь скалярное произведение может быть отрицательным, что это означает? Это означает, что свет падает позади полигона. Если модель хорошая (обычно не наша забота, а 3д моделеров), то мы просто можем этот треугольник не рисовать. Это позволяет быстро убрать часть невидимых треугольников. В англоязычной литературе называется Back-face culling.
Модель моей головы выглядит детальнее? Ну так в ней четверть миллиона треугольников. Ничего, детали мы добавим позже, получив картинку, которую я дал для затравки в первой статье.
Обратите внимание, внутренняя полость рта нарисовалась поверх губ. Ну а что, такое быстрое отсечение невидимых треугольников убирает всё ненужное только для выпуклых моделей. Эти огрехи мы уберём в следующий раз, закодировав z-buffer.
Источник