首页 > 解决方案 > 顶点着色器中的采样返回缓冲区始终返回 0 和 float1 而不是 float4

问题描述

我现在完全迷失了。几天来一直试图读取顶点着色器中的后缓冲区,但没有任何运气。

我正在尝试从后台缓冲区读取顶点位置,它是相邻像素。(我试图计算一个顶点周围有多少黑色像素,以及像素着色器中是否有任何颜色顶点为红色)。我创建了一个单独的 ID3D11Texture2D 和一个 SRV 与 backBuffer 一起使用。我将后台缓冲区复制到此 SRV 的资源中。使用 VSSetShaderResources 绑定 SRV,但似乎无法在顶点着色器中读取它。

我将在这里分享一些创建这些元素的代码,并包括一些 RenderDoc 屏幕截图,这些屏幕截图不断显示 SRV 被绑定到 VS 阶段并具有与之关联的正确纹理,但每个 Load 或 [] 运算符或 tex2dlod 或SampleLevel(我也绑定了一个 SamplerState)只是一直返回一个 1.0 值,而其余的 float4 永远不会返回,这意味着我只能得到一个 float1。如果有人想看,我还将包含一个 renderdoc 捕获文件。

这是 rastertek.com 网站上教程 42 中的一个简单场景,有一个地平面,上面有一个立方体和一个球体:

https://i.imgur.com/cbVC48E.gif

// Here is some code when creating the secondary texture and SRV that houses a //backBuffer
// Get the pointer to the back buffer.
    result = m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
    if(FAILED(result))
    {
        MessageBox((*(hwnd)), L"Get the pointer to the back buffer FAILED", L"Error", MB_OK);
        return false;
    }


    // Create another texture2d that we will use to make an SRV out of, and this texture2d will be used to copy the backbuffer to so we can read it in a shader
    D3D11_TEXTURE2D_DESC bbDesc;
    backBufferPtr->GetDesc(&bbDesc);
    bbDesc.MipLevels = 1;
    bbDesc.ArraySize = 1;
    bbDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    bbDesc.Usage = D3D11_USAGE_DEFAULT;
    bbDesc.MiscFlags = 0;
    bbDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    result = m_device->CreateTexture2D(&bbDesc, NULL, &m_backBufferTx2D);
    if (FAILED(result))
    {
        MessageBox((*(m_hwnd)), L"Create a Tx2D for backbuffer SRV FAILED", L"Error", MB_OK);
        return false;
    }
    D3D11_SHADER_RESOURCE_VIEW_DESC descSRV;
    ZeroMemory(&descSRV, sizeof(descSRV));
    descSRV.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    descSRV.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    descSRV.Texture2D.MipLevels = 1;
    descSRV.Texture2D.MostDetailedMip = 0;
    result = GetDevice()->CreateShaderResourceView(m_backBufferTx2D, &descSRV, &m_backBufferSRV);
    if (FAILED(result))
    {
        MessageBox((*(m_hwnd)), L"Creating BackBuffer SRV FAILED.", L"Error", MB_OK);
        return false;
    }

    // Create the render target view with the back buffer pointer.
    result = m_device->CreateRenderTargetView(backBufferPtr, NULL, &m_renderTargetView);

首先,我将场景渲染为全白,然后将其复制到 SRV 并将其绑定到应该对其进行采样的下一个着色器。当我使用顶点在屏幕上的位置对后台缓冲区进行采样时,我期望得到一个返回的 float4(1.0, 1.0, 1.0, 1.0) 值

https://i.imgur.com/N9CYg8c.png

如事件浏览器左上角所示,有 3 个 drawindexed 调用将所有内容渲染为白色,然后是 CopyResource。我选择了下一个(第四个)DrawIndexed,在右侧用红色勾勒的是下一个着色器的输入,清楚地表明 backBuffer 已成功绑定到顶点着色器。

现在是给我带来麻烦的部分

https://i.imgur.com/ENuXk0n.png

我将调试这个左上角的顶点,如屏幕截图所示,顶点着色器有一个 Texture2D prevBackBuffer: register(t0); 写在顶部

https://i.imgur.com/8cihNsq.png

当尝试对左侧相邻像素进行采样时,这行代码返回 newCoord = float2(158, 220) 当在纹理视图中输入这些像素值时,我得到了这个像素

https://i.imgur.com/DT72Fl1.png

所以到目前为止坐标还可以,正如我所概述的那样,当我对这个像素进行采样时,我期望得到一个 float4(0.0, 0.0, 0.0, 1,0) 返回(我试图计算一个周围有多少黑色像素顶点,以及像素着色器中是否有任何颜色顶点为红色)

而且,当我在更改像素坐标后立即对该像素进行采样时,因为负载从左下角计算像素,所以我需要 newCoord = float2(158, 379),我得到了这个

https://i.imgur.com/8SuwOzz.png

为什么会这样,即使超出范围,负载也应该返回全零,因为我不确定从左下角开始的整个负载计数我尝试使用左上角坐标 (158, 220) 进行采样但最终得到 0.0 , ?, ?, ?

我完全被难住了,不知道下一步该尝试什么。我尝试过使用示例状态:

// Create a clamp texture sampler state description.
    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
    samplerDesc.MipLODBias = 0.0f;
    samplerDesc.MaxAnisotropy = 1;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
    samplerDesc.BorderColor[0] = 0;
    samplerDesc.BorderColor[1] = 0;
    samplerDesc.BorderColor[2] = 0;
    samplerDesc.BorderColor[3] = 0;
    samplerDesc.MinLOD = 0;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

    // Create the texture sampler state.
    result = device->CreateSamplerState(&samplerDesc, &m_sampleStateClamp);

但在读取纹理时仍然没有得到正确的 float4。

任何想法,建议,我都会在这一点上接受任何东西。哦,这是我正在检查的框架的 RenderDoc 文件: http ://www.mediafire.com/file/1bfiqdpjkau4l0n/my_capture.rdc/file

标签: c++directx-11samplevertex-shadertexture2d

解决方案


因此,根据我的经验,从后台缓冲区读取并不是您一开始就想做的操作。如果必须对渲染场景进行任何操作,最好的方法是将场景渲染到中间纹理,对该纹理执行操作,然后将最终场景渲染到后台缓冲区。这通常是动态阴影之类的事情的完成方式 - 从光的角度渲染场景,并解释生成的缓冲区以获得阴影值,然后将其应用于最终场景(这也是动态光源的原因)受限于商业游戏引擎——它们使用起来相当昂贵)。

在这里可以应用类似的想法。首先,将整个场景渲染到中间纹理,绑定为渲染目标视图(其中像素格式由程序员指定)。接下来,将该中间纹理重新绑定为着色器资源视图,并使用边缘检测着色器和真正的后台缓冲区(其中像素格式由硬件定义)再次渲染场景。

从根本上说,这就是我认为的问题所在 - 后台缓冲区是依赖于设备的资源,它的格式可以根据硬件而改变。因此,从着色器中使用它并不安全,因为您并不总是知道格式是什么。另一方面,与设备无关的资源将始终具有相同的格式,您可以从着色器中随意使用它


推荐阅读