D3D11: Как нарисовать простую линию с выравниванием по пикселям?

Я попытался провести линию между двумя вершинами с помощью 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. Установите RasterizerState, BlendState, Viewport, ...

  4. Нарисуйте вершины как D3D11_PRIMITIVE_TOPOLOGY_LINELIST

Проблема: кажется, что линия короче на один пиксель. Он начинается в заданной координате пикселя и идеально подходит. Направление линии выглядит правильным, но пиксель, в котором я хочу, чтобы линия заканчивалась, все еще не окрашен. Похоже, что линия короче всего на один пиксель ...

Объясняет ли какой-либо учебник эту проблему или у кого-нибудь есть такая же проблема? Насколько я помню, в D3D9 было не так сложно.

Пожалуйста, спросите, нужна ли вам дополнительная информация.

Спасибо Стефан

РЕДАКТИРОВАТЬ: обнаружены правила растеризации для d3d10 (должны быть одинаковыми для d3d11): http://msdn.microsoft.com/en-us/library/cc627092%28v=vs.85%29.aspx#Line_1

Надеюсь, это поможет мне понять ...


person sandicz    schedule 05.05.2011    source источник
comment
Вам больше не нужно настраивать координаты пикселей в DX10 +, это всего лишь одно из множества улучшений (это не делается на уровне драйвера iirc). эта проблема напоминает мне старую ошибку OpenGL, которая существовала при отрисовке списка строк ...   -  person Necrolis    schedule 05.05.2011
comment
когда я не добавляю 0,5f к координатам, линия становится толщиной 2 пикселя, что для меня означает, что она находится точно между 2 пикселями в цели рендеринга   -  person sandicz    schedule 05.05.2011
comment
цель рендеринга: 20x20 пикселей линии вершины: (1,1) - ›(18,1) на цели рендеринга линия заполняет пиксели от (1,1) до (17,1) с добавленной корректировкой 0,5f   -  person sandicz    schedule 05.05.2011
comment
msdn.microsoft.com/en-us/library /bb205073(v=VS.85).aspx см. раздел «Сопоставление текселей с пикселями». Вы случайно не используете DX11 с интерфейсом DX9 (он же D3D_FEATURE_LEVEL_9_x?   -  person Necrolis    schedule 05.05.2011
comment
Я использую D3D_FEATURE_LEVEL_11_0 с устройством аппаратного рендеринга (nvidia geforce gtx 580). настройка выполняется до того, как вершинный шейдер попадет в центр пикселя, поэтому координаты вершин равны (1.5,1.5) и (18.5,1.5). поскольку нет разницы между текселем и пикселем (целевого объекта рендеринга), после этого больше не требуется регулировка   -  person sandicz    schedule 06.05.2011


Ответы (3)


Согласно правилам растеризации (ссылка в вопросе выше) я мог бы найти решение, которое должно работать:

  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 }
};

Если вы знаете лучшее решение, дайте мне знать.

person sandicz    schedule 06.05.2011
comment
ошибка: просто отсортируйте StartX ‹EndX (поскольку координаты y принадлежат им ...) и проверьте: if (fStartY› fEndY) fYEndOff = 0.0f; - person sandicz; 06.05.2011
comment
Ну, нельзя просто добавить к линии 0,5,0,5 лишних, или вы меняете наклон. Для этого вам нужно добавить 0,5 * уклона (т.е. сделать линию длиннее на 0,5 пикселя). - person Aaron; 06.05.2011
comment
вы правы, мое решение не такое точное, как мне хотелось бы, но на моих тестах (несколько строк на мишени рендеринга 20x20 пикселей) результаты были нормальными. Я попробую ваше предложение. Спасибо :) - person sandicz; 09.05.2011

Я не знаю D3D11, но ваша проблема очень похожа на состояние рендеринга D3DRS_LASTPIXEL из D3D9 - возможно, есть эквивалент для D3D11, который вам нужно изучить.

person Aaron    schedule 05.05.2011
comment
да, похоже, это то, что мне нужно. но в d3d11 больше нет рендеринга. я буду искать что-то равное. Благодарность :) - person sandicz; 06.05.2011

Я столкнулся с той же проблемой, и я исправил ее благодаря этому обсуждению.

Мои вершины хранятся в буфере вершин 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, и, похоже, он работает хорошо.

person Pcp    schedule 25.09.2015
comment
Вы можете ознакомиться с DirectX Tool KIt и классом PrimitiveBatch. - person Chuck Walbourn; 02.10.2015