d3d12龙书学习之MiniEngine的最小化实现(五) 龙书第9章 纹理贴图

    xiaoxiao2023-11-27  156

    文章目录

    工程结构优化MiniEngine的纹理操作类添加shapes所用的纹理添加水体所用的纹理总结

    工程结构优化

    在MiniEngine项目中,核心文件都放在Core文件夹下,显得文件很多,阅读起来可能不是很方便。这里稍微做一下整理。根据功能分好文件夹,对应VS中已经分好的文件夹。

    MiniEngine的纹理操作类

    在MiniEngine中提供了非常方便的纹理类,TextureManager。简单阅读一下,使用起来也是非常的简单

    // 指定纹理文件夹的相对路径 TextureManager::Initialize(L"Textures/"); // 定义一个MangedTexture的指针数组,这里里面存放读取到的纹理 const ManagedTexture* MatTextures[3] = {}; MatTextures[0] = TextureManager::LoadFromFile(L"bricks", true); MatTextures[1] = TextureManager::LoadFromFile(L"stone", true); MatTextures[2] = TextureManager::LoadFromFile(L"tile", true); // 这里就取出来了对应纹理的SRV MatTextures[0]->GetSRV();

    可以简单的查看下纹理创建的部分,发现在FindOrLoadTexture中new了ManagedTexture对象,保存在s_TextureCache中。 而s_TextureCache的定义map< wstring, unique_ptr > s_TextureCache; 采用unique_ptr指针管理ManagedTexture。 调用TextureManager::Shutdown() 即可释放对象。

    这里会管理所有载入的纹理。问题就是没有提供比较只能的释放系统,只能Shutdown整体释放,直接拿来制作游戏会不够方便。未来要优化一下这个释放的机制。

    Shutdown按照原先工程就在Graphics::Shutdow写入就好了。

    添加相关联的其余文件,文件列表

    FileUtility.h FileUtility.cpp dds.h DDSTextureLoader.h DDSTextureLoader.cpp TextureManager.h TextureManager.cpp

    这里还需要注意一点,我这里采用了跟MiniEngine一样的方法,通过NuGet添加了zlib的库。 右键点击项目,选NuGet,然后搜索zlib添加就可以了。我也把对应的代码做了上传。 NuGet就相当于nodejs中的npm,还是比较方便的。

    这种管理库的方式还是很现代的,可以发现项目目录下多了一个‘packages.config’的文件,打开,里面就是本项目所包含的库。 可以很方便的通过NuGet加载。不过我这里把加载好的库也做了上传。

    添加shapes所用的纹理

    接下来就是参照d3d12book第九章的代码,开始搬运了。 首先把shader修改下。

    cbuffer VSConstants : register(b0) { float4x4 modelToWorld; float4x4 gTexTransform; // add 顶点纹理到目标纹理的转换矩阵。 比如传入一个缩放矩阵,就可以把定点对应的纹理坐标缩放 }; cbuffer PassConstants : register(b1) { float4x4 gViewProj; }; struct VertexIn { float3 PosL : POSITION; float3 NormalL : NORMAL; float2 TexC : TEXCOORD; // add 该顶点的纹理坐标 }; struct VertexOut { float4 PosH : SV_POSITION; float3 PosW : POSITION; float3 NormalW : NORMAL; float2 TexC : TEXCOORD; // 传给ps shader的最终纹理坐标 }; VertexOut main(VertexIn vin) { VertexOut vout = (VertexOut)0.0f; // 把顶点转换到世界坐标系 float4 posW = mul(float4(vin.PosL, 1.0f), modelToWorld); vout.PosW = posW.xyz; // 法向量转换到世界坐标系 vout.NormalW = mul(vin.NormalL, (float3x3)modelToWorld); // 顶点转换到投影坐标系 vout.PosH = mul(posW, gViewProj); // 直接返回纹理 float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform); // add 计算纹理坐标 vout.TexC = texC).xy; // add 取纹理坐标的x和y返回给ps shader。2d纹理只有xy有意义,对应tu和tv return vout; }

    ps shader参照d3d12book的代码改下就可以了,就是添加了一个纹理,我这里放在了t0槽中

    Texture2D gDiffuseMap : register(t0); // add 纹理 float4 main(VertexOut pin) : SV_Target0 { // 这里直接调用函数取出纹理坐标所对应的纹理,也就是像素值 float4 diffuseAlbedo = gDiffuseMap.Sample(gsamAnisotropicWrap, pin.TexC) * gDiffuseAlbedo; ... float4 ambient = gAmbientLight * diffuseAlbedo; // add 这里gDiffuseAlbed->diffuseAlbedo ... Material mat = { diffuseAlbedo, gFresnelR0, shininess }; // add 这里gDiffuseAlbed->diffuseAlbedo ... litColor.a = diffuseAlbedo.a; // add 这里gDiffuseAlbed->diffuseAlbedo return litColor; }

    修改我们的renderItem

    struct renderItem { Matrix4 modelToWorld; Matrix4 texTransform; int indexCount; int startIndex; int baseVertex; XMFLOAT4 diffuseAlbedo; XMFLOAT3 fresnelR0; float roughness; D3D12_CPU_DESCRIPTOR_HANDLE srv; //add 该渲染目标对应的纹理视图 };

    修改定点结构体

    // 顶点结构体 struct Vertex { XMFLOAT3 Pos; XMFLOAT3 Normal; XMFLOAT2 TexC; // add 该顶点的纹理坐标 };

    修改传入shader的常量缓冲区。这里跟d3d12book的不一致,我放在了常量缓冲区的0槽中。

    __declspec(align(16)) struct ObjectConstants { Matrix4 World = Matrix4(kIdentity); // 把物体从模型坐标转换到世界坐标 Matrix4 texTransform = Matrix4(kIdentity); //add 该顶点所用纹理的转换矩阵 };

    填充对应结构体新加成员的值。修改根签名等等。这里就不说了,直接看下效果。 对应的github: https://github.com/mversace/DirectX12-MiniEngine-Dragon/tree/57f5cdb95b73bb30204c312beac5da22762a18e6

    添加水体所用的纹理

    这里方式跟上边一样的。照着做就可以了 唯一需要稍微提下的是,这里实现了水体的移动。这个移动是通过传入shader的gMatTransform矩阵实现的。

    这个矩阵用于纹理的最后转换。这个矩阵的操作是在AnimateMaterials函数中。 因为我们用的是Matrix4类型,这里跟书中有些不太一样。稍微注意下

    void GameApp::AnimateMaterials(float deltaT) { // Scroll the water material texture coordinates. // 获取矩阵的第4行的0,1位置。这俩位置代表的是移动矩阵的x和y坐标。 float tu = (float)m_renderItemWaves.matTransform.GetW().GetX(); float tv = (float)m_renderItemWaves.matTransform.GetW().GetY(); // 随时间增长 tu += 0.1f * deltaT; tv += 0.02f * deltaT; if (tu >= 1.0f) tu -= 1.0f; if (tv >= 1.0f) tv -= 1.0f; // 赋值回去 m_renderItemWaves.matTransform.SetW({ tu, tv, 0.0f, 1.0f }); }

    在传入该矩阵时注意,还需要转置以下

    // 设置常量缓冲区数据 ObjectConstants obc; obc.World = m_renderItemWaves.modelToWorld; obc.texTransform = m_renderItemWaves.texTransform; obc.matTransform = Transpose(m_renderItemWaves.matTransform); // 传入纹理移动的转置矩阵 gfxContext.SetDynamicConstantBufferView(0, sizeof(obc), &obc);

    那么在vs shader中,最终纹理乘以这个矩阵,实现了纹理的随时间移动。至于纹理离开了[0.0f, 1.0f]范围会怎么样,书中写的很详细了。 看下最终结果:

    github: https://github.com/mversace/DirectX12-MiniEngine-Dragon/tree/5c54a00834d00b086fa66f31e2716f7083dfb114

    总结

    纹理这部分本身还是很简单的。只是载入,存入默认缓冲区,不同的目标指定对应的纹理,顶点指定对应的纹理坐标,shader中读取纹理并取出对应的颜色值就可以了。

    最新回复(0)