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

#c #directx-11

#c #directx-11

Вопрос:

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

Что я сделал:

  1. Я добавил 0,5 f к координатам пикселей каждой вершины, чтобы соответствовать системе координат texel / pixel (я прочитал страницы 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(amp;MatrixOrthoProj,0.0f, (f32)uRTWidth,0.0f, (f32)uRTHeight, 0.0f, 1.0f); D3DXMatrixTranspose(amp;cbConstant.m_matorthoproj,amp;MatrixOrthoProj);

  3. Установите RasterizerState, BlendState, Viewport, …

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

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

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

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

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

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

Я надеюсь, это поможет мне понять…

Комментарии:

1. Вам больше не нужно настраивать координаты пикселей в DX10 , это всего лишь одно из многих улучшений (это не сделано на уровне iirc драйвера). эта проблема напоминает мне о старой ошибке OpenGL, которая существовала при рисовании списка линий…

2. когда я не добавляю 0,5 f к координатам, линия становится толщиной в 2 пикселя, что для меня означает, что она находится ровно между 2 пикселями в цели рендеринга

3. цель рендеринга: вершины линии 20x20px: (1,1) -> (18,1) на цели рендеринга линия заполняет пиксели от (1,1) до (17,1) с добавленной корректировкой 0,5 f

4. msdn.microsoft.com/en-us/library/bb205073 (v = VS.85).aspx смотрите раздел «Сопоставление текселов с пикселями». вы случайно используете DX11 с интерфейсом DX9 (он же D3D_FEATURE_LEVEL_9_x ?

5. я использую D3D_FEATURE_LEVEL_11_0 с аппаратным устройством визуализации (nvidia geforce gtx 580). корректировка выполняется до того, как вершинный шейдер попадает в центр пикселя, поэтому координаты вершин равны (1.5,1.5) и (18.5,1.5). поскольку нет разницы между texel и pixel (цели рендеринга), после этого корректировка больше не выполняется

Ответ №1:

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

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

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

Комментарии:

1. ошибка: просто отсортируйте StartX < EndX (поскольку им принадлежат координаты y …) и проверьте: if(fStartY > fEndY) fYEndOff = 0.0f;

2. Ну, вы не можете просто добавить 0,5, 0,5 дополнительно к линии или изменить наклон. Для этого вам нужно добавить наклон 0,5 * (т. Е. сделать линию на 0,5 пикселя длиннее)

3. вы правы, мое решение не такое точное, каким я хотел бы его видеть, но в моих тестах (несколько строк на целевой рендеринге размером 20×20 пикселей) результаты были в порядке. Я попробую ваше предложение. Спасибо 🙂

Ответ №2:

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

Комментарии:

1. верно, похоже, это то, что мне нужно. но в d3d11 больше нет renderstates. я буду искать что-то равное. спасибо 🙂

Ответ №3:

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

Мои вершины хранятся в буфере вершин 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 constamp; parStart,
                              int2 constamp; 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::TVertexColoramp; v0 = baseAddress[0];
    render::vertex::TVertexColoramp; 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, и, похоже, это работает хорошо.

Комментарии:

1. Возможно, вы захотите взглянуть на DirectX Tool KIt и PrimitiveBatch класс.