(以下内容大部分来自DXSpec,逐步更新中…)
Format
首先以R8G8B8A8为例,代表使用了R、G、B、Alpha四个通道,每个通道使用了8个bit,总共使用了32个bit,所以也称BPP/BitCount/BPE(叫法来自DXSPEC)为32
还有一种R8G8B8X8,其中X8代表忽略的透明度信息。
有时候8位最多代表256种颜色,我们需要更多,因此还会有R32G32B32A32这种format,有时候不需要Alpha信息,会有R32G32B32这种format
在DirectX中,每一种format后面还有一个格式修饰符,下面详细解释这些
-
_UNORM:无符号的归一化整数,即没有负数,在资源中解释为无符号整数,在着色器中解释为 [0,1] 范围内的无符号归一化浮点值,比如,2位(指的每一个通道bit)UNORM表示0.0f,1/3, 2/3和1.0f。归一化即指将0,1,2,3表示为[0,1.0f]这个过程
-
_FLOAT:浮点数,DX是有符号浮点数,在SPEC中规定:32位浮点数使用 IEEE 754 单精度 的标准:1位符号、8位指数偏移、23位数值部分;16位的浮点数使用半精度(s10e5)的格式:1位符号、5位指数偏移、10位数值部分。还有特殊的,比如DXGI_FORMAT_R11G11B10_FLOAT,
-
_SNORM:带符号的归一化整数,在着色器中解释为[-1,1]中的有符号归一化浮点数,(5 位值 01111 映射到 1.0f,10000 映射到 -1.0f)
-
_UINT:无符号整数,3位的UINT即表示01234567
-
_BC:压缩纹理格式,详情见下表,注意在所有尺寸上创建的块压缩纹理必须是大小 4 的倍数,且不能用作管道的输出。(来自微软文档)
-
_ASTC:ARM开发的纹理压缩格式,DXGI Format好像没有这种?
-
_TYPELESS:无类型数据,但是有已经定义的bit数,也就是说,可以创建一个无类型资源,但当资源绑定到着色器时,就必须应用或着色器解析这个类型,即CreateSRV/RT/UAV时,比如R8G8B8A8_TYPELESS,在Create View时必须用R8G8B8A8_UNORM/SINT等其中一种去解释这个。(也有例外,raw buffer(注意这里的resource dimeason必须是BufferEX,BufferEX才可以指定raw的flag)可以指定成R32_Typeless,但是struct buffer只能指定 unknown format
-
_SRGB:标准的RGB数据,
介绍一下里面的特殊类型format
- block compress类型
block compress指的是块压缩类型,这里的压缩不同于我们常见的jpg,png等格式,这些格式往往是按规律在进行压缩,而GPU的并行访问像素很明显不能这样压缩,因此出现了诸如DXTC(S3TC)、ASTC等块压缩格式。使用压缩格式好处是能降低GPU的带宽占用率。
这里以DXGI_FORMAT_BC1_UNORM为例,也就是DXT1,DXT系列的压缩原理有点类似调色板
该算法以4x4像素作为一个块,仅支持1位的alpha。其只存储两个16位color,另外两个color则是插值计算得到,每个color的layout是R5G5R5,在4x4的块中还存在index用来查询每个像素的颜色,因为只有四种颜色,所以每个像素仅需2bit即可查到。这样总共就需要16x2 + 16 + 16,即8个字节。而正常我们则需要16x3,48个字节来存储。如下图
其中计算插值的颜色的公式为(color0和color1分别是最小色和最大色)
color_2 = 2/3color_0 + 1/3color_1
color_3 = 1/3color_0 + 2/3color_1
如果color0大于color1,代表不透明,使用上述方法计算,反之,则代表透明,计算方式如下
color_2 = 1/2color_0 + 1/2color_1;
color_3 = 0;
后面的BC系列format都支持多个alpha,因此DXT1一般也只在2D场景使用。
BC2是在BC1的基础上,每个像素增加了一个4bit的alpha,即BC2的每个block使用8个字节。BC2对应DX9的DXT2和DXT3。
BC3,BC3是在BC2上提升了alpha bit,但如果直接给每个像素一个8bit apha那总共就需要24(8+16)个byte,为此,BC3的alpha使用了和颜色一样的方式,每个block使用2个8bit的alpha,同时每个像素使用3bit的索引,同时这也意味着,我们要在中间插值出6个alpha,这样总的block的大小还是16(8+(16+16x3)/8),实际上BC3也支持插值4个alpha,选择方式是对比两个原始alpha的大小。具体公式可参考如下
if( alpha_0 > alpha_1 )
{
// 6 interpolated alpha values.
alpha_2 = 6/7*alpha_0 + 1/7*alpha_1; // bit code 010
alpha_3 = 5/7*alpha_0 + 2/7*alpha_1; // bit code 011
alpha_4 = 4/7*alpha_0 + 3/7*alpha_1; // bit code 100
alpha_5 = 3/7*alpha_0 + 4/7*alpha_1; // bit code 101
alpha_6 = 2/7*alpha_0 + 5/7*alpha_1; // bit code 110
alpha_7 = 1/7*alpha_0 + 6/7*alpha_1; // bit code 111
}
else
{
// 4 interpolated alpha values.
alpha_2 = 4/5*alpha_0 + 1/5*alpha_1; // bit code 010
alpha_3 = 3/5*alpha_0 + 2/5*alpha_1; // bit code 011
alpha_4 = 2/5*alpha_0 + 3/5*alpha_1; // bit code 100
alpha_5 = 1/5*alpha_0 + 4/5*alpha_1; // bit code 101
alpha_6 = 0; // bit code 110
alpha_7 = 255; // bit code 111
}
BC4,BC1存储2个8bit的单通道颜色,索引改成了每个像素3bit,每个block占用还是8个字节。
BC5:BC5则是在BC4的基础上又增加了两个8bit的单通道颜色,并且每个像素又新增了3bit的索引,可以理解为BC4的翻倍,这样每个block的总大小就是16.
BC6:BC6是专门为了支持HDR出现的,其使用的是48位的color值,不支持alpha
BC7;BC7则主要针对是RGBA数据的压缩
需要注意的是。因为compress的format基本上都是在4*4的,所以width/height也必须被4整除,即实际可能会额外填充,比如30额外填充成32,在实际被采样时,将忽略这些填充的。并且因为我们的color是插值出来的,如果实际颜色不在颜色空间一条直线上,有可能被忽略掉,例如下图右边的绿色就被忽略掉了
除过上述这些压缩格式,常用的还有ASTC,在移动设备很流行,PC也支持,并且不一定像前面的必须4X4,支持动态的Tile Size调整,并且还支持2D/3D贴图等。
- planar类型,主要指的是有2个或3个平面format。以NV12为例,其layout如下
Y1 Y2
Y3 Y4
U V
同时对于planar这种format,因为有两层或三层planar,在创建SRV时候,一般会将亮度和色度分开来创建。比如对于DXGI_FORMAT_NV12,对于Y一般会创建成R8,对于UV则创建成R8G8。 - packed类型,指的是只有一个平面的format,和上面类似,只不过因为只有一层平面,也就不会去分开创建SRV。
资源
buffer
对于buffer,不存在mipmap和array,所以它只有一个subresource。一般bind flag主要有vertex buffer,index buffer、constant buffer和stream out。需要注意的是buffer在api看来是没有format的,(在driver里是假定有的),但在绑定(set)IndexBuffer的时候需要指定为R16_UINT和R32_UINT之一。在constant buffer里,format被假定为R32G32B32A32,也就是16byte,这可能是constant buffer的byteWith必须16字节对齐的原因,
Structured Buffer
Structured Buffer即结构化缓冲区,也只有Buffer才能被创建成结构化,Structured意思是将resource标识成单个连续的内存块,每个内存块的大小即Stride,可以理解成的结构数组,因为是在内存上连续的固定结构,所以有很多优势。
RawBuffer
访问Buffer最方便的方法是将其视为一个巨大的bit组成的包,RawBuffer就类似这样,它允许以32位对齐寻址来访问Buffer,
Texture
Texture都支持mipmap和array,这也意味着它会有多个subresource,所以总的subresource数量是miplevel X arraysize。api可以指定要访问的subresource的索引,比如在创建ShaderResourceView的时候使用D3D11_TEX1D_SRV 的MostDetailedMip和MipLevels,MostDetailedMip指明开始使用的纹理子资源,Miplevel指明要访问其后的mip数量。以Texture1D为例如下,要访问第1,2个subresource可使MostDetailedMip为1,Miplevel为2,
Texture1D
Texture2D
只有Texture2D支持多重采样
Texture3D
Texture3D不支持arraySize
采样
Filter
Filter可以简单理解是纹理在实际使用的时候去适应各种大小的方式。
在D3D11_FILTER中有很多枚举值,但其实可以分为三个类型:
- PONT:最邻近采样,是最简单的方式,简单地说就是将Src图像投影到Dst图像上,取其位置四舍五入的Src像素值,公式如下
srcX = dstX / scale //scale表示放大倍数,dstX,Y表示放大后图像的坐标
srcY = dstY / scale//srcX,Y表示其对应的原始的图像坐标,四舍五入后取这个像素值
- LINEAR:线性采样
- ANISOTROPIC:各向异性过滤
Blit
在DirectX11中,有以下种方法可以传输数据
-
D3D11_SUBRESOURCE_DATA :在create resource时直接填入,Usage如果是IMMUTABLE则只能使用此方法传输数据,因为后面其不能再修改;限制是msaa资源不能使用
-
UpdateSubresource: 用于将内存数据拷贝到subresource中;限制:1)Usage不能是immutable和dynamic;2)不能是depth stencil resource;3)不能是msaa资源;
-
CopySubResourceRegion:blit单个subresource,并且可以设置box;限制1)subresource必须不相同,也就是指针不相同;2)必须是同一dimension;3)具有兼容的DXGI格式,比如 可以将DXGI_FORMAT_R32G32B32_FLOAT纹理复制到DXGI_FORMAT_R32G32B32_UINT纹理,因为这两种格式都位于DXGI_FORMAT_R32G32B32_TYPELESS组中;4)不能被Map;5)DXGI_SAMPLE_DESC也必须相同;
-
CopyResource:blit整个resource;限制1)比上述多了一个必须具有相同的尺寸(height、width、depth、miplevel、arraysize)
-
Map/UnMap:获取子资源中包含数据的指针,并拒绝GPU访问该资源,如果有可能会阻塞GPU流水线
DDS
我们一般把压缩(block compress)或非压缩格式存储在DDS文件中,DDS文件PC平台用的非常多。DDS文件可以直接在VS中加载打开,也可以在SDK的DirectX Texture Tool中打开如下:(这个需要安装DXSDK_jun10.exe,),
在这些工具中,都有各种操作可以编辑dds,比如修改format,修改宽高等,具体操作不再赘述。
在应用使用中,有两种方式去解析DDS,分别是DXSDK_jun10.exe安装之后带的和DirectxTex里面的。由于DXSDK_jun10这种已经不被官方推荐,所以我们一般使用后者,