如何在DirectX / C ++中进行平移/缩放操作

问题描述 投票:0回答:1

出于学习目的,我试图创建简单的绘图程序。现在,我试图添加平移和缩放功能。这是我如何基于是否单击wasd来更新摄像机m_rightwardm_upward为1和0的方式。

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

现在,我可以根据需要绘制线条。问题是我无法进行平移和缩放。它根本无法移动屏幕。我猜我如何填充线条和顶点着色器输出是错误的。如果你们当中的任何一个都可以帮助我,我会很高兴。谢谢

c++ directx directx-11
1个回答
0
投票

您的主要问题来自对3D空间工作原理的误解。

3D渲染管道中使用了五个主要空间:局部空间,世界空间,视图空间,投影空间/同质剪辑空间和屏幕空间。用于在它们之间转换的矩阵以它们转换为的空间命名:

世界矩阵从本地空间转换为世界空间。

<< [View Matrix从世界空间转换为View Space。

[Projection Matrix

从视图空间转换为投影空间。光栅化器基于D3D11_VIEWPORT调用中指定的ID3D11DeviceContext::RSSetViewports结构中的设置进行最终转换。

您的系统非常简单,您不需要世界矩阵。由于您大概是在使用3D API制作2D绘图程序,因此可以直接在世界空间中为每条线指定点。

但是,为了支持相机操作,需要一个视图矩阵,并且为了正确进行渲染,需要一个投影矩阵和一个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; }

最后阶段是添加新点。由于增加了视图和投影矩阵,因此从鼠标按下事件获得的坐标不是世界空间坐标。您需要将它们转换为剪辑空间,然后将它们乘以组合的视图/投影矩阵的逆矩阵,以将其转换为世界空间中的真实坐标。然后,您可以像在代码中一样将新的线段添加到带状区域。 

到剪辑空间的转换是基本代数-光栅化器执行的转换是:

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

转换回投影空间:

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

从那里,您可以进行矩阵向量乘法以获取世界空间中的X和Y坐标。由于此应用程序是2D(大概是2D),因此您可以忽略丢失的Z分量,而仅获取X和Y。但是,如果您转换为3D,则需要将X和Y坐标中的光线投射到世界中才能找到要选择的对象,或以其他算法固定Z坐标。
© www.soinside.com 2019 - 2024. All rights reserved.