画三角形

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <QApplication>
#include "D3DWidget.h"

int main(int argc, char* argv[]) {
// 强制 Qt 不使用它默认的渲染器干扰原生绘制
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);

QApplication a(argc, argv);
D3DWidget w;
w.resize(800, 600);
w.show();
return a.exec();
}

D3DWidget.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#pragma once
#include <QWidget>
#include <d3d11.h>
#include <wrl/client.h> // 使用 Microsoft::WRL::ComPtr 简化指针管理

using Microsoft::WRL::ComPtr;

class D3DWidget : public QWidget {
Q_OBJECT
public:
explicit D3DWidget(QWidget* parent = nullptr);
~D3DWidget() override;

protected:
// 关键:重写这些以接管窗口控制权
QPaintEngine* paintEngine() const override { return nullptr; }
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;

private:
void initD3D();
void render();

ComPtr<ID3D11Device> m_device;
ComPtr<ID3D11DeviceContext> m_context;
ComPtr<IDXGISwapChain> m_swapChain;
ComPtr<ID3D11RenderTargetView> m_renderTargetView;

bool m_initialized = false;
struct Vertex {
float x, y, z;
float r, g, b, a;
};

// 在 D3DWidget 类私有成员中添加:
ComPtr<ID3D11VertexShader> m_vertexShader;
ComPtr<ID3D11PixelShader> m_pixelShader;
ComPtr<ID3D11InputLayout> m_inputLayout;
ComPtr<ID3D11Buffer> m_vertexBuffer;
};

D3DWidget.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include "D3DWidget.h"
#include <QWindow>
#include <d3dcompiler.h>

// 另外,确保你也包含了 d3d11.h
#include <d3d11.h>
// 在 D3DWidget.cpp 顶部定义着色器源码
const char* g_shaderSource = R"(
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;
}
)";

D3DWidget::D3DWidget(QWidget* parent) : QWidget(parent) {
// 告诉 Qt 这是一个原生窗口,不要在上面做双缓冲绘制
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground);
}

D3DWidget::~D3DWidget() {}

void D3DWidget::initD3D() {
DXGI_SWAP_CHAIN_DESC sd = {};
sd.BufferCount = 1;
sd.BufferDesc.Width = width();
sd.BufferDesc.Height = height();
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = (HWND)this->winId(); // 关键:绑定 Qt 句柄
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;

UINT createDeviceFlags = 0;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };

HRESULT hr = D3D11CreateDeviceAndSwapChain(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags,
featureLevels, 1, D3D11_SDK_VERSION, &sd,
&m_swapChain, &m_device, &featureLevel, &m_context
);

if (SUCCEEDED(hr)) {
m_initialized = true;
resizeEvent(nullptr); // 触发第一次 RenderTarget 创建
}

// 1. 编译并创建着色器
ComPtr<ID3DBlob> vsBlob, psBlob, errorBlob;

// 编译顶点着色器
D3DCompile(g_shaderSource, strlen(g_shaderSource), nullptr, nullptr, nullptr, "VShader", "vs_5_0", 0, 0, &vsBlob, &errorBlob);
m_device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &m_vertexShader);

// 编译像素着色器
D3DCompile(g_shaderSource, strlen(g_shaderSource), nullptr, nullptr, nullptr, "PShader", "ps_5_0", 0, 0, &psBlob, &errorBlob);
m_device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &m_pixelShader);

// 2. 创建输入布局 (Input Layout)
D3D11_INPUT_ELEMENT_DESC ied[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
m_device->CreateInputLayout(ied, 2, vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &m_inputLayout);

// 3. 创建顶点缓冲区
Vertex vertices[] = {
{ 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }, // 顶点:红色
{ 0.45f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f }, // 右下:绿色
{-0.45f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f } // 左下:蓝色
};

D3D11_BUFFER_DESC bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(vertices);
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;

D3D11_SUBRESOURCE_DATA srd = {};
srd.pSysMem = vertices;
m_device->CreateBuffer(&bd, &srd, &m_vertexBuffer);

m_initialized = true;
}

void D3DWidget::resizeEvent(QResizeEvent* event) {
if (!m_initialized) return;

// 清理旧视图
m_renderTargetView.Reset();

// 调整 SwapChain 大小
m_swapChain->ResizeBuffers(0, width(), height(), DXGI_FORMAT_UNKNOWN, 0);

// 重新创建 RenderTargetView
ComPtr<ID3D11Texture2D> backBuffer;
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
m_device->CreateRenderTargetView(backBuffer.Get(), nullptr, &m_renderTargetView);

// 设置视口
D3D11_VIEWPORT vp;
vp.Width = (FLOAT)width();
vp.Height = (FLOAT)height();
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
m_context->RSSetViewports(1, &vp);
}

void D3DWidget::paintEvent(QPaintEvent* event) {
if (!m_initialized) initD3D();
render();
}

void D3DWidget::render() {
if (!m_initialized) return;

// 清理背景
float clearColor[] = { 0.0f, 0.2f, 0.1f, 1.0f };
m_context->ClearRenderTargetView(m_renderTargetView.Get(), clearColor);

// 设置渲染目标
m_context->OMSetRenderTargets(1, m_renderTargetView.GetAddressOf(), nullptr);

// 绑定着色器和布局
m_context->VSSetShader(m_vertexShader.Get(), nullptr, 0);
m_context->PSSetShader(m_pixelShader.Get(), nullptr, 0);
m_context->IASetInputLayout(m_inputLayout.Get());

// 绑定顶点缓冲区
UINT stride = sizeof(Vertex);
UINT offset = 0;
m_context->IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), &stride, &offset);
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

// 绘制!
m_context->Draw(3, 0);

m_swapChain->Present(1, 0);
update();
}

CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
cmake_minimum_required(VERSION 3.20)
project(DrawOpenGL LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)

# 1. 查找 Qt6
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

# 2. 查找 Windows 系统 D3D 库
find_library(D3D11_LIB d3d11)
find_library(DXGI_LIB dxgi)
find_library(D3DCOMPILER_LIB d3dcompiler)

# 3. 添加源文件
add_executable(${PROJECT_NAME}
src/main.cpp
src/D3DWidget.h
src/D3DWidget.cpp
)

# 4. 链接所有库
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
${D3D11_LIB}
${DXGI_LIB}
${D3DCOMPILER_LIB}
)

# 输出到项目下的 bin 文件夹
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")