Roo*_*kie 4 c++ opengl blending
我试图实现淡入淡出效果,但我不知道该怎么做.我尝试了几件事但由于opengl的工作方式而失败了
我会解释它是如何工作的:
如果我绘制1个白色像素和后255帧中的白色像素移动它围绕每个帧用于一个像素的一些方向,每个帧中的屏幕像素将获得一个R/G/B值以下(范围0-255),从而将完全黑了.因此,如果我移动白色像素,我会看到渐变轨迹从白色到黑色均匀地与前一像素颜色相比1色值差异.
编辑:我更喜欢知道非着色的方式,但如果它不可能,那么我也可以接受着色器方式.
编辑2:由于这里有一些混乱,我想告诉我,我可以通过在整个场景上绘制一个黑色透明四边形来做到这种效果.但是,这不起作用,因为我希望它工作; 有在黑暗中的像素可以得到一个限制,所以它总是会留下一些像素的"可见"(零以上颜色值),因为:1*0.9 = 0.9 - >四舍五入到1再等我能"修复"通过缩短路径,但我希望能够尽可能地调整路径长度,而不是双线性(如果那是正确的词)插值我想要线性(所以它总是从每个r减少-1, g,b值为0-255比例,而不是使用百分比值).
编辑3:还有一些混乱,所以我们要清楚:我想通过从glClear()禁用GL_COLOR_BUFFER_BIT来改善效果,我不想看到我的屏幕上的像素FOREVER,所以我想让它们更黑暗的时间,通过在我的场景上绘制一个四边形,将每个像素的颜色值减1(在0-255比例范围内).
编辑4:我会让它变得简单,我想要OpenGL方法,效果应尽量少用电,内存或带宽.这个效果应该可以在不清除屏幕像素的情况下工作,所以如果我在我的场景上绘制一个透明的四边形,之前绘制的像素会变暗等.但如上所述几次,它不能很好地工作.大的NO是:1)从屏幕读取像素,在for循环中逐个修改它们然后上传回来.2)使用不同的暗度渲染我的物体X次以模拟轨迹效果.3)乘以颜色值不是一个选项,因为它不会使像素变成黑色,它们将永远保持在屏幕上一定的亮度(见上面某处的解释).
如果我绘制1个白色像素和后255帧中的白色像素移动它围绕每个帧用于一个像素的一些方向,每个帧中的屏幕像素将获得一个R/G/B值以下(范围0-255),从而将完全黑了.因此,如果我移动白色像素,我会看到渐变轨迹从白色到黑色均匀地与前一像素颜色相比1色值差异.
在我解释如何做到这一点之前,我想说你想要的视觉效果是一种可怕的视觉效果,你不应该使用它.从每种RGB颜色中减去一个值将产生不同的颜色,而不是相同颜色的较暗版本.RGB颜色(255,128,0),如果从中减去128次,将变为(128,0,0).第一种颜色是棕色,第二种颜色是深红色.这些都不一样.
现在,既然你没有真正解释过这个问题,我必须做出一些猜测.我假设您正在渲染的内容中没有"对象".没有国家.你只是在任意位置画东西,你不记得你在哪里画的东西,也不想记住画在哪里.
要做你想做的事,你需要两个屏幕外的缓冲区.我建议使用FBO和屏幕大小的纹理.基本算法很简单.使用从您编写的颜色"减去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);
}
Run Code Online (Sandbox Code Playgroud)
下一步是将前一帧的图像绘制到当前图像.
要做到这一点,我们需要有前一个和当前FBO的概念.这是通过两个变量完成的:currIndex和prevIndex.这些值是纹理,渲染缓冲区和FBO的GLuint数组的索引.它们应该初始化(在初始化期间,而不是每个帧),如下所示:
currIndex = 0;
prevIndex = 1;
Run Code Online (Sandbox Code Playgroud)
在你的绘图程序中,第一步是绘制前一帧,减去一帧(同样,我强烈建议在这里使用真正的混合).
这不是完整的代码; 会有伪代码,我希望你填写.
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);
Run Code Online (Sandbox Code Playgroud)
该RenderFullscreenQuadWithTexture函数完全按照其说法执行:使用当前绑定的纹理渲染四倍大小的屏幕.程序对象BlenderProgramObject是GLSL着色器,它执行我们的混合操作.它从纹理中取出并进行混合.再一次,我假设您知道如何设置着色器等等.
片段着色器将具有如下所示的main函数:
shaderOutput = texture(prevImage, texCoord) - (1.0/255.0);
Run Code Online (Sandbox Code Playgroud)
我再次强烈建议:
shaderOutput = texture(prevImage, texCoord) * (0.05);
Run Code Online (Sandbox Code Playgroud)
如果您不知道如何使用着色器,那么您应该学习.但是如果你不想,那么你可以使用glTexEnv函数获得相同的效果.如果你不知道那些是什么,我建议学习着色器 ; 从长远来看,这样容易得多.
现在,你只需渲染你想要的一切.只是不要解开FBO; 我们仍然希望渲染它.
通常,您将使用swapbuffer调用来显示渲染的结果.但自从我们向FBO提交后,我们就无法做到这一点.相反,我们必须做一些不同的事情.我们必须将我们的图像blit到backbuffer然后交换缓冲区.
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
Run Code Online (Sandbox Code Playgroud)
现在我们还需要做一件事:切换我们正在使用的图像.前一个图像变为当前图像,反之亦然:
std::swap(currIndex, prevIndex);
Run Code Online (Sandbox Code Playgroud)
而且你已经完成了.
您可能希望使用glBlendFunc(GL_ONE,GL_SRC_ALPHA)渲染alpha从1.0到0.0的黑色矩形.
编辑以回复您的评论(回复不适合评论):
通过简单的淡入淡出操作,您无法根据年龄淡化单个像素.通常,渲染目标不会"记住"在先前帧中绘制的内容.我可以想办法通过交替渲染到一对FBO中的一个并使用它们的alpha通道来实现这一点,但是你需要一个着色器.那么你要做的是首先渲染FBO包含其先前位置的像素,将alpha值减小1,当alpha == 0时将其丢弃,否则在alpha减小时将它们变暗,然后将像素渲染到其当前位置alpha == 255.
如果你只有移动像素:
render FBO 2 to FBO 1, darkening each pixel in it by a scale (skip during first pass)
render moving pixels to FBO 1
render FBO 1 to FBO 2 (FBO 2 is the "age" buffer)
render FBO 2 to screen
Run Code Online (Sandbox Code Playgroud)
如果你想修改一些场景(即有场景和移动像素):
set glBlendFunc (GL_ONE, GL_ZERO)
render FBO 2 to FBO 1, reducing each alpha > 0.0 in it by a scale (skip during first pass)
render moving pixels to FBO 1
render FBO 1 to FBO 2 (FBO 2 is the "age" buffer)
render the scene to screen
set glBlendFunc (GL_ONE, GL_SRC_ALPHA)
render FBO 2 to screen
Run Code Online (Sandbox Code Playgroud)
实际上,比例应该是(浮点)/ 255.0/255.0,以使组件同样消失(并且在其他组件之前没有一个以较低值开始的组件变为零).
如果您只有几个移动像素,则可以在之前的所有位置重新渲染像素,最多返回255"滴答".
由于您无论如何都需要重新渲染每个像素,只需使用正确的颜色渐变渲染每个像素:颜色越深,像素越旧.如果你有很多像素,双FBO方法可能会有效.
我正在编写刻度线,而不是帧,因为取决于渲染器和硬件,帧可能需要不同的时间,但您可能希望像素轨迹在恒定时间内消失.这意味着你需要在这么多毫秒之后调暗每个像素,保持它们之间的帧颜色.
| 归档时间: |
|
| 查看次数: |
7155 次 |
| 最近记录: |