You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Direct3D 12和Visual C++动态修改顶点颜色?

解决方案:Direct3D 12每帧更新顶点颜色绘制三向渐变三角形

针对你的需求,核心是利用Direct3D 12的上传堆资源实现高频顶点颜色更新,同时依托硬件原生支持的三角形插值完成渐变渲染。以下是具体实现步骤:

1. 定义顶点结构体

顶点包含固定位置和需要每帧更新的颜色,结构体需符合D3D12内存对齐要求:

struct Vertex {
    XMFLOAT2 position; // 固定屏幕位置(正交投影下可直接用像素坐标或归一化坐标)
    XMFLOAT4 color;    // 每帧更新的顶点颜色
};

// 输入布局描述,对应着色器输入
const D3D12_INPUT_ELEMENT_DESC inputElements[] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, sizeof(XMFLOAT2), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};

2. 创建可频繁更新的顶点缓冲区

使用**上传堆(Upload Heap)**创建顶点缓冲区,这类资源允许CPU直接写入、GPU高效读取,适配100Hz高频更新场景:

// 假设你有N个三角形,共3*N个顶点
UINT vertexCount = 3 * triangleCount;
UINT vertexBufferSize = vertexCount * sizeof(Vertex);

// 初始化顶点数据(位置固定,颜色设初始值)
std::vector<Vertex> vertices(vertexCount);
for (int i = 0; i < triangleCount; ++i) {
    // 设置第i个三角形的三个顶点固定位置
    vertices[3*i + 0].position = XMFLOAT2(x1, y1);
    vertices[3*i + 1].position = XMFLOAT2(x2, y2);
    vertices[3*i + 2].position = XMFLOAT2(x3, y3);
    // 初始颜色赋值
    vertices[3*i + 0].color = XMFLOAT4(r1, g1, b1, 1.0f);
    vertices[3*i + 1].color = XMFLOAT4(r2, g2, b2, 1.0f);
    vertices[3*i + 2].color = XMFLOAT4(r3, g3, b3, 1.0f);
}

// 创建上传堆顶点缓冲区
D3D12_HEAP_PROPERTIES uploadHeapProps = { D3D12_HEAP_TYPE_UPLOAD };
D3D12_RESOURCE_DESC vbDesc = {};
vbDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
vbDesc.Width = vertexBufferSize;
vbDesc.Height = 1;
vbDesc.DepthOrArraySize = 1;
vbDesc.MipLevels = 1;
vbDesc.Format = DXGI_FORMAT_UNKNOWN;
vbDesc.SampleDesc.Count = 1;
vbDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

ID3D12Resource* pVertexBuffer;
device->CreateCommittedResource(
    &uploadHeapProps,
    D3D12_HEAP_FLAG_NONE,
    &vbDesc,
    D3D12_RESOURCE_STATE_GENERIC_READ,
    nullptr,
    IID_PPV_ARGS(&pVertexBuffer)
);

// 映射顶点缓冲区到CPU地址,上传堆可保持永久映射无需频繁Unmap
Vertex* pMappedVertices = nullptr;
pVertexBuffer->Map(0, nullptr, reinterpret_cast<void**>(&pMappedVertices));
// 写入初始顶点数据
memcpy(pMappedVertices, vertices.data(), vertexBufferSize);

3. 每帧更新顶点颜色并渲染

因为顶点位置固定,每帧仅需修改color字段,再提交绘制命令:

// 每帧更新与渲染逻辑(多媒体定时器回调或渲染循环内)
void UpdateAndRender() {
    // 1. 根据MIDI同步逻辑更新每个顶点的颜色
    for (int i = 0; i < vertexCount; ++i) {
        // 替换为你的颜色计算逻辑(如随音乐节奏动态调整)
        pMappedVertices[i].color = XMFLOAT4(newR, newG, newB, 1.0f);
    }

    // 2. 记录渲染命令
    ID3D12GraphicsCommandList* pCmdList = ...; // 获取当前帧的命令列表
    pCmdList->Reset(pCmdAllocator, pPipelineState);

    pCmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    // 设置顶点缓冲区视图
    D3D12_VERTEX_BUFFER_VIEW vbView = {};
    vbView.BufferLocation = pVertexBuffer->GetGPUVirtualAddress();
    vbView.StrideInBytes = sizeof(Vertex);
    vbView.SizeInBytes = vertexBufferSize;
    pCmdList->IASetVertexBuffers(0, 1, &vbView);

    // 绘制所有三角形
    pCmdList->DrawInstanced(vertexCount, 1, 0, 0);

    pCmdList->Close();

    // 3. 提交命令列表并同步CPU/GPU
    ID3D12CommandQueue* pCmdQueue = ...;
    pCmdQueue->ExecuteCommandLists(1, reinterpret_cast<ID3D12CommandList**>(&pCmdList));

    // 用Fence同步,避免CPU更新过快覆盖GPU正在读取的数据
    UINT64 currentFenceValue = ++fenceValue;
    pCmdQueue->Signal(pFence, currentFenceValue);
    if (pFence->GetCompletedValue() < currentFenceValue) {
        HANDLE eventHandle = CreateEvent(nullptr, FALSE, FALSE, nullptr);
        pFence->SetEventOnCompletion(currentFenceValue, eventHandle);
        WaitForSingleObject(eventHandle, INFINITE);
        CloseHandle(eventHandle);
    }
}

4. 配套着色器实现

顶点着色器直接传递位置和颜色,像素着色器输出插值后的颜色,硬件自动完成三向渐变:

// 顶点着色器
struct VSInput {
    float2 position : POSITION;
    float4 color : COLOR;
};

struct PSInput {
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

PSInput VS(VSInput input) {
    PSInput output;
    output.position = float4(input.position, 0.0f, 1.0f); // 正交投影下直接输出位置
    output.color = input.color;
    return output;
}

// 像素着色器
float4 PS(PSInput input) : SV_TARGET {
    return input.color;
}

关键注意事项

  • 上传堆映射:上传堆资源可保持永久映射状态,无需每帧调用Map/Unmap,减少开销。
  • 同步策略:100Hz帧率下必须用Fence同步CPU和GPU,防止颜色数据被GPU读取时覆盖,避免画面错误。
  • 带宽优化:仅更新顶点颜色字段,固定位置无需修改,节省内存带宽。
  • 正交投影:保留已设置的正交矩阵,确保顶点位置直接对应屏幕坐标,无需额外3D变换。

内容的提问来源于stack exchange,提问作者Chris Korda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 04:55:12