原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引

代码工程地址:

https://github.com/jiabaodan/Direct12BookReadingNotes



学习目标

  1. 回顾视景坐标系变换的数学算法;
  2. 熟悉第一人称摄像机的功能;
  3. 实现第一人称摄像机;
  4. 理解如何动态索引一组纹理。


1 回顾视景坐标系变换



如果QW = (Qx, Qy, Qz, 1), uW = (ux, uy, uz, 0), vW = (vx, vy, vz, 0)并且wW = (wx, wy, wz, 0)。根据第三章4.3节,我们可以知道从视景坐标系变化到世界坐标系的变换矩阵为:



根据第三章4.5节,我们需要的是它的逆矩阵。因为世界坐标系和视景坐标系只变换位置和旋转,所以:





所以视景坐标系变换矩阵为:



2 摄像机类

为了封装摄像机相关的代码,我们封装和实现了一个Camera类。它的数据主要分为下面几类:位置,右向,向上的向量和看向的向量;原点,视景坐标系在世界坐标系下的xyz轴:

  1. class Camera
  2. {
  3. public:
  4. Camera();
  5. ˜Camera();
  6. // Get/Set world camera position.
  7. DirectX::XMVECTOR GetPosition()const;
  8. DirectX::XMFLOAT3 GetPosition3f()const;
  9. void SetPosition(float x, float y, float z);
  10. void SetPosition(const DirectX::XMFLOAT3& v);
  11. // Get camera basis vectors.
  12. DirectX::XMVECTOR GetRight()const;
  13. DirectX::XMFLOAT3 GetRight3f()const;
  14. DirectX::XMVECTOR GetUp()const;
  15. DirectX::XMFLOAT3 GetUp3f()const;
  16. DirectX::XMVECTOR GetLook()const;
  17. DirectX::XMFLOAT3 GetLook3f()const;
  18. // Get frustum properties.
  19. float GetNearZ()const;
  20. float GetFarZ()const;
  21. float GetAspect()const;
  22. float GetFovY()const;
  23. float GetFovX()const;
  24. // Get near and far plane dimensions in view space coordinates.
  25. float GetNearWindowWidth()const;
  26. float GetNearWindowHeight()const;
  27. float GetFarWindowWidth()const;
  28. float GetFarWindowHeight()const;
  29. // Set frustum.
  30. void SetLens(float fovY, float aspect, float zn, float zf);
  31. // Define camera space via LookAt parameters.
  32. void LookAt(DirectX::FXMVECTOR pos,
  33. DirectX::FXMVECTOR target,
  34. DirectX::FXMVECTOR worldUp);
  35. void LookAt(const DirectX::XMFLOAT3& pos,
  36. const DirectX::XMFLOAT3& target,
  37. const DirectX::XMFLOAT3& up);
  38. // Get View/Proj matrices.
  39. DirectX::XMMATRIX GetView()const;
  40. DirectX::XMMATRIX GetProj()const;
  41. DirectX::XMFLOAT4X4 GetView4x4f()const;
  42. DirectX::XMFLOAT4X4 GetProj4x4f()const;
  43. // Strafe/Walk the camera a distance d.
  44. void Strafe(float d);
  45. void Walk(float d);
  46. // Rotate the camera.
  47. void Pitch(float angle);
  48. void RotateY(float angle);
  49. // After modifying camera position/orientation, call to rebuild the view matrix.
  50. void UpdateViewMatrix();
  51. private:
  52. // Camera coordinate system with coordinates relative to world space.
  53. DirectX::XMFLOAT3 mPosition = { 0.0f, 0.0f, 0.0f };
  54. DirectX::XMFLOAT3 mRight = { 1.0f, 0.0f, 0.0f };
  55. DirectX::XMFLOAT3 mUp = { 0.0f, 1.0f, 0.0f };
  56. DirectX::XMFLOAT3 mLook = { 0.0f, 0.0f, 1.0f };
  57. // Cache frustum properties.
  58. float mNearZ = 0.0f;
  59. float mFarZ = 0.0f;
  60. float mAspect = 0.0f;
  61. float mFovY = 0.0f;
  62. float mNearWindowHeight = 0.0f;
  63. float mFarWindowHeight = 0.0f;
  64. bool mViewDirty = true;
  65. // Cache View/Proj matrices.
  66. DirectX::XMFLOAT4X4 mView = MathHelper::Identity4x4();
  67. DirectX::XMFLOAT4X4 mProj = MathHelper::Identity4x4();
  68. };


3 选择一些方法实现


3.1 返回XMVECTOR变量

我们提供了一些返回XMVECTOR变量的方法,这个只是为了方便:

  1. XMVECTOR Camera::GetPosition()const
  2. {
  3. return XMLoadFloat3(&mPosition);
  4. }
  5. XMFLOAT3 Camera::GetPosition3f()const
  6. {
  7. return mPosition;
  8. }

3.2 SetLens

我们通过SetLens函数来设置视锥体:

  1. void Camera::SetLens(float fovY, float aspect, float zn, float zf)
  2. {
  3. // cache properties
  4. mFovY = fovY;
  5. mAspect = aspect;
  6. mNearZ = zn;
  7. mFarZ = zf;
  8. mNearWindowHeight = 2.0f * mNearZ * tanf(0.5f*mFovY );
  9. mFarWindowHeight = 2.0f * mFarZ * tanf(0.5f*mFovY );
  10. XMMATRIX P = XMMatrixPerspectiveFovLH(mFovY, mAspect, mNearZ, mFarZ);
  11. XMStoreFloat4x4(&mProj, P);
  12. }

3.3 通过视锥体派生出来的数据

  1. float Camera::GetFovX()const
  2. {
  3. float halfWidth = 0.5f*GetNearWindowWidth();
  4. return 2.0f*atan(halfWidth / mNearZ);
  5. }
  6. float Camera::GetNearWindowWidth()const
  7. {
  8. return mAspect * mNearWindowHeight;
  9. }
  10. float Camera::GetNearWindowHeight()const
  11. {
  12. return mNearWindowHeight;
  13. }
  14. float Camera::GetFarWindowWidth()const
  15. {
  16. return mAspect * mFarWindowHeight;
  17. }
  18. float Camera::GetFarWindowHeight()const
  19. {
  20. return mFarWindowHeight;
  21. }

3.4 变换摄像机

对于一个第一人称摄像机,如果无视碰撞检测,我们希望:

  1. 向看向的方向前进或者后退;
  2. 左右移动;
  3. 向上下旋转;
  4. 左右旋转。
  1. void Camera::Walk(float d)
  2. {
  3. // mPosition += d*mLook
  4. XMVECTOR s = XMVectorReplicate(d);
  5. XMVECTOR l = XMLoadFloat3(&mLook);
  6. XMVECTOR p = XMLoadFloat3(&mPosition);
  7. XMStoreFloat3(&mPosition, XMVectorMultiplyAdd(s, l, p));
  8. }
  9. void Camera::Strafe(float d)
  10. {
  11. // mPosition += d*mRight
  12. XMVECTOR s = XMVectorReplicate(d);
  13. XMVECTOR r = XMLoadFloat3(&mRight);
  14. XMVECTOR p = XMLoadFloat3(&mPosition);
  15. XMStoreFloat3(&mPosition, XMVectorMultiplyAdd(s, r, p));
  16. }
  17. void Camera::Pitch(float angle)
  18. {
  19. // Rotate up and look vector about the right vector.
  20. XMMATRIX R = XMMatrixRotationAxis(XMLoadFloat3(&mRight), angle);
  21. XMStoreFloat3(&mUp, XMVector3TransformNormal(XMLoadFloat3(&R));
  22. XMStoreFloat3(&mLook, XMVector3TransformNormal(XMLoadFloat3(&mLook), R));
  23. }
  24. void Camera::RotateY(float angle)
  25. {
  26. // Rotate the basis vectors about the world yaxis.
  27. XMMATRIX R = XMMatrixRotationY(angle);
  28. XMStoreFloat3(&mRight, XMVector3TransformNormal(XMLoadFloat3(&R));
  29. XMStoreFloat3(&mUp, XMVector3TransformNormal(XMLoadFloat3(&mUp), R));
  30. XMStoreFloat3(&mLook, XMVector3TransformNormal(XMLoadFloat3(&mLook), R));
  31. }

3.5 创建视景坐标系变换矩阵

UpdateViewMatrix函数的第一部分是重新标准正交化摄像机的向右,向上和看向的向量。因为经过变换后,由于数值问题可能导致它们不再标准正交;第二部分就是计算矩阵:

  1. void Camera::UpdateViewMatrix()
  2. {
  3. if(mViewDirty)
  4. {
  5. XMVECTOR R = XMLoadFloat3(&mRight);
  6. XMVECTOR U = XMLoadFloat3(&mUp);
  7. XMVECTOR L = XMLoadFloat3(&mLook);
  8. XMVECTOR P = XMLoadFloat3(&mPosition);
  9. // Keep camera’s axes orthogonal to each other and of unit length.
  10. L = XMVector3Normalize(L);
  11. U = XMVector3Normalize(XMVector3Cross(L, R));
  12. // U, L already ortho-normal, so no need to normalize cross product.
  13. R = XMVector3Cross(U, L);
  14. // Fill in the view matrix entries.
  15. float x = -XMVectorGetX(XMVector3Dot(P, R));
  16. float y = -XMVectorGetX(XMVector3Dot(P, U));
  17. float z = -XMVectorGetX(XMVector3Dot(P, L));
  18. XMStoreFloat3(&mRight, R);
  19. XMStoreFloat3(&mUp, U);
  20. XMStoreFloat3(&mLook, L);
  21. mView(0, 0) = mRight.x;
  22. mView(1, 0) = mRight.y;
  23. mView(2, 0) = mRight.z;
  24. mView(3, 0) = x;
  25. mView(0, 1) = mUp.x;
  26. mView(1, 1) = mUp.y;
  27. mView(2, 1) = mUp.z;
  28. mView(3, 1) = y;
  29. mView(0, 2) = mLook.x;
  30. mView(1, 2) = mLook.y;
  31. mView(2, 2) = mLook.z;
  32. mView(3, 2) = z;
  33. mView(0, 3) = 0.0f;
  34. mView(1, 3) = 0.0f;
  35. mView(2, 3) = 0.0f;
  36. mView(3, 3) = 1.0f;
  37. mViewDirty = false;
  38. }
  39. }


4 摄像机Demo注释

我们删除以前老的摄像机相关的变量mPhi, mTheta, mRadius, mView, 和mProj,添加新的变量:

  1. Camera mCam;

然后在屏幕尺寸变化的时候,不再直接计算透视矩阵,而是SetLens:

  1. void CameraApp::OnResize()
  2. {
  3. D3DApp::OnResize();
  4. mCamera.SetLens(0.25f*MathHelper::Pi, AspectRatio(), 1.0f, 1000.0f);
  5. }

在UpdateScene方法中:

  1. void CameraApp::UpdateScene(float dt)
  2. {
  3. if( GetAsyncKeyState(‘W’) & 0x8000 )
  4. mCamera.Walk(10.0f*dt);
  5. if( GetAsyncKeyState(‘S’) & 0x8000 )
  6. mCamera.Walk(-10.0f*dt);
  7. if( GetAsyncKeyState(‘A’) & 0x8000 )
  8. mCamera.Strafe(-10.0f*dt);
  9. if( GetAsyncKeyState(‘D’) & 0x8000 )
  10. mCamera.Strafe(10.0f*dt);

在OnMouseMove方法中:

  1. void CameraAndDynamicIndexingApp::OnMouseMove(WPARAM btnState, int x, int y)
  2. {
  3. if( (btnState & MK_LBUTTON) != 0 )
  4. {
  5. // Make each pixel correspond to a quarter of a degree.
  6. float dx = XMConvertToRadians(0.25f*static_cast<float>(x - mLastMousePos.x));
  7. float dy = XMConvertToRadians(0.25f*static_cast<float>(y - mLastMousePos.y));
  8. mCamera.Pitch(dy);
  9. mCamera.RotateY(dx);
  10. }
  11. mLastMousePos.x = x;
  12. mLastMousePos.y = y;
  13. }

最终,视景和透视投影矩阵可以通过摄像机实例访问:

  1. mCamera.UpdateViewMatrix();
  2. XMMATRIX view = mCamera.View();
  3. XMMATRIX proj = mCamera.Proj();



5 动态索引

动态索引的思路非常简单,我们在着色器程序中动态索引一组资源,本Demo中,资源是一组纹理。索引可以通过多种方法定义:

  1. 可以是常量缓冲中的一个元素;
  2. 可以是一个系统ID:SV_PrimitiveID, SV_VertexID, SV_DispatchThreadID, or SV_InstanceID;
  3. 可以是通过计算得到的结果;
  4. 可以是纹理中的值;
  5. 可以是顶点结构中的组件。

下面是一个常量缓冲中的索引例子:

  1. cbuffer cbPerDrawIndex : register(b0)
  2. {
  3. int gDiffuseTexIndex;
  4. };
  5. Texture2D gDiffuseMap[4] : register(t0);
  6. float4 texValue = gDiffuseMap[gDiffuseTexIndex].Sample(gsamLinearWrap, pin.TexC);

对于当前Demo,我们的目标是:最小化我们每帧设置的descriptors的数量。我们设置物体的常量缓冲,材质常量缓冲和和漫反射问题贴图。最小化descriptors可以让我们的根签名更小,这代表每个绘制调用造成更少的性能开销;并且这个技术对实例化技术非常有用(下章讲解),我们的策略如下:

  1. 创建一个结构化缓冲保存所有的材质数据;
  2. 在物体常量缓冲中添加一个MaterialIndex值来指定使用的材质的索引;
  3. 绑定所有SRV descriptors每帧一次(之前每个渲染物体绑定一次);
  4. 在材质数据中添加DiffuseMapIndex值来指定使用的纹理贴图。

根据上面的设置,我们只需要对每个渲染物体设置逐物体的常量缓冲。然后使用MaterialIndex来匹配材质,使用DiffuseMapIndex来匹配纹理。

  1. struct MaterialData
  2. {
  3. DirectX::XMFLOAT4 DiffuseAlbedo = { 1.0f, 1.0f, 1.0f, 1.0f };
  4. DirectX::XMFLOAT3 FresnelR0 = { 0.01f, 0.01f, 0.01f };
  5. float Roughness = 64.0f;
  6. // Used in texture mapping.
  7. DirectX::XMFLOAT4X4 MatTransform = MathHelper::Identity4x4();
  8. UINT DiffuseMapIndex = 0;
  9. UINT MaterialPad0;
  10. UINT MaterialPad1;
  11. UINT MaterialPad2;
  12. };
  13. MaterialBuffer = std::make_unique<UploadBuffer<MaterialData>>(device, materialCount, false);

然后根据着色器,更新根签名:

  1. CD3DX12_DESCRIPTOR_RANGE texTable;
  2. texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 4, 0, 0);
  3. // Root parameter can be a table, root descriptor or root constants.
  4. CD3DX12_ROOT_PARAMETER slotRootParameter[4];
  5. // Perfomance TIP: Order from most frequent to least frequent.
  6. slotRootParameter[0].InitAsConstantBufferView(0);
  7. slotRootParameter[1].InitAsConstantBufferView(1);
  8. slotRootParameter[2].InitAsShaderResourceView(0, 1);
  9. slotRootParameter[3].InitAsDescriptorTable(1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL);
  10. auto staticSamplers = GetStaticSamplers();
  11. // A root signature is an array of root parameters.
  12. CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4,
  13. slotRootParameter,
  14. (UINT)staticSamplers.size(),
  15. staticSamplers.data(),
  16. D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_

现在,绘制任何渲染物体之前,我们可以绑定所有材质和纹理SRV每帧一次:

  1. void CameraAndDynamicIndexingApp::Draw(const GameTimer& gt)
  2. {

  3. auto passCB = mCurrFrameResource->PassCB->Resource();
  4. mCommandList->SetGraphicsRootConstantBufferView(1, passCB->GetGPUVirtualAddress());
  5. // Bind all the materials used in this scene. For structured buffers,
  6. // we can bypass the heap and set as a root descriptor.
  7. auto matBuffer = mCurrFrameResource->MaterialBuffer->Resource();
  8. mCommandList->SetGraphicsRootShaderResourceView(2, matBuffer->GetGPUVirtualAddress());
  9. // Bind all the textures used in this scene. Observe
  10. // that we only have to specify the first descriptor in the table.
  11. // The root signature knows how many descriptors are expected in the table.
  12. mCommandList->SetGraphicsRootDescriptorTable(3,
  13. mSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
  14. DrawRenderItems(mCommandList.Get(), mOpaqueRitems);

  15. }
  16. void CameraAndDynamicIndexingApp::DrawRenderItems(
  17. ID3D12GraphicsCommandList* cmdList,
  18. const std::vector<RenderItem*>& ritems)
  19. {

  20. // For each render item…
  21. for(size_t i = 0; i < ritems.size(); ++i)
  22. {
  23. auto ri = ritems[i];

  24. cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
  25. cmdList->DrawIndexedInstanced(ri->IndexCount, 1,
  26. ri->StartIndexLocation, ri- >BaseVertexLocation, 0);
  27. }
  28. }

然后更新ObjectConstants结构(已经添加并更新MaterialIndex):

  1. // UpdateObjectCBs…
  2. ObjectConstants objConstants;
  3. XMStoreFloat4x4(&objConstants.World, XMMatrixTranspose(world));
  4. XMStoreFloat4x4(&objConstants.TexTransform, XMMatrixTranspose(texTransform));
  5. **objConstants.MaterialIndex = e->Mat->MatCBIndex;**

着色器代码更新:

  1. // Include structures and functions for lighting.
  2. #include LightingUtil.hlsl
  3. struct MaterialData
  4. {
  5. float4 DiffuseAlbedo;
  6. float3 FresnelR0;
  7. float Roughness;
  8. float4x4 MatTransform;
  9. uint DiffuseMapIndex;
  10. uint MatPad0;
  11. uint MatPad1;
  12. uint MatPad2;
  13. };
  14. // An array of textures, which is only supported in shader model 5.1+. Unlike
  15. // Texture2DArray, the textures in this array can be different sizes and
  16. // formats, making it more flexible than texture arrays.
  17. Texture2D gDiffuseMap[4] : register(t0);
  18. // Put in space1, so the texture array does not overlap with these resources.
  19. // The texture array will occupy registers t0, t1, …, t3 in space0.
  20. StructuredBuffer<MaterialData> gMaterialData : register(t0, space1);
  21. SamplerState gsamPointWrap : register(s0);
  22. SamplerState gsamPointClamp : register(s1);
  23. SamplerState gsamLinearWrap : register(s2);
  24. SamplerState gsamLinearClamp : register(s3);
  25. SamplerState gsamAnisotropicWrap : register(s4);
  26. SamplerState gsamAnisotropicClamp : register(s5);
  27. // Constant data that varies per frame.
  28. cbuffer cbPerObject : register(b0)
  29. {
  30. float4x4 gWorld;
  31. float4x4 gTexTransform;
  32. uint gMaterialIndex;
  33. uint gObjPad0;
  34. uint gObjPad1;
  35. uint gObjPad2;
  36. };
  37. // Constant data that varies per material.
  38. cbuffer cbPass : register(b1)
  39. {
  40. float4x4 gView;
  41. float4x4 gInvView;
  42. float4x4 gProj;
  43. float4x4 gInvProj;
  44. float4x4 gViewProj;
  45. float4x4 gInvViewProj;
  46. float3 gEyePosW;
  47. float cbPerObjectPad1;
  48. float2 gRenderTargetSize;
  49. float2 gInvRenderTargetSize;
  50. float gNearZ;
  51. float gFarZ;
  52. float gTotalTime;
  53. float gDeltaTime;
  54. float4 gAmbientLight;
  55. // Indices [0, NUM_DIR_LIGHTS) are directional lights;
  56. // indices [NUM_DIR_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHTS) are point lights;
  57. // indices [NUM_DIR_LIGHTS+NUM_POINT_LIGHTS,
  58. //
  59. NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)
  60. // are spot lights for a maximum of MaxLights per object.
  61. Light gLights[MaxLights];
  62. };
  63. struct VertexIn
  64. {
  65. float3 PosL : POSITION;
  66. float3 NormalL : NORMAL;
  67. float2 TexC : TEXCOORD;
  68. };
  69. struct VertexOut
  70. {
  71. float4 PosH : SV_POSITION;
  72. float3 PosW : POSITION;
  73. float3 NormalW : NORMAL;
  74. float2 TexC : TEXCOORD;
  75. };
  76. VertexOut VS(VertexIn vin)
  77. {
  78. VertexOut vout = (VertexOut)0.0f;
  79. // Fetch the material data.
  80. MaterialData matData = gMaterialData[gMaterialIndex];
  81. // Transform to world space.
  82. float4 posW = mul(float4(vin.PosL, 1.0f), gWorld);
  83. vout.PosW = posW.xyz;
  84. // Assumes nonuniform scaling; otherwise, need to use inverse-transpose
  85. // of world matrix.
  86. vout.NormalW = mul(vin.NormalL, (float3x3)gWorld);
  87. // Transform to homogeneous clip space.
  88. vout.PosH = mul(posW, gViewProj);
  89. // Output vertex attributes for interpolation across triangle.
  90. float4 texC = mul(float4(vin.TexC, 0.0f, 1.0f), gTexTransform);
  91. vout.TexC = mul(texC, matData.MatTransform).xy;
  92. return vout;
  93. }
  94. float4 PS(VertexOut pin) : SV_Target
  95. {
  96. // Fetch the material data.
  97. MaterialData matData = gMaterialData[gMaterialIndex];
  98. float4 diffuseAlbedo = matData.DiffuseAlbedo;
  99. float3 fresnelR0 = matData.FresnelR0;
  100. float roughness = matData.Roughness;
  101. uint diffuseTexIndex = matData.DiffuseMapIndex;
  102. // Dynamically look up the texture in the array.
  103. diffuseAlbedo *= gDiffuseMap[diffuseTexIndex].Sample(gsamLinearWrap, pin.TexC);
  104. // Interpolating normal can unnormalize it, so renormalize it.
  105. pin.NormalW = normalize(pin.NormalW);
  106. // Vector from point being lit to eye.
  107. float3 toEyeW = normalize(gEyePosW - pin.PosW);
  108. // Light terms.
  109. float4 ambient = gAmbientLight*diffuseAlbedo;
  110. Material mat = { diffuseAlbedo, fresnelR0, roughness };
  111. float4 directLight = ComputeDirectLighting(gLights, mat, pin.PosW, pin.NormalW, toEyeW);
  112. float4 litColor = ambient + directLight;
  113. // Common convention to take alpha from diffuse albedo.
  114. litColor.a = diffuseAlbedo.a;
  115. return litColor;
  116. }

为了总结本章,动态索引三个额外的用途如下:

  1. 合并使用不同纹理的网格到一个渲染项目,这样可以在同一个绘制调用中绘制它们。网格可以在顶点结构中保存texture/material属性;
  2. 一个rendering-pass中包含多个纹理(纹理有不同的大小和格式);
  3. 使用不用的纹理和材质实例化渲染项目,材质使用SV_InstanceID值作为索引。我们可以在下一章看到例子。


6 总结

  1. 我们通过摄像机的位置和方向来定义相机坐标系;
  2. 在相机类中添加透视投影矩阵;
  3. 添加前后左右移动,以及上下左右旋转;
  4. 动态索引是新的着色器5.1模型的功能,它可以让我们动态索引一组不同大小和格式的纹理。


7 练习

2、修改摄像机Demo,添加Roll函数,让相机可以围绕前向向量旋转(空战游戏中很有用):

Camera类添加代码:

  1. void Roll(float angle); // 添加Roll
  2. void Camera::Roll(float angle)
  3. {
  4. // Rotate up and look vector about the look vector.
  5. XMMATRIX R = XMMatrixRotationAxis(XMLoadFloat3(&mLook), angle);
  6. XMStoreFloat3(&mUp, XMVector3TransformNormal(XMLoadFloat3(&mUp), R));
  7. XMStoreFloat3(&mRight, XMVector3TransformNormal(XMLoadFloat3(&mRight), R));
  8. mViewDirty = true;
  9. }

然后主App类中的RollAndBoxes::OnKeyboardInput函数中添加代码:

  1. // 添加Roll
  2. if (GetAsyncKeyState('Q') & 0x8000)
  3. mCamera.Roll(3.0f*dt);
  4. if (GetAsyncKeyState('E') & 0x8000)
  5. mCamera.Roll(-3.0f*dt);

Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引的更多相关文章

  1. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十二章:四元数(QUATERNIONS)

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十二章:四元数(QUATERNIONS) 学习目标 回顾复数,以及 ...

  2. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十九章:法线贴图 学习目标 理解为什么需要法线贴图: 学习法线贴图如 ...

  3. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十六章:实例化和截头锥体裁切

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十六章:实例化和截头锥体裁切 代码工程地址: https://git ...

  4. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十四章:曲面细分阶段 代码工程地址: https://github. ...

  5. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader)

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader) 代码工 ...

  6. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十八章:立方体贴图

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十八章:立方体贴图 代码工程地址: https://github.c ...

  7. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 全书总结

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 全书总结 本系列文章中可能有很多翻译有问题或者错误的地方:并且有些章节 ...

  8. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- Direct12优化 第一章:向量代数 1.向量计算的时候,使用XMV ...

  9. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十三章:角色动画

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十三章:角色动画 学习目标 熟悉蒙皮动画的术语: 学习网格层级变换 ...

随机推荐

  1. Redis学习笔记03-持久化

    redis是一个内存型数据库,这就意味着,当主机重启或者宕机时,内存中的数据会被清空,redis可能会丢失数据.为了保存数据,实现数据持久化就必须要有一种机制,可以将redis数据库的数据保留在硬盘上 ...

  2. leetcode 847. Shortest Path Visiting All Nodes 无向连通图遍历最短路径

    设计最短路径 用bfs 天然带最短路径 每一个状态是 当前的阶段 和已经访问过的节点 下面是正确但是超时的代码 class Solution: def shortestPathLength(self, ...

  3. 如何提高英语听力(内容摘自NECCS)+ 乘法表

    乘法表 print('\n'.join([' '.join(['%s*%s=%-2s'%(y,x,x*y) for y in range(1,x+1)]) for x in range(1,10)]) ...

  4. Broken Keyboard UVA 11988 数组实现链表

    这个构造十分巧妙,,,又学到一招,有点类似数组实现的邻接表 #include <iostream> #include <string.h> #include <cstdi ...

  5. 使用Jedis操作Redis-使用Java语言在客户端操作---String类型

    前提:需要引入Jedis的jar包. /** * 我的redis在Linux虚拟机Centos7中,192.168.222.129是我虚拟机的ip地址. */ private static Jedis ...

  6. 字符串无法分割 split无效: java split()使用“.” “\” "|" "*" "+"要转义

    .是特殊字符 特殊字符需要转义. 改成split(“\\.”)

  7. el表达式 jsp页面取list的长度

    方法1 ${cimlistForJsp.size()} 方法2,引入 <%@ taglib prefix="fn" uri="http://java.sun.com ...

  8. IO流10 --- 缓冲流(字节型)实现非文本文件的复制 --- 技术搬运工(尚硅谷)

    字节型缓冲流,BufferedOutputStream默认缓冲区大小 8192字节byte,满了自动flush() @Test public void test6(){ File srcFile = ...

  9. Mac系统常用快捷键大全

    苹果Mac系统常用快捷键有很多,但是很多童鞋对于这些mac快捷键都不是很熟悉,今天小编为大家整理了一份Mac系统常用快捷键大全,大家快收藏起来吧!平时在使用mac系统的时候可以提高不少工作效率哦! M ...

  10. Nginx负载均衡反向代理

    http{ upstream test.com { server 118.118.66.88:8080; } server { listen 80; server_name www.test.com; ...