d3d12龙书学习之MiniEngine的最小化实现(十一) 龙书第15章 第一人称摄像机 动态索引

第一人称摄像机

我们原来的摄像机主要是通过鼠标来旋转、放大图象。旋转用的是比较符合观察的那种。
本章呢要做这个第一人称视角的摄像机。实际上就是根据玩家的操作,来不断调整摄像机的位置以及朝向。
整体来说没什么难度。

在MiniEngine中有一个CameraController类,这个类本身实现的就是第一人称视角摄像机
来看下调用方式:

// 初始化
m_Camera.SetEyeAtUp({ 0.0f, 2.0f, -15.0f }, { 0.0f, 2.0f, 0.0f }, Math::Vector3(Math::kYUnitVector));
m_Camera.SetZRange(1.0f, 10000.0f);
m_CameraController.reset(new GameCore::CameraController(m_Camera, Math::Vector3(Math::kYUnitVector)));

// 在场景的update时
m_CameraController->Update(deltaT);

// 然后m_Camera中的参数就是最新的了

对于类:CameraController来说,本身实现的是右手坐标系的。我们camera已经改过了。这次把摄像机控制也一起改下。同时修改一下移动的速度

CameraController::CameraController( Camera& camera, Vector3 worldUp ) : m_TargetCamera( camera )
{
    m_WorldUp = Normalize(worldUp);
    m_WorldNorth = Normalize(Cross(Vector3(kXUnitVector), m_WorldUp));		// 修改为左手坐标系
    m_WorldEast = Cross(m_WorldUp, m_WorldNorth);		// 修改为左手坐标系

    m_HorizontalLookSensitivity = 2.0f;
    m_VerticalLookSensitivity = 2.0f;
    m_MoveSpeed = 100.0f;		// 移动速度
    m_StrafeSpeed = 100.0f;	// 转向速度
    m_MouseSensitivityX = 1.0f;
    m_MouseSensitivityY = 1.0f;

    m_CurrentPitch = Sin(Dot(camera.GetForwardVec(), m_WorldUp));

    Vector3 forward = Normalize(Cross(camera.GetRightVec(), m_WorldUp));		// 修改为左手坐标系
    m_CurrentHeading = ATan2(-Dot(forward, m_WorldEast), Dot(forward, m_WorldNorth));

    m_FineMovement = true;
    m_FineRotation = true;
    m_Momentum = true;

    m_LastYaw = 0.0f;
    m_LastPitch = 0.0f;
    m_LastForward = 0.0f;
    m_LastStrafe = 0.0f;
    m_LastAscent = 0.0f;
}

屏蔽掉以前在update中做的摄像机代码就可以了。至于这个控制器中的其他参数,可以自行研究下。

动态索引

动态索引这个,主要就是提高效率。
当渲染目标很多时,按照我们现在做法,每一个渲染目标自身有3个特殊的缓冲区需要设置

  1. 转换矩阵
  2. 纹理SRV
  3. 材质

这个动态索引的方式是,在每一次渲染的时候,只设置一次纹理以及材质参数。然后通过渲染目标中设置一个索引来取出自身对应的纹理信息。

整体来说也比较简单。

  1. 先设置两个结构来存储所有的纹理SRV以及材质信息。
    // 存储所有的纹理参数
    StructuredBuffer m_mats;
    // 存储所有的纹理资源
    D3D12_CPU_DESCRIPTOR_HANDLE m_srvs[4];
    
  2. 设置对应的根签名
    // 创建根签名
    m_RootSignature.Reset(4, 1);
    m_RootSignature.InitStaticSampler(0, Graphics::SamplerLinearWrapDesc);
    m_RootSignature[0].InitAsConstantBuffer(0);
    m_RootSignature[1].InitAsConstantBuffer(1);
    m_RootSignature[2].InitAsBufferSRV(0);		// 存储材质信息
    m_RootSignature[3].InitAsDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 4); // 存储本项目中的4个纹理
    m_RootSignature.Finalize(L"15 RS", D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    
  3. shader的入参就不再细说了,shader直接看代码就好
  4. 在每一帧绘制时,设置所有材质和纹理信息
    // 设置全部的纹理参数
    gfxContext.SetBufferSRV(2, m_mats);
    
    // 设置全部的纹理资源
    gfxContext.SetDynamicDescriptors(3, 0, 4, &m_srvs[0]);
    
    gfxContext.SetPipelineState(m_mapPSO[E_EPT_DEFAULT]);
    drawRenderItems(gfxContext, m_vecRenderItems[(int)RenderLayer::Opaque]);
    

MiniEngine中的InitAsDescriptorRange

这个设置根描述符参数的函数,第二个参数填入的是槽id,这里需要注意下

m_RootSignature[0].InitAsDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 4)

此时占用的实际是t0-t3,所以如果想再设置一个最少要从4开始。否则会报错。例如:

m_RootSignature[0].InitAsDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 4);	// 占据t0-t3
m_RootSignature[1].InitAsDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 4, 4);    // 占据t4-t7
m_RootSignature[2].InitAsDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 7, 1);    // t7已经被占用,根签名创建会报错

简单对比下采用动态索引和非动态索引的帧数变化

实际变化很小,因为我们的渲染目标很少
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值