如何在 directx/c++ 中进行 panning/zooming 运算

How to do panning/zooming operation in directx/c++

出于学习目的,我正在尝试创建简单的绘图 program.Right,现在我正在尝试添加平移和缩放功能。 这就是我更新我的相机的方式 m_rightwardm_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;
}

现在我可以画线了 wanted.The 问题是我无法进行平移和缩放 work.It 只是不移动屏幕 all.I 我猜我是怎么填充的linestrip 和顶点着色器输出是 wrong.If 你们中的任何一个能帮助我解决这个问题我会很高兴。 谢谢

这里的主要问题源于对 3D space 工作原理的误解。

在 3D 渲染管道中主要使用了五个 space:本地 Space、世界 Space、视图 Space、投影 Space/Homogeneous 剪辑Space,屏幕 Space。用于在它们之间进行转换的矩阵以它们转换为的 space 命名:

世界矩阵从本地Space转换为世界Space。

View Matrix 从 World Space 转换为 View Space。

投影矩阵从视图Space转换为投影Space。

光栅化器根据 ID3D11DeviceContext::RSSetViewports 调用中指定的 D3D11_VIEWPORT 结构中的设置进行最终转换。

您的系统非常简单,不需要世界矩阵。由于您可能正在使用 3D API 制作 2D 绘图程序,您可以直接在世界 space.

中指定每条线的点

然而,为了支持相机操作,您需要一个视图矩阵,并且为了正确进行渲染,您将需要一个投影矩阵和一个 D3D11_VIEWPORT 具有客户区大小的结构填写。

在您的代码中,您创建了一个完美的正交投影矩阵(尽管您的近平面和远平面规范应该是您支持的最小和最大缩放)。但是,您没有正确创建视图矩阵。视图矩阵是相机世界矩阵的逆转置,可以直接用XMMatrixLookAtLHXMMatrixLookToLH创建(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;
}

最后阶段是添加新点。由于添加了视图和投影矩阵,您从鼠标按下事件中获得的坐标不是世界坐标 space。您需要将它们转换为剪辑 space,然后将它们乘以组合 view/projection 矩阵的逆矩阵,将它们转换为世界中的真实坐标 space。然后您可以将新的线段添加到条带中,就像您在代码中所做的那样。

剪辑 space 的转换是基本代数 - 光栅化器所做的转换是:

[ScreenX, ScreenY] = [(ProjX + 1)*(ScreenWidth/2), (ProjY + 1)*(ScreenHeight/2)]

转换回投影space:

[ProjX, ProjY] = [(ScreenX * (2 / ScreenWidth)) - 1, (ScreenY * (2 / ScreenHeight)) - 1]

从那里,您可以进行矩阵向量乘法以获得世界中的 X 和 Y 坐标 space。由于此应用程序是 2D(大概),您可以忽略丢失的 Z 分量而只使用 X 和 Y。但是,如果您转换为 3D,则需要从 X 和 Y 坐标向外投射一条射线以找到select 的对象,或以某种方式用不同的算法修复 Z 坐标。