Если я нарисую 1 белый пиксель и переместу его вокруг каждого кадра на один пиксель в некотором направлении, каждый кадр пикселов экрана будет получать на одно значение R / G / B меньше (в диапазоне 0-255), таким образом после255 кадров белого пикселя будут полностью черными.Поэтому, если бы я переместил белый пиксель вокруг, я увидел бы градиентный след, идущий от белого к черному равномерно, с разницей в 1 цвет по сравнению с предыдущим цветом пикселя.
Прежде чем объяснить, как это сделать, яЯ хотел бы сказать, что визуальный эффект, к которому вы стремитесь, это ужасный визуальный эффект, и вы не должны его использовать.Вычитание значения из каждого из цветов RGB даст другой цвет , а не более темную версию того же цвета.Цвет RGB (255,128,0), если вы вычесть 1 из него 128 раз, станет (128, 0, 0).Первый цвет коричневый, второй темно-красный.Это не одно и то же.
Теперь, так как вы не очень хорошо объяснили это, я должен сделать некоторые предположения.Я предполагаю, что в том, что вы делаете, нет «объектов».Там нет государства.Вы просто рисуете вещи в произвольных местах, и вы не помните, что и где вы нарисовали, и не хотите помнить, что и где было нарисовано.
Чтобы делать то, что вы хотите, вам нужно два за кадромбуферы.Я рекомендую использовать FBO s и текстуры размером с экран для них.Основной алгоритм прост.Вы визуализируете изображение предыдущего кадра в текущее изображение, используя режим наложения, который «вычитает 1» из цветов, которые вы пишете.Затем вы рендерите новый материал, который вы хотите, к текущему изображению.Затем вы отображаете это изображение.После этого вы выбираете, какое изображение является предыдущим, а какое - текущим, и выполняете процесс заново.
Примечание. Следующий код примет на себя функциональность OpenGL 3.3.
Инициализация
Итак, сначала, во время инициализации (после инициализации OpenGL), вы должны создать текстуры размером с экран.Вам также понадобятся два буфера глубины размером с экран.
GLuint screenTextures[2];
GLuint screenDepthbuffers[2];
GLuint fbos[2]; //Put these definitions somewhere useful.
glGenTextures(2, screenTextures);
glGenRenderbuffers(2, screenDepthbuffers);
glGenFramebuffers(2, fbos);
for(int i = 0; i < 2; ++i)
{
glBindTexture(GL_TEXTURE_2D, screenTextures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, screenDepthBuffers[i]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCREEN_WIDTH, SCREEN_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[i]);
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, screenTextures[i], 0);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, screenDepthBuffers[i]);
if(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
//Error out here.
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
Рисование предыдущего кадра
Следующим шагом будет рисование изображения предыдущего кадра на текущем изображении.
ДляДля этого нам нужно иметь концепцию предыдущего и текущего FBO.Это делается с помощью двух переменных: currIndex
и prevIndex
.Эти значения являются индексами в наших массивах GLuint для текстур, буферов рендеринга и FBO.Они должны быть инициализированы (во время инициализации, а не для каждого кадра) следующим образом:
currIndex = 0;
prevIndex = 1;
В вашей процедуре рисования первым шагом является рисование предыдущего кадра, вычитая один (опять же, я настоятельно рекомендую использоватьреальная смесь здесь).
Это не будет полный код;будет псевдокод, который я ожидаю от вас заполнить.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[currIndex]);
glClearColor(...);
glClearDepth(...);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, screenTextures[prevIndex]);
glUseProgram(BlenderProgramObject); //The shader will be talked about later.
RenderFullscreenQuadWithTexture();
glUseProgram(0);
glBindTexture(GL_TEXTURE_2D, 0);
Функция RenderFullscreenQuadWithTexture
делает именно то, что говорит: визуализирует квадратор размера экрана, используя текущую связанную текстуру.Программный объект BlenderProgramObject
- это шейдер GLSL, который выполняет нашу операцию смешивания.Он извлекает из текстуры и смешивает.Опять же, я предполагаю, что вы знаете, как настроить шейдер и т. Д.
Фрагментный шейдер будет иметь основную функцию, которая выглядит примерно так:
shaderOutput = texture(prevImage, texCoord) - (1.0/255.0);
Опять же, яНастоятельно советуем это:
shaderOutput = texture(prevImage, texCoord) * (0.05);
Если вы не знаете, как использовать шейдеры, то вам следует научиться.Но если вы не хотите, то вы можете получить тот же эффект, используя функцию glTexEnv .И если вы не знаете, что это такое, я предлагаю обучение шейдерам ;в конечном итоге все намного проще.
Ничья как обычно
Теперь вы просто отрисовываете все как обычно.Только не отвязывай FBO;мы по-прежнему хотим визуализировать его.
Отображение визуализированного изображения на экране
Обычно для отображения результатов вашего рендеринга вы используете вызов swapbuffer.Но так как мы рендерились в FBO, мы не можем этого сделать.Вместо этого мы должны сделать что-то другое.Мы должны скопировать наше изображение в буфер и затем поменять местами буферы.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[currIndex]);
glBlitFramebuffer(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WDITH, SCREEN_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
//Do OpenGL swap buffers as normal
Переключить изображения
Теперь нам нужно сделать еще одну вещь: переключить изображения, которые мы используем.Предыдущее изображение становится текущим и наоборот:
std::swap(currIndex, prevIndex);
И все готово.