#c #directx-11
#c #directx-11
Вопрос:
Я попытался нарисовать линию между двумя вершинами с помощью D3D11. У меня есть некоторый опыт работы с D3D9 и D3D11, но, похоже, в D3D11 возникает проблема с рисованием линии, которая начинается в одном заданном пикселе и заканчивается в другом.
Что я сделал:
-
Я добавил 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 } };
-
Сгенерировал матрицу ортопроекции, соответствующую цели рендеринга:
D3DXMatrixOrthoOffCenterLH(amp;MatrixOrthoProj,0.0f, (f32)uRTWidth,0.0f, (f32)uRTHeight, 0.0f, 1.0f); D3DXMatrixTranspose(amp;cbConstant.m_matorthoproj,amp;MatrixOrthoProj);
-
Установите RasterizerState, BlendState, Viewport, …
- Нарисуйте вершины как 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:
В соответствии с правилами растеризации (ссылка в вопросе выше) Возможно, я нашел решение, которое должно сработать:
- отсортируйте вершины StartX < EndX и startY < EndY
- добавьте (0.5 / 0.5) к начальной вершине (как я делал раньше), чтобы переместить вершину в центр пикселя
- добавьте (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
класс.