Войти
ПрограммированиеСтатьиГрафика

Реализация карт теней с использованием GLSL шейдеров (2 стр)

Автор:

Рендер в текстуру

Рендер сцены в текстуру с позиции источника света у нас осуществляет функция RenderToShadowMap(). Давайте разберёмся, что она делает:

void RenderToShadowMap()
{
  glViewport(0, 0, texDepthSizeX, texDepthSizeY);
  if (shadowRenderType)
  { // Вариант #1
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbDepth);
    // Очищать текстуру нужно значением соответствующим дальней плоскости отсечения
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  }
  else
  { // Вариант #2
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbDepth2);
    // В этом случае у нас нет буфера цвета, поэтому очищать его не нужно
    glClear(GL_DEPTH_BUFFER_BIT);
  }
  // Сдвиг полигонов - нужен для того, чтобы не было z-fighting'а
  glEnable(GL_POLYGON_OFFSET_FILL);
  glPolygonOffset(2.0, 500.0);

  // Установить матрицы камеры света
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(90.0, 1.0, 30.0, 300.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(lightPos[0], lightPos[1], lightPos[2], lightEye[0], lightEye[1], lightEye[2], 
              lightUp[0], lightUp[1], lightUp[2]);

  // Сохраняем эти матрицы, они нам понадобятся для расчёта матрицы источника света
  glGetFloatv(GL_PROJECTION_MATRIX, lightProjectionMatrix);
  glGetFloatv(GL_MODELVIEW_MATRIX, lightModelViewMatrix);

  // Напомню, что в первом варианте нам нужен шейдер,
  // чтобы сохранить глубину в текстуру, во втором это не к чему.
  if (shadowRenderType)
  { // Вариант #1
    glUseProgramObjectARB(shaderDepth);
    DrawModel();
    glUseProgramObjectARB(0);
  }
  else
  { // Вариант #2
    DrawModel();
  }

  glDisable(GL_POLYGON_OFFSET_FILL);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

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

Шейдер записи глубины в текстуру

Что касаемо шейдеров, хочу заметить, что если вы будете использовать возможности GLSL 1.20, не указав в шейдере версию, то можно нарваться не только на предупреждения от компилятора, но, что ещё хуже, и на ошибки.

Прикрепив текстуру как буфер цвета, нам нужен шейдер, который будет записывать в текстуру значение глубины. Шейдер достаточно прост.

Вершинный шейдер:

#version 120

void main(void)
{
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Фрагментный шейдер:

#version 120

void main(void)
{
  gl_FragColor = vec4(gl_FragCoord.z);
}

Ничего сложного шейдер не делает, просто записывает значение глубины фрагмента.

В примере шейдеры называются depth.vert и depth.frag.

gl_FragCoord.z можно заменить следующей записью:

  float z = pos.z / pos.w * 0.5 + 0.5;

Где pos - это позиция вершины, посчитанная в вершинном шейдере следующим образом:

  pos = gl_ModelViewProjectionMatrix * gl_Vertex;

Делением на w мы переводим z координату из однородных в декартовы, результатом данной операции будет число в диапазоне
[-1..1].

Затем мы переводим из диапазона [-1..1] в диапазон [0..1] с помощью выражения * 0.5 + 0.5. Подобное делать не обязательно, если у нас текстура глубины не зажимается (clamp) в диапазон [0..1]. Если мы уберём приведение к [0..1] это повлечёт за собой изменения в другом шейдере, используемом для наложения тени на сцену, поэтому мы делать этого не будем. Мы будем использовать gl_FragCoord.z — это быстрее и проще.

Теперь мы имеем полное представление того, как сохранить значения глубины в текстуре. Перейдём к рендерингу сцены с тенью.

Рендер сцены с тенями

Рендер сцены с тенями у нас осуществляет функция RenderShadowedScene(). Давайте взглянем на неё подробнее:

void RenderShadowedScene()
{
  glViewport(0, 0, scrWidth, scrHeight);
  glClearColor(0.0, 0.0, 0.0, 1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Устанавливаем матрицы камеры
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(90.0, double(scrWidth) / double(scrHeight), 1.0, 1000.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslatef(0.0, 0.0, -rz);
  glRotatef(ry, 1.0, 0.0, 0.0);
  glRotatef(rx, 0.0, 1.0, 0.0);

  // Сохраняем матрицы, они нам нужны для вычисления освещения
  // Инвертированная матрица используется в расчёте матрицы источника света
  glGetFloatv(GL_PROJECTION_MATRIX, cameraProjectionMatrix);
  glGetFloatv(GL_MODELVIEW_MATRIX, cameraModelViewMatrix);
  InverseMatrix(cameraInverseModelViewMatrix, cameraModelViewMatrix);

  // Вычисляем матрицу источника света, подробно её рассмотрим немного позднее
  glPushMatrix();
  glLoadIdentity();
  glTranslatef(0.5, 0.5, 0.5); // + 0.5
  glScalef(0.5, 0.5, 0.5); // * 0.5
  glMultMatrixf(lightProjectionMatrix);
  glMultMatrixf(lightModelViewMatrix);
  glMultMatrixf(cameraInverseModelViewMatrix);
  glGetFloatv(GL_MODELVIEW_MATRIX, lightMatrix);
  glPopMatrix();

  GLhandleARB shaderShadow = 0;
  GLuint uniformShadow_shadowMap = 0;
  GLuint uniformShadow_lightMatrix = 0;
  GLuint uniformShadow_lightPos = 0;
  GLuint uniformShadow_lightDir = 0;

  if (shadowRenderType)
  { // Вариант #1
    glBindTexture(GL_TEXTURE_2D, texDepth);

    shaderShadow = shaderShadowTex2D;
    uniformShadow_shadowMap = uniformShadowTex2D_shadowMap;
    uniformShadow_lightMatrix = uniformShadowTex2D_lightMatrix;
    uniformShadow_lightPos = uniformShadowTex2D_lightPos;
    uniformShadow_lightDir = uniformShadowTex2D_lightDir;
  }
  else
  { // Вариант #2
    glBindTexture(GL_TEXTURE_2D, texDepth2);

    // Для второго варианта включаем режим сравнения текстуры, о нём подробно я расскажу позже
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);

    shaderShadow = shaderShadowShad2D;
    uniformShadow_shadowMap = uniformShadowShad2D_shadowMap;
    uniformShadow_lightMatrix = uniformShadowShad2D_lightMatrix;
    uniformShadow_lightPos = uniformShadowShad2D_lightPos;
    uniformShadow_lightDir = uniformShadowShad2D_lightDir;
  }

  // Биндим шейдер и передаём юниформы необходимые для расчёта освещения и светового пятна
  glUseProgramObjectARB(shaderShadow);
  glUniform1iARB(uniformShadow_shadowMap, 0);
  glUniformMatrix4fvARB(uniformShadow_lightMatrix, 1, false, lightMatrix);
  float mvLightPos[3];
  mvLightPos[0] = cameraModelViewMatrix[0] * lightPos[0] + cameraModelViewMatrix[4] * lightPos[1] +
    cameraModelViewMatrix[8] * lightPos[2] + cameraModelViewMatrix[12];
  mvLightPos[1] = cameraModelViewMatrix[1] * lightPos[0] + cameraModelViewMatrix[5] * lightPos[1] +
    cameraModelViewMatrix[9] * lightPos[2] + cameraModelViewMatrix[13];
  mvLightPos[2] = cameraModelViewMatrix[2] * lightPos[0] + cameraModelViewMatrix[6] * lightPos[1] +
    cameraModelViewMatrix[10] * lightPos[2] + cameraModelViewMatrix[14];
  glUniform3fARB(uniformShadow_lightPos, mvLightPos[0], mvLightPos[1], mvLightPos[2]);
  float lightDir[3];
  lightDir[0] = lightEye[0] - lightPos[0];
  lightDir[1] = lightEye[1] - lightPos[1];
  lightDir[2] = lightEye[2] - lightPos[2];
  glUniform3fARB(uniformShadow_lightDir, lightDir[0], lightDir[1], lightDir[2]);

  // Рисуем плоскость и модель
  DrawPlane();
  DrawModel();

  glUseProgramObjectARB(0);

  if (!shadowRenderType)
  { // Вариант #2
    // Не забываем выключать режим сравнения
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
  }
  glBindTexture(GL_TEXTURE_2D, 0);

  // Отображаем положение источника света
  glColor3f(1.0, 1.0, 1.0);
  glPushMatrix();
  float lightInverseModelViewMatrix[16];
  InverseMatrix(lightInverseModelViewMatrix, lightModelViewMatrix);
  glMultMatrixf(lightInverseModelViewMatrix);
  glutSolidSphere(1.0, 10.0, 10.0);
  glPopMatrix();
}

Хочу заметить, что плоскость не рендерится в текстуру глубины, так как не отбрасывает тени.

Думаю, пояснения требуют расчёт матрицы источника света и режим сравнения текстуры. О матрице источника света я расскажу ниже по тексту, а о режиме сравнения текстуры мы поговорим несколько позже.

Страницы: 1 2 3 4 Следующая »

#GLSL, #OpenGL, #shader, #тени

1 февраля 2009 (Обновление: 14 сен. 2009)

Комментарии [226]