DirectX11入门到实践-(4)逻辑篇

先来回顾一下,现在我们已经有窗口、设备、输入、缓冲区、着色器等渲染所需要的一切了,渲染框架已经基本完备,输入顶点后就可以绘制出一些简单的图形。但是,如果我们要渲染更复杂的世界,光有这些是不够的,需要有"逻辑框架",也就是引擎的对象组成部分,下面我们就来做这一步。

对象
一般来说,一个对象应该具有pos、rotation、forward等属性及其操作方法,并包含其渲染数据(vertex、index、constant buffer)。最后,不要忘了保存并及时更新它的worldMatrix变换方式
[Object.h]
#include "Vertex.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "ConstantBuffer.h"
#include <vector>
using namespace DirectX;

class Object
{
public:
const XMVECTOR & GetPositionVector() const;
const XMFLOAT3 & GetPositionFloat3() const;
const XMVECTOR & GetRotationVector() const;
const XMFLOAT3 & GetRotationFloat3() const;
void SetPosition(const XMVECTOR & position) // 设置位置等操作后,不忘更新一下WorldMatrix
{
XMStoreFloat3(&this->position, position);
this->positionVector = position;
this->UpdateWorldMatrix();
}
void SetPosition(const XMFLOAT3 & position);
void SetPosition(float x, float y, float z);
void AdjustPosition(const XMVECTOR & position);
void AdjustPosition(const XMFLOAT3 & position);
void AdjustPosition(float x, float y, float z);
void SetRotation(const XMVECTOR & rotation);
void SetRotation(const XMFLOAT3 & rotation);
void SetRotation(float x, float y, float z);
void AdjustRotation(const XMVECTOR & rotation);
void AdjustRotation(const XMFLOAT3 & rotation);
void AdjustRotation(float x, float y, float z);
void SetLookAtPos(XMFLOAT3 lookAtPos);
const XMVECTOR & GetForwardVector();
const XMVECTOR & GetRightVector();
const XMVECTOR & GetUpVector();
const XMMATRIX& GetWorldMatrix();
void XM_CALLCONV SetWorldMatrix(XMMATRIX world);
void virtual UpdateWorldMatrix(Object* parent = nullptr) // 更新世界坐标变换矩阵,这是一个细活
{
this->worldMatrix = XMMatrixRotationRollPitchYaw(this->rotation.x, this->rotation.y, this->rotation.z) * XMMatrixTranslation(this->position.x, this->position.y, this->position.z);
if (parent != nullptr)
this->worldMatrix *= parent->GetWorldMatrix();
// 修正单位方向向量的朝向
XMMATRIX vecRotationMatrix = XMMatrixRotationRollPitchYaw(0.0f, this->rotation.y, 0.0f);
this->vecForward = XMVector3TransformCoord(this->DEFAULT_FORWARD_VECTOR, vecRotationMatrix);
this->vecRight = XMVector3TransformCoord(this->DEFAULT_RIGHT_VECTOR, vecRotationMatrix);
this->vecUp = XMVector3TransformCoord(this->DEFAULT_UP_VECTOR, vecRotationMatrix);
}
protected:
ID3D11Device * device = nullptr;
ID3D11DeviceContext * deviceContext = nullptr;
ConstantBuffer<CB_VS_VertexShader> * cb_vs_vertexshader = nullptr;
ID3D11ShaderResourceView * texture = nullptr;
VertexBuffer<Vertex> vertexBuffer;
IndexBuffer indexBuffer;
XMMATRIX worldMatrix = XMMatrixIdentity();
XMVECTOR positionVector;
XMVECTOR rotationVector;
XMFLOAT3 position;
XMFLOAT3 rotation;
const XMVECTOR DEFAULT_FORWARD_VECTOR = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);
const XMVECTOR DEFAULT_RIGHT_VECTOR = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f);
const XMVECTOR DEFAULT_UP_VECTOR = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
XMVECTOR vecForward;
XMVECTOR vecRight;
XMVECTOR vecUp;
};

建议看原文,首先我们有 W世界矩阵 = R旋转矩阵 * T位移矩阵 (不考虑缩放的情况下)
我们可以把上述变换看作:将物体从世界坐标原点搬移到世界坐标系对应的位置,并按其坐标轴做对应朝向和大小的调整。

现在我们需要做的是从世界坐标系转换到观察空间坐标系,如果把摄像机看做物体的话,实际上观察空间坐标系就是摄像机物体的局部坐标系(右方向为X轴,上方向为Y轴,目视方向为Z轴)。因此我们现在做的是从世界坐标系变换回摄像机的局部坐标系,即世界矩阵的逆变换:(也就是 将相机变换到坐标轴原点的逆矩阵)
V = (RT)^−1 = T^−1 * R^−1 = T^-1 * R^T

Draw的时候,将这个viewProjectionMatrix(由相机给出camera.GetViewMatrix() * camera.GetProjectionMatrix())和worldMatrix 相乘得到wvp矩阵。传给vertexshader,由此shader就可以知道各顶点要绘制的位置了

子物体跟随的原理:
M3子物体世界矩阵 = M1^-1 子物体变换矩阵 * M0 父物体变换矩阵 * M2 父物体世界矩阵

XMMatrixTranslation函数:构建平移矩阵
XMMatrixRotationRollPitchYaw函数:构建旋转矩阵

Mesh-Model
这里我们为了方便load资源文件,用了assimp插件
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
using namespace DirectX;

class Mesh
{
public:
// Mesh构造函数:根据mesh的vertices和indices构建顶点和索引缓冲区
Mesh(ID3D11Device * device, ID3D11DeviceContext * deviceContext, std::vector<Vertex> & vertices, std::vector<DWORD> & indices)
{
this->deviceContext = deviceContext;
HRESULT hr = this->vertexBuffer.Initialize(device, vertices.data(), vertices.size());
hr = this->indexBuffer.Initialize(device, indices.data(), indices.size());
}
Mesh(const Mesh & mesh);
// Draw函数就根据mesh已知的这些数据,进行DrawIndexed绘制
void Draw()
{
UINT offset = 0;
this->deviceContext->IASetVertexBuffers(0, 1, this->vertexBuffer.GetAddressOf(), this->vertexBuffer.StridePtr(), &offset);
this->deviceContext->IASetIndexBuffer(this->indexBuffer.Get(), DXGI_FORMAT::DXGI_FORMAT_R32_UINT, 0);
this->deviceContext->DrawIndexed(this->indexBuffer.IndexCount(), 0, 0);
}
private:
VertexBuffer<Vertex> vertexBuffer;
IndexBuffer indexBuffer;
ID3D11DeviceContext * deviceContext;
};

class Model : public Object
{
public:
// 从预定义的vertex和index加载的模型
bool Initialize(std::vector<Vertex>& vertexVector, std::vector<DWORD>& indexVector, ID3D11Device* device, ID3D11DeviceContext * deviceContext, ID3D11ShaderResourceView * texture, ConstantBuffer<CB_VS_VertexShader> & cb_vs_vertexshader)
{
this->device = device;
this->deviceContext = deviceContext;
this->texture = texture;
this->cb_vs_vertexshader = &cb_vs_vertexshader;
meshes.push_back(Mesh(this->device, this->deviceContext, vertexVector, indexVector)); // 借由构建Mesh来构造Model,Model可以包含多个Mesh
this->SetPosition(0.0f, 0.0f, 0.0f);
this->SetRotation(0.0f, 0.0f, 0.0f);
this->UpdateWorldMatrix();
return true;
}
// 从文件中加载的模型
bool Initialize(const std::string & filePath, ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_VertexShader> & cb_vs_vertexshader)
{
...除了mesh部分靠LoadFile,
try
{
if (!this->LoadModel(filePath))
return false;
}
catch (COMException & exception)
{
ErrorLogger::Log(exception);
return false;
}
...其他和mesh的Initialize一样
}
void SetTexture(ID3D11ShaderResourceView* texture);
void virtual Draw(const XMMATRIX & viewProjectionMatrix)
{
// 计算并保存World-View-Projection矩阵
this->cb_vs_vertexshader->data.matrix = this->worldMatrix * viewProjectionMatrix;
this->cb_vs_vertexshader->data.matrix = XMMatrixTranspose(this->cb_vs_vertexshader->data.matrix);
this->cb_vs_vertexshader->ApplyChanges();
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
this->deviceContext->PSSetShaderResources(0, 1, &this->texture); // Set Texture
if (!meshes.empty()) // 每一个mesh都画
{
for (int i = 0; i < meshes.size(); i++)
meshes[i].Draw();
}
else
{
this->deviceContext->IASetIndexBuffer(this->indexBuffer.Get(), DXGI_FORMAT::DXGI_FORMAT_R32_UINT, 0);
UINT offset = 0;
this->deviceContext->IASetVertexBuffers(0, 1, this->vertexBuffer.GetAddressOf(), this->vertexBuffer.StridePtr(), &offset);
this->deviceContext->DrawIndexed(this->indexBuffer.IndexCount(), 0, 0); //Draw
}
}

protected:
// LoadModel通过Assimp::Importer来ReadFile
bool LoadModel(const std::string & filePath)
{
Assimp::Importer importer;
const aiScene* pScene = importer.ReadFile(filePath, aiProcess_Triangulate | aiProcess_ConvertToLeftHanded);
if (pScene == nullptr)
return false;
// ReadFile得到的是一个nodeScene,需要对每一个Node递归处理其mesh
this->ProcessNode(pScene->mRootNode, pScene);
return true;
}
// 递归处理,每个结点的mesh挂到Model上
void ProcessNode(aiNode * node, const aiScene * scene)
{
for (UINT i = 0; i < node->mNumMeshes; i++) // 处理结点上的每个mesh
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(this->ProcessMesh(mesh, scene)); // Get Vertices & Indices => Mesh
}
for (UINT i = 0; i < node->mNumChildren; i++) // DFS
{
this->ProcessNode(node->mChildren[i], scene);
}
}
Mesh ProcessMesh(aiMesh * mesh, const aiScene * scene);
std::vector<Mesh> meshes;
};
这样封装好一个,我们画一个model,只需要定义+init,然后draw即可

Prefab
为了方便创建各种模型,我们提供了Prefab静态接口
常见模型:Sphere,Box,Cylinder,Plane...
[Prefab.hpp]
#include <vector>
#include <string>
#include <map>
#include "Vertex.h"
#include "VertexBuffer.h"

class Prefab
{
public:
// 网格数据
struct MeshData
{
std::vector<Vertex> vertexVec; // 顶点数组
std::vector<DWORD> indexVec; // 索引数组
MeshData()
{
// 需检验索引类型合法性
static_assert(sizeof(DWORD) == 2 || sizeof(DWORD) == 4, "The size of IndexType must be 2 bytes or 4 bytes!");
static_assert(std::is_unsigned<DWORD>::value, "IndexType must be unsigned integer!");
}
};
// 创建球体网格数据,levels和slices越大,精度越高。
static MeshData CreateSphere(float radius = 1.0f, int levels = 20, int slices = 20);
// 创建立方体网格数据
static MeshData CreateBox(float width = 2.0f, float height = 2.0f, float depth = 2.0f);
// 创建圆柱体网格数据,slices越大,精度越高。
static MeshData CreateCylinder(float radius = 1.0f, float height = 2.0f, int slices = 20);
// 创建只有圆柱体侧面的网格数据,slices越大,精度越高
static MeshData CreateCylinderSide(float radius = 1.0f, float height = 2.0f, int slices = 5);
// 创建一个平面
static MeshData CreatePlane(const DirectX::XMFLOAT3& center, const DirectX::XMFLOAT2& planeSize = { 10.0f, 10.0f }, const DirectX::XMFLOAT2& maxTexCoord = { 1.0f, 1.0f });
static MeshData CreatePlane(float centerX = 0.0f, float centerY = 0.0f, float centerZ = 0.0f, float width = 10.0f, float depth = 10.0f, float texU = 1.0f, float texV = 1.0f)
{
using namespace DirectX;
MeshData meshData;
meshData.vertexVec.resize(4);
VertexData vertexData;
DWORD vIndex = 0;
// 平面就画了一个四边形
vertexData = { XMFLOAT3(centerX - width / 2, centerY, centerZ - depth / 2), XMFLOAT2(0.0f, texV) };
ConvertToVertexType(meshData.vertexVec[vIndex++], vertexData);
vertexData = { XMFLOAT3(centerX - width / 2, centerY, centerZ + depth / 2), XMFLOAT2(0.0f, 0.0f) };
ConvertToVertexType(meshData.vertexVec[vIndex++], vertexData);
vertexData = { XMFLOAT3(centerX + width / 2, centerY, centerZ + depth / 2), XMFLOAT2(texU, 0.0f) };
ConvertToVertexType(meshData.vertexVec[vIndex++], vertexData);
vertexData = { XMFLOAT3(centerX + width / 2, centerY, centerZ - depth / 2), XMFLOAT2(texU, texV) };
ConvertToVertexType(meshData.vertexVec[vIndex++], vertexData);
meshData.indexVec = { 0, 1, 2, 2, 3, 0 }; // 4个顶点两个三角形顺序
return meshData;
}
private:
struct VertexData
{
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT2 texCoord;
};
// 将我们构造的VertexData类型转为Vertex类型
static void ConvertToVertexType(Vertex& vertexDst, const VertexData& vertexSrc)
{
vertexDst.position = vertexSrc.position;
vertexDst.texCoord = vertexSrc.texCoord;
}
private:
static const std::map<std::string, std::pair<size_t, size_t>> semanticSizeMap;
};
设置vertex, index, init, 最后draw
box和plane最好画,给出每个面的点和连线就行了;cylinder是分为两个圆面和一个弧面画的,弧面按圆面上的点以一定精度连线
sphere的话 需要根据经度和纬度的迭代计算出来

相机
class Camera : public Object // 相机是没有实际模型的,因此只继承Object
{
public:
Camera();
enum class Mode
{
FirstPerson, // 只能WASD控制移动的模式
ThirdPerson, // 跟随物体,并可绕物体旋转的模式
Free // 自由自动和旋转的模式
};
Mode mode;

void SetFrustum(float fovDegrees, float aspectRatio, float nearZ, float farZ)
{
float fovRadians = (fovDegrees / 360.0f) * XM_2PI;
this->projectionMatrix = XMMatrixPerspectiveFovLH(fovRadians, aspectRatio, nearZ, farZ);
}
void SetTarget(Model* target);
void RemoveTarget();
void UpdateViewMatrix();
const XMMATRIX & GetViewMatrix() const;
const XMMATRIX & GetProjectionMatrix() const;
float GetDistance() const;
float GetRotationX() const;
float GetRotationY() const;
// 绕物体垂直旋转(限制角度)
void RotateX(float rad);
// 绕物体水平旋转
void RotateY(float rad);
// 拉近物体
void Approach(float dist);
// 设置初始绕X轴的弧度(限制角度)
void SetRotationX(float rotX);
// 设置初始绕Y轴的弧度
void SetRotationY(float rotY);
// 设置初始距离
void SetDistance(float dist);
// 设置最小最大允许距离
void SetDistanceMinMax(float minDist, float maxDist);
private:
Model* target = nullptr;
XMMATRIX viewMatrix;
XMMATRIX projectionMatrix;
XMVECTOR vecLook;
float distance;
// 最小允许距离,最大允许距离
float minDistance, maxDistance;
};

void Camera::UpdateViewMatrix()
{
// 第三人称模式
if (target != nullptr && mode == Mode::ThirdPerson)
{
// 球面坐标系计算相机实际位置,效果比XMMatrixLookAtLH计算得到的ViewMatrix更好
position.x = target->GetPositionFloat3().x + distance * sinf(rotation.x) * cosf(rotation.y);
position.z = target->GetPositionFloat3().z + distance * sinf(rotation.x) * sinf(rotation.y);
position.y = target->GetPositionFloat3().y + distance * cosf(rotation.x);
positionVector = XMLoadFloat3(&position);
vecLook = XMVector3Normalize(XMLoadFloat3(&target->GetPositionFloat3()) - positionVector);
}
// 其他模式
else
{
XMMATRIX rotationMatrix = XMMatrixRotationRollPitchYaw(this->rotation.x, this->rotation.y, this->rotation.z);
vecLook = XMVector3TransformCoord(this->DEFAULT_FORWARD_VECTOR, rotationMatrix);
}
vecRight = XMVector3Normalize(XMVector3Cross(XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f), vecLook));
vecUp = XMVector3Cross(vecLook, vecRight);
XMFLOAT3 look, right, up;
// 更新向量
XMStoreFloat3(&look, vecLook);
XMStoreFloat3(&right, vecRight);
XMStoreFloat3(&up, vecUp);
// 根据前面计算的结果得到视点矩阵
this->viewMatrix = {
right.x, up.x, look.x, 0.0f,
right.y, up.y, look.y, 0.0f,
right.z, up.z, look.z, 0.0f,
-XMVectorGetX(XMVector3Dot(positionVector, vecRight)), -XMVectorGetX(XMVector3Dot(positionVector, vecUp)), -XMVectorGetX(XMVector3Dot(positionVector, vecLook)), 1.0f
};
}

void Camera::Approach(float dist)
{
distance += dist;
// 限制距离在[minDistance, maxDistance]之间
if (distance < minDistance)
distance = minDistance;
else if (distance > maxDistance)
distance = maxDistance;
}
void Camera::SetRotationX(float rotX)
{
rotation.x = XMScalarModAngle(rotX);
// 将上下视野角度限制在[pi/6, pi/2],即余弦值[0, cos(pi/6)]之间
if (rotation.x < XM_PI / 6)
rotation.x = XM_PI / 6;
else if (rotation.x > XM_PIDIV2)
rotation.x = XM_PIDIV2;
}
void Camera::SetRotationY(float rotY)
{
rotation.y = XMScalarModAngle(rotY);
}

Skybox
设置天空盒
class Skybox
{
public:
bool Initialize(ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_VertexShader> & cb_vs_vertexshader, float radius); // 初始化其实和Box的差不多,不过预先要创建好6张贴图
void Draw(const Camera& camera, const XMMATRIX & viewProjectionMatrix);
void SetTexture(ID3D11ShaderResourceView* texture, int index);
private:
ID3D11Device* device = nullptr;
ID3D11DeviceContext * deviceContext = nullptr;
ID3D11ShaderResourceView* texture[6];
ConstantBuffer<CB_VS_VertexShader> * cb_vs_vertexshader = nullptr;
VertexBuffer<Vertex> vertexBuffer;
IndexBuffer indexBuffer;
XMMATRIX worldMatrix = XMMatrixIdentity();
float boxRadius;
// 前后左右上下顺序
std::vector<std::wstring> textureFile = {
L"Data\\Textures\\1.jpg", L"Data\\Textures\\2.jpg",
L"Data\\Textures\\3.jpg", L"Data\\Textures\\4.jpg",
L"Data\\Textures\\5.jpg", L"Data\\Textures\\6.jpg", };
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> texturePtr[6];
};

void Skybox::Draw(const Camera& camera, const XMMATRIX & viewProjectionMatrix)
{
// 根据相机移动
XMFLOAT3 position = camera.GetPositionFloat3();
this->worldMatrix = XMMatrixTranslation(position.x, position.y, position.z);
this->cb_vs_vertexshader->data.matrix = this->worldMatrix * viewProjectionMatrix;
this->cb_vs_vertexshader->data.matrix = XMMatrixTranspose(this->cb_vs_vertexshader->data.matrix); // 转置
this->cb_vs_vertexshader->ApplyChanges();
this->deviceContext->VSSetConstantBuffers(0, 1, this->cb_vs_vertexshader->GetAddressOf());
UINT indexCount = this->indexBuffer.IndexCount() / 6;
this->deviceContext->IASetIndexBuffer(this->indexBuffer.Get(), DXGI_FORMAT::DXGI_FORMAT_R32_UINT, 0);
UINT offset = 0;
this->deviceContext->IASetVertexBuffers(0, 1, this->vertexBuffer.GetAddressOf(), this->vertexBuffer.StridePtr(), &offset);
for (int i = 0; i < 6; ++i)
{
this->deviceContext->PSSetShaderResources(0, 1, &this->texture[i]); //Set Texture
this->deviceContext->DrawIndexed(indexCount, indexCount*i, 0); //Draw
}
}
原理就是一个内部贴图的巨球(叫盒子只是因为纹理是立方体盒子,实际上直接映射到球体就行了)
我就先画了个1000半径的立方体,for循环6张贴图,DDS不知为啥用不了只能WIC,然后渲染立方体内面,根据固定的顺序……最后记得要把fov设置的比半径大,否则会被bgcolor覆盖背景。
现在推荐的做法为:总是先清空渲染目标和深度/模板缓冲区,天空盒的绘制留到最后。

光照模型

class Light :public Object
{
public:
bool Initialize(ID3D11Device * device, ID3D11DeviceContext * deviceContext);
void SetLightProperties(XMFLOAT3 color = XMFLOAT3(1.0f, 1.0f, 1.0f), float strength = 1.0f);
private:
// 暂时就只实现了一个全局光
ConstantBuffer<CB_PS_Light> cb_vs_light;
};

bool Light::Initialize(ID3D11Device * device, ID3D11DeviceContext * deviceContext)
{
this->device = device;
this->deviceContext = deviceContext;
HRESULT hr = this->cb_vs_light.Initialize(device, deviceContext);
COM_ERROR_IF_FAILED(hr, "Failed to initialize constant buffer.");
return true;
}
void Light::SetLightProperties(XMFLOAT3 color, float strength)
{
this->cb_vs_light.data.ambientLightColor = color;
this->cb_vs_light.data.ambientLightStrength = strength;
this->cb_vs_light.ApplyChanges();
this->deviceContext->PSSetConstantBuffers(0, 1, this->cb_vs_light.GetAddressOf());
}

将模型组装成Car
class Car:public Model
{
public:
bool Initialize(ID3D11Device * device, ID3D11DeviceContext * deviceContext, ConstantBuffer<CB_VS_VertexShader> & cb_vs_vertexshader);
void Draw(const XMMATRIX& viewProjectionMatrix);
void WheelRoll(float deltaTime, bool forward = true);
bool dontDraw = false;
protected:
void UpdateWorldMatrix(Object* parent = nullptr);
private:
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> bodyTexture;
Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> wheelTexture;
float rollSpeed = 0.01f;
Model wheels[4];
};

这一切逻辑的触发位置:
在我们Graphics文件中,有定义这些绘制对象,并在InitializeScene和RenderFrame中对其进行绘制,除此之外还可加入字体、帧数计数等
bool Graphics::InitializeScene()
{
try
{
// Load Texture
HRESULT hr = CreateWICTextureFromFile(this->device.Get(), L"Data\\Textures\\plane.jpg", nullptr, planeTexture.GetAddressOf());
COM_ERROR_IF_FAILED(hr, "Failed to create wic texture from file.");
// Initialize Constant Buffers
hr = this->cb_vs_vertexshader.Initialize(this->device.Get(), this->deviceContext.Get());
COM_ERROR_IF_FAILED(hr, "Failed to initialize constant buffer.");
hr = this->cb_ps_pixelshader.Initialize(this->device.Get(), this->deviceContext.Get());
COM_ERROR_IF_FAILED(hr, "Failed to initialize constant buffer.");
if(!skybox.Initialize(this->device.Get(), this->deviceContext.Get(), this->cb_vs_vertexshader, 1000))
return false;
if (!light.Initialize(this->device.Get(), this->deviceContext.Get()))
return false;
if(!car.Initialize(this->device.Get(), this->deviceContext.Get(), this->cb_vs_vertexshader))
return false;
auto planeMeshData = Prefab::CreatePlane(XMFLOAT3(0, -0.8f, 0), XMFLOAT2(100, 100));
if (!plane.Initialize(planeMeshData.vertexVec, planeMeshData.indexVec, this->device.Get(), this->deviceContext.Get(), this->planeTexture.Get(), this->cb_vs_vertexshader))
return false;
light.SetLightProperties(XMFLOAT3(1, 1, 1), 0.8f);
camera.SetFrustum(90.0f, static_cast<float>(windowWidth) / static_cast<float>(windowHeight), 0.1f, 2000.0f);
camera.SetTarget(&car);
camera.SetDistance(5.0f);
camera.SetDistanceMinMax(3.0f, 10.0f);
}
catch (COMException & exception)
{
ErrorLogger::Log(exception);
return false;
}
return true;
}

void Graphics::RenderFrame()
{
float bgcolor[] = { 1.0f, 1.0f, 1.0f, 0.0f };
this->deviceContext->ClearRenderTargetView(this->renderTargetView.Get(), bgcolor);
this->deviceContext->ClearDepthStencilView(this->depthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
this->deviceContext->IASetInputLayout(this->vertexshader.GetInputLayout());
this->deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
this->deviceContext->RSSetState(this->rasterizerState.Get());
this->deviceContext->OMSetDepthStencilState(this->depthStencilState.Get(), 0);
this->deviceContext->OMSetBlendState(NULL, NULL, 0xFFFFFFFF);
this->deviceContext->PSSetSamplers(0, 1, this->samplerState.GetAddressOf());
this->deviceContext->VSSetShader(vertexshader.GetShader(), NULL, 0);
this->deviceContext->PSSetShader(pixelshader.GetShader(), NULL, 0);
XMMATRIX matrix = camera.GetViewMatrix() * camera.GetProjectionMatrix();
this->plane.Draw(matrix);
this->skybox.Draw(camera, matrix);
this->car.Draw(matrix);
// Draw Text
static int fpsCounter = 0;
static std::string fpsString = "FPS: 0";
// 按键切换视角: 1-第一人称 2-第三人称 \nW/S/A/D 前进/后退/左转/右转 \n第三人称下鼠标右键移动控制视野\n
static std::wstring text = L"Input: 1-FirstPerson 2-ThirdPeron \nW/S/A/D Move \nRight mouse button motion control vision\n";
fpsCounter += 1;
if (fpsTimer.GetElapsedTime() > 1000.0)
{
fpsString = "FPS: " + std::to_string(fpsCounter);
fpsCounter = 0;
fpsTimer.Restart();
}
spriteBatch->Begin();
spriteFont->DrawString(spriteBatch.get(), StringConverter::StringToWstring(fpsString).c_str(), DirectX::XMFLOAT2(0, 0), DirectX::Colors::White, 0.0f, DirectX::XMFLOAT2(0.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f));
spriteFont->DrawString(spriteBatch.get(), text.c_str(), DirectX::XMFLOAT2(0, 30), DirectX::Colors::White, 0.0f, DirectX::XMFLOAT2(0.0f, 0.0f), DirectX::XMFLOAT2(1.0f, 1.0f));
spriteBatch->End();
static int counter = 0;
this->swapchain->Present(0, NULL);
}





comments powered by Disqus