Урок 4. Разноцветный куб

Добро пожаловать на 4 урок по OpenGL. В этом уроке мы будем учится следующим вещам:
  • Нарисуем объемный куб вместо уже порядком надоевшего плоского треугольника.
  • Добавим разноцветности
  • Узнаем что такое Z-буфер.

Рисуем куб

У куба есть 6 квадратных граней. Но так как OpenGL ничего не знает о гранях и умеет работать лишь с треугольниками нужно разбить каждую квадратную грань на 2 треугольника и у нас получится 12 треугольников. Запишем координаты вершин точно так же, как и для одного треугольника.

// Наши вершины. Три дробных числа подряд дают нам одну координату 3д вершины.3 вершины подряд дают нам один треугольник.
// У куба 6 граней с 2 треугольниками на каждой грани, что дает на 6*2=12 треугольников, или 12*3=36 вершин
static const GLfloat g_vertex_buffer_data[] = {
   -1.0f,-1.0f,-1.0f, // начало 1 треугольника
   -1.0f,-1.0f, 1.0f,
   -1.0f, 1.0f, 1.0f, // конец первого треугольника
   1.0f, 1.0f,-1.0f, // начало второго треугольника
   -1.0f,-1.0f,-1.0f,
   -1.0f, 1.0f,-1.0f, // конец второго треугольника
   1.0f,-1.0f, 1.0f,
   -1.0f,-1.0f,-1.0f,
   1.0f,-1.0f,-1.0f,
   1.0f, 1.0f,-1.0f,
   1.0f,-1.0f,-1.0f,
   -1.0f,-1.0f,-1.0f,
   -1.0f,-1.0f,-1.0f,
   -1.0f, 1.0f, 1.0f,
   -1.0f, 1.0f,-1.0f,
   1.0f,-1.0f, 1.0f,
   -1.0f,-1.0f, 1.0f,
   -1.0f,-1.0f,-1.0f,
   -1.0f, 1.0f, 1.0f,
   -1.0f,-1.0f, 1.0f,
   1.0f,-1.0f, 1.0f,
   1.0f, 1.0f, 1.0f,
   1.0f,-1.0f,-1.0f,
   1.0f, 1.0f,-1.0f,
   1.0f,-1.0f,-1.0f,
   1.0f, 1.0f, 1.0f,
   1.0f,-1.0f, 1.0f,
   1.0f, 1.0f, 1.0f,
   1.0f, 1.0f,-1.0f,
   -1.0f, 1.0f,-1.0f,
   1.0f, 1.0f, 1.0f,
   -1.0f, 1.0f,-1.0f,
   -1.0f, 1.0f, 1.0f,
   1.0f, 1.0f, 1.0f,
   -1.0f, 1.0f, 1.0f,
   1.0f,-1.0f, 1.0f
};

Создаем, заполняем и конфигурируем OpenGL буфер стандартными OpenGL функциями(glGenBuffers, glBindBuffer, glBufferData, glVertexAttribPointer), если забыли, то гляньте в урок 2. Рисование так же не отличается ни чем. Нужно лишь задать правильно количество вершин.
// Рисуем треугольники !
glDrawArrays(GL_TRIANGLES, 0, 12*3); 

Несколько заметок по поводу кода:
  • Наша модель захардкожена  в программе. То есть если мы хотим её поменять, то нам нужно изменить исходный код и перекомпилировать. В уроке 7 мы научимся загружать модели динамически.
  • Каждая вершина тут записана по меньшей мере 3 раза(можете сами поискать строчку «“-1.0f,-1.0f,-1.0f» в предыдущем куске кода). Это ужасное расточительство. В уроке 9 мы научимся более экономно использовать память

Теперь у нас есть все необходимые куски для рисования куба в белом цвете. Подготовьте шейдер к работе! Вперед, пробуйте!

Добавляем цвета

Цвет – с точки зрения видеокарты, точно то же самое, что и позиция: просто данные. В терминах OpenGL – это «атрибуты». Мы пользовались этими атрибутами вызывая функцию glEnableVertexAttribArray() и glVertexAttribPointer(). Теперь можно попробовать добавить еще один атрибут. Код будет почти такой же.
Сначала нужно объявить наши цвета: один RGB триплет на каждую вершину. Я тут вписал их от балды, поэтому результат может быть не такой красивый, как хотелось бы, но вы можете попробовать сделать красивее.

// Один цвет на каждую вершину. Числа от фонаря.
static const GLfloat g_color_buffer_data[] = {
   0.583f,  0.771f,  0.014f,
   0.609f,  0.115f,  0.436f,
   0.327f,  0.483f,  0.844f,
   0.822f,  0.569f,  0.201f,
   0.435f,  0.602f,  0.223f,
   0.310f,  0.747f,  0.185f,
   0.597f,  0.770f,  0.761f,
   0.559f,  0.436f,  0.730f,
   0.359f,  0.583f,  0.152f,
   0.483f,  0.596f,  0.789f,
   0.559f,  0.861f,  0.639f,
   0.195f,  0.548f,  0.859f,
   0.014f,  0.184f,  0.576f,
   0.771f,  0.328f,  0.970f,
   0.406f,  0.615f,  0.116f,
   0.676f,  0.977f,  0.133f,
   0.971f,  0.572f,  0.833f,
   0.140f,  0.616f,  0.489f,
   0.997f,  0.513f,  0.064f,
   0.945f,  0.719f,  0.592f,
   0.543f,  0.021f,  0.978f,
   0.279f,  0.317f,  0.505f,
   0.167f,  0.620f,  0.077f,
   0.347f,  0.857f,  0.137f,
   0.055f,  0.953f,  0.042f,
   0.714f,  0.505f,  0.345f,
   0.783f,  0.290f,  0.734f,
   0.722f,  0.645f,  0.174f,
   0.302f,  0.455f,  0.848f,
   0.225f,  0.587f,  0.040f,
   0.517f,  0.713f,  0.338f,
   0.053f,  0.959f,  0.120f,
   0.393f,  0.621f,  0.362f,
   0.673f,  0.211f,  0.457f,
   0.820f,  0.883f,  0.371f,
   0.982f,  0.099f,  0.879f
};

Создайте буфер и заполните его точно так же, как и в предыдущих случаях:
GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);

Конфигурирование почти такое же:
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(
   1,                                // атрибут. Нет особых причин писать 1, главное, чтобы совпадало со значением в шейдере.
   3,                                // размер
   GL_FLOAT,                         // тип
   GL_FALSE,                         // нормализировано ли?
   0,                                // шаг
   (void*)0                          //смещение в буфере
);

Теперь нужно подключить этот буфер в шейдере:
layout(location = 1) in vec3 vertexColor;

В нашем случае мы не будем делать ничего особенного с этими цветами. Мы просто передадим его во фрагментный шейдер:
// Исходящие данные; будут интерполированы для каждого фрагмента.
out vec3 fragmentColor;
void main(){
   [...]
    // Цвет каждой вершины будет интерполирован
  // чтобы рассчитать цвет каждого фрагмента
   fragmentColor = vertexColor;
}

Во фрагментном шейдере мы объявляем fragmentColor снова:

// Интерполированные значения из вершинного шейдера
in vec3 fragmentColor;

…и копируем его в исходный цвет фрагмента:
// Результирующий цвет color = цвет полученный из вершинного шейдера,
// интерполированный между тремя вершинами треугольника
color = fragmentColor;

Вот что у нас получается в результате:

Как-то не красиво и плохо… Чтобы понять, почему так получилось, давайте разберем, что получается, когда мы рисуем «дальние» и «ближние» треугольники:
В принципе так и надо.
А теперь нарисуем «дальний» треугольник последним:

В итоге получается, что «дальний» треугольник перекрывает «ближний», хотя, по идее, такого быть не должно. Это же и получилось с нашим кубом: Некоторые грани, которые должны были бы быть сзади, повылазили вперед. Нам поможет Z-буфер!
  • Если вы не видите проблемы, попробуйте изменить позицию камеры на (4,3,-3)

 Z-буфер

Чтобы избавить нас от этой противной проблемы, нужно просто сохранять «глубину»(Z компоненту) для каждого фрагмента в отдельном буфере, и каждый раз когда нам хочется нарисовать фрагмент, мы должны проверять, можем ли мы его записать(если новый фрагмент ближе к нам чем предыдущий записанный).
Это можно было бы сделать и самому, но гораздо проще попросить это сделать за нас видеокарту.
glEnable(GL_DEPTH_TEST);
// Принимать фрагменты которые ближе к камере
glDepthFunc(GL_LESS);

И этого достаточно, чтобы решить все наши проблемы:


Упражнения

  • Нарисуйте куб и треугольник в разных положениях. Для этого нужно сгенерировать две МВП матрицы и сделать 2 drawcall в главном цикле, но можно обойтись одним шейдером.
  • Установите новые цвета для куба. Например, случайные на каждом кадре. Или в зависимости от позиции каждой вершины…или еще как-нибудь. 

1 комментарий:

  1. Возникла проблема: все заливает одним цветом на экране, код полностью из исходников, что это может быть?

    ОтветитьУдалить