D3D11:如何绘制一个简单的像素对齐线?

7
我尝试使用D3D11绘制两个顶点之间的线段。我在D3D9和D3D11上都有一些经验,但是在D3D11中绘制一条从一个像素开始到另一个像素结束的线段似乎是有问题的。
我所做的是:
  1. 我将每个顶点的像素坐标增加了0.5f,以适应纹理/像素坐标系统(我阅读了Microsoft页面,了解了D3D9和D3D11坐标系统之间的差异):

    f32 fOff = 0.5f; ColoredVertex newVertices[2] = { { D3DXVECTOR3(fStartX + fOff, fStartY + fOff,0), vecColorRGB }, { D3DXVECTOR3(fEndX + fOff, fEndY + fOff,0), vecColorRGB } };

  2. 生成正交投影矩阵以适应渲染目标:

    D3DXMatrixOrthoOffCenterLH(&MatrixOrthoProj,0.0f,(f32)uRTWidth,0.0f,(f32)uRTHeight,0.0f,1.0f); D3DXMatrixTranspose(&cbConstant.m_matOrthoProjection,&MatrixOrthoProj);

  3. 设置光栅化器状态、混合状态、视口等

  4. 将顶点绘制为D3D11_PRIMITIVE_TOPOLOGY_LINELIST
问题: 该线似乎短了一个像素。它从给定的像素坐标开始并完美地适配。线的方向看起来正确,但我想让线结束的像素仍未着色。看起来这条线只是短了一个像素...
是否有任何教程解释此问题或是否有人遇到了同样的问题?我记得在D3D9中没有这么困难。
如果您需要进一步的信息,请提出要求。
谢谢,Stefan
编辑:找到了d3d10的光栅化规则(应该适用于d3d11): http://msdn.microsoft.com/en-us/library/cc627092%28v=vs.85%29.aspx#Line_1 我希望这会帮助我理解...

您不再需要在DX10+中调整像素坐标,这只是众多改进之一(据我所知,它已经在驱动程序级别上完成)。这个问题让我想起了一个旧的OpenGL bug,它存在于线列表绘制中... - Necrolis
当我没有在坐标上添加0.5f时,直线就会变为2像素宽,这对我来说意味着它恰好位于渲染目标的两个像素之间。 - sandicz
渲染目标:20x20像素 线顶点:(1,1)->(18,1) 在渲染目标上,该线从(1,1)到(17,1)填充像素,并添加0.5f的调整。 - sandicz
请查看以下链接中的“将纹素映射到像素”部分:http://msdn.microsoft.com/en-us/library/bb205073(v=VS.85).aspx。你有没有使用DX11和DX9接口(又称`D3D_FEATURE_LEVEL_9_x`)? - Necrolis
我正在使用D3D_FEATURE_LEVEL_11_0和硬件渲染设备(nvidia geforce gtx 580)。调整是在顶点着色器之前完成的,以命中像素的中心,因此顶点的坐标为(1.5,1.5)和(18.5,1.5)。由于渲染目标上的纹素和像素之间没有区别,所以此后不需要再进行任何调整。 - sandicz
3个回答

4
根据光栅化规则(问题上面的链接),我可能已经找到了一个应该可以工作的解决方案:
1. 对顶点进行排序,StartX < EndX 且 StartY < EndY 2. 将(0.5/0.5)添加到起始顶点(如之前所做)以将顶点移动到像素中心 3. 将(1.0/1.0)添加到结束顶点,将顶点移动到右下角
这是为了告诉光栅化器应该绘制线条的最后一个像素。
f32 fXStartOff = 0.5f;
f32 fYStartOff = 0.5f;
f32 fXEndOff = 1.0f;
f32 fYEndOff = 1.0f;

ColoredVertex newVertices[2] = 
{
    { D3DXVECTOR3((f32)fStartX + fXStartOff, (f32)fStartY + fYStartOff,0), vecColorRGB },
    { D3DXVECTOR3((f32)fEndX + fXEndOff , (f32)fEndY + fYEndOff,0), vecColorRGB }
};

如果您知道更好的解决方案,请告诉我。

错误:只需按照StartX < EndX排序(因为y坐标属于它们...),并测试:if(fStartY > fEndY) fYEndOff = 0.0f; - sandicz
好的 - 你不能只是在线条上额外添加0.5,0.5,否则你会改变斜率。要做到这一点,你需要添加0.5 * 斜率(即使线条长度增加0.5像素)。 - Aaron
你说得对,我的解决方案不如我所期望的那样精确,但在我的测试中(在一个20x20像素的渲染目标上有几行)结果还可以。我会尝试你的建议。谢谢 :) - sandicz

1

我不熟悉D3D11,但你的问题听起来很像D3D9中的D3DRS_LASTPIXEL渲染状态 - 也许你需要研究一下D3D11中的相应状态。


没错,听起来这就是我需要的。但是在D3D11中已经没有渲染状态了,我会寻找相同的东西。谢谢 :) - sandicz

0

我遇到了完全相同的问题,通过这个讨论,我解决了它。

我的顶点存储在一个D3D11_PRIMITIVE_TOPOLOGY_LINELIST顶点缓冲区中。

感谢这篇有用的帖子,你让我今天修复了这个错误。一开始,这比我想象的要棘手得多。

这是我的一些代码行。

// projection matrix code
float width = 1024.0f;
float height = 768.0f;
DirectX::XMMATRIX offsetedProj = DirectX::XMMatrixOrthographicRH(width, height, 0.0f, 10.0f);
DirectX::XMMATRIX proj = DirectX::XMMatrixMultiply(DirectX::XMMatrixTranslation(- width / 2, height / 2, 0), offsetedProj);

// view matrix code
// screen top left pixel is 0,0 and bottom right is 1023,767
DirectX::XMMATRIX viewMirrored = DirectX::XMMatrixLookAtRH(eye, at, up);
DirectX::XMMATRIX mirrorYZ = DirectX::XMMatrixScaling(1.0f, -1.0f, -1.0f);
DirectX::XMMATRIX view = DirectX::XMMatrixMultiply(mirrorYZ, viewMirrored);

// draw line code in my visual debug tool.
void TVisualDebug::DrawLine2D(int2 const& parStart,
                              int2 const& parEnd,
                              TColor parColorStart,
                              TColor parColorEnd,
                              float parDepth)

{
    FLine2DsDirty = true;

    // D3D11_PRIMITIVE_TOPOLOGY_LINELIST
    float2 const startFloat(parStart.x() + 0.5f, parStart.y() + 0.5f);
    float2 const endFloat(parEnd.x() + 0.5f, parEnd.y() + 0.5f);
    float2 const diff = endFloat - startFloat;
    // return normalized difference or float2(1.0f, 1.0f) if distance between the points is null. Then multiplies the result by something a little bigger than 0.5f, 0.5f is not enough.
    float2 const diffNormalized =  diff.normalized_replace_if_null(float2(1.0f, 1.0f)) * 0.501f;

    size_t const currentIndex = FLine2Ds.size();
    FLine2Ds.resize(currentIndex + 2);
    render::vertex::TVertexColor* baseAddress = FLine2Ds.data() + currentIndex;
    render::vertex::TVertexColor& v0 = baseAddress[0];
    render::vertex::TVertexColor& v1 = baseAddress[1];
    v0.FPosition = float3(startFloat.x(), startFloat.y(), parDepth);
    v0.FColor = parColorStart;
    v1.FPosition = float3(endFloat.x() + diffNormalized.x(), endFloat.y() + diffNormalized.y(), parDepth);
    v1.FColor = parColorEnd;
}

我测试了几个DrawLine2D调用,看起来它们运行良好。


你可能想要查看 DirectX Tool KitPrimitiveBatch 类。 - Chuck Walbourn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接