Ваша главная проблема здесь связана с неправильным пониманием того, как работает трехмерное пространство.
В конвейере трехмерного рендеринга используются пять основных пространств: Локальное пространство, Пространство мира, Пространство вида, Пространство проекции / Однородное пространство клипа и пространство экрана. Матрицы, используемые для преобразования между ними, названы по пространству, в которое они преобразуются:
Матрица мира преобразует из локального пространства в мировое пространство.
Представление Матрица преобразует из Пространства Мира в Пространство Видения.
Матрица проекции преобразует Пространство Представления в Пространство Проекции.
Растеризатор выполняет окончательное преобразование на основе настройки в структуре D3D11_VIEWPORT
, указанные в вызове ID3D11DeviceContext::RSSetViewports
.
Ваша система достаточно проста, и вам не нужна матрица мира. Поскольку вы, вероятно, создаете программу для 2D-рисования с помощью 3D API, вы можете указать точки для каждой линии непосредственно в мировом пространстве.
Однако для поддержки операций с камерой вам потребуется матрица вида, и в Для правильного рендеринга вам понадобится матрица проекции и структура D3D11_VIEWPORT
с размером заполненной клиентской области.
В вашем коде вы создаете ортогональную матрицу проекции c, которая идеально (хотя ваши характеристики ближней и дальней плоскости должны соответствовать минимальному и максимальному масштабированию, который вы поддерживаете). Однако вы не создаете матрицу представления должным образом. Матрица вида - это обратная транспонирование матрицы мира камеры, и ее можно создать непосредственно с помощью XMMatrixLookAtLH
или XMMatrixLookToLH
(LookTo, вероятно, будет лучшим вариантом для вашей системы).
Наконец, когда вы обновляете постоянный буфер шейдера новой матрицей, умножаете вместе матрицы представления и проекции перед записью новой комбинированной матрицы в буфер.
В результате этой новой сложности вы больше не требуется большая часть кода в вершинном шейдере - вы можете выполнить умножение напрямую:
VS_OUTPUT vsmain(VS_INPUT input)
{
VS_OUTPUT output = (VS_OUTPUT) 0;
output.position = mul(input.position, m_viewprojection);
output.color = input.color;
return output;
}
Последний этап - добавление новых точек. Из-за добавленного вида и матриц проекции координаты, которые вы получаете от события нажатия мыши, не являются мировыми пространственными координатами. Вам необходимо преобразовать их в пространство отсечения, а затем умножить их на обратную величину объединенной матрицы вида / проекции, чтобы преобразовать их в реальные координаты в мировом пространстве. Затем вы можете добавить новый отрезок линии на полосу, так же, как у вас в коде.
Преобразование в пространство клипа основано c алгебра - преобразование, которое делает растеризатор:
[ScreenX, ScreenY] = [(ProjX + 1)*(ScreenWidth/2), (ProjY + 1)*(ScreenHeight/2)]
Чтобы преобразовать обратно в пространство проекции:
[ProjX, ProjY] = [(ScreenX * (2 / ScreenWidth)) - 1, (ScreenY * (2 / ScreenHeight)) - 1]
Оттуда вы можете выполнить умножение матрицы на вектор, чтобы получить координаты X и Y в мировом пространстве. Поскольку это приложение 2D (предположительно), вы можете проигнорировать потерянный компонент Z и просто взять X и Y. Однако, если вы когда-нибудь преобразуете в 3D, вам нужно будет проецировать луч из координат X и Y в мир, чтобы найти объект, который нужно выбрать, или как-то исправить координату Z с помощью другого алгоритма.