渲染纹理过程
其余过程与渲染三角形一致,如下是多的操作步骤:
- 编写着色器
具体见下[着色器]一节。 - 创建纹理
使用CreateTexture2D
创建一个带数据的纹理图像。 - 使用纹理创建资源视图
传入创建的纹理,使用CreateSamplerState
创建一个着色器资源视图。(注意:该纹理的BindFlags
需要设置为D3D11_BIND_SHADER_RESOURCE
)。使用GenerateMips
为资源视图创建Mipmap层级。 - 绑定资源视图到管线
使用PSSetShaderResources
将资源视图绑定到像素着色器中。 - 创建采样器状态
使用CreateSamplerState
创建一个纹理采样器对象。 - 绑定采样器状态到管线
使用PSSetSamplers
将创建的纹理采样器对象绑定到像素着色器中。
纹理映射
要将.dds图形映射到像素我们用的是Texel坐标系统。这个系统将将整数值的像素变换到0.0f和1.0f之间的浮点数。例如,如果一张纹理宽为256个像素,那么第一个像素映射为0.0f,第256个像素映射为1.0f,中间的第128个像素映射为0.5f。
在texel坐标系统中,水平方向的值名为“U"”,垂直方向为“V”。水平方向左边为0.0,右边为1.0。竖直方向顶部为0.0,底部为1.0。例如,左上角为U 0.0、V 0.0,右下角为U 1.0、 V 1.0。下图就表示了纹理坐标系统:
纹理坐标
着色器
结构体定义
- 顶点着色器输入结构
struct tVinputType
{
float4 position : myPOSITION; // 顶点位置(x/y/z/w)
float2 tex : myTEXCOORD; // 纹理坐标(u/v)
};
- 像素着色器输入结构
struct tPinputtype
{
float4 position : SV_POSITION; // 位置。SV_POSITION像素着色器内部变量
float2 tex : myTEXCOORD; // 纹理坐标
};
其中myPOSITION
和myTEXCOORD
都是自定义语义,在创建顶点输入布局中指定。另,float4 position : myPOSITION;
,由于只使用了2D,这里可以只传入float2
类型的位置信息,在着色器中自动补全到float4
。
const D3D11_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{ "myPOSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // float4类型
{ "myTEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12u, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // float2类型
};
顶点着色器
纹理的顶点着色器中,输入需要顶点位置和纹理坐标,之前的三角形渲染中,顶点着色器中使用的输入是顶点位置和颜色。
tPinputtype TextureVsMain(tVinputType input)
{
tPinputtype output;
// w分量不从CPU传入,而默认设置为1.0f
input.position.w = 1.0f;
output.position = input.position;
output.tex = input.tex;
return output;
}
像素着色器
/////////////
// GLOBALS //
/////////////
Texture2D myShaderTexture; // 使用PSSetShaderResources绑定到着色器的纹理资源
SamplerState mySampleType; // 使用PSSetSamplers绑定到着色器的采样器状态
// 入口函数
float4 TexturePsMain(tPinputtype input) : SV_TARGET
{
float4 textureColor;
// Sample函数使用采样器状态(mySampleType)和该像素的纹理坐标(tex),
// 来确定并返回多边形面上此UV位置的像素值。
textureColor = myShaderTexture.Sample(mySampleType, input.tex);
return textureColor;
}
Texture2D myShaderTexture
,为定义为2D纹理的纹理资源,类型Texture2D
,这是绑定到渲染管线的那个纹理资源(即使用PSSetShaderResources
绑定到像素着色器的纹理资源)。这样shader就可以访问这个纹理并用于绘制了。
SamplerState mySampleType
,为采样状态。采样状态可以通过PSSetSamplers
设置到像素着色器。
2D纹理的描述
typedef struct D3D11_TEXTURE2D_DESC
{
UINT Width; // 纹理宽度
UINT Height; // 纹理高度
UINT MipLevels; // 允许的Mip等级数
UINT ArraySize; // 可以用于创建纹理数组,这里指定纹理的数目,单个纹理使用1
DXGI_FORMAT Format; // DXGI支持的数据格式,默认DXGI_FORMAT_R8G8B8A8_UNORM
DXGI_SAMPLE_DESC SampleDesc; // MSAA描述
D3D11_USAGE Usage; // 使用D3D11_USAGE枚举值指定数据的CPU/GPU访问权限
UINT BindFlags; // 使用D3D11_BIND_FLAG枚举来决定该数据的使用类型
UINT CPUAccessFlags; // 使用D3D11_CPU_ACCESS_FLAG枚举来决定CPU访问权限
UINT MiscFlags; // 使用D3D11_RESOURCE_MISC_FLAG枚举
} D3D11_TEXTURE2D_DESC;
typedef struct DXGI_SAMPLE_DESC
{
UINT Count; // MSAA采样数
UINT Quality; // MSAA质量等级
} DXGI_SAMPLE_DESC;
MipLevels
- 如果你希望它不产生mipmap,则应当指定为1(只包含最大的位图本身)
- 如果你希望它能够产生完整的mipmap,可以指定为0,这样你就不需要手工去算这个纹理最大支持的mipmap等级数了,在创建好纹理后,可以再调用ID3D11Texture2D::GetDesc来查看实际的MipLevels值是多少
- 如果你指定的是其它的值,这里举个例子,该纹理的宽高为400x400,mip等级为3时,该纹理会产生400x400,200x200和100x100的mipmap
-
SampleDesc
对于经常作为着色器资源的纹理,通常是不能对其开启MSAA的,应当把Count设为1,Quality设为0 -
Format
它用于指定纹理存储的数据格式,最常用的就是DXGI_FORMAT_R8G8B8A8_UNORM了。这种格式在内存的排布可以用下面的结构体表示:
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
Usage
D3D11_USAGE | CPU读 | CPU写 | GPU读 | GPU写 |
---|---|---|---|---|
D3D11_USAGE_DEFAULT | √ | √ | ||
D3D11_USAGE_IMMUTABLE | √ | |||
D3D11_USAGE_DYNAMIC | √ | √ | ||
D3D11_USAGE_STAGING | √ | √ | √ | √ |
-
D3D11_USAGE_DEFAULT
它可以使用下面的这些方法来更新纹理:
ID3D11DeviceContext::UpdateSubresource
(更建议使用此方法更新数据)
ID3D11DeviceContext::CopyResource
ID3D11DeviceContext::CopySubresourceRegion
通过DDSTextureLoader
或WICTextureLoader
创建出来的纹理默认都是这种类型。 -
D3D11_USAGE_IMMUTABLE
必须在创建阶段就完成纹理资源的初始化。此后GPU只能读取,也无法对纹理再进行修改。
D3D11_USAGE_IMMUTABLE
非常适合纹理等数据,因为此类数据通常从某种文件格式读入内存。因此,当您使用D3D11_USAGE_IMMUTABLE
创建纹理时,GPU 会直接将该纹理读取到内存中。 -
D3D11_USAGE_DYNAMIC
这种纹理通常需要频繁从CPU写入,使用ID3D11DeviceContext::Map
方法将显存映射到内存,经过修改后再调用ID3D11DeviceContext::Unmap
方法应用更改。
D3D11_USAGE_DYNAMIC
通常用于具有顶点数据的资源和常量缓冲区。
该类型纹理MipLevels
必须为1,且不能是纹理数组,即ArraySize
必须为1。 -
D3D11_USAGE_STAGING
完全允许在CPU和GPU之间的数据传输,但它只能作为一个类似中转站的资源,而不能绑定到渲染管线上,即你也不能用该纹理生成mipmaps,即MipLevels
必须为1。
例如,你有一个D3D11_USAGE_DEFAULT
类型的纹理A,想要将其数据拷贝到内存中,因为D3D11_USAGE_DEFAULT
类型的纹理没有CPU读的权限,因此此时需要借助D3D11_USAGE_STAGING
类型纹理做中转,即新建一个D3D11_USAGE_STAGING
类型的纹理B,通过ID3D11DeviceContext::CopyResource
或者ID3D11DeviceContext::CopySubresourceRegion
方法将A纹理的数据复制到B纹理中,再通过ID3D11DeviceContext::Map
方法将B纹理的数据映射到内存中。
BindFlags
D3D11_BIND_FLAG | 描述 |
---|---|
D3D11_BIND_RENDER_TARGET | 纹理可以作为渲染目标的输出点,并且指定它可以用于生成mipmaps |
D3D11_BIND_SHADER_RESOURCE | 纹理可以作为着色器资源绑定到渲染管线 |
D3D11_BIND_STREAM_OUTPUT | 纹理可以作为流输出阶段的输出点 |
D3D11_BIND_DEPTH_STENCIL | 纹理可以作为深度/模板缓冲区 |
D3D11_BIND_UNORDERED_ACCESS | 纹理可以绑定到无序访问视图作为输出 |
CPUAccessFlags
-
0
可以获得更好的资源优化操作。 -
D3D11_CPU_ACCESS_WRITE
内存数据可以通过Map
和Unmap
的方式拷贝到纹理中。
纹理可以作为渲染目标的输出点,并且指定它可以用于生成mipmaps。与D3D11_BIND_RENDER_TARGET
一致。 -
D3D11_CPU_ACCESS_READ
纹理可以作为着色器资源绑定到渲染管线。与D3D11_BIND_SHADER_RESOURCE
一致。
-
MiscFlags
D3D11_RESOURCE_MISC_GDI_COMPATIBLE
启用与 GDI 兼容的资源。设置D3D11_RESOURCE_MISC_GDI_COMPATIBLE
标志允许通过IDXGISurface1::GetDC
在表面上呈现GDI。它有许多的限制,请参考官方说明。
网友评论