Всем доброго времени суток! Вчера я написал статью, в которой кратко описывал язык программирования Си. По какой-то причине она не очень зашла и при 100+ показах было всего 2 просмотра. Но это неважно, сегодня в таком случае вместо скучных типов данных языка Си мы напишем простую программу для рисования чего-нибудь интересного на экране. Конечно же, писать будем на языке Си. Начну я с 2D графики, а потом перейду на какие-то элементы 3D графики, но уже в других статьях. Желаю вам удачного просмотра!
OpenGL
Или же Open Graphycs Library - библиотека от Khronos Group для работы с 2D и 3D графикой. Она написана на Си и используется для сравнительно простого взаимодействия с видеокартой. Однако сейчас она будет использоваться в основном косвенно.
GLUT
Что означает OpenGL Utility Toolkit - библиотека для приложений, используемых OpenGL. В ней есть много полезных функций для упрощенного рисования на экран.
Порисуем в 2D
Начать я хочу с основ программирования на языке Си, используя GLUT, то есть сначала покажу, как инициализировать GLUT для работы с пользователем, а потом, как что-то нарисовать на экране. Затем я реализую игру LIFE, используя эту библиотеку
Итак, начнем. Уберем все, что было в нашем стартовом проекте и начнем писать новый код. И первое новшество - функция main принимает параметры. Первый - количество аргументов командной строки (arguments count, часто пишут как ArgC). Второй - собственно аргументы командной строки (argumets variables, пишут ArgV). Первый параметр имеет тип int, второй - char ** (но обычно пишут char *ArgV[ ], чтобы подчеркнуть, что это массив). При этом функция main возвращает целое число - код ошибки или 0 в случае успеха.
Итак, начнем!
Для того, чтобы мы могли вызвать какие-то функции GLUT, нужно подключить файл glut.h в начале файла. После этого все функции GLUT доступны для вызова.
Для начала нужно проинициализировать GLUT. Делается это с помощью вызова функции glutInit. В параметры вам нужно передать адрес на переменную, хранящую количество аргументов командной строки и сами аргументы. После этого нужно инициализировать, как именно мы будем рисовать. Для этого вызывается функция glutInitDisplayMode, в параметры которой мы передадим число, собранное с помощью побитовых И. Так мы обозначим, что пользоваться мы будем цветовым пространством RGB, а также включим двойную буферизацию.
Двойная буферизация
Если вкратце, это делается для повышения качества результирующего изображения. Сначала все то, что мы рисуем, идет в некий вспомогательный буфер, а потом рисуется на экран, пока во вспомогательный буфер заносятся какие-то другие данные. При этом буфер, рисуемый на экране, называют первичным, а вспомогательный буфер - вторичным.
Вернемся к нашим баранам функциям
Далее необходимо создать окно, в которое мы собственно будем рисовать. Для этого инициализируем его позицию, размер, и, наконец, вызовем функцию его создания. Далее я предлагаю запустить нашу программу. Она ожидаемо закроется. Так вот, чтобы GLUT рисовал постоянно, надо после инициализации вызвать функцию glutMainLoop.
Но и это еще не все. Мы хотим, чтобы приложение могло что-то рисовать и, возможно, получать ввод с клавиатуры. Для этого напишем две соответствующие функции - Display и Keyboard.
Функция Display
И начать я хочу с введения вспомогательной переменной - буфер кадра - это будет массив беззнаковых char-ов. Также я напишу функцию для установки какого-то пикселя в тот или иной цвет.
Я изменил размер окна и теперь оно размеров 1920x1080. Но буфер кадра в 4 раза меньше. Каким цветом закрасится все остальное? Ответ прост - цветом очистки, который мы можем поставить в инициализации, вызвав функцию glClearColor c 4-мя параметрами - компонентами цвета.
Далее напишем функцию для рисования. Она ничего не будем получать в параметры и ничего не будет возвращать. Сначала нужно очистить экран, для чего надо вызвать функцию glClear с параметром GL_COLOR_BUFFER_BIT, то есть, мы очищаем полностью весь буфер цвета указанным ранее (или черным, если не указали). Далее необходимо указать текущую растровую позицию нашего окна (то есть начальную позицию отрисовки), увеличение пикселя по координатам (x и y). Затем можно уже рисовать наши пиксели на экран.
Но чтобы все сработало, надо вызвать функцию завершения рисования glFinish, которая не получает параметров. После этого нужно поменять первичный и вторичный буферы местами функцией glutSwapBuffers, также не получающей параметров, и затем послать сигнал перерисовки окна функцией glutPostRedisplay.
Разумеется, GLUT не знает, что именно эту функцию вы хотите использовать как функцию рисования. Поэтому ему надо явно об этом сказать, вызвав функцию glutDisplayFunc на этапе инициализации. В параметры нам нужно передать собственно функцию Display.
Функция Keyboard
Как и говорилось ранее, нужна для получения данных с клавиатуры и принимает 3 параметра: код нажатой клавиши и текущую позицию мыши. Давайте сделаем выход по escape:
Аналогично передаем ее в функцию glutKeyboardFunc в качестве параметра, теперь функция main выглядит так:
Давайте по пробелу рисовать пиксель случайного цвета на экране. Для этого просто добавим одно условие в Keyboard:
И теперь, наконец, запустим программу:
А теперь давайте напишем игру "Жизнь". Вкратце правила: даны клетки двух типов: живые и мертвые. Каждый ход у каждой клетка проверяется количество живых соседей. Если оно больше 3 или меньше 2, то клетка погибает. Однако если у какой-либо мертвой клетки количество соседей равно 3, то такая клетка оживает. Давайте напишем функцию получения количества соседей и функцию генерации нового поколения клеток:
Здесь f1 - текущее поколение, f2 - следующее. Память под них выделяется динамически.
Очевидно, что при генерации состояние клетки должно записываться в новое поле, а браться из старого.
Теперь, когда все игровые функции реализованы, давайте сделаем функцию рисования всего этого. Но перед этим надо написать функцию, которая поменяет состояния системы клеток.
Ну и, конечно, функция рисования, но так как мне сегодня весело, вместо обычного заднего фона я сделаю, чтобы на заднем фоне были концентрические окружности:
Также я написал очень большую функцию для заполнения поля разными интересными фигурами, которую я не буду показывать целиком, потому что она огромная, но я покажу самый интересный ее фрагмент:
Вызовем функцию glutFullScreen для усиления эффекта - и наслаждаемся:
Вот что-то подобное у меня получилось. Пишите, стоит ли мне продолжать тему компьютерной графики и оставляйте другие комментарии. Если я увижу какой-то отклик с вашей стороны, мне будет проще делать новые статьи (спойлер: следующая статья будет про 3D в GLUT). Подписывайтесь на канал и ждите новых статей!
Если кто-то хочет исходный код, пишите в комментариях, я с удовольствием его скину :)
#программирование