CSharpGL(47)你好,Framebuffer!

Framebuffer对象(FBO)是一种复杂的OpenGL对象。使用自定义的framebuffer,可以实现离屏渲染,进而实现很多高级功能,例如阴影。

下载

CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

FBO基本结构

【注:本节(FBO基本结构)是翻译的(https://www.khronos.org/opengl/wiki/Framebuffer_Object),略有修改。】

类似其它的OpenGL对象,FBO也有一套glGen, glDelete, glBind的API。

FBO这套API里的target可接受3种值:GL_FRAMEBUFFER, GL_READ_FRAMEBUFFER或GL_DRAW_FRAMEBUFFER。后两种允许你可以让读操作(glReadPixels等)和写操作(所有的渲染命令)发生到不同的FBO上。GL_FRAMEBUFFER 则将读写发生到同一个FBO。

名词术语

为了叙述方便,首先定义一些术语。

Image

像素的二维数组(Pixel[ , ]),有特定的格式。

Layered Image

相同大小和格式的一组Image。

Texture

包含若干Image的OpenGL对象。这些Image的格式相同,但大小未必相同(例如不同mipmap level的Image大小是不同的)。Texture可以以多种方法被shader读取。

Renderbuffer

包含1个Image的OpenGL对象。不能被shader读取。只能被创建,然后放到FBO里。

Attach

把一个对象关联(附着)到另一个对象上。附着attach不同于绑定binding。对象被绑定到上下文context,对象被附着到另一个对象。

Attachment point

Framebuffer对象里可以让Image或Layered Image附着的位置。只有符合规定的图像格式才能被附着。

Framebuffer-attachable image

格式符合规定,可以被附着到framebuffer对象的Image。

Framebuffer-attachable layered image

格式符合规定,可以被附着到framebuffer对象的Layered Image。

附着点Attachment Point

FBO有若干Image的附着点(位置):

GL_COLOR_ATTACHMENTi

这些附着点的数量依不同的实现而不同。你可以用GL_MAX_COLOR_ATTACHMENTS 查询一个OpenGL实现支持的颜色附着点的数量。最少有8个,所以你最少可以放心使用附着点0-7。这些附着点只能让可渲染色彩的Image来附着。所有compressed image formats都不是可渲染色彩的,所以都不能附着到FBO。

GL_DEPTH_ATTACHMENT

这个附着点只能让depth格式的Image附着。附着的Image就成了此FBO的depth buffer。**注意**,即使你不打算从深度附着点上读取什么东西,也应该给深度附着点设定一个Image。

GL_STENCIL_ATTACHMENT

这个附着点只能让stencil格式的Image附着。附着的Image就成了此FBO的stencil buffer。

GL_DEPTH_STENCIL_ATTACHMENT

这是“depth+stencil”的简写。附着的Image既是depth buffer又是stencil buffer。注意:如果你使用GL_DEPTH_STENCIL_ATTACHMENT,你应当使用一个以packed depth-stencil为内部格式的Texture或Renderbuffer。

Attaching Images

现在我们已经知道了Image可以附着到FBO的哪些位置上,我们可以开始谈谈如何将Image附着到FBO上。首先,我们必须用glBindFramebuffer把FBO绑定到context。

Attaching Texture

首先来了解一下各种类型的Texture:

图中列出了8种类型的Texture。上方分别是1D Texture、2D Texture、3D Texture和2D Array Texture,下方分别是1D Array、Cubemap Texture、Rectangle Texture和Buffer Texture。大多数Texture都支持mipmap(上图中每个Texture从上到下分别为mipmap level0,1,2,3…)。

你可以将基本上任何类型的Texture里的Image附着到FBO。不过,FBO是被设计来做2D渲染的。所以有必要考虑一下不同类型的Texture是如何映射到FBO里的Image的。记住,Texture就是一组Image,Texture可能包含多个mipmap level,每个mipmap level都可能包含1到多个Image。

然后,对照上图,不同类型的Texture映射到FBO里的Image的方式如下:

1D Texture里的Image被视作高度为1的2D Image。1个Image可以被mipmap level标识。

2D Texture里的Image就照常使用了。1个Image可以被mipmap level标识。

3D Texture的1个mipmap level被视作2D Image的集合,此集合的元素数量即为此mipmap level的Z坐标。Z坐标的每个整数值都是一个单独的2D层(layer)。所以3D Texture里的的一个Image由layer和mipmap level共同标识。记得3D Texture的不同mipmap level的Z坐标数量是不同的。

Rectangle Textures只有1个2D Image,因此直接用mipmap level 0标识。

Cubemap Textures里每个mipmap都包含6个2D Image。因此,1个Image可以被面target和mipmap level标识。然而有些API函数里,1个mipmap level里的各个face是用layer索引标识的。

1D或2D Array Textures的每个mipmap level都包含多个Image,其数量等于数组元素的数量。因此,每个Image可以被layer(数组索引)和mipmap level标识。1D Array Texture里,每个Image都是高度为1。与3D Texture不同的是,layer不随mipmap层的递进而改变。(即各个mipap level的layer数量都相同)

Cubemap Array Textures类似2D Array Texture,只是Image数量乘以6。因此一个2D Image由layer(具体的说是layer-face)和mipmap level标识。(这个太难画我就不画了)

Buffer Textures不能被附着到FBO。

上面带下划线的字很重要,因为他们对应了下面的API函数(用于附着Texture)的参数:

 voidglFramebufferTexture1D(GLenum target​, GLenum attachment​, GLenum textarget​, GLuint texture​, GLint level​);
voidglFramebufferTexture2D(GLenum target​, GLenum attachment​, GLenum textarget​, GLuint texture​, GLint level​);
void glFramebufferTextureLayer(GLenum target​, GLenum attachment​, GLuint texture​, GLint level​, GLint layer​);

参数target与glBind用的相同。但是这里GL_FRAMEBUFFER的意思不是“既可读又可写”(那没有意义),他是和GL_DRAW_FRAMEBUFFER相同的意思。参数attachment是上面介绍的附着点。

参数texture是你想要附着到FBO的的Texture的名字。如果你传入“0”,就会清除指定的attachment位置上的附着物(不管附着物是什么)。

因为Texture可能包含多个Image,你必须详细说明要将哪个Image附着到附着点。除textarget之外,参数都符合上文的定义。

当附着一个非cubemap的Texture时,textarget应当是合适的类型:GL_TEXTURE_1D, GL_TEXTURE_2D_MULTISAMPLE等。当附着一个非数组的cubemap时,你必须使用glFramebufferTexture2D函数,且textarget必须是cubemap binding的6个target之一。当附着一个cubemap array时,你必须使用TextureLayer,用layer标识layer-face。

注意:如果OpenGL4.5或ARB_direct_state_access可用,那么glFramebufferTextureLayer可以接受非数组cubemap类型的Texture。他会被视作只有1个layer(即6个layer-face)的数组cubemap类型的Texture。这意味着你永远不需要使用glFramebufferTexture2D或者glFramebufferTexture1D

又注意:有一个函数glFramebufferTexture3D,专用于3D Texture。但是你不应该使用他,因为TextureLayer函数能够完成他所有的功能。

Attaching Renderbuffer

Renderbuffers也可以被附着到FBO。实际上,这也是除了创建他们之外唯一的使用方法。

 void glFramebufferRenderbuffer(GLenum target​, GLenum attachment​, GLenum renderbuffertarget​, GLuint renderbuffer​);

参数与附着Texture的类似。参数renderbuffertarget必须是GL_RENDERBUFFER,参数renderbuffer是renderbuffer的名字。

Layered Images

Layered Image,如前所述,是一组有序的大小相同的Image。多种Texture都可以被认为是layered。

1D或2D Array Texture的1个mipmap level就是一个Layered Image,数组的元素数就是层数。3D Texture的1个mipmap level同样也是一个Layered Image,层数就是此mipmap level的depth。Cubemap Texture的1个mipmap level也是一个Layered Image,他有且只有6个layer,每个face是一个,且face的顺序与下面的枚举值相同:

Layer number

Cubemap face

0

GL_TEXTURE_CUBE_MAP_POSITIVE_X

1

GL_TEXTURE_CUBE_MAP_NEGATIVE_X

2

GL_TEXTURE_CUBE_MAP_POSITIVE_Y

3

GL_TEXTURE_CUBE_MAP_NEGATIVE_Y

4

GL_TEXTURE_CUBE_MAP_POSITIVE_Z

5

GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

对于cubemap array texture,Layer 代表的是layer-face的索引。他是带layer的face,按上表排列。所以如果你想渲染到第三个layer的+z face,你就要设置gl_Layer 为(2 * 6) + 4或者16。

每个Texture,被用作Layered Image的时候,都有特定数量的layer。对于Array Texture或3D Texture,layer数就是Texture 的depth。对于cubemap,总是有且只有6个layer:每个face即为1个layer。Cubmap Array 有6*layer(layer-face数)。

使用下述指令可以将Texture的一个mipmap level附着为一个Layered Image:

 void glFramebufferTexture(GLenum target​, GLenum attachment​, GLuint texture​, GLint level​);

参数含义与上文的相同。实际上,如果你不要求附着Array Texture, Cubemap或3D Texture的单独一个Image,那么这个函数可以代替很多glFramebufferTexture1D,2D或Layer。但如果texture是这种情况,那么给定的整个mipmap level将作为一个Layered Image整体被附着,即此Layered Image里所有的layer都会被附着。(译者注:有什么用呢?下面立即分解)

Layered Image用于Layered Rendering,即向FBO的不同Layer发送不同的图元(在同一次渲染中形成不同的图像)。

Empty framebuffers

有时候会需要向一个没有附着对象的FBO渲染。显然fragment shader的输出不会写入到任何地方,但是渲染过程还是可以正常进行的。这对于shader的arbitrary reading and writing of image data是有用的。

但是,图元的渲染总是基于FBO的性质(大小,sample数量等)进行的,这些性质通常由被附着的Image定义。如果没有附着Image,这些性质就必须用其它的方式定义。

没有附着Image的FBO的性质可以用下述函数设置:

 void glFramebufferParameteri(GLenum target​, GLenum pname​, GLint param​);

target是FBO绑定的位置。如果要设置width,就设pname为GL_FRAMEBUFFER_DEFAULT_WIDTH;,如果要设置height,就设pname为GL_FRAMEBUFFER_DEFAULT_HEIGHT。

Layered FBO可以通过设置GL_FRAMEBUFFER_DEFAULT_LAYERS 为大于0的值来模仿。Multisample FBO可以通过设置GL_FRAMEBUFFER_DEFAULT_SAMPLES 为大于0的值来模仿。Fixed multisample位置可以通过设置GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 为非零值来模仿。

注意,仅在FBO没有附着对象的时候,这些参数才会起作用。如果附着了Image,那么这些参数会被无视。你应该仅在你想要使用无Image的FBO时才设置这些值。

Framebuffer Completeness

FBO里每个附着点都对能附着的Image的格式有要求。但是,如果附着了不符合要求的Image,不会立即产生GL error。在使用不合适的设置的FBO时才会引发错误。为了安全地使用FBO,必须检测各种可能出现的问题(例如Image的大小等)。

一个可以正常使用的FBO被称作是“完整的FBO”。想要测试FBO的完整性,请调用这个函数:

 GLenum glCheckFramebufferStatus(GLenum target​);

你不是非得调用这个函数不可。但是,使用不完整的FBO是错误的,所以检测一下总是好的。

如果FBO能用,会返回GL_FRAMEBUFFER_COMPLETE 。否则就是有问题。

FBO in C#

FBO最复杂的操作就是Attach不同类型的Texture。根据上文,可以总结出来,只需要glFramebufferTexture和glFramebufferTextureLayer两个函数就可以实现对所有类型Texture的Attach的支持。Wiki说OpenGL3.2开始才支持glFramebufferTexture,这我就不管了。

         /// <summary>
/// Attach a level of the <paramref name="texture"/> as a logical buffer to the currently bound framebuffer object.
/// If there are multiple images in one mipmap level of the <paramref name="texture"/>, then we will start 'layered rendering'.
/// <para>Bind() this framebuffer before invoking this method.</para>
/// </summary>
/// <param name="target">GL_FRAMEBUFFER is equivalent to GL_DRAW_FRAMEBUFFER</param>
/// <param name="texture">Specifies the texture object to attach to the framebuffer attachment point named by <paramref name="location"/>.</param>
/// <param name="location">Specifies the attachment point of the framebuffer.</param>
/// <param name="mipmapLevel">Specifies the mipmap level of <paramref name="texture"/> to attach.</param>
public void Attach(FramebufferTarget target, Texture texture, AttachmentLocation location, int mipmapLevel = )
{
if (texture == null) { throw new ArgumentNullException("texture"); } if (location == AttachmentLocation.Color)
{
if (this.nextColorAttachmentIndex >= Framebuffer.maxColorAttachmentCount)
{ throw new IndexOutOfRangeException("Not enough color attach points!"); } glFramebufferTexture((uint)target, GL.GL_COLOR_ATTACHMENT0 + this.nextColorAttachmentIndex, texture != null ? texture.Id : , mipmapLevel);
this.nextColorAttachmentIndex++;
}
else
{
glFramebufferTexture((uint)target, (uint)location, texture != null ? texture.Id : , mipmapLevel);
}
} /// <summary>
/// Attach a single layer of a <paramref name="cubemapArrayTexture"/> to the currently bound framebuffer object.
/// <para>Bind() this framebuffer before invoking this method.</para>
/// </summary>
/// <param name="target">GL_FRAMEBUFFER is equivalent to GL_DRAW_FRAMEBUFFER</param>
/// <param name="cubemapArrayTexture">texture must either be null or an existing cube map array texture.</param>
/// <param name="location">attachment point.</param>
/// <param name="layer">Specifies the layer of <paramref name="cubemapArrayTexture"/> to attach.</param>
/// <param name="face">Specifies the face of <paramref name="cubemapArrayTexture"/> to attach.</param>
/// <param name="mipmapLevel">Specifies the mipmap level of <paramref name="cubemapArrayTexture"/> to attach.</param>
public void Attach(FramebufferTarget target, Texture cubemapArrayTexture, AttachmentLocation location, int layer, CubemapFace face, int mipmapLevel = )
{
this.Attach(target, cubemapArrayTexture, location, (layer * + (int)((uint)face - GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X)), mipmapLevel);
} /// <summary>
/// Attach a single layer of a <paramref name="texture"/> to the currently bound framebuffer object.
/// <para>Bind() this framebuffer before invoking this method.</para>
/// </summary>
/// <param name="target">GL_FRAMEBUFFER is equivalent to GL_DRAW_FRAMEBUFFER</param>
/// <param name="texture">texture must either be null or an existing three-dimensional texture, one- or two-dimensional array texture, cube map array texture, or multisample array texture.</param>
/// <param name="location">attachment point.</param>
/// <param name="layer">Specifies the layer of <paramref name="texture"/> to attach.</param>
/// <param name="mipmapLevel">Specifies the mipmap level of <paramref name="texture"/> to attach.</param>
public void Attach(FramebufferTarget target, Texture texture, AttachmentLocation location, int layer, int mipmapLevel = )
{
if (location == AttachmentLocation.Color)
{
if (this.nextColorAttachmentIndex >= Framebuffer.maxColorAttachmentCount)
{ throw new IndexOutOfRangeException("Not enough color attach points!"); } glFramebufferTextureLayer((uint)target, GL.GL_COLOR_ATTACHMENT0 + this.nextColorAttachmentIndex, texture != null ? texture.Id : , mipmapLevel, layer);
this.nextColorAttachmentIndex++;
}
else
{
glFramebufferTextureLayer((uint)target, (uint)location, texture != null ? texture.Id : , mipmapLevel, layer);
}
}

Attach Texture

总结

CSharpGL(47)你好,Framebuffer!的更多相关文章

  1. 单独编译使用WebRTC的音频处理模块

    块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...

  2. Android实例] android获取web服务器端session并验证登陆

    传统网页实现用户登陆一般采用session或cookie记录用户基本信息又或者两者结合起来使用.android也可以采用session实现用户登陆验证并记录用户登陆状态时的基本信息,session是在 ...

  3. JMeter(2) 集成jmeter+ant+jenkins

    一.ant安装 $su root $vi /etc/bashrc 插入两行(i+enter插入) export ANT_HOME=/usr/local/apache-ant-1.9.3 export ...

  4. ora flashback详解

    使用oracle数据库时,难免会碰到一些问题. 例:1.如何回滚已经commit了的数据 2.如何查询已经被覆盖掉的数据[update],或者被delete了的数据 3.如何将数据恢复到某个时间点 我 ...

  5. CSharpGL(29)初步封装Texture和Framebuffer

    +BIT祝威+悄悄在此留下版了个权的信息说: CSharpGL(29)初步封装Texture和Framebuffer +BIT祝威+悄悄在此留下版了个权的信息说: Texture和Framebuffe ...

  6. CSharpGL(31)[译]OpenGL渲染管道那些事

    CSharpGL(31)[译]OpenGL渲染管道那些事 +BIT祝威+悄悄在此留下版了个权的信息说: 开始 自认为对OpenGL的掌握到了一个小瓶颈,现在回头细细地捋一遍OpenGL渲染管道应当是一 ...

  7. CSharpGL(30)用条件渲染(Conditional Rendering)来提升OpenGL的渲染效率

    CSharpGL(30)用条件渲染(Conditional Rendering)来提升OpenGL的渲染效率 当场景中有比较复杂的模型时,条件渲染能够加速对复杂模型的渲染. 条件渲染(Conditio ...

  8. CSharpGL(25)一个用raycast实现体渲染VolumeRender的例子

    CSharpGL(25)一个用raycast实现体渲染VolumeRender的例子 本文涉及的VolumeRendering相关的C#代码是从(https://github.com/toolchai ...

  9. CSharpGL(15)用GLSL渲染2种类型的文字

    CSharpGL(15)用GLSL渲染2种类型的文字 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo,更适合 ...

随机推荐

  1. 使用微软URLRewriter.dll的url实现任意后缀名重写

    <?xml version="1.0"?> <!--先引用URLRewriter.dll,放置于Bin目录--> <configuration> ...

  2. win10 UWP MessageDialog 和 ContentDialog

    我之前开发一个软件 winMarkdown,这个软件在关闭需要提示用户还没有保存东西,需要保存,如果用户选择退出,那么把数据存放. 在Metro程序中,没有传统的窗口,当我们要用需要交互的消息提示时, ...

  3. win10 UWP 蜘蛛网效果

    我看见了知乎首页登录背景和普通的地球人写的博客,发现了个好看的效果. 那么我来告诉大家如何做这个效果. 第一步是在 Canvas 画点,第二步是让点移动,第三步是画线 在 Canvas 画一个点 我们 ...

  4. CMake必知必会

    CMake 文档 https://cmake.org/cmake/help/v3.7/index.html 需要阅读的文档 item note link cmake-buildsystem(7) cm ...

  5. PHP中foreach()用法汇总

    这篇文章主要给大家详细介绍了PHP中foreach()用法以及相关的示例,十分的细致,有需要的小伙伴可以参考下. PHP 4 引入了 foreach 结构,和 Perl 以及其他语言很像.这只是一种遍 ...

  6. IDEA启动后页面没有tomcat server选项,显示灰色问号和红叉不能使用

    说明:自己好几次硬盘莫名其妙读不出来导致电脑重启后idea没有了tomcat选项,原来的tomcat上显示灰色的问号和红色小叉子,网上搜了好久加上自己摸索,终于解决了.现在记一下也分享一下,省的下回又 ...

  7. 自学Java HashMap源码

    自学Java HashMap源码 参考:http://zhangshixi.iteye.com/blog/672697 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提 ...

  8. Asp.net MVC4高级编程学习笔记-模型学习第四课基架与模型绑定20171027

    MVC模型 一.构建基架. MVC中的基架可以为应用程序提供CURD各种功能生成所需要的样板代码.在添加控制器的时候可以选择相应的模板以及实体对象来生成相应的模板代码. 首先定义一个模型类如下所示: ...

  9. maven 集成tomcat6,tomcat7

    1. maven 集成 tomcat6的配置 maven自带的是tomcat6插件,所以不配置的话也可以,默认tomcat6,8080端口,需要更改端口或者编码方式等,也可以自己再配置一次: < ...

  10. myeclipse10破解失败,正解

    此文图借用他人的,很是经典 第一步:输入任意用户名 第二步:点击Systemid... 按钮,自动生成本机器的systemid.ps:确定本机的systemid,如果和破解器生成不一致,需要copy本 ...