06/12/2020
纹理贴图
基础纹理介绍
纹理坐标系(UV坐标系)
一张图片有横坐标轴和竖坐标轴,横坐标轴叫u,竖坐标轴叫v,原点在图片的左上角
纹理读取
DirectXTK中有DDSTextureLoader和WICTextureLoader的源文件和头文件,可以方便的解析dds和wic文件格式
createDDSTextureFromFile函数–从文件读取DDS纹理
HRESULT CreateDDSTxtureFromFile(
ID3D11Device* d3dDevice, //Device
const wchar_t* szFileName, //dds file name
ID3D11Resource** texture, //输出一个指向资源接口类的接口
ID3D11ShaderResourceView** textureView, //输出一个指向着色器资源视图的指针
size_t maxsize = 0,
DDS_ALPHA_MODE* alphaMode = nullptr
);
createWICTextureFromFile函数
HRESULT CreateWICTxtureFromFile(
ID3D11Device* d3dDevice, //Device
const wchar_t* szFileName, //dds file name
ID3D11Resource** texture, //输出一个指向资源接口类的接口
ID3D11ShaderResourceView** textureView, //输出一个指向着色器资源视图的指针
size_t maxsize = 0
);
注意:这里主要为了创建一个ID3D11ShaderResourceView着色器资源视图,因为纹理资源不能直接使用
HLSL 变动
- 加入uv顶点
- 加入纹理
- 加入采样器
Texture2D gTex:register(t0); //保存2D纹理信息
SamplerState gSamLinear:register(s0); //确定采样器如何采样
struct VertexPosNormalTex
{
float3 PosL:POSITION;
float3 NormalL:NORMAL;
float2 Tex:TEXCOORD;
};
顶点布局
struct VertexPosNormalTex
{
DirectX::XMFLOAT3 pos;
DirectX::XMFLOAT3 normal;
DirectX::XMFLOAT2 tex;
static const D3D11_INPUT_ELEMENT_DESC inputLayout[3];
};
小细节
- HLSL有寄存器是常量缓冲区,采样器状态和纹理2D,通常我们找对应关系是跟着不同插入槽的名城,然而顶点布局设置了语义描述形式
- 所以说HLSL结构体或者缓冲名字可以和C++名字不同,它们基本不是根据名字传递数据的
过滤器(纯理论)
- 处理图片被放大后,像素空间变大之后,空缺的像素如何填充的问题
- 处理图片缩小后,像素损失问题
图片放大(插值法)
常量插值法
线性插值法
图片缩小(mipmap技术)
牺牲一些内存代价的方式来获得高效的拟合效果
mipmap
各项异性过滤
对纹理采样
设置着色器资源(*SSetShaderResources)
void ID3D11DeviceContext::PSSetShaderResources(
UINT StartSlot, //对应HLSL的register(t*)
UINT NumViews, //着色器资源视图数目
ID3D11ShaderResourceView* const* ppShaderResourceViews //着色器资源视图数组
);
创建采样器状态
typedef struct D3D11_SAMPLER_DESC
{
D3D11_FILTER Filter; // 所选过滤器
D3D11_TEXTURE_ADDRESS_MODE AddressU; // U方向寻址模式
D3D11_TEXTURE_ADDRESS_MODE AddressV; // V方向寻址模式
D3D11_TEXTURE_ADDRESS_MODE AddressW; // W方向寻址模式
FLOAT MipLODBias; // mipmap等级偏移值,最终算出的mipmap等级会加上该偏移值
UINT MaxAnisotropy; // 最大各向异性等级(1-16)
D3D11_COMPARISON_FUNC ComparisonFunc; // 这节不讨论
FLOAT BorderColor[ 4 ]; // 边界外的颜色,使用D3D11_TEXTURE_BORDER_COLOR时需要指定
FLOAT MinLOD; // 若mipmap等级低于MinLOD,则使用等级MinLOD。最小允许设为0
FLOAT MaxLOD; // 若mipmap等级高于MaxLOD,则使用等级MaxLOD。必须比MinLOD大
} D3D11_SAMPLER_DESC;
// 初始化采样器状态
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
HR(m_pd3dDevice->CreateSamplerState(&sampDesc, m_pSamplerState.GetAddressOf()));
选择过滤器
//枚举值 缩小 放大 mipmap
D3D11_FILTER_MIN_MAG_MIP_POINT // 点采样 点采样 点采样
D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR // 点采样 点采样 线性采样
D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT // 点采样 线性采样 点采样
D3D11_FILTER_MIN_MAG_MIP_LINEAR // 线性采样 线性采样 线性采样
D3D11_FILTER_ANISOTROPIC // 各向异性 各向异性 各向异性
选择寻址模式
D3D11_TEXTURE_ADDRESS_BORDER
D3D11_TEXTURE_ADDRESS_CLAMP
D3D11_TEXTURE_ADDRESS_WRAP
D3D11_TEXTURE_ADDRESS_MIRROR_ONCE
D3D11_TEXTURE_ADDRESS_MIRROR
像素着色器阶段设置采样器状态
void ID3D11DeviceContext::PSSetSamplers(
UINT StartSlot, // [In]起始槽索引 (s)
UINT NumSamplers, // [In]采样器状态数目
ID3D11SamplerState * const * ppSamplers); // [In]采样器数组
// 像素着色阶段设置好采样器
m_pd3dImmediateContext->PSSetSamplers(0, 1, m_pSamplerState.GetAddressOf());
总结
- 初始化图片纹理并创建着色器资源视图(CreateDDSTextureFromFile)
- 更新HLSL着色器和顶点布局格式
- 初始化采样器状态 (CreateSamplerState)
- 像素着色器阶段设置好采样器和着色器资源视图 (PSSetSamplers 和 PSSetShaderResources)
后续补充
渲染管道可以创建多个顶点或者像素着色器,使用的时候需要绑定需要的着色器
HLSL 纹理采样
采取纹理的颜色
float4 texColor = g_Tex.Sample(g_SamLinear, pIn.Tex);
float4 litColor = texColor * (ambient + diffuse) + spec;
litColor.a = texColor.a * g_Material.Diffuse.a;
- Texture2D 有一个Sample函数可以在纹理中选取颜色,必须有采样器,因为如果纹理坐标超出了uv坐标,就会采取采样器的设置好的寻址模式
- texColor影响了计算好后的环境光和漫反射光,如果没有采集到纹理颜色,会导致物体无法绘制出来
绘制不同的纹理到六面体
//六面体的索引左边
meshData.indexVec = {
0, 1, 2, 2, 3, 0, // 右面(+X面)
4, 5, 6, 6, 7, 4, // 左面(-X面)
8, 9, 10, 10, 11, 8, // 顶面(+Y面)
12, 13, 14, 14, 15, 12, // 底面(-Y面)
16, 17, 18, 18, 19, 16, // 背面(+Z面)
20, 21, 22, 22, 23, 20 // 正面(-Z面)
};
// 通过不同的索引坐标决定六面体的不同面,再更改纹理资源视图
m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate.GetAddressOf());
m_pd3dImmediateContext->DrawIndexed(6, 0, 0); //六面体的右面
m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pAlpha.GetAddressOf());
m_pd3dImmediateContext->DrawIndexed(6, 6, 0); //六面体左面
纹理分量乘法
Texture2D g_Tex : register(t0); //插入槽 t0 对应一个纹理视图
Texture2D g_Tex1 : register(t1); //插入槽 t1 对应另一个纹理视图
//PS着色器中的纹理颜色分量乘法
float4 texColor = g_Tex.Sample(g_SamLinear, pIn.Tex);
float4 texColor1 = g_Tex1.Sample(g_SamLinear, pIn.Tex);
texColor = texColor * texColor1; //分量乘法 Float4 texColor = {t1.r * t2.r, t1.g * t2.g, t1.b * t2.b, t1.a * t2.a}