编辑: 好吧,我忘记包含一些相关函数,使您很难识别错误。对此我很抱歉。 所以我再次更新了我的问题
我在应用程序中遇到了立方体旋转的特殊问题。立方体仅在我移动鼠标或按下按键时旋转,并且我希望能够连续旋转而不依赖于用户输入。
详情:
鼠标移动:当我移动鼠标时,立方体会正确旋转,但我需要它连续旋转,而不需要不断移动鼠标。 按键:按下一个键会立即旋转立方体。但是,我希望只要按住该键就可以继续旋转。 窗口关闭按钮:单击窗口的关闭按钮(X 按钮)不会按预期关闭应用程序窗口。 期望:
连续旋转立方体,无需不断移动鼠标或按键。 窗口关闭按钮的正确功能可关闭应用程序窗口。 当前实施:
立方体旋转在渲染函数中处理,由鼠标移动和按键触发。 应用程序循环(while 循环)使用 PeekMessage 和 GetMessage 函数处理输入事件。
主要.cpp
#include "WindowManager.h"
#include "EngineGraphics.h"
ID3D11Device* gp_Device;
ID3D11DeviceContext* gp_DeviceContext;
IDXGIDevice* gp_DXGI_Device;
IDXGIAdapter* gp_DXGI_Adapter;
IDXGIFactory* gp_DXGI_Factory;
IDXGISwapChain* gp_DXGI_SwapChain;
ID3D11VertexShader* gp_VS;
ID3D11PixelShader* gp_PS;
ID3D11InputLayout* gp_InputLayout;
ID3D11Buffer* gp_VBuffer;
ID3D11Buffer* gp_IBuffer;
ID3D11RasterizerState* gp_rs;
ID3D11RenderTargetView* gp_BackBuffer;
float input;
Vertex Cube[] = {
{ XMFLOAT3(-0.5f, -0.5f, -0.5f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(-0.5f, 0.5f, -0.5f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(0.5f, 0.5f, -0.5f), XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(0.5f, -0.5f, -0.5f), XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f) },
// Back face
{ XMFLOAT3(-0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(0.5f, -0.5f, 0.5f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },
{ XMFLOAT3(0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(-0.5f, 0.5f, 0.5f), XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f) }
};
WORD CubeIndices[] = {
// Front face
0, 1, 2,
0, 2, 3,
// Back face
4, 5, 6,
4, 6, 7,
// Left face
4, 7, 1,
4, 1, 0,
// Right face
3, 2, 6,
3, 6, 5,
// Top face
1, 7, 6,
1, 6, 2,
// Bottom face
4, 0, 3,
4, 3, 5
};
void InitD3D(HWND& hWnd, RECT& wr);
void InitPipeline();
void InitGraphics();
void Render();
void CleanD3D();
using namespace ExtroEngine;
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hWnd;
UINT style = CS_HREDRAW | CS_VREDRAW;
WindowManager::InitWindowExW(style, WinProc, 0, 0, hInstance, L"Scene", NULL, IDC_ARROW, NULL, NULL, CreateSolidBrush(RGB(0, 0, 0)));
hWnd = CreateWindowExW(NULL, L"Scene", L"Scene", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 600,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
RECT wr = { 0, 0, SCENE_WIDTH, SCENE_HEIGHT };
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
InitD3D(hWnd, wr);
MSG msg = { 0 };
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT)
break;
}
Render();
}
CleanD3D();
return 0;
}
void InitD3D(HWND& hWnd, RECT& wr)
{
HRESULT hr = 0;
GetClientRect(hWnd, &wr);
D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_10_1 };
D3D_DRIVER_TYPE DriveType[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE };
DXGI_SWAP_CHAIN_DESC swpDesc;
ZeroMemory(&swpDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
swpDesc.BufferCount = 1;
swpDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swpDesc.BufferDesc.Width = SCENE_WIDTH_MAX_HD;
swpDesc.BufferDesc.Height = SCENE_HEIGHT_MAX_HD;
swpDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swpDesc.OutputWindow = hWnd;
swpDesc.SampleDesc.Count = 1;
swpDesc.SampleDesc.Quality = 0;
swpDesc.Windowed = TRUE;
for (auto DriveSelect : DriveType)
{
hr = D3D11CreateDeviceAndSwapChain(NULL, DriveSelect, NULL, NULL, NULL, NULL,
D3D11_SDK_VERSION, &swpDesc, &gp_DXGI_SwapChain, &gp_Device, featureLevels, &gp_DeviceContext);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr)) {
MessageBox(NULL, L"Error Create Device And Swap Chain", L"Error", MB_OK);
CleanD3D();
return;
}
ID3D11Texture2D* pBackBuffer;
gp_DXGI_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
gp_Device->CreateRenderTargetView(pBackBuffer, NULL, &gp_BackBuffer);
pBackBuffer->Release();
gp_DeviceContext->OMSetRenderTargets(1, &gp_BackBuffer, NULL);
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = SCENE_WIDTH_MAX_HD;
viewport.Height = SCENE_HEIGHT_MAX_HD;
gp_DeviceContext->RSSetViewports(1, &viewport);
InitPipeline();
InitGraphics();
}
// ------------- KHỞI TẠO BỘ BIÊN DỊCH ĐỒ HOẠ
void InitPipeline()
{
HRESULT hr;
ID3DBlob* VS, * PS;
hr = D3DCompileFromFile(L"ColorVPS.hlsl", NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE,
"VShader", "vs_5_0", 0, 0, &VS, NULL);
if (FAILED(hr)) {
MessageBox(NULL, L"Compile From File {ColorVPS.hlsl} Error With {VShader}",
L"Error", MB_OK);
CleanD3D();
return;
}
hr = D3DCompileFromFile(L"ColorVPS.hlsl", NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE,
"PShader", "ps_5_0", 0, 0, &PS, NULL);
if (FAILED(hr)) {
MessageBox(NULL, L"Compile From File {ColorVPS.hlsl} Error With {PShader}",
L"Error", MB_OK);
CleanD3D();
return;
}
hr = gp_Device->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &gp_VS);
if (FAILED(hr)) {
MessageBox(NULL, L"Create Vertex Shader Error With {VS}",
L"Error", MB_OK);
CleanD3D();
return;
}
hr = gp_Device->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &gp_PS);
if (FAILED(hr)) {
MessageBox(NULL, L"Create Pixel Shader Error With {PS}",
L"Error", MB_OK);
CleanD3D();
return;
}
gp_DeviceContext->VSSetShader(gp_VS, NULL, 0);
gp_DeviceContext->PSSetShader(gp_PS, NULL, 0);
D3D11_INPUT_ELEMENT_DESC InputShader[] =
{
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
{"COLOR",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0}
};
gp_Device->CreateInputLayout(InputShader, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &gp_InputLayout);
gp_DeviceContext->IASetInputLayout(gp_InputLayout);
}
// -------------- KHỞI TẠO ĐỒ HOẠ
void InitGraphics() {
HRESULT hr;
// Tạo và gán giá trị cho Vertex Buffer (gp_VBuffer)
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(Vertex) * ARRAYSIZE(Cube);
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Cube;
hr = gp_Device->CreateBuffer(&bd, &InitData, &gp_VBuffer);
if (!SUCCEEDED(hr)) {
MessageBox(NULL, L"Create Vertex Buffer Error",
L"Error", MB_OK);
CleanD3D();
return;
}
// Tạo và gán giá trị cho Index Buffer (gp_IBuffer)
D3D11_BUFFER_DESC ib = {};
ib.Usage = D3D11_USAGE_IMMUTABLE;
ib.ByteWidth = sizeof(WORD) * ARRAYSIZE(CubeIndices);
ib.BindFlags = D3D11_BIND_INDEX_BUFFER;
D3D11_SUBRESOURCE_DATA initData = {};
initData.pSysMem = CubeIndices;
hr = gp_Device->CreateBuffer(&ib, &initData, &gp_IBuffer);
if (FAILED(hr)) {
MessageBox(NULL, L"Create Index Buffer Error",
L"Error", MB_OK);
CleanD3D();
return;
}
// Tạo và thiết lập Rasterizer State (gp_rs)
D3D11_RASTERIZER_DESC rasterizerDesc = {};
rasterizerDesc.FillMode = D3D11_FILL_SOLID;
rasterizerDesc.CullMode = D3D11_CULL_BACK; // or whatever cull mode you need
hr = gp_Device->CreateRasterizerState(&rasterizerDesc, &gp_rs);
if (FAILED(hr)) {
MessageBox(NULL, L"Create Rasterizer State Error",
L"Error", MB_OK);
CleanD3D();
return;
}
}
//-------------------VẼ CỬA SỔ-------------------//
void Render()
{
float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
gp_DeviceContext->ClearRenderTargetView(gp_BackBuffer, clearColor);
gp_DeviceContext->VSSetShader(gp_VS, NULL, 0);
gp_DeviceContext->PSSetShader(gp_PS, NULL, 0);
gp_DeviceContext->IASetInputLayout(gp_InputLayout);
gp_DeviceContext->OMSetRenderTargets(1, &gp_BackBuffer, NULL);
///////////////////// THIS ///////////////////////////////
input = Input::GetKeyRaw(K);
for (int i = 0; i < ARRAYSIZE(Cube); i++)
{
Rotation(0.05f, input, 0.05f, Cube[i].ScreenCoordinates);
}
////////////////////////////////////////////////////////////
D3D11_MAPPED_SUBRESOURCE map;
gp_DeviceContext->Map(gp_VBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &map);
memcpy(map.pData, Cube, sizeof(Cube));
gp_DeviceContext->Unmap(gp_VBuffer, NULL);
UINT stride = sizeof(Vertex);
UINT offset = 0;
gp_DeviceContext->IASetIndexBuffer(gp_IBuffer, DXGI_FORMAT_R16_UINT, 0);
gp_DeviceContext->RSSetState(gp_rs);
gp_DeviceContext->IASetVertexBuffers(0, 1, &gp_VBuffer, &stride, &offset);
gp_DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
gp_DeviceContext->DrawIndexed(ARRAYSIZE(CubeIndices), 0, 0);
gp_DXGI_SwapChain->Present(0, 0);
}
//-------------------DỌN DẸP CỬA SỔ -------------//
void CleanD3D()
{
SafeRelease(gp_Device);
SafeRelease(gp_DeviceContext);
SafeRelease(gp_DXGI_Device);
SafeRelease(gp_DXGI_Adapter);
SafeRelease(gp_DXGI_Factory);
SafeRelease(gp_DXGI_SwapChain);
SafeRelease(gp_VS);
SafeRelease(gp_PS);
SafeRelease(gp_InputLayout);
SafeRelease(gp_IBuffer);
SafeRelease(gp_rs);
SafeRelease(gp_BackBuffer);
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
Render();
ValidateRect(hWnd, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
WindowManager.h
#pragma once
#include <Windows.h>
struct WindowManager
{
static void InitWindowExW(UINT style, WNDPROC lpfnWndProc, int cbClsExtra, int cbWndExtra, HINSTANCE hInstance, LPCWSTR lpszClassName, LPCWSTR lpszMenuName, LPCWSTR hCursor, LPCWSTR hIcon, LPCWSTR hIconSm, HBRUSH hbrBackground) {
WNDCLASSEXW wndClass = {};
wndClass.cbSize = sizeof(WNDCLASSEXW);
wndClass.style = style;
wndClass.lpfnWndProc = lpfnWndProc;
wndClass.cbClsExtra = cbClsExtra;
wndClass.hInstance = hInstance;
wndClass.lpszClassName = lpszClassName;
wndClass.lpszMenuName = lpszMenuName;
wndClass.hCursor = LoadCursorW(hInstance, hCursor);
wndClass.hIcon = LoadIconW(hInstance, hIcon);
wndClass.hIconSm = LoadIconW(hInstance, hIconSm);
wndClass.hbrBackground = hbrBackground;
if (!RegisterClassExW(&wndClass)) {
}
}
};
EngineGraphics.h
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <d3dcompiler.h>
#include "ExtroEngine.h"
#include "EngineUtility.h"
#define SCENE_HEIGHT 600
#define SCENE_HEIGHT_MAX_HD 3300
#define SCENE_WIDTH_MAX_HD 2240
#define SCENE_WIDTH 600
using namespace DirectX;
struct Vertex
{
XMFLOAT3 ScreenCoordinates;
XMFLOAT4 Color;
};
template<typename T>
inline void SafeRelease(T& ptr)
{
if (ptr != NULL)
{
ptr->Release();
ptr = NULL;
}
}
ExtroEngine.cpp
#include "ExtroEngine.h"
namespace ExtroEngine
{
float Input::GetKeyRaw(Key key)
{
MSG msg = { 0 };
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
switch (msg.message)
{
case WM_KEYDOWN:
if (msg.wParam == key)
{
return 1.0f;
}
break;
}
}
return 0.0f;
}
void Rotation(float AngleX, float AngleY, float AngleZ, XMFLOAT3& point)
{
float rX = AngleX * (XM_PI / 180.0f);
float rY = AngleY * (XM_PI / 180.0f);
float rZ = AngleZ * (XM_PI / 180.0f);
XMMATRIX rotationMatrixX = XMMatrixRotationX(rX);
XMMATRIX rotationMatrixY = XMMatrixRotationY(rY);
XMMATRIX rotationMatrixZ = XMMatrixRotationZ(rZ);
XMMATRIX finalRotationMatrix = rotationMatrixX * rotationMatrixY * rotationMatrixZ;
XMVECTOR pointVector = XMLoadFloat3(&point);
pointVector = XMVector3TransformCoord(pointVector, finalRotationMatrix);
XMStoreFloat3(&point, pointVector);
}
void Translation(float x, float y, float z, XMFLOAT3& point)
{
XMVECTOR pV = XMLoadFloat3(&point);
XMMATRIX translationMatrix = XMMatrixTranslation(x, y, z);
XMVECTOR transformedPoint = XMVector3TransformCoord(pV, translationMatrix);
XMStoreFloat3(&point, transformedPoint);
}
float Time::DeltaTime = 0.0f;
}
ExtroEngine.h
#pragma once
#include "Key.h"
#include <DirectXMath.h>
using namespace DirectX;
namespace ExtroEngine
{
class Transform
{
public:
XMFLOAT3 Position;
XMFLOAT3 Rotation;
XMFLOAT3 Scale;
Transform()
{
Position = { 0.0f,0.0f,0.0f };
Rotation = { 0.0f,0.0f,0.0f };
Scale = { 0.0f,0.0f,0.0f };
}
};
struct Input
{
static float GetKeyRaw(Key key);
};
struct Time
{
static float DeltaTime;
};
void Rotation(float AngleX, float AngleY, float AngleZ, XMFLOAT3& point);
void Translation(float x, float y, float z, XMFLOAT3& point);
}
和Key.h
#pragma once
#include <Windows.h>
typedef enum Key {
A = 'A', B = 'B', C = 'C', D = 'D', E = 'E', F = 'F', G = 'G', H = 'H', I = 'I', J = 'J', K = 'K',
L = 'L', M = 'M', N = 'N', O = 'O', P = 'P', Q = 'Q', R = 'R', S = 'S', T = 'T', U = 'U', V = 'V',
W = 'W', X = 'X', Y = 'Y', Z = 'Z',
NUM_0 = '0', NUM_1 = '1', NUM_2 = '2', NUM_3 = '3', NUM_4 = '4', NUM_5 = '5', NUM_6 = '6',
NUM_7 = '7', NUM_8 = '8', NUM_9 = '9',
ESC = VK_ESCAPE
} Key;
添加ColorVPS.hlsl
struct VOut
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
VOut output;
output.position = position;
output.color = color;
return output;
}
float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
return color;
}
void main(in float4 position : POSITION, in float4 color : COLOR, out float4 oPosition : SV_POSITION, out float4 oColor : COLOR)
{
VOut output = VShader(position, color);
oPosition = output.position;
oColor = output.color;
}
注意事项: 我使用 DirectX 11 来渲染和处理输入事件。
应用程序运行时没有任何错误或崩溃,但所描述的立方体旋转和窗口关闭按钮问题仍然存在。
我想知道我的代码错误在哪里
有多种方法可以解决此问题。
首先,您可以使用使用消息和消息队列中的官方消息循环,因此将您的
while(TRUE) ...
替换为:
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// don't put Render() here
}
}
然后在
WM_PAINT
中进行渲染循环。这就是您想要做的,但一个重要的细节如doc所说:
返回值 如果应用程序处理此消息,则返回零。
因此,将您的
WM_PAINT
处理替换为:
case WM_PAINT:
Render();
return 0; // we handle it
这对 Windows 来说实际上意味着您从未验证更新区域,因此 Windows 不断发送
WM_PAINT
消息。
另一个解决方案(更常见)是将
Render
保留在当前消息循环中,并处理 WM_PAINT
只是验证区域,如下所示,这样 Windows 就不会永远发送它:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
return 0;
并更改当前的
Input::GetKeyRaw(Key key)
实现,因为它运行另一个消息循环,这会导致奇怪的事情。将 WM_KEYDOWN
处理移到主循环中,并将键盘状态存储在某处,以便您可以在 Render()
中使用它。