美文网首页
【DirectX12笔记】 第9章 纹理贴图

【DirectX12笔记】 第9章 纹理贴图

作者: crossous | 来源:发表于2019-07-27 15:34 被阅读0次

前言

  这章看起来异常的快,大概是因为原理和opengl相差不大。我们先做下比较。

对比

  无论是纹理本身还是采样器,都可以看做是Shader中的常量缓冲区。当然,Opengl和DirectX还是有一定区别的。
  在opengl中,我们都是在DrawCall前,将纹理灌入到内存(或显存)中得到地址,然后指定采样器如何采样(包括UV过滤,采样方法等),这些都是在CPU中进行的,然后将地址传入到Uniform中,在glsl中直接就是唯一的采样器的类型,如果想在一次呈现中用不同的采样方法,只能才DrawCall后进行。
  而在DirectX中,采样器作为一个对象,我们可以定义很多个,纹理和opengl一样,都是作为Shader资源,当DrawCall时,我们可以传入很多个采样器,渲染过程中可以根据需要换采样器,从而在一次DrawCall时做出不同的采样方法。

程序

  想要渲染纹理,首先要改变的是输入布局,毕竟我们要UV坐标才能进行采样(327页):

//void TexWavesApp::BuildShadersAndInputLayout()
mShaders["standardVS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", nullptr, "VS", "vs_5_0");
mShaders["opaquePS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", nullptr, "PS", "ps_5_0");
    
mInputLayout =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};

  我们需要加载图像文件,关于DDS格式文件介绍见328页,加载图片见330页,调用相关方法即可。
  同样作为抽象,我们定义了纹理类:

struct Texture
{
    std::string Name;
    std::wstring Filename;

    Microsoft::WRL::ComPtr<ID3D12Resource> Resource = nullptr;
    Microsoft::WRL::ComPtr<ID3D12Resource> UploadHeap = nullptr;
};

  一个着色器资源,上传堆用来上传数据。Texture被App的mTextures记录着,Material类中记录着引用纹理在堆中的索引,RenderItem通过引用Material间接得到纹理,同时还增加了一个纹理变换的成员:

XMFLOAT4X4 TexTransform = MathHelper::Identity4x4();

  我们看hlsl中的常量缓冲区,采样器和纹理都属于常量缓冲区:

Texture2D    gDiffuseMap : register(t0);//纹理
SamplerState gsamPointWrap        : register(s0);//采样器

  相应的,根描述符也需要改变,书中用描述符表,所以要为其创建描述符堆和视图,CrateApp.cpp中的BuildDescriptorHeaps方法如下:

void CrateApp::BuildDescriptorHeaps()
{
    D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};//Shader资源视图描述符
    srvHeapDesc.NumDescriptors = 1;
    srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));//创建堆


    CD3DX12_CPU_DESCRIPTOR_HANDLE hDescriptor(mSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());

    auto woodCrateTex = mTextures["woodCrateTex"]->Resource;
 
    D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;//涉及到分量互换,填这个就好
    srvDesc.Format = woodCrateTex->GetDesc().Format;//视图格式
    srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;//维度
    srvDesc.Texture2D.MostDetailedMip = 0;//[0, MipLevels-1]
    srvDesc.Texture2D.MipLevels = woodCrateTex->GetDesc().MipLevels;
    srvDesc.Texture2D.ResourceMinLODClamp = 0.0f;//最小层级

    md3dDevice->CreateShaderResourceView(woodCrateTex.Get(), &srvDesc, hDescriptor);//创建视图
}

  仔细看看,有点不对劲啊,常量缓冲区多了俩,为何只创建了一个材质的,采样器去哪了?
  首先,在342页有比较详细的采样器创建方法,和其他常量缓冲区的根参数一样,描述符参照此页的D3D12_SAMPLER_DESC即可。
  事例中创建的方法略有不同,我们对采样器的功能往往不会要求太多,所以有了静态采样器一说(见344页)。事例中就是这种方法,提前预备好了6种静态采样器,这样功能或许没有之前的强大,但足够我们用了。书中还表示,用户只能定义2032个静态采样器……我是想不出有什么场合会用到这么多采样器。
  其实之前就提到过,在创建根签名描述符时,就有参数:

std::array<const CD3DX12_STATIC_SAMPLER_DESC, 6> CrateApp::GetStaticSamplers()
{
//创建6个静态采样器,代码在344页
    const CD3DX12_STATIC_SAMPLER_DESC pointWrap(
        0, // shaderRegister
        D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
        D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressU
        D3D12_TEXTURE_ADDRESS_MODE_WRAP,  // addressV
        D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW

//......

    return { 
        pointWrap, pointClamp,
        linearWrap, linearClamp, 
        anisotropicWrap, anisotropicClamp };
}


//void CrateApp::BuildRootSignature()方法内
auto staticSamplers = GetStaticSamplers();

CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4, slotRootParameter,
    (UINT)staticSamplers.size(), staticSamplers.data(),//传入静态采样器
    D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

  曾经这两个参数传入的是0和nullptr,是因为我们还没用到过采样器,现在我们学会了创建方法,即可传入静态采样器。
  然后就是hlsl中采样的方法,在像素着色器中发生了一些小的改变:

float4 PS(VertexOut pin) : SV_Target
{
//采样后,与反射率相乘
    float4 diffuseAlbedo = gDiffuseMap.Sample(gsamLinear, pin.TexC) * gDiffuseAlbedo;

    pin.NormalW = normalize(pin.NormalW);
    float3 toEyeW = normalize(gEyePosW - pin.PosW);
    float4 ambient = gAmbientLight*diffuseAlbedo;

    const float shininess = 1.0f - gRoughness;
    Material mat = { diffuseAlbedo, gFresnelR0, shininess };//构建一个新的mat(材质)再传入光照计算
    float3 shadowFactor = 1.0f;
    float4 directLight = ComputeLighting(gLights, mat, pin.PosW,
        pin.NormalW, toEyeW, shadowFactor);

    float4 litColor = ambient + directLight;

    // Common convention to take alpha from diffuse material.
    litColor.a = diffuseAlbedo.a;

    return litColor;
}

  这样一来,板条箱的程序(Crate)完毕,而海岛程序涉及到,如何让本来平铺的贴图变小,并且让贴图重复采样,使地面看起来更精细,如何让海面的UV随着时间流动。
  这还要归功于渲染项以及常量缓冲区增加的TexTransform,渲染项中,将其设定为放大5,5,1的Scale矩阵,这样岛的贴图会更精细,而每帧更新的Update中调用了AnimateMaterials方法,让海面的矩阵设定为Translate矩阵,并且根据时间改变(见355页)。
  总结一下使用顺序:
1.在LoadTextures方法中读取并初始化Texture,将所有Texture存到成员mTextures
2.因为是常量缓冲且用描述符表,所以要创建堆,在方法BuildDescriptorHeaps中,有几个纹理就创建几个
3.同样在这个方法中,要从成员mTextures中取出Texture,填写描述符结构体,创建SRV视图且将材质塞入
4.使用时,先找到堆的句柄(首位置),然后偏移到材质位置,之后用设置描述符表的方法cmdList->SetGraphicsRootDescriptorTable(0, tex);,即可将材质设为常量缓冲区。

相关文章

  • 【DirectX12笔记】 第9章 纹理贴图

    前言   这章看起来异常的快,大概是因为原理和opengl相差不大。我们先做下比较。 对比   无论是纹理本身还是...

  • Agisoft PhotoScan-相关应用-使用xNormal

    使用xNormal烘焙环境遮挡纹理贴图 本教程演示如何使用xNormal为纹理模型计算环境遮挡贴图。 模型必须纹理...

  • 《Unity Shader入门精要》| 读书笔记(三)

    继续来看纹理! 第7章 基础纹理 普通纹理 纹理映射:用一张纹理贴图来代替物体的漫反射颜色,漫反射颜色类似于绘画里...

  • 008-基础纹理下篇

    Mip贴图 Mip贴图是一种强大的纹理技巧,它不仅可以提高渲染性能,而且可以改善场景的显示质量.它使用标准纹理贴图...

  • 09-OpenGL 纹理基础(下) - Mip贴图

    Mip贴图(多级渐远纹理) 1. 什么是Mip贴图? Mip贴图是一种功能强大的纹理技巧。他可以提高渲染性能同时可...

  • OpenGL ES for Android (立方体贴图)

    简介 我们前面学过纹理贴图,我们可以用6个2D纹理的纹理构建成立方体,而立方体贴图本身就是包含6个2D纹理的纹理,...

  • 七.OpenGL Mip贴图 压缩

    纹理Mip贴图 Mip贴图是一种强大的纹理技巧,它不仅可以提高渲染性能,而且可以改善场景的显示质量.它使用标准纹理...

  • Shader操作uv实现岩浆和河流效果

    介绍 纹理贴图移动特效产生岩浆、瀑布效果实现原理是改变动态改变纹理坐标uv的值,使之移动 修改UV值来实现纹理贴图...

  • 8-3.Mip贴图

    Mip贴图 Mip 贴图是一种功能强大的纹理技巧,它不仅 Mip 贴图由一些列纹理图像组成,每个图像大小在某个轴的...

  • OpenGL纹理常用API

    读取纹理 载⼊入纹理 其他纹理操作 纹理对象 设置纹理参数 关于贴图方式(GL_TEXTURE_WRAP_S、GL...

网友评论

      本文标题:【DirectX12笔记】 第9章 纹理贴图

      本文链接:https://www.haomeiwen.com/subject/apherctx.html