Ogre HardwareBuffer [翻译]

来源:互联网 发布:网络层ddos 编辑:程序博客网 时间:2024/04/20 07:32

5 Hardware Buffers

顶点缓存,索引缓存以及像素缓存的大多数特性都是从HardwareBuffer继承的,一个硬件缓存的一般前提是它是一个内存块,在这里你能做想要的任何事情,缓存本身没有格式与它相关,二是与使用它的方法相关。也就是说,硬件缓存就像使用”malloc”分配的内存块,只不过区别是它位于GPU或者AGP中。

相关知识:

三种内存:AGP内存,显卡本地内存,系统内存。其中我们都知道显卡本地内存就是显存,系统内存就是咱那内存条,那这AGP内存是个啥玩意啊?其实是因为在以前显卡内存都很小,那时还是在显存是16M,32M为主流的时候,如果你运行一个需要很多纹理的3D程序,那么显存一会就不够用了,那该咋办呢?只好问系统内存借点用用了!这就是AGP内存的由来,在我们电脑BIOS中有个设置AGP Aperture的选项,这里就是设置显卡可以使用系统内存的最大允许值,通常是设置为64M。注意,这里只是说最大允许值,并不是一开机他就把这64M给拿走了,你的256内存就变成192了!而是你的内存依然还是256M,只是限制显卡最多可以使用64M的系统内存。

再说说这三个内存的速度的不同吧!

系统内存当然是人家CPU读和写操作最快啦!而显卡读写系统内存就会相对于使用自己的显存慢上很多很多!AGP内存是显卡读和写的速度一般,当然肯定没有显卡使用显存速度快啦!CPU就相对复杂点了,CPU读取AGP内存速度很慢,但是写的速度却并不会慢,而是速度一般,比使用系统内存慢那么一点,也就是说适合CPU去写但不适合读。有人就要问了,同样是系统内存只不过名字不一样,咋速度的就有差别了呢?这个嘛,我也不太清楚,老外没有说的太详细,大家只要记住就行了!
最后说的就是显存了,这个很简单,当然是显卡读和写的速度最快,而CPU读和写的速度肯定要慢好多的!
说了三个内存的区别,现在说说他们都有什么用处吧!这里涉及一个D3DUSAGE枚举量,D3DUSAGE_DYNAMIC,这个变量是在你创建资源时使用到的,它指示D3D将资源指定为动态的,而动态的意思就是需要经常修改,修改通常是CPU进行修改,所以动态资源应该放在AGP内存中。这样对速度的影响可以减至最小。

 

5.1 hardware buffer manager

HardwareBufferManager类是几何系统里面的所有对象的工厂接口,你一般都是通过这个类来创建和销毁用来定义几何体的对象。它是一个单件,所以可以使用HardwareBufferManager::getSingleton()来访问它--必须注意的是它只在RenderSystem被初始化之后才能保证存在(在调用Root::Initialise之后),这是因为这些对象始终都是通过具体的API来创建,尽管可以通过一个通用的接口来访问。

例如:

VertexDeclaration* decl = HardwareBufferManager::getSingleton().createVertexDeclaration();

HardwareVertexBufferSharedPtr vbuf =

          HardwareBufferManager::getSingleton().createVertexBuffer(

                    3*sizeof(Real), // size of one whole vertex

                    numVertices, // number of vertices

                    HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage

                    false); // no shadow buffer

 

5.2 Buffer Usage

由于硬件缓存中得内存在场景绘制过程中很可能面临激烈争夺,在它使用的过程中对它的访问类型就变得极为重要;是否需要经常更新缓存,使用要能够从缓存中回读信息,这些都是图形卡如何操作缓存的重要因素。用于创建缓存使用的方法和具体参数取决于是顶点缓存还是索引缓存,它们有一个通用的参数,”usage”。

硬件缓存最常见的类型是不经常更新、从不被读取。createVertexBuffer 或 createIndexBuffer的usage参数可以是下面中的一个:

HBU_STATIC:这意味着你不需要经常更新这个缓存,但是你可以会偶尔想要会回读它

HBU_STATIC_WRITE_ONLY:意味着不需要经常更新缓存,并且不需要回读它。但是你可以从设定的影子缓存来读取它。这是一个最优的用法设置

HBU_DYNAMIC:意味着需要经常更新缓存,同时你希望要从它那里读取数据。这是最少见的缓存设置

HBU_DYNAMIC_WRITE_ONLY:意味着你想要经常更新缓存,但是你从不去读取它。但是,你可以从你设定的它的影子缓存里读取它的数据。如果你使用这个选项,同时每帧替换掉整个缓存的内容,这个时候应该使用HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE 替代,因为在有些平台它有更好的性能

HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE :这意味你想要在一个非常规律的基础上,大多数情况下是每帧,去替换掉整个缓存的内容。通过选择这个选项,你可以让系统从在任何时候必须关心丢失存在(existing)内容中解放出来,你会在下一帧去替换它。在有些平台上这个可以产生巨大的性能差异,因而你可以在任何想要规律更新一个缓存的时候使用这个选项。注意如果按这种方式创建缓存,在锁定它的内存用于写入的时候必须使用HBL_DISCARD标签

      小心的选择缓存的用法对于取得最优的性能非常的重要。如果你面临一个需要经常更新顶点缓存的情形,考虑下你是否真的需要更新它的所有部分或者只是其中的一部分。如果是后面这种情况,考虑使用多于一个缓存,只用于需要更新的数据,使用HBU_DYNAMIC缓存。

      应该总是尝试使用_WRITE_ONLY的形式,这意味着你不能直接从缓存读取数据,这是个良好的习惯,因为从硬件缓存读取非常的慢。如果需要从它读取数据,使用下节说的影子缓存。

 

5.3 Shadow Buffers

前面提到从硬件缓存读取数据非常慢。但是如果你必须要读取顶点缓存中的数据,你必须设置 createVertexBuffer 或 createIndexBuffer的参数’shadowBuffer’为真。这促使硬件缓存在系统内存里面生成一个副本,你可以从这里读取数据,而不会比从正常内存中读取有多大劣势。这个操作是当你向硬件缓存中写入诗句的时候,它会首先更新系统内存的拷贝,然后再更新硬件缓存,作为一个单独的拷贝操作,因而这个技术在写入数据的时候会有额外的开销。除非真的需要从它读取数据,否则不要使用它。

 

5.4 锁定缓存

为了读取或者更新一个硬件缓存,你必须先“锁定”它。这执行两个功能—它告诉图形卡你想要访问这个缓存(这会对它的绘制队列产生影响),然后它返回一个能让你操作的指针。注意如果你已经请求读取这个缓存(并且记住,如果这个缓存创建的时候没有设定影子缓存,你不应该请求访问),硬件缓存的内容已经被拷贝到系统内存中了,以便于让你访问。基于同样的原因,当你操作完成之后,你必须为它解锁;如果你锁定缓存用于写入,这将触发将修改信息上传到图形硬件的过程

 

锁定参数

锁定缓存的时候,使用下面中的方法:

// Lock the entire buffer

pBuffer->lock(lockType);

// Lock only part of the buffer

pBuffer->lock(start, length, lockType);

      第一个调用锁定整个缓存,第一个只锁定从”start”开始的”length”字节的区块。这个可以比锁定整个内存块更快因为它只要迁移更少的数据,但是如果你后面又要更新缓存的其余数据的话就不一定了,因为像这样在小块间操作意味着你不能使用HBL_DISCARD。

      锁定类型参数可以对程序的性能产生巨大的作用,特别是你不使用影子缓存的情况下。

      HBL_NORMAL:这种类型的锁定允许读取和写入缓存,这是最不好的一个操作因为基本上你在告诉显卡你能够做任何操作。如果你使用影子缓存,它要求缓存数据从显卡来回传输,如果使用影子缓存的话这个影响是最小的

      HBL_READ_ONLY:意味着你只想要读取缓存的内容。最好是在缓存设定了影子缓存的时候使用,因为这个时候数据不需要从显卡中下载

      HBL_DISCARD:这意味着你很乐意丢弃显卡上这个缓存里的所有内容。言下之意是你不会从这个缓存读取数据,也意味着如果这个缓存正在被绘制的话,显卡可以避免抛锚,因为它将给你一个完全不同的缓存数据。如果锁定一个没有使用影子缓存的缓存话,尽可能的使用这个参数。如果使用了影子参数的话,它的影响就比较小,尽管有影子缓存,它更倾向于一次性锁定整个缓存,因为它允许影子缓存使用HBL_DISCARD,当它上传更新的内容到真正缓存的时候。

      HBL_NO_OVERWRITE:这个很有用,如果你只是要锁定缓存的一部分的话,因为那样就不能使用HBL_DISCARD。它告诉显卡你承诺不更改这帧中已经在绘制中的缓存的那部分。这个也是只在没有设定影子缓存的缓存上才有用。

      一旦你锁定了一个缓存,你可以使用返回的指针进行操作(只是不要尝试在使用了HBL_DISCARD的情况下去读取数据,或者在使用HBL_READ_ONLY的情况下去写入数据)。内容的更改基于缓存的类型。

 

5.5 实用缓存小建议

创建时的usage模式和在读取/更新时的锁定选项的相互作用对于性能是很重要的,这里是一些小提示:

1.            力争使用HBU_STATIC_WRITE_ONLY创建“完美”的缓存,不使用影子缓存,同时只使用HBL_DISCARD来锁定对它进行填充。不再去动它

2.            如果你需要经常更新缓存,你必须使用折中的方式。在创建的时候使用HBU_DAYNAMIC_WRITE_ONLY(仍然不使用影子缓存),同时使用HBL_DISCARD来锁定整个缓存,或者使用HBL_NO_OVERWRITE来锁定部分缓存

3.            如果你真的需要从缓存读取数据,在创建它的时候使用影子缓存。确保在为读取锁定缓存的时候使用HBL_READ_ONLY,因为它将避免与未锁定的缓存相关的上传操作。你也可以将这一点和前面的两点结合起来使用

4.            如果你发现对于不同的顶点元素顶点缓存的使用模式是不同的话,就讲顶点缓存分成多个。

5.6 Hardware Vertex Buffers

 

5.6.1 VertexData

VertexData类包含了所有顶点相关用于绘制几何体的信息。新的绘制操作需要指向VertexData对象的指针,同时它也用于在MeshSubMesh来存储顶点位置,法线,贴图坐标等数据。VertexData可以单独使用(用于绘制非索引几何体,这里面顶点的数据流定义了三角形),或者是和索引数据相结合,这些索引数据使用顶点数据中的索引来定义三角形。

VertexData类有许多重要的成员

vertexStart:绑定的缓存中用于开始读取顶点数据的位置,这可以让你为多个绘制对象使用一个缓存

vertexCount:在这个绘制组中的顶点的个数

vertexBufferBinding:一个指向VertexBufferBinding对象的指针,用来定义哪些缓存绑定到哪些数据源。同时,它通过VertexData为你创建

 

5.6.2 Vertex Declarations

顶点声明定义了用于绘制你想显示在屏幕上的几何体的顶点输入。基本上这意味着对于每个顶点,你想要给予一定量的数据集到图形管道中,用来影响在三角形绘制的时候如何显示(绘制成个什么样子)。顶点声明让你从任意数量的缓存拉取数据项(这里我们称为顶点元素,由VertexElement类表示),这些缓存对于这个特定的元素可以是共享的,也可以是专用的。你必须自己确定缓存的内容在按VertexDeclaration所指明的方式进行解释的时候能有意义。

注:

由于可以将多种类型的数据放在同一个缓存中,要么是交替存放(绝大多数都是这种情况),要么就是按顺序排放,这就需要确定一些信息来决定如何访问这些数据,假如一个缓存中同时存放了顶点位置,纹理坐标以及法线这三种数据,并且它们交替存放,这个时候就要对顶点声明添加三个元素,分别对应三种类型的数据。

要添加一个元素到VertexDeclaration,调用addElement方法,这个方法的参数如下:

         Source:这告诉这个声明所需添加的元素从哪个缓存拉取,注意这只是一个索引,从0到绑定为顶点数据源的缓存个数减1。参见5.6.3节中的缓存绑定以了解一个真正的缓存如何与一个source index绑定起来。用这种方式(而不是使用缓存指针)能让你非常方便地重新绑定一个顶点的source,而不用更改顶点声明自身的格式

         Offset:元素在缓存中的字节偏移量(例如缓存中存放了顶点和纹理,则顶点元素的offset0,因为顶点在起始处,纹理元素的offset则为sizeof(3floats),因为存放顶点数据需要3float字节)

         Type:定义顶点输入的数据类型,包含它的长度,有这么几种类型

        enum VertexElementType

      {

        VET_FLOAT1 = 0,

        VET_FLOAT2 = 1,

        VET_FLOAT3 = 2,

        VET_FLOAT4 = 3,

        /// alias to more specific colour type - use the current rendersystem's colour packing

                   VET_COLOUR = 4,

                   VET_SHORT1 = 5,

                   VET_SHORT2 = 6,

                   VET_SHORT3 = 7,

                   VET_SHORT4 = 8,

        VET_UBYTE4 = 9,

        /// D3D style compact colour

        VET_COLOUR_ARGB = 10,

        /// GL style compact colour

        VET_COLOUR_ABGR = 11

     };

         Semantic:定义了元素的类型

enum VertexElementSemantic {

                   /// Position, 3 reals per vertex

                   VES_POSITION = 1,

                   /// Blending weights

                   VES_BLEND_WEIGHTS = 2,

        /// Blending indices

        VES_BLEND_INDICES = 3,

                   /// Normal, 3 reals per vertex

                   VES_NORMAL = 4,

                   /// Diffuse colours

                   VES_DIFFUSE = 5,

                   /// Specular colours

                   VES_SPECULAR = 6,

                   /// Texture coordinates

                   VES_TEXTURE_COORDINATES = 7,

        /// Binormal (Y axis if normal is Z)

        VES_BINORMAL = 8,

        /// Tangent (X axis if normal is Z)

        VES_TANGENT = 9

         };

         Index:这个参数只有在一个顶点声明中支持多余一个同类型的元素时才使用。例如,如果你支持多于一个的纹理坐标集,可以设置第一个坐标集的index0,第二个为1

         可以为重复调用addElement来添加顶点输入结构中的多个元素。

 

重要考量

         理论上你对于顶点的格式有完全的控制权,但是实际上有一些限制。老版的DIRECTX硬件对于每个缓存中的元素限定了顺序,特别是Direct 9以前的硬件有下面的限定:

顶点元素必须按下面的顺序添加,同时共享缓存中的元素的顺序必须如下:

1.       positions

2.       blending weights

3.       normals

4.       diffuse colours

5.       specular colours

6.       texture coordinates

0开始,按顺序来,中间不能有间隙

5.6.4 Vertex Buffer Bindings

顶点缓存绑定指的是将一个顶点缓存与5.6.2中使用的source index联系起来。

 

创建顶点缓存

 

          HardwareVertexBufferSharedPtr vbuf =

          HardwareBufferManager::getSingleton().createVertexBuffer(

                    3*sizeof(Real), // size of one whole vertex

                    numVertices, // number of vertices

                    HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage

                    false); // no shadow buffer

注意到我们使用5.1中硬件缓存管理器来创建我们的顶点缓存,同时这个方法返回了一个叫做HardwareVertexBuffferSharedPtr的指针,而不是一个原始指针。这是因为缓存会被引用计数,你能够使用一个顶点缓存作为多个几何体的存储源,因而一个标准的指针并不很好,因为你不知道什么时候不同的用户使用完了它。这个HardwareVertexBuffferSharedPtr类通过存放它自己被使用次数来管理它自身的销毁,当最后一个HardwareVertexBuffferSharedPtr最销毁后,这个缓存会被自动被其自身销毁。

参数:

vertexSize:指的是一个整体顶点的字节数(整体顶点是指一个顶点元素的整体,可能包括顶点,纹理,法线等)。

numVertices:顶点的个数

usage:告诉系统你想要如何使用这个缓存

useShadowBuffer:告诉系统时候使用影子缓存

 

绑定顶点缓存

         将一个创建的缓存绑定到一个源索引

vertexBufferBinding->setBinding(0, vbuf);

         这使得缓存vbuf与源索引0绑定起来,然后任何从源索引0拉取的顶点数据实际上都是从这个缓存获取的

 

5.6.4 更新顶点缓存

更新顶点缓存的复杂性完全取决于它的内容如何被存放。你可以锁定一个缓存,但是你怎样把数据写入禁区非常取决于它存放了什么。

现在家丁有一个缓存只包含了顶点位置,所以它每个顶点只有3个浮点的数据,这种情况,所需要的数据写入操作是:

          Real* pReal = static_cast<Real*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));

然后再以三个Real块的方式写入位置,如果在这个缓存中还有其他浮点型的数据,就稍微有点复杂了,你需要写入替代元素。但是如果你有不同类型的元素,或者你需要从元素自身派生出如何写入顶点数据。那么有一些基于VertexElement类有用的方法可以帮到你。

首先,锁定这个缓存,但是将结果转换为unsigned char *类型而不是一个特定的类型,接着,对于每个存放在这个缓存中的元素(你可以同过调用VertexDeclaration::findElementsBySource查找得到),调用VertexElement::baseVertexPointerToElement,这可以把指针从缓存中顶点的起始位置偏移到指定元素的起始位置,然后允许你使用正确类型的指针去进行操作,下面是例子:

// Get base pointer

unsigned char* pVert = static_cast<unsigned char*>(vbuf->lock(HardwareBuffer::HBL_READ_ONLY));//这里我表示怀疑…

Real* pReal;

for (size_t v = 0; v < vertexCount; ++v)

{

          // Get elements

          VertexDeclaration::VertexElementList elems = decl->findElementsBySource(bufferIdx);

          VertexDeclaration::VertexElementList::iterator i, iend;

          for (i = elems.begin(); i != elems.end(); ++i)

          {

                    VertexElement& elem = *i;

                    if (elem.getSemantic() == VES_POSITION)

                    {

                               elem.baseVertexPointerToElement(pVert, &pReal);

                               // write position using pReal

 

                    }

                   

                    ...

                   

                   

          }

          pVert += vbuf->getVertexSize();

}

vbuf->unlock();

 

 

5.7 Hardware Index Buffers

索引缓存用于绘制几何体,通过间接的引用缓存中的位置来构建三角形,而不是通过序列化的读取顶点来构建三角形。索引缓存比顶点缓存更简单,因为他们只是一系列的索引,但是它们可以存放在硬件上,并且和顶点缓存一样能再多个几何体间共享,因而创建和锁定的规则都是一样的

 

5.7.1 IndexData

这个类包含了用一系列索引来绘制几何体的信息,它的成员如下:

indexStart:用于这个几何体片段的起始索引,在对于再多个几何体片段间使用单个索引缓存是很有用的

indexCount:这个绘制对象的索引的个数

indexBuffer:用于存放索引的索引缓存

 

创建一个索引缓存

HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().

          createIndexBuffer(

                    HardwareIndexBuffer::IT_16BIT, // type of index

                    numIndexes, // number of indexes

                    HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage

                    false); // no shadow buffer   

返回的类型是一个共享指针可以进行引用计数

         indexType:有两种类型的索引,16位和32位。它们两个都表现得一样,只是后者可以寻址更大的顶点缓存。如果你的缓存包含多于65536个顶点,你需要使用32位的索引。注意到你只能在需要的时候使用32位索引,因为它们会比16位的产生更大的开销,并且在一些老的系统上不支持

         numIndexes:缓存中索引的个数,和顶点缓存一起使用的话,你必须考虑到你是否能够使用一个被多个几何片段共享的索引缓存,因为在不经常切换索引缓存的时候能有性能提升

         usage:用法

         useShadowBuffer:是否使用影子缓存

 

5.7.2更新索引缓存

更新索引缓存只有在锁定了缓存用于写入的时候才能进行。锁定返回了一个void型指针,然后必须强制转换成合适的类型,对于索引缓存这个必须要么是一个unsigned short(16bit),要么是一个unsigned long(32bit)。比如:

unsigned short* pIdx = static_cast<unsigned short*>(ibuf->lock(HardwareBuffer::HBL_DISCARD));

 

 

5.8 Hardware Pixel Buffers

硬件像素缓存是一种特殊的缓存用于在图形卡中存储图形数据,一般用作纹理。像素缓存能够代表一个1维的,2维或者3维的同学,一个纹理可以包含多个这样的缓存。与顶点缓存和索引缓存不同,像素缓存并不直接创建。当创建一个纹理时,用于存放这个数据的像素缓存就被自动创建。

 

5.8.1纹理

一个纹理是一张图片,可以应用到三维模型的表面。在OGRE中,纹理通过Texture资源类表示。

创建一个纹理

         纹理通过TextureManger来创建,在大多数情况中它们直接被Ogre的资源系统从图像文件创建。如果你读到这里,你一定很想要自己手动创建一个纹理,那样你就可以自己用图像数据来填充它。这个通过TextureManger::createAManual来完成

ptex = TextureManager::getSingleton().createManual(

    "MyManualTexture", // Name of texture

    "General", // Name of resource group in which the texture should be created

    TEX_TYPE_2D, // Texture type

    256, // Width

    256, // Height

    1, // Depth (Must be 1 for two dimensional textures)

    0, // Number of mipmaps

    PF_A8R8G8B8, // Pixel format

    TU_DYNAMIC_WRITE_ONLY // usage

);

这个例子创建了一个名为MyManualTexture的纹理,在资源组General中。它是一个方形的2维纹理,宽256,高256,它没有mipmaps,内部格式为PF_A8R8G8B8,使用方式为TU_DYNAMIC_WRITE_ONLY

 

纹理用法

         除了5.2中描述的硬件缓存用法,纹理缓存还有一些特定的用法标签:

         TU_AUTOMIPMAP:用于这个纹理的mipmaps将图形硬件会被自动产生,没有定义具体的算法,但是你可以假定它是一个2x2的盒型滤波器

         TU_RENDERTARGET:这个纹理将被作为一个绘制目标,例如作为一个到纹理的绘制目标,设置这个标签将会忽略除了TU_AUTOMIPMAP外的所有其他纹理用法

         TU_DEFAULT:这实际上是一个用法标签的组合,等价于TU_AUTOMIPMAP|TU_STATIC_WRITE_ONLY,资源系统使用这些标签来从图像加载纹理

 

获取一个PixelBuffer

         一个纹理可以由多个像素缓存组成。从一个纹理对象获取一个像素缓存使用Texture::getBuffer(face,mipmap)face对于non-cubemap贴图必须为0,对于cubemap纹理它指明使用的面,是在5.8.3接种描述的纹理类型的立方体面中的一个。Mipmap对于0级的mipmap水平为0,第一级的mipmap水平为1,以此类推。对于有自动mipmap生成(TU_AUTOMIPMAP)的纹理,只有level 0可以被访问,其他的必须由绘制API来维护,一个简单的使用例子如下:

          // Get the PixelBuffer for face 0, mipmap 0.

          HardwarePixelBufferSharedPtr ptr = tex->getBuffer(0,0);

 

5.8.2更新像素缓存

像素缓存可以用两种方法去更新,一个是简单、方便的方法,一个是更困难(但在一些情况中更快速)的方法。两种方法都使用PixelBox对象来表示内存中的图像数据。

blitFromMemory

将一副图像更新到像素缓存的简单方法是使用HardwarePixelBuffer::blitFromMemory,这采用一个PixelBox对象,它完成所有需要的像素格式的转换盒伸缩,下面是例子:

// Manually loads an image and puts the contents in a manually created texture

Image img;

img.load("elephant.png", "General");

// Create RGB texture with 5 mipmaps

TexturePtr tex = TextureManager::getSingleton().createManual(

    "elephant",

    "General",

    TEX_TYPE_2D,

    img.getWidth(), img.getHeight(),

    5, PF_X8R8G8B8);

// Copy face 0 mipmap 0 of the image to face 0 mipmap 0 of the texture.

tex->getBuffer(0,0)->blitFromMemory(img.getPixelBox(0,0));

 

直接内存锁定

         一个从像素缓存来回传输数据的更高级的方法是采用锁定,通过锁定一个像素缓存你可以直接访问到它的内容,不论它在GPU里面的内部格式是怎样的。

/// Lock the buffer so we can write to it

buffer->lock(HardwareBuffer::HBL_DISCARD);

const PixelBox &pb = buffer->getCurrentLock();

 

/// Update the contents of pb here

/// Image data starts at pb.data and has format pb.format

/// Here we assume data.format is PF_X8R8G8B8 so we can address pixels as uint32.

uint32 *data = static_cast<uint32*>(pb.data);

size_t height = pb.getHeight();

size_t width = pb.getWidth();

size_t pitch = pb.rowPitch; // Skip between rows of image

for(size_t y=0; y<height; ++y)

{

    for(size_t x=0; x<width; ++x)

{

        // 0xRRGGBB -> fill the buffer with yellow pixels

        data[pitch*y + x] = 0x00FFFF00;

}

}

 

/// Unlock the buffer again (frees it for use by the GPU)

buffer->unlock();

 

5.8.3 纹理类型

当前的硬件支持4中类型的纹理,其中的三种只在维数上有区别,第四中是特别的,不同类型如下:

TEX_TYPE_1D:一维纹理,和一维纹理坐标一起使用

TEX_TYPE_2D:二维纹理,和二维纹理坐标一起使用

TEX_TYPE_3D:三维体积纹理,和三维纹理坐标一起使用

TEX_TYPE_CUBE_MAP:立方图(六个二维纹理,每个立方体面一个),和三维纹理坐标一起使用

立方图纹理

+X(face 0)-X(face 1)+Y(face 2)-Y(face 3)+Z(face 4)-Z(face 5)

 

5.8.4 像素格式

         一个像素的格式描述像素数据的存储格式,它定义了像素在内存中的编码方式,OGRE中定义了下面的像素格式类型

 

Native endian formats

这些是在内存中的本地尾端的整数,这意味着一个格式为PF_A8R8G8B8的图像可以看作32位整数的数组,在十六进制中定义为0XAARRGGBB,字母的意思在下面被描述

 

Byte formats(PF_BYTE_*)

         这些格式每个通道有一个字节,内存中的通道的组织顺序由格式的名称来确定,例如,PF_BYTE_RGBA包含一个有4个字节的块,一个字节存Red,一个存Green,一个存Blue,一个存Alpha

 

Short formats(PF_SHORT_*)

         这些格式每个通道为一个无符号整形(16位),通道的顺序也是通过名称来确定

 

Float16 formats(PF_FLAOT16_*)

         这些格式每个通道为一个16位的浮点数,通道的顺序通过名称来确定

Float32 formats(PF_FLOAT32_*)

         这些格式每个通道为一个32位的浮点数,通道顺序通过名称来确定

 

Compressed formats(PF_DXT[1-5])

         S3TC压缩纹理格式,可以在http://en.wikipedia.org/wiki/S3TC找到具体信息

 

颜色通道

         RGBA的值为01.0

         L:亮度值,从01.0

         X:不使用这个通道

如果在一个格式中没有定义RGB或者L,它们的默认值都为0.对于Alpha通道就不一样,如果没有定义Alpha,它的默认值为1

 

像素格式的完全列表

Byte formats

PF_BYTE_RGB, PF_BYTE_BGR, PF_BYTE_BGRA, PF_BYTE_RGBA, PF_BYTE_L, PF_BYTE_LA, PF_BYTE_A

Short formats

PF_SHORT_RGBA

Float16 formats

PF_FLOAT16_R, PF_FLOAT16_RGB, PF_FLOAT16_RGBA

Float32 formats

PF_FLOAT32_R, PF_FLOAT32_RGB, PF_FLOAT32_RGBA

8 bit native endian formats

PF_L8, PF_A8, PF_A4L4, PF_R3G3B2

16 bit native endian formats

PF_L16, PF_R5G6B5, PF_B5G6R5, PF_A4R4G4B4, PF_A1R5G5B5

24 bit native endian formats

PF_R8G8B8, PF_B8G8R8

32 bit native endian formats

PF_A8R8G8B8, PF_A8B8G8R8, PF_B8G8R8A8, PF_R8G8B8A8, PF_X8R8G8B8, PF_X8B8G8R8, PF_A2R10G10B10 PF_A2B10G10R10

Compressed formats

PF_DXT1, PF_DXT2, PF_DXT3, PF_DXT4, PF_DXT5

 

 

 

5.8.5 Pixel boxes

Ogre中所有使用或者返回原始图像数据的方法都返回一个PixelBox对象。一个PixelBox是一个描述内存中体(3D),图像(2D)或者线段(1D)的基单元。它描述了存放图像数据的内存的位置和格式,但是它自身不做任何内存管理,pixel box的data成员指向的内存中,像素被存为一个连续的“深度”(沿Z方向)块,每个包含“宽度”(X)个“高度”行(Y)的像素。不使用的维数的值必须置为1,如一维图像有下标(width,1,1),二维图像有下标(width,height,1)。PixelBox有如下的成员

 

data:指向内存中图像数据的一个部分

format:图像数据的像素格式(在上一节中有描述)

 

rowPitch:一行最左边的像素到下一行最左边之间的元素的个数,对于压缩格式这个值等于getWidth()返回的值

 

slicePitch:一个深度块和下一个深度块的左上角像素间的元素个数,必须是rowPitch的倍数,对于压缩格式这个值必须等于getWidth()*getHeight()

 

 

它也有一些有用的方法:

getWidth():获取这个box的宽度

getHeight():获取这个box的高度,如果是一维图像这个值为1

getDepth():获取这个box的深度,如果是一维和二维图像,这个值为1

setConsecutive():设置rowPitch和slicePitch以让数据在内存中连续存放

getRowSkip():获取一行最右边和下一行最左边间的元素的格式,如果行是连续的话值为0

getSliceSkip():获取一个块的右下角像素与下一个块的左上角的像素间的元素,如果块连续的话值为0

isConsecutive():返回内存中的缓存是否是连续的(或者说pitches是否等于维数)

getConsecutiveSize():获取如果在内存中连续存放的话这个图像的字节数

0 0