GLuint TextureCreateEmpty(GLint internalFormat, GLenum format, GLsizei width, GLsizei height)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
OPENGL_CHECK_FOR_ERRORS();
return texture;
}
GLuint colorTexture = 0, depthTexture = 0;
colorTexture = TextureCreateEmpty(GL_RGBA8, GL_RGB, windowWidth, windowHeight);
depthTexture = TextureCreateEmpty(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, windowWidth, windowHeight);
Теперь необходимо создать FBO и присоединить к нему созданные текстуры:
GLuint renderFBO = 0;
GLenum fboStatus;
glGenFramebuffers(1, &renderFBO);
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOT_ATTACHMENT0, colorTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);
if ((fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
{
LOG_ERROR("glCheckFramebufferStatus error 0x%X\n", fboStatus);
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
Для того, чтобы произвести рендер в созданный FBO мы делаем его активным и выводим все необходимые объекты:
glBindFramebuffer(GL_FRAMEBUFFER, renderFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
RenderScene();
После этих действий мы получим две текстуры colorTexture, со значениями цвета, и depthTexture, со значениями глубины. Эти две текстуры мы используем для создания экранного эффекта. В данном уроке мы используем только текстуру цвета, для простых экранных эффектов, более сложные экранные эффекты потребуют также текстуру глубины и, довольно часто, текстуру нормалей.
Рендер полноэкранного прямоугольника
Чтобы создать простой экранный эффект, например такой как "обесцвечивание" или "сепия", нам понадобится текстура цвета, полученная на предыдущем шаге, и возможность вывести полноэкранный прямоугольник с этой текстурой. Текстура уже есть, теперь создадим прямоугольник:
#define GL_OFFSET(x) ((const GLvoid *)(x))
struct fsqVertex
{
float3 position;
float2 texcoord;
};
GLuint fsqVAO = 0, fsqVBO = 0;
const fsqVertex fsqVertices[6] = {
{{-1.0f, -1.0f, 0.0f}, {0.0f,0.0f}},
{{ 1.0f, -1.0f, 0.0f}, {1.0f,0.0f}},
{{-1.0f, 1.0f, 0.0f}, {0.0f,1.0f}},
{{ 1.0f, -1.0f, 0.0f}, {1.0f,0.0f}},
{{ 1.0f, 1.0f, 0.0f}, {1.0f,1.0f}},
{{-1.0f, 1.0f, 0.0f}, {0.0f,1.0f}}
};
glGenVertexArrays(1, &fsqVAO);
glBindVertexArray(fsqVAO);
glGenBuffers(1, &fsqVBO);
glBindBuffer(GL_ARRAY_BUFFER, fsqVBO);
glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(fsqVertex), fsqVertices, GL_STATIC_DRAW);
glVertexAttribPointer(VERT_POSITION, 3, GL_FLOAT, GL_FALSE,
sizeof(fsqVertex), GL_OFFSET(0));
glEnableVertexAttribArray(VERT_POSITION);
glVertexAttribPointer(VERT_TEXCOORD, 2, GL_FLOAT, GL_FALSE,
sizeof(fsqVertex), GL_OFFSET(sizeof(float3)));
glEnableVertexAttribArray(VERT_TEXCOORD);
Весь смысл создания такого полноэкранного прямоугольника в том, что нам не придется использовать какие либо матрицы, все его вершины уже заданы в нормализованных координатах, таким образом мы можем использовать glViewport чтобы задать место вывода этого прямоугольника, в этом уроке мы будем использовать всю доступную область окна:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ShaderProgramBind(posteffectProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, colorTexture);
glUniform1i(glGetUniformLocation(posteffectProgram, "colorTexture"), 0);
glViewport(0, 0, windowWidth, windowHeight);
glBindVertexArray(fsqVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
Остается только реализовать какой-нибудь экранный эффект в шейдерной программе posteffectProgram, загрузить ее и получить готовую картинку на экране.
Примеры экранных эффектов
В исходных кодах к уроку реализованы 6 различных экранных эфектов:
- Обесцвечивание (grayscale)
- Сепия (sepia)
- Инверсия (inverse)
- Размытие (blur)
- Рельеф (emboss)
- Псевдо-аберрация (aberration)
Рассмотрим два эффекта - обесцвечивание и рельеф, последний представлен на сркиншоте к этому уроку.
Вершинный шейдер для всех экранных эффектов один и тот же, он просто получает на вход вершинные атрибуты полноэкранного прямоугольника, устанавливает позицию вершины и передает во фрагментный шейдер интерполированные текстурные координаты:
#version 330 core
layout(location = VERT_POSITION) in vec3 position;
layout(location = VERT_TEXCOORD) in vec2 texcoord;
out Vertex
{
vec2 texcoord;
} Vert;
void main(void)
{
Vert.texcoord = texcoord;
gl_Position = vec4(position, 1.0);
}
Фрагментный шейдер для эффекта "обесцвечивания":
#version 330 core
uniform sampler2D colorTexture;
in Vertex
{
vec2 texcoord;
} Vert;
layout(location = FRAG_OUTPUT0) out vec4 color;
const vec3 factor = vec3(0.27, 0.67, 0.06);
vec3 filter(in vec2 texcoord)
{
return vec3(dot(factor, texture(colorTexture, texcoord).rgb));
}
void main(void)
{
vec3 texel = Vert.texcoord.x < 0.5 ? filter(Vert.texcoord)
: texture(colorTexture, Vert.texcoord).rgb;
color = vec4(texel, 1.0);
}
Фрагментный шейдер для эффекта "рельеф":
#version 330 core
uniform sampler2D colorTexture;
in Vertex
{
vec2 texcoord;
} Vert;
layout(location = FRAG_OUTPUT0) out vec4 color;
#define KERNEL_SIZE 9
const float kernel[KERNEL_SIZE] = float[](
2.0, 0.0, 0.0,
0.0, -1.0, 0.0,
0.0, 0.0, -1.0
);
const vec2 offset[KERNEL_SIZE] = vec2[](
vec2(-1.0,-1.0), vec2( 0.0,-1.0), vec2( 1.0,-1.0),
vec2(-1.0, 0.0), vec2( 0.0, 0.0), vec2( 1.0, 0.0),
vec2(-1.0, 1.0), vec2( 0.0, 1.0), vec2( 1.0, 1.0)
);
const vec3 factor = vec3(0.27, 0.67, 0.06);
vec3 filter(in vec2 texcoord)
{
vec2 pstep = vec2(1.0) / vec2(textureSize(colorTexture, 0));
vec4 res = vec4(0.5);
for (int i = 0; i < KERNEL_SIZE; ++i)
res += texture(colorTexture, texcoord + offset[i] * pstep) * kernel[i];
return vec3(dot(factor, vec3(res)));
}
void main(void)
{
vec3 texel = Vert.texcoord.x < 0.5 ? filter(Vert.texcoord)
: texture(colorTexture, Vert.texcoord).rgb;
color = vec4(texel, 1.0);
}
Полезные ссылки
Исходный код и программа
Доступ к исходному коду уроку с проектом для MSVC можно получить двумя способами:
#эффекты, #OpenGL, #уроки
20 февраля 2011
(Обновление: 7 авг 2014)
Комментарии [34]