写作之处的初衷是作为learnopengl的解释文档,或者代码库来用,不过我慢慢发现这样做挺没意思的,因为learnopengl已经足够详细了,并且随着逐代的更新,我们这个代码库可能还会过时,因此我想还是把一些重要的概念加以巩固,突出,来形成我们自己的特色。
(实际上你看过1-3篇就已经具备了常见OpenGL框架的阅读能力,要实现一些复杂的效果就不得不考虑一些高级的逻辑,而非图形本身)
OK,回到本篇的正题——渲染到纹理
我主要参考这篇文章:
教程14:渲染到纹理 (opengl-tutorial.org)
他已经比较详细,但是讲解中可能还不够清晰(我估计是翻译版本,不太符合中国人的阅读习惯),所以我想把我认为重要的概念和步骤进行介绍和标记。
一、渲染到纹理基本思路
在有别于后台缓冲之外,创建一个可以存储渲染结果的RTV与DSV纹理,用来存储我们希望渲染到纹理的资源。之后可以切换到后台缓冲,创建四边形面片把我们存储的渲染纹理作为纹理资源重新贴图到四边形面片上(可以很灵活的控制贴图位置,甚至于直接输出到后台作为纹理图片)。
二、渲染到纹理基本步骤
1.开辟存储RTV与DSV的帧缓冲内存
GLuint FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
2.创建RTV,设置其格式为GL_TEXTURE_2D,并使用一张空的RGB资源图像作为其载体
GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
//RTV格式设置
glBindTexture(GL_TEXTURE_2D, renderedTexture);
//设置最后一个参数索引为0的空白图片作为存储RTV的地方
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3.创建DSV
// The depth buffer
GLuint depthrenderbuffer;
glGenRenderbuffers(1, &depthrenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 800, 600);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer);
4.将RTV设置为渲染流水线能访问的资源,占据TEXTURE0的位置,并设置渲染到指定帧缓冲(而非后台)
// 将"renderedTexture"设置为我们布局0号位置来源
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
// Set the list of draw buffers.
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
//使用FramebufferName代表我们最开始建立的存储RTV,DSV的容器(使用0默认表示后台)
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0, 0, 800, 600);
注意:glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
使用GL_COLOR_ATTACHMENT0将renderedTexture的数据存到0#位置上,那么这个0#位置是什么:
layout(location = 0) out vec3 color;
就是我们在渲染纹理的fs中指定布局为0的变量。换句话说,我们将color作为fs的输出,原本是直接放入后台缓冲的,现在定义其输出到布局为0的后台某个存储空间,而这个布局为0的存储空间就是通过GL_COLOR_ATTACHMENT0关联的renderedTexture。也就是说我们给color赋值,实际就是更改renderedTexture。
通过:glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);就实现了渲染到我们规定的纹理空间之中。
对应的fs:
#version 330 core
layout(location = 0) out vec3 color;
void main()
{
//可以有计算逻辑,取决于需求,不过最终就是给color赋值,存储到我们设置的
//0#布局颜色空间
color=vec3(FragColor.xyz);
}
5.正常配置shader,将我们希望的场景渲染到renderedTexture中,也就是到了后台的GL_COLOR_ATTACHMENT0中关联空间(对标TEXTURE0)。换句话说如果这之后你改变TEXTURE0会把之前的结果覆盖掉,这点要注意,所以如果加载纹理要注意。
6.使用渲染出的纹理
1).切换渲染目标
//将渲染到纹理的资源渲染到屏幕,使用0默认后台
glBindFramebuffer(GL_FRAMEBUFFER, 0);
2).新一轮渲染(可能涉及改变相机.....)然后,创建四边形面片,使用新的shader,此时要注意使用纹理采样:
fs:
#version 330 core
out vec4 FragColor;
in vec2 UV;
//不需要绑定uniform,因为默认开启0
uniform sampler2D renderedTexture;
void main(){
FragColor = texture( renderedTexture, UV);
}
还是展示一下vs,方便我们产生逻辑:
#version 330 core
attribute vec3 aPos;
attribute vec2 aUV;
out vec2 UV;
void main()
{
UV = (aPos.xy+vec2(1,1))/2.0;
gl_Position=vec4(aPos,1.0f);
}
注意:
1.渲染到纹理的思路是很简单的:增加一个渲染目标,切换流水线渲染目标到我们指定的RTV,将场景渲染到我们指定的纹理中,然后切换回默认(后台渲染目标),把之前渲染得到的纹理作为后续采样的资源对新场景(可以是四边形(相当与一个画布))进行应用。
2.我们存到RTV渲染目标的值可以是RGB值,也可以是深度值,那么就得到一张深度图,可以用来进行Shadow Mapping或者其他(当然教程中也有关于深度纹理的渲染介绍,主要就是参数的配置,这里就不具体介绍了)。文章来源:https://www.toymoban.com/news/detail-405383.html
3.要注意渲染循环的使用,正常逻辑:循环开始:切换渲染到纹理——切换纹理到后台——Swap——重新循环开始。文章来源地址https://www.toymoban.com/news/detail-405383.html
到了这里,关于4.OpenGL学习笔记——渲染到纹理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!