Как выполнить операцию панорамирования / масштабирования в DirectX / C ++ - PullRequest
0 голосов
/ 18 февраля 2020

В учебных целях я пытаюсь создать простую программу для рисования. Сейчас я пытаюсь добавить функции панорамирования и масштабирования. Вот как я обновляю свою камеру m_rightward и m_upward равны 1 и 0, если щелкнуть по wasd.

void Camera::updateCamera(int width, int height)
{
    this->height = height;
    this->width = width;

    cc.client_height = this->height;
    cc.client_width = this->width;

    cc.m_time = ::GetTickCount();

    //panning and zooming in here
    new_pos = world_cam.getTranslation() + world_cam.getZDirection()*(m_forward*0.1f);

    new_pos = new_pos + world_cam.getXDirection()*(m_rightward * 20);

    new_pos = new_pos + world_cam.getYDirection()*(m_upward * 20);

    world_cam.setTranslation(new_pos);

    temp = world_cam;
    temp.inverse();

    cc.m_view = temp;


    cc.m_proj.setOrthoLH
    (
        width,
        height,
        -1.0f,
        1.0f
    );  

}

, и вот как я заполняю свою полосу для рисования

void AppWindow::onLeftMouseDown(const Point & mouse_pos)
{   
    Point pos = screenToClient(mouse_pos);
    clickedPoint = Vector3D(pos.m_x, pos.m_y, 0.0f);

    if (isLPressed)
    {

        if (counter == 0)
        {
            LineStrip.push_back(Line(clickedPoint));
            counter++;
        }
        else
        {
            LineStrip.back().addPoint(clickedPoint);
            counter++;
        }

    }

}

и это вершинный шейдер

VS_OUTPUT vsmain(VS_INPUT input)
{
    VS_OUTPUT output = (VS_OUTPUT) 0;

    float4 new_pos = mul(input.position, m_world);

    float clip_x = (new_pos.x / client_width) * 2.0 - 1.0;
    float clip_y = 1.0 - (new_pos.y / client_height) * 2.0;

    output.position = float4(clip_x, clip_y, 0, 1);

    output.color = input.color;

    return output;
}

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

1 Ответ

0 голосов
/ 23 февраля 2020

Ваша главная проблема здесь связана с неправильным пониманием того, как работает трехмерное пространство.

В конвейере трехмерного рендеринга используются пять основных пространств: Локальное пространство, Пространство мира, Пространство вида, Пространство проекции / Однородное пространство клипа и пространство экрана. Матрицы, используемые для преобразования между ними, названы по пространству, в которое они преобразуются:

Матрица мира преобразует из локального пространства в мировое пространство.

Представление Матрица преобразует из Пространства Мира в Пространство Видения.

Матрица проекции преобразует Пространство Представления в Пространство Проекции.

Растеризатор выполняет окончательное преобразование на основе настройки в структуре 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 с помощью другого алгоритма.

...