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

如何在DirectX11中实现获取屏幕指定坐标像素颜色的函数

问题:实现DirectX11获取指定坐标像素颜色的函数

需要实现如下函数,用于获取DirectX11当前屏幕帧中指定(x,y)坐标的单个像素颜色:

RGBTRIPLE GetPixelColor(int x, int y)

目前已编写以下代码:

//For each Call to Present() do the following:

//Get Device
ID3D11Device* device;
HRESULT gd = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
assert(gd == S_OK);

//Get context
ID3D11DeviceContext* context;
device->GetImmediateContext(&context);

//get back buffer
ID3D11Texture2D* backbufferTex;
HRESULT gb = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backbufferTex);
assert(gb == S_OK);

非DirectX专家,请教如何从缓冲区、上下文或其他途径获取(x,y)坐标的像素颜色。


解决方案

DirectX11的后台缓冲区默认不可直接被CPU读取,需要通过**过渡纹理(Staging Texture)**中转,才能读取像素数据。具体实现步骤如下:

1. 核心思路

  • 从后台缓冲区获取纹理尺寸、像素格式等信息
  • 创建支持CPU读取的过渡纹理
  • 将后台缓冲区内容复制到过渡纹理
  • 映射过渡纹理到CPU内存,读取指定坐标的像素值
  • 清理所有COM资源(避免内存泄漏)

2. 完整函数实现

RGBTRIPLE GetPixelColor(int x, int y, IDXGISwapChain* pSwapChain)
{
    // 默认返回黑色像素
    RGBTRIPLE result = {0, 0, 0};

    ID3D11Device* device = nullptr;
    HRESULT gd = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
    if (FAILED(gd))
        return result;

    ID3D11DeviceContext* context = nullptr;
    device->GetImmediateContext(&context);
    if (!context)
    {
        device->Release();
        return result;
    }

    ID3D11Texture2D* backbufferTex = nullptr;
    HRESULT gb = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backbufferTex);
    if (FAILED(gb))
    {
        context->Release();
        device->Release();
        return result;
    }

    // 获取后台缓冲区的属性描述
    D3D11_TEXTURE2D_DESC backbufferDesc;
    backbufferTex->GetDesc(&backbufferDesc);

    // 检查坐标是否在有效范围内
    if (x < 0 || x >= (int)backbufferDesc.Width || y < 0 || y >= (int)backbufferDesc.Height)
    {
        backbufferTex->Release();
        context->Release();
        device->Release();
        return result;
    }

    // 配置过渡纹理的描述(支持CPU读取)
    D3D11_TEXTURE2D_DESC stagingDesc = backbufferDesc;
    stagingDesc.Usage = D3D11_USAGE_STAGING;
    stagingDesc.BindFlags = 0;
    stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    stagingDesc.MiscFlags = 0;

    ID3D11Texture2D* stagingTex = nullptr;
    HRESULT createStaging = device->CreateTexture2D(&stagingDesc, nullptr, &stagingTex);
    if (FAILED(createStaging))
    {
        backbufferTex->Release();
        context->Release();
        device->Release();
        return result;
    }

    // 复制后台缓冲区内容到过渡纹理
    context->CopyResource(stagingTex, backbufferTex);

    // 将过渡纹理映射到CPU可访问内存
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    HRESULT mapResult = context->Map(stagingTex, 0, D3D11_MAP_READ, 0, &mappedResource);
    if (SUCCEEDED(mapResult))
    {
        // 计算目标像素在内存中的偏移位置(注意行间距为RowPitch)
        BYTE* pixelData = (BYTE*)mappedResource.pData;
        pixelData += y * mappedResource.RowPitch + x * sizeof(RGBTRIPLE);
        
        // 读取像素颜色值
        result = *(RGBTRIPLE*)pixelData;

        context->Unmap(stagingTex, 0);
    }

    // 释放所有COM资源
    stagingTex->Release();
    backbufferTex->Release();
    context->Release();
    device->Release();

    return result;
}

关键注意事项

  • 坐标合法性检查:必须确保(x,y)在纹理宽高范围内,否则会触发非法内存访问
  • 像素格式匹配:示例假设后台缓冲区格式与RGBTRIPLE兼容(如DXGI_FORMAT_R8G8B8A8_UNORM),实际使用时需根据backbufferDesc.Format调整读取逻辑
  • 性能优化:如果需要频繁读取像素,建议将过渡纹理的创建逻辑移到函数外部复用,避免重复创建开销
  • COM资源释放:所有ID3D11*接口对象使用完毕后必须调用Release(),防止内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 10:15:40