c++ - 如何在directx/c++中进行平移/缩放操作
问题描述
出于学习目的,我正在尝试创建简单的绘图程序。现在我正在尝试添加平移和缩放功能。这就是我更新相机的方式m_rightward
,并且m_upward
根据是否单击 wasd 为 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;
}
现在我可以随心所欲地画线。问题是我无法进行平移和缩放工作。它根本不移动屏幕。我猜我如何填充线条和顶点着色器输出是错误的。如果你有你们帮我解决这个问题,我会很高兴的。谢谢
解决方案
您在这里的主要问题源于对 3D 空间如何工作的误解。
3D 渲染管道中使用了五个主要空间:局部空间、世界空间、视图空间、投影空间/同质剪辑空间和屏幕空间。用于在它们之间转换的矩阵以它们转换为的空间命名:
世界矩阵从局部空间转换为世界空间。
视图矩阵从世界空间转换为视图空间。
投影矩阵从视图空间转换为投影空间。
光栅化器根据调用D3D11_VIEWPORT
中指定的结构中的设置进行最终转换。ID3D11DeviceContext::RSSetViewports
你的系统很简单,不需要世界矩阵。由于您可能正在使用 3D API 制作 2D 绘图程序,因此您可以直接在世界空间中指定每条线的点。
但是,为了支持相机操作,您需要一个视图矩阵,并且为了正确进行渲染,您将需要一个投影矩阵和一个D3D11_VIEWPORT
填充了客户区大小的结构。
在您的代码中,您创建了一个完美的正交投影矩阵(尽管您的近平面和远平面规格应该是您支持的最小和最大缩放)。但是,您没有正确创建视图矩阵。视图矩阵是相机世界矩阵的逆转置,可以直接用XMMatrixLookAtLH
or创建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;
}
最后阶段是添加新点。由于添加了视图和投影矩阵,您从鼠标按下事件获得的坐标不是世界空间坐标。您需要将它们转换为剪辑空间,然后将它们乘以组合视图/投影矩阵的倒数,以将它们转换为世界空间中的真实坐标。然后您可以将新的线段添加到条带中,与您在代码中的方式相同。
到剪辑空间的转换是基本代数 - 光栅化器所做的转换是:
[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 坐标。
推荐阅读
- javascript - 使用自定义名称属性在 Firebase 中存储视频 blob,返回未定义
- mysql - 如何在 MySQL 中查询 3 个 MTM 表
- python - 在没有循环的 __init__.py 中导入东西
- mysql - 为什么默认 INT(11)
- keyboard - 软件如何将鼠标按钮识别为快捷键的击键?
- javascript - 如何将 svelte :global() 与 sass/scss 一起使用?
- python - 如何使用python解析多行cli输出
- python - 如何使用子进程获取过去二进制文件的输入提示?
- python - 从数据框中删除所有非唯一行
- android-gradle-plugin - 在 Android Studio 的 Ionic 项目中构建资产包