DX11 纹理
目录
题目&要求
-
题目
-
- 给一个立方体六个面贴不同的纹理,并且使用方向光光照
-
- 利用纹理数组实现2D纹理动画(09项目中的火焰动画)
-
- 给一个立方体六个面贴上同一个纹理,这个纹理利用给定的两张纹理进行合成,并且实现纹理在立方体面上的旋转(提示:顶点着色器),关闭光照,还可以利用上上一节课所写的代码来操控立方体的旋转
-
要求
-
- 以上三个作业写在同一个项目中,并且利用数字键盘进行切换。
存在问题
- 从这次作业发现,前面基础非常不扎实,从而导致这次作业推迟交。
- 对imgui暂时没有实现。
- 利用纹理数组实现2D纹理动画,一个数组索引没有找出来,所以…后面会回头处理。
学习情况&整体思路
- 学习情况
-
- 现在大概明白的整体实现的流程,但是还有许多细节没办法处理。
- 可能到现在,基础不是很扎实,基础薄弱。
- 这次作业对纹理有了个基本的认识,同时加深了渲染管线的印象。
- 整体思路
-
- 1、首先处理纹理坐标的顶点结构体,然后是对HLSL代码的处理,接下来就是创建着色器了,着色器有4个,两个顶点布局,在InitEffect()函数中实现。
-
- 2、然后是对网络模型初始化ResetMesh(meshData),设置设置常量缓冲区,新建用于VS和PS的常量缓冲区,初始化纹理(3个作业的纹理初始化), 初始化采样器状态。初始化常量缓冲区的值,更新PS常量缓冲区资源。然后就是给渲染管线各个阶段绑定好所需资源啦。
-
- 3、基本的完成后,就是GameApp::DrawScene()的绘制几何模型和绘制Direct2D部分。
-
- 4、最后是GameApp::UpdateScene(float dt),播放动漫和实现了。
一、顶点输入布局
虽然渲染管线的那篇文章说过,但也是简单的提了一下,现在就这详细说一下
来看看Geometry.h中的模板实现各种几何体的方法,这里就正方体来说一下
1、首先是输入布局的描述
struct VertexData
{
DirectX::XMFLOAT3 pos;//顶点 (0,12)//字节
DirectX::XMFLOAT3 normal;//法线 (12,24)
DirectX::XMFLOAT4 tangent;//切线(24,40)
DirectX::XMFLOAT4 color;//颜色 (40,56)
DirectX::XMFLOAT2 tex;//纹理 (56,64)
};
//input 基础元素
//使用D3D11_INPUT_ELEMENT_DESC结构体来描述待传入结构体中每个成员的具体信息
//(语义名,语义索引,数据格式,输入槽索引(0-15),初始位置(字节偏移量),输入类型,忽略)
//通过语义、数据类型和起始偏移量,我们就可以建立起C++顶点缓冲区数据和HLSL顶点之间的联系(1)
typedef struct D3D11_INPUT_ELEMENT_DESC {
LPCSTR SemanticName; //语义名
UINT SemanticIndex; //语义索引
DXGI_FORMAT Format; //数据格式
UINT InputSlot; //输入槽索引(0-15)
UINT AlignedByteOffset //初始位置(字节偏移量)
D3D11_INPUT_CLASSIFICATION InputSlotClass; //输入类型
UINT InstanceDataStepRate; //目前指定为0;其他值只用于高级实例技术。
} D3D11_INPUT_ELEMENT_DESC;
const D3D11_INPUT_ELEMENT_DESC VertexPosNormalTex::inputLayout[3] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
struct VertexPosNormalTex
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 Tex : TEXCOORD;
};
来看看是怎么联系起来的
这是一种通过语义名和语义索引提供了一种将顶点元素映射为顶点着色器参数的方法
-
在来看看第五个参数AlignedByteOffset初始位置(字节偏移量)
-
AlignedByteOffset:对于单个输入槽来说,该参数表示从顶点结构体的起始位置到顶点元素的起始位置之间的字节偏移量。例如在下面的顶点结构体中,元素Pos的字节偏移量为0,因为它的起始位置与顶点结构体的起始位置相同;元素Normal的字节偏移量为12,因为必须跳过由Pos占用的字节才能到达Normal的起始位置;元素tangent的字节偏移量为24,因为必须跳过由Pos和Normal占用的字节才能到达tangent的起始位置;其他以此类推下去
struct VertexData { DirectX::XMFLOAT3 pos;//顶点 (0,12)//字节 DirectX::XMFLOAT3 normal;//法线 (12,24) DirectX::XMFLOAT4 tangent;//切线(24,40) DirectX::XMFLOAT4 color;//颜色 (40,56) DirectX::XMFLOAT2 tex;//纹理 (56,64) };
2、接下来就是在设备创建输入布局
// 创建顶点布局(3D)
// ([In]输入布局描述,[In]上述数组元素个数,[In]顶点着色器字节码,[In]顶点着色器字节码长度,[out]获取的输入布局)
HR(m_pd3dDevice->CreateInputLayout(VertexPosNormalTex::inputLayout, ARRAYSIZE(VertexPosNormalTex::inputLayout),
blob->GetBufferPointer(), blob->GetBufferSize(), m_pVertexLayout3D.GetAddressOf()));
3、数据输入对不对,来看这一章的CreateBox
template<class VertexType, class IndexType>
inline MeshData<VertexType, IndexType> CreateBox(float width, float height, float depth, const DirectX::XMFLOAT4 & color)
{
using namespace DirectX;
// 网格数据
//template<class VertexType = VertexPosNormalTex, class IndexType = DWORD>
//struct MeshData
//{
// std::vector<VertexType> vertexVec; // 顶点数组
// std::vector<IndexType> indexVec; // 索引数组
//};
MeshData<VertexType, IndexType> meshData;
meshData.vertexVec.resize(24);
// struct VertexData
// {
// DirectX::XMFLOAT3 pos;//顶点
// DirectX::XMFLOAT3 normal;//法线
// DirectX::XMFLOAT4 tangent;//切线
// DirectX::XMFLOAT4 color;//颜色
// DirectX::XMFLOAT2 tex;//纹理
// };
Internal::VertexData vertexDataArr[24];
float w2 = width / 2, h2 = height / 2, d2 = depth / 2;
//可能这里你要问了,为什么需要写这么多的顶点,上一次不是说了索引吗
//那是因为
//对于棱角分明的物体,如立方体,一共由12个三角形组成,2个三角形的4个顶点构成一个面。由于该面的4个顶点要求法向量朝向一致,而一个顶点虽然与立方体的三个面邻接,但是法向量只有一个,因此需要分化出3个包含不同法向量的顶点。最终用于绘制该立方体的顶点数就需要24个,是立方体顶点数的3倍!
//就是因为下面有法线和切线的赋值,每一个顶点都不一样
// 右面(+X面)
vertexDataArr[0].pos = XMFLOAT3(w2, -h2, -d2);
vertexDataArr[1].pos = XMFLOAT3(w2, h2, -d2);
vertexDataArr[2].pos = XMFLOAT3(w2, h2, d2);
vertexDataArr[3].pos = XMFLOAT3(w2, -h2, d2);
// 左面(-X面)
vertexDataArr[4].pos = XMFLOAT3(-w2, -h2, d2);
vertexDataArr[5].pos = XMFLOAT3(-w2, h2, d2);
vertexDataArr[6].pos = XMFLOAT3(-w2, h2, -d2);
vertexDataArr[7].pos = XMFLOAT3(-w2, -h2, -d2);
// 顶面(+Y面)
vertexDataArr[8].pos = XMFLOAT3(-w2, h2, -d2);
vertexDataArr[9].pos = XMFLOAT3(-w2, h2, d2);
vertexDataArr[10].pos = XMFLOAT3(w2, h2, d2);
vertexDataArr[11].pos = XMFLOAT3(w2, h2, -d2);
// 底面(-Y面)
vertexDataArr[12].pos = XMFLOAT3(w2, -h2, -d2);
vertexDataArr[13].pos = XMFLOAT3(w2, -h2, d2);
vertexDataArr[14].pos = XMFLOAT3(-w2, -h2, d2);
vertexDataArr[15].pos = XMFLOAT3(-w2, -h2, -d2);
// 背面(+Z面)
vertexDataArr[16].pos = XMFLOAT3(w2, -h2, d2);
vertexDataArr[17].pos = XMFLOAT3(w2, h2, d2);
vertexDataArr[18].pos = XMFLOAT3(-w2, h2, d2);
vertexDataArr[19].pos = XMFLOAT3(-w2, -h2, d2);
// 正面(-Z面)
vertexDataArr[20].pos = XMFLOAT3(-w2, -h2, -d2);
vertexDataArr[21].pos = XMFLOAT3(-w2, h2, -d2);
vertexDataArr[22].pos = XMFLOAT3(w2, h2, -d2);
vertexDataArr[23].pos = XMFLOAT3(w2, -h2, -d2);
for (UINT i = 0; i < 4; ++i)
{
// 右面(+X面)
vertexDataArr[i].normal = XMFLOAT3(1.0f, 0.0f, 0.0f);
vertexDataArr[i].tangent = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);
vertexDataArr[i].color = color;
// 左面(-X面)
vertexDataArr[i + 4].normal = XMFLOAT3(-1.0f, 0.0f, 0.0f);
vertexDataArr[i + 4].tangent = XMFLOAT4(0.0f, 0.0f, -1.0f, 1.0f);
vertexDataArr[i + 4].color = color;
// 顶面(+Y面)
vertexDataArr[i + 8].normal = XMFLOAT3(0.0f, 1.0f, 0.0f);
vertexDataArr[i + 8].tangent = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);
vertexDataArr[i + 8].color = color;
// 底面(-Y面)
vertexDataArr[i + 12].normal = XMFLOAT3(0.0f, -1.0f, 0.0f);
vertexDataArr[i + 12].tangent = XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f);
vertexDataArr[i + 12].color = color;
// 背面(+Z面)
vertexDataArr[i + 16].normal = XMFLOAT3(0.0f, 0.0f, 1.0f);
vertexDataArr[i + 16].tangent = XMFLOAT4(-1.0f, 0.0f, 0.0f, 1.0f);
vertexDataArr[i + 16].color = color;
// 正面(-Z面)
vertexDataArr[i + 20].normal = XMFLOAT3(0.0f, 0.0f, -1.0f);
vertexDataArr[i + 20].tangent = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);
vertexDataArr[i + 20].color = color;
}
//这里是纹理
for (UINT i = 0; i < 6; ++i)
{
vertexDataArr[i * 4].tex = XMFLOAT2(0.0f, 1.0f);
vertexDataArr[i * 4 + 1].tex = XMFLOAT2(0.0f, 0.0f);
vertexDataArr[i * 4 + 2].tex = XMFLOAT2(1.0f, 0.0f);
vertexDataArr[i * 4 + 3].tex = XMFLOAT2(1.0f, 1.0f);
}
//这里是因为模板,需要处理数据成员偏移量
for (UINT i = 0; i < 24; ++i)
{
Internal::InsertVertexElement(meshData.vertexVec[i], vertexDataArr[i]);
}
// 索引数组
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面)
};
return meshData;
}
4、还没完呢,都说是模板了,像struct VertexData{}结构体里面的数据成员,不是所有几何体都要用上的。
//这里是因为模板,需要处理数据成员偏移量
for (UINT i = 0; i < 24; ++i)
{
Internal::InsertVertexElement(meshData.vertexVec[i], vertexDataArr[i]);
}
// 根据目标顶点类型选择性将数据插入
template<class VertexType>
inline void InsertVertexElement(VertexType& vertexDst, const VertexData& vertexSrc)
{
// 第一步,建立内存映射关系
static std::string semanticName;
static const std::map<std::string, std::pair<size_t, size_t>> semanticSizeMap = {
{"POSITION", std::pair<size_t, size_t>(0, 12)},
{"NORMAL", std::pair<size_t, size_t>(12, 24)},
{"TANGENT", std::pair<size_t, size_t>(24, 40)},
{"COLOR", std::pair<size_t, size_t>(40, 56)},
{"TEXCOORD", std::pair<size_t, size_t>(56, 64)}
};
// 第二步,确定当前顶点需要插入的字节偏移位置
//这里i < ARRAYSIZE(VertexType::inputLayout)是模板,对应上Vertex.cpp里面创建的所有D3D11_INPUT_ELEMENT_DESC的数据,看你用什么什么了
for (size_t i = 0; i < ARRAYSIZE(VertexType::inputLayout); i++)
{
semanticName = VertexType::inputLayout[i].SemanticName;
const auto& range = semanticSizeMap.at(semanticName);
//reinterpret_cast 转换
//通过重新解释底层位模式在类型间转换。
//reinterpret_cast <新类型> (表达式)
//根据顶点的数据成员的偏移量来拷贝
//这个是模板写法,不同的顶点格式创建相应的顶点数据
memcpy_s(reinterpret_cast<char*>(&vertexDst) + VertexType::inputLayout[i].AlignedByteOffset,
range.second - range.first,
reinterpret_cast<const char*>(&vertexSrc) + range.first,
range.second - range.first);
}
}
这里是不是就可以解释的通了
5、外面创建就是这个样子了
auto meshData = Geometry::CreateBox();
ResetMesh(meshData);//基本的顶点缓冲区,索引缓冲区创建
auto meshData = Geometry::Create2DShow();
ResetMesh(meshData);//基本的顶点缓冲区,索引缓冲区创建
二、纹理坐标系
纹理坐标系和屏幕、图片坐标系的有些相似,它们的U轴都是水平朝右,V轴竖直向下。但是纹理的X和Y的取值范围都为[0.0f, 1.0f]
,分别映射到[0, Width]
和[0, Height]
代码实现为(基于正方体)
Internal::VertexData vertexDataArr[24];
.......
for (UINT i = 0; i < 6; ++i)
{
vertexDataArr[i * 4].tex = XMFLOAT2(0.0f, 1.0f);
vertexDataArr[i * 4 + 1].tex = XMFLOAT2(0.0f, 0.0f);
vertexDataArr[i * 4 + 2].tex = XMFLOAT2(1.0f, 0.0f);
vertexDataArr[i * 4 + 3].tex = XMFLOAT2(1.0f, 1.0f);
}
是不是刚刚好正方体的每一个面四个顶点对应着纹理四个点
纹理读取
HRESULT CreateDDSTextureFromFile(
ID3D11Device* d3dDevice, // [In]D3D设备
const wchar_t* szFileName, // [In]dds图片文件名
ID3D11Resource** texture, // [Out]输出一个指向资源接口类的指针,也可以填nullptr
ID3D11ShaderResourceView** textureView, // [Out]输出一个指向着色器资源视图的指针,也可以填nullptr
size_t maxsize = 0, // [In]忽略
DDS_ALPHA_MODE* alphaMode = nullptr); // [In]忽略
// 初始化木箱纹理
HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"Texture\\WoodCrate.dds", nullptr, m_pWoodCrate.GetAddressOf()));
HRESULT CreateWICTextureFromFile(
ID3D11Device* d3dDevice, // [In]D3D设备
const wchar_t* szFileName, // [In]wic所支持格式的图片文件名
ID3D11Resource** texture, // [Out]输出一个指向资源接口类的指针,也可以填nullptr
ID3D11ShaderResourceView** textureView, // [Out]输出一个指向着色器资源视图的指针,也可以填nullptr
size_t maxsize = 0); // [In]忽略
// 初始化火焰纹理
WCHAR strFile[40];
m_pFireAnims.resize(120);
for (int i = 1; i <= 120; ++i)
{
//函数wsprintf()将一系列的字符和数值输入到缓冲区
//wsprintf(输出缓冲区,格式字符串, 需输出的参数)
wsprintf(strFile, L"..\\Tex\\FireAnim\\Fire%03d.bmp", i);
HR(CreateWICTextureFromFile(m_pd3dDevice.Get(), strFile, nullptr, m_pFireAnims[static_cast<size_t>(i) - 1].GetAddressOf()));
}
注意的是,纹理并不能直接绑定到着色器中,需要为纹理创建对应的着色器资源视图才能够给着色器使用
void ID3D11DeviceContext::PSSetShaderResources(
UINT StartSlot, // [In]起始槽索引,对应HLSL的register(t*)
UINT NumViews, // [In]着色器资源视图数目
ID3D11ShaderResourceView * const *ppShaderResourceViews // [In]着色器资源视图数组
);
//然后调用方法如下:
m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate.GetAddressOf());
//这样在HLSL里对应regisgter(t0)的g_Tex存放的就是木箱表面的纹理了。
三、纹理采样
过滤器就直接看Xjun师兄的吧
所谓采样,就是根据纹理坐标取出纹理对应位置最为接近的像素。并非单单读取纹理就行了。
因为但大多数时候绘制出的纹理会比所用的纹理大或小,这样就还涉及到了采样器使用什么方式(如常量插值法、线性插值法、各向异性过滤)来处理图片放大、缩小的情况。
先来看看一些寻址模式
D3D11_TEXTURE_ADDRESS_MODE
是单个方向的寻址模式,有时候纹理坐标会超过1.0或者小于0.0,这时候寻址模式可以解释边界外的情况,含义如下:
D3D11_TEXTURE_ADDRESS_WRAP
是将指定纹理坐标分量的值[t, t + 1], t ∈ Z映射到[0.0, 1.0],因此作用到u和v分量时看起来就像是把用一张贴图紧密平铺到其他位置上:
D3D11_TEXTURE_ADDRESS_MIRROR
在每个整数点处翻转纹理坐标值。例如u在[0.0, 1.0]按正常纹理坐标寻址,在[1.0, 2.0]内则翻转,在[2.0, 3.0]内又回到正常的寻址,以此类推:
D3D11_TEXTURE_ADDRESS_CLAMP
对指定纹理坐标分量,小于0.0的值都取作0.0,大于1.0的值都取作1.0,在[0.0, 1.0]的纹理坐标不变:
D3D11_TEXTURE_BORDER_COLOR
对于指定纹理坐标分量的值在[0.0, 1.0]外的区域都使用BorderColor
进行填充
D3D11_TEXTURE_ADDRESS_MIRROR_ONCE
相当于MIRROR和CLAMP的结合,仅[-1.0,1.0]的范围内镜像有效,若小于-1.0则取-1.0,大于1.0则取1.0,在[-1.0, 0.0]进行翻转。
创建呢也是差不多
// 初始化采样器状态
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
//sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;// 所选过滤器
sampDesc.Filter = D3D11_FILTER_ANISOTROPIC;
//寻址模式 改为通过将每个不在[0,1]2区间内的(u,v)映射为程序员指定的某个颜色来扩展纹理
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;// U方向寻址模式
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;// V方向寻址模式
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;// W方向寻址模式
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0; // 若mipmap等级低于MinLOD,则使用等级MinLOD。最小允许设为0
sampDesc.MaxLOD = D3D11_FLOAT32_MAX; // 若mipmap等级高于MaxLOD,则使用等级MaxLOD。必须比MinLOD大
//CreateSamplerState([In]采样器状态描述,[Out]输出的采样器)
HR(m_pd3dDevice->CreateSamplerState(&sampDesc, m_pSamplerState.GetAddressOf()));
设备上创建了采样器,也要绑定在渲染管线上才能用
void ID3D11DeviceContext::PSSetSamplers(
UINT StartSlot, // [In]起始槽索引
UINT NumSamplers, // [In]采样器状态数目
ID3D11SamplerState * const * ppSamplers); // [In]采样器数组
//HLSL代码中
//#include "LightHelper.hlsli"
//Texture2D g_Tex : register(t0);
//SamplerState g_SamLinear : register(s0);
//根据前面的HLSL代码,samLinear使用了索引为0起始槽,所以需要这样调用:
// 像素着色阶段设置好采样器
m_pd3dImmediateContext->PSSetSamplers(0, 1, m_pSamplerState.GetAddressOf());
四、其他资源绑定
// ******************
// 给渲染管线各个阶段绑定好所需资源
// 设置图元类型
m_pd3dImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//设定输入布局
m_pd3dImmediateContext->IASetInputLayout(m_pVertexLayout3D.Get());
// 默认绑定3D着色器
m_pd3dImmediateContext->VSSetShader(m_pVertexShader3D.Get(), nullptr, 0);
// VS常量缓冲区对应HLSL寄存于b0的常量缓冲区
m_pd3dImmediateContext->VSSetConstantBuffers(0, 1, m_pConstantBuffers[0].GetAddressOf());
// PS常量缓冲区对应HLSL寄存于b1的常量缓冲区
//PSSetConstantBuffers([In]起始槽索引,对应HLSL的register(t*),[In]着色器资源视图数目,[In]着色器资源视图数组)
m_pd3dImmediateContext->PSSetConstantBuffers(1, 1, m_pConstantBuffers[1].GetAddressOf());
// 像素着色阶段设置好采样器
//PSSetSamplers([In]起始槽索引,[In]采样器状态数目,[In]采样器数组) 索引为0起始槽
m_pd3dImmediateContext->PSSetSamplers(0, 1, m_pSamplerState.GetAddressOf());
//像素着色器
m_pd3dImmediateContext->PSSetShader(m_pPixelShader3D.Get(), nullptr, 0);
作业相关代码&代码分析
上面的是基本的东西,下面来说说作业的实现
首先看HLSL中的修改
Basic.hlsli
#include "LightHelper.hlsli"
Texture2D g_Tex : register(t0);
SamplerState g_SamLinear : register(s0);
//利用的是混合纹理,那么需要两个纹理资源和采样器
Texture2D g_Tex1 : register(t1);//以索引为1起始槽
SamplerState g_SamLinear1 : register(s1);以索引为1起始槽
cbuffer VSConstantBuffer : register(b0)
{
matrix g_World;
matrix g_View;
matrix g_Proj;
matrix g_WorldInvTranspose;
matrix g_RotationTex; //这里声明一个旋转,用于纹理旋转
}
Basic_3D_VS.hlsl
#include "Basic.hlsli"
// 顶点着色器(3D)
VertexPosHWNormalTex VS(VertexPosNormalTex vIn)
{
VertexPosHWNormalTex vOut;
matrix viewProj = mul(g_View, g_Proj);
float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);
vOut.PosH = mul(posW, viewProj);
vOut.PosW = posW.xyz;
vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);
// vOut.Tex = vIn.Tex;//这是原来的,纹理不变化
vOut.Tex = mul(float4(vIn.Tex, 0.0f,1.0f), g_RotationTex);//将纹理顶点实现旋转,达到旋转目的
return vOut;
}
Basic_3D_PS.hlsl
#include "Basic.hlsli"
//Texture2DArray gTexArry : register(t0);
// 像素着色器(2D)
float4 PS(VertexPosHTex pIn) : SV_Target
{
//通过分量乘法实现,这里是两个纹理的混合
//color = sampler1.color×scalar + sampler2.color×(1-scalar)
return g_Tex.Sample(g_SamLinear, pIn.Tex) * g_Tex1.Sample(g_SamLinear1,pIn.Tex);
}
-
来看看外面是怎么实现的
-
作业一
-
- 1、改变木盒纹理,让他可以存入更多纹理。
-
-
ComPtr<ID3D11ShaderResourceView> m_pWoodCrate[6]; // 木盒纹理
-
2、初始化木箱6个面的纹理
-
HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\one.dds", nullptr, m_pWoodCrate[0].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\two.dds", nullptr, m_pWoodCrate[1].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\three.dds", nullptr, m_pWoodCrate[2].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\four.dds", nullptr, m_pWoodCrate[3].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\five.dds", nullptr, m_pWoodCrate[4].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\six.dds", nullptr, m_pWoodCrate[5].GetAddressOf()));
-
-
3、
-
// 键盘切换模式 if (m_KeyboardTracker.IsKeyPressed(Keyboard::D1) && m_CurrMode != ShowMode::WoodCrate) { // 播放木箱动画 m_CurrMode = ShowMode::WoodCrate; //绑定顶点布局 m_pd3dImmediateContext->IASetInputLayout(m_pVertexLayout3D.Get()); //创建数据 auto meshData = Geometry::CreateBox(); //初始化了正方体基本资源 ResetMesh(meshData); //绑定顶点着色器 m_pd3dImmediateContext->VSSetShader(m_pVertexShader3D.Get(), nullptr, 0); //绑定显示着色器 m_pd3dImmediateContext->PSSetShader(m_pPixelShader3D.Get(), nullptr, 0); //这里就不需要拿到纹理资源了,会在绘制那里实现 //m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate[0].GetAddressOf()); }
-
4、绘制几何模型,每一个面的资源视图都不一样
-
// 绘制几何模型 if (m_CurrMode == ShowMode::WoodCrate) { //这里,上面不是说存在两个纹理混合吗,而以索引为1起始槽存放的是上面第二个图片的纹理 //不处理的话,是一直在那里的 //就会和我现在以索引为0起始槽的纹理混合 //所有重新改变纹理,白色纹理(1,1,1,1)混合不影响原理的 //可能还有其他方法,我现在暂时用这个 m_pd3dImmediateContext->PSSetShaderResources(1, 1, m_pWoodCrate1[1].GetAddressOf());//因为混合问题,需要一个白色背景 //因为这个是正方体,上面绘制的时候是不是有36个索引值 // 索引数组 //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[0].GetAddressOf()); //绘制(索引数量为6,始为0,0) m_pd3dImmediateContext->DrawIndexed(m_IndexCount/6, 0, 0); m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate[1].GetAddressOf()); //绘制(索引数量为6,始为6,0) m_pd3dImmediateContext->DrawIndexed(m_IndexCount / 6, (m_IndexCount / 6)*1, 0); m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate[2].GetAddressOf()); //绘制(索引数量为6,始为12,0) m_pd3dImmediateContext->DrawIndexed(m_IndexCount / 6, (m_IndexCount / 6) * 2, 0); m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate[3].GetAddressOf()); //绘制(索引数量为6,始为18,0) m_pd3dImmediateContext->DrawIndexed(m_IndexCount / 6, (m_IndexCount / 6) * 3, 0); m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate[4].GetAddressOf()); //绘制(索引数量为6,始为24,0) m_pd3dImmediateContext->DrawIndexed(m_IndexCount / 6, (m_IndexCount / 6) * 4, 0); m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate[5].GetAddressOf()); //绘制(索引数量为6,始为30,0) m_pd3dImmediateContext->DrawIndexed(m_IndexCount / 6, (m_IndexCount / 6) * 5, 0); }
-
-
作业二(还没有实现,有大概思路)(所以不改动原来的代码)
-
-
对HLSL改动(就gTexIndex这个索引值做不出来)
-
#include "LightHelper.hlsli" Texture2D g_Tex : register(t0); Texture2DArray g_TexArray : register(t1); SamplerState g_SamLinear : register(s0); // 像素着色器(2D) float4 PS(VertexPosHTex pIn) : SV_Target { return g_TexArray.Sample(g_SamLinear, float3(pIn.Tex,gTexIndex)); //return g_Tex.Sample(g_SamLinear, pIn.Tex); }
-
初始化纹理(好像要本图片都转成dds格式,太多…)
-
public: GameApp(HINSTANCE hInstance); ~GameApp(); bool Init(); void OnResize(); void UpdateScene(float dt); void DrawScene(); std::vector<std::wstring> FileName; // 初始化火焰纹理 FileName.push_back(L"..\\Texture\\WoodCrate.dds"); //FileName.push_back(L"..\\Texture\\Fire002"); FileName.push_back(L"..\\Texture\\003.dds"); //FileName.push_back(L"..\\Texture\\004.dds"); FileName.push_back(L"..\\Texture\\005.dds"); //FileName.push_back(L"..\\Texture\\Fire006"); //FileName.push_back(L"..\\Texture\\F007"); HR(CreateTexture2DArrayFromFile(m_pd3dDevice.Get(), m_pd3dImmediateContext.Get(), FileName,nullptr,m_pFireAnims.GetAddressOf(), false));
else { m_pd3dImmediateContext->PSSetShaderResources(1, 1, m_pWoodCrate1[1].GetAddressOf());//因为混合问题,需要一个白色背景 m_pd3dImmediateContext->DrawIndexed(m_IndexCount, 0, 0); }
-
-
作业3
-
-
1、创建一个新的木盒纹理(存放3张图)
-
ComPtr<ID3D11ShaderResourceView> m_pWoodCrate1[3]; //同时声明一下 struct VSConstantBuffer { DirectX::XMMATRIX world; DirectX::XMMATRIX view; DirectX::XMMATRIX proj; DirectX::XMMATRIX worldInvTranspose; DirectX::XMMATRIX texMat; //定义旋转矩阵 };
-
2、对HLSL的改动(上面)
-
3、初始化纹理
-
HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\flarealpha.dds", nullptr, m_pWoodCrate1[2].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\flare.dds", nullptr, m_pWoodCrate1[0].GetAddressOf())); HR(CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"..\\Tex\\s.dds", nullptr, m_pWoodCrate1[1].GetAddressOf())); //混合问题,需要一个白色纹理,处理第一个和第二个作业
-
4、改变寻址模式
-
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;// U方向寻址模式 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;// V方向寻址模式 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;// W方向寻址模式
-
5、绘制几何模型
-
else if (m_CurrMode == ShowMode::WoodCrate1) { //HLSL中的两个采样器,这里绑定一下 m_pd3dImmediateContext->PSSetSamplers(0, 1, m_pSamplerState.GetAddressOf()); m_pd3dImmediateContext->PSSetSamplers(1, 1, m_pSamplerState.GetAddressOf()); //HLSL里对应regisgter(t0)的g_Tex存放的就是m_pWoodCrate1[0]的纹理了。 //HLSL里对应regisgter(t1)的g_Tex存放的就是m_pWoodCrate1[2]的纹理了。 m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate1[0].GetAddressOf()); m_pd3dImmediateContext->PSSetShaderResources(1, 1, m_pWoodCrate1[2].GetAddressOf()); //m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate1[0].GetAddressOf()); //绘制,数量是m_IndexCount(模板) m_pd3dImmediateContext->DrawIndexed(m_IndexCount, 0, 0); }
-
6、旋转(这个是纹理旋转)
-
static float phi1 = 0.0f; phi1 += 0.001f; //位置偏移,所以先移动再旋转,然后移回来 //因为上面数据那里的纹理,是不是在(0,0)(1,0)(0,1)(1,1) //中心是(0,0,0),不移动的话就这样以中心旋转,你可以想象一下是什么什么 XMMATRIX texMat = XMMatrixTranslation(-0.5f, -0.5f, 0.0f) * XMMatrixRotationZ(phi1) * XMMatrixTranslation(0.5f, 0.5f, 0.0f); //这个数据会被放入常量缓冲器 m_VSConstantBuffer.texMat = XMMatrixTranspose(texMat); //纹理旋转
-
-
作业一和作业三正方体旋转
-
-
static float phi = 0.0f, theta = 0.0f; phi += 0.0001f, theta += 0.00015f; XMMATRIX W = XMMatrixRotationX(phi) * XMMatrixRotationY(theta); m_VSConstantBuffer.world = XMMatrixTranspose(W); m_VSConstantBuffer.worldInvTranspose = XMMatrixTranspose(InverseTranspose(W));
-
7、键盘切换灯光类型
-
//键盘切换灯光类型 if (m_KeyboardTracker.IsKeyPressed(Keyboard::W)) { if (m_PSConstantBuffer.numPointLight == 1) m_PSConstantBuffer.numPointLight = 0; else m_PSConstantBuffer.numPointLight = 1; D3D11_MAPPED_SUBRESOURCE mappedData; HR(m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData)); memcpy_s(mappedData.pData, sizeof(PSConstantBuffer), &m_PSConstantBuffer, sizeof(PSConstantBuffer)); m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0);//更新一下常量缓冲区 }
-
-
绘制Direct2D部分
-
-
if (m_pd2dRenderTarget != nullptr) { m_pd2dRenderTarget->BeginDraw(); static const WCHAR* textStr = L"切换显示: 1-木箱(3D) 2-火焰(2D) 3-旋转木箱(3D)\n" L" W-关灯\n" L" 按住Q-鼠标"; m_pd2dRenderTarget->DrawTextW(textStr, (UINT)wcslen(textStr), m_pTextFormat.Get(), D2D1_RECT_F{ 0.0f, 0.0f, 600.0f, 200.0f }, m_pColorBrush.Get()); static const WCHAR* textStrName = L"2022********第三次小组培训\n" L"姓名:***\n" L"作业完成日期:2022年04月08日\n"; m_pd2dRenderTarget->DrawTextW(textStrName, (UINT)wcslen(textStrName), m_pTextFormat.Get(), D2D1_RECT_F{ 950.0f, 0.0f, 2800.0f, 2800.0f }, m_pColorBrush.Get()); HR(m_pd2dRenderTarget->EndDraw()); }
-
思考&小结
- 这次作业可能是真的让我有点崩溃了,同时也是这次作业让我知道了基础不扎实的问题,怎么说呢,这次作业遇见了许多的问题,问题的出现,才知道自己的知识点是多么的薄弱,这也是没办法的事了。怎么办呢,只能继续走下去,同时有时间一定回头仔细看看,好好巩固一下自己的知识点。
- 需要出现的问题多,但是呢,同时也给我带来了更多的知识点。这样说吧,出现问题,然后拼命的想着去解决,虽然是会很累很累,但是在解决问题的同时,也学会了很多,不是吗?所谓一分耕耘一分收获嘛。虽然遗憾的是这次作业不能很好的完成,我也尽力,真的!这次作业就让我彻彻底底的明白,什么是坚持下去了,真的有那么一刻是想放弃的,放弃了就可以好好休息,可是我当初是为了什么进来的,怎么说放弃就放弃了呢,还是努力,继续加油!
- 对于下周的计划呢…就是按作业的来,要什么就学什么。数据结构,DX11,一步步来,打好基础非常重要!