请问一个3D变换的问题

realizz 2009-07-07 09:44:52
现在我有一个空间坐标点,已知camera的坐标和视角以及矩阵,已知投影的矩阵,已知viewport,怎么获得这个点投影在屏幕上的坐标?

我的做法是 :先把camera和proj的矩阵相乘,再去乘以viewport对应的矩阵,再用这个矩阵去变换那个坐标点的向量,怎么出来的就是不对呢?

D3DXVECTOR4 g_vec; //变换后的坐标
D3DXVECTOR4 vec = D3DXVECTOR4(0.0f,0.0f,0.0f,1.0f); //在world坐标中的原始坐标
D3DVIEWPORT9 vp; //视口
D3DXMATRIX mvp; //视口变换的矩阵

DXUTGetD3D9Device()->GetViewport(&vp); //获取当前视口
mvp._11 = (vp.Width) / 2;
mvp._22 = (-vp.Height) / 2;
mvp._33 = vp.MaxZ - vp.MinZ;
mvp._41 = vp.X + (vp.Width)/2;
mvp._42 = vp.Y + (vp.Height)/2;
mvp._43 = vp.MinZ;
mvp._44 = 1.0f;

D3DXMATRIX mtemp; //综合变换矩阵

D3DXMatrixMultiply(&mtemp,g_Camera.GetMatrixCamera(),g_Camera.GetMatrixProj()); //先把视角矩阵和投影矩阵相乘

D3DXMatrixMultiply(&mtemp,&mtemp,&mvp); //再乘视口变换矩阵

D3DXVec4Transform(&g_vec,&vec,&mtemp); //再用综合变换矩阵去变换vec得到g_vec

在视角正对(0,0,0)的时候,算出来应该是处于屏幕正中才对,但是结果就是不对,郁闷,求助

...全文
668 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
songking5566 2009-11-08
  • 打赏
  • 举报
回复
mark
mmx369 2009-07-29
  • 打赏
  • 举报
回复

mark
xqhrs232 2009-07-21
  • 打赏
  • 举报
回复
做个记好!!!
qq845284425 2009-07-21
  • 打赏
  • 举报
回复

mark
realizz 2009-07-10
  • 打赏
  • 举报
回复
恩,恩,感谢楼上几位

已经完美解决,3Q
xingzhe2001 2009-07-09
  • 打赏
  • 举报
回复
当然我说的是除以w以后。
根据d3d的文档,透视矩阵有两种算法,
第一种,根据w, h, n, f。
透视矩阵为
2*n/w 0 0 0
0 2*n/h 0 0
0 0 f/(f-zn) 1
0 0 n*f/(n-f) 0

(x,y,z,1)在经过这样的变换后为(x*2n/w, y*2n/w,f(z-n)/f-n, z)。 注意最后的w就是变换前的z,这样的向量再除以w就可以把xy坐标归一到[-1,1]区间,把z归一到[0,1]区间。

这样变换后你可以知道屏幕四角的四个点为 TopLeft(-1,1),TopRight(1,1),BottomLeft(-1,-1),BottomRight(1,-1),如果是设z为near或者你传的是二位的投影变换,对应的四个坐标分别是TopLeft(-w/2,h/2),TopRight(w/2,h/2),BottomLeft(-w/2,-h/2),BottomRight(w/2,-h/2).

你现在想得到的结果是知道一个点(x,y,z)在屏幕上的位置,那么在经过透视矩阵的变换,然后再除以w(在这里就是变换前的z),再映射到屏幕上。屏幕的坐标系和这个坐标系是上下颠倒的,而且中心在左上角。那么你把透视后的坐标先变化到屏幕左上角,所以需要x=(x+1)/2, y=(1-y)/2,再拉伸到窗口大小(比如w*h的视口),x=x*w,y=y*h. z的话不需要变化。

这样写成矩阵形式就是
w/2 -h/2 0 0
0 0 0 0
0 0 1 0
w/2 h/2 0 1

如果需要z填入合适的z变换

另一种透视矩阵的计算是fov的
xScale 0 0 0
0 yScale 0 0
0 0 zf/(zf-zn) 1
0 0 -zn*zf/(zf-zn) 0
where:
yScale = cot(fovY/2)

xScale = yScale / (w/h)

可以看到x/y=h/w。
一般我们设fov=PAI/2就是90度,那么yscale=1,xscale=h/w。根据fov的原理可以知道当摄像机距离投影平面的距离=h/2,那么当z=h/2的时候,就是物体在投影平面上的时候,屏幕左上角同样为(w/2,h/2)。(x = w/2 * xscale/z = w/2*xscale/(h/2) = w/2*(h/w)/(h/2) = 1。经过透视变换再乘以上面的矩阵就可以算出屏幕的坐标.

xingzhe2001 2009-07-09
  • 打赏
  • 举报
回复
那你算出的坐标是-1到1的,要变换的屏幕像素的坐标还是需要变换的,乘矩阵是最方便的
realizz 2009-07-08
  • 打赏
  • 举报
回复
楼上可能误解我的意思了,我现在的目的不是为了渲染,而是为了求出空间中“某一个点”经过 “视图空间变换” 再经过 “投影变换”,再经过“视口变换”以后,在屏幕上的坐标点。

也就是说,是不经过渲染pipline的。

还是感谢你一下。继续哭求高手指点。
haodaniu 2009-07-08
  • 打赏
  • 举报
回复
world matrix * camara matrix * proj matrix就会变换成最后的image space,在这里楼主的世界矩阵是单位矩阵,而用DX是不需要乘以视口的,只有做自己的软件渲染器才需要这么乘,一般来说DX的pipeline已经做了最后一步,即你等于多乘了一次视口
tkminigame 2009-07-08
  • 打赏
  • 举报
回复
其实将点转换成相机坐标后就可以直接算出在屏幕的位置了,空间关系非常明确,简单的投影公式就能算出,不用再乘以矩阵。
realizz 2009-07-08
  • 打赏
  • 举报
回复
这里更正一下,手一快,复制错了。

//修改w值,使之为1。
g_vec.w = 1.0f;

应该是

g_vec = g_vec/g_vec.w;

千万不要直接赋值为1,要不也是错的,恩
xingzhe2001 2009-07-08
  • 打赏
  • 举报
回复
mvp._11 = (vp.Width) / 2;
mvp._22 = (-vp.Height) / 2;
mvp._33 = vp.MaxZ - vp.MinZ;
mvp._41 = (vp.Width)/2;
mvp._42 = (vp.Height)/2;
mvp._43 = vp.MinZ;
mvp._44 = 1.0f;

试一下这个,因为directx下经过model*view*proj后坐标的范围应该是xy在[-1,1]。那么再乘以这个矩阵后,xyz={x*w/2+w/2, -y*h/2+h/2}. x的取值范围[0,w], y取值范围[0,h]。 z的话看你要什么值 深度值还是原始的z,如果是深度值,那可以把z除以w,如果是原始的深度,应该直接去w就可以了。
realizz 2009-07-08
  • 打赏
  • 举报
回复
好了总算搞定了,明白问题出在哪里了。

简单的说就是不要使用3次变换的综合变换矩阵,只使用前2次变换,再更改vec的w值,使之为1,再进行“视口变换”。

在进行完“视图空间变换” 和 “投影变换”这2次变换以后,vector的w值会变化,不再为1。再用mvp进行视口变换时,由于_41 _42 的非0存在 使最终的坐标值出现偏差。



D3DXVECTOR4 g_vec; //变换后的坐标
D3DXVECTOR4 vec = D3DXVECTOR4(0.0f,0.0f,0.0f,1.0f); //设此点在world坐标中为原始坐标
D3DVIEWPORT9 vp; //视口
D3DXMATRIX mvp;
//因为下面要直接修改值,所以初始化一下,否则会出 _XX 值为乱值导致的问题
D3DXMatrixIdentity(&mvp); //视口变换的矩阵

DXUTGetD3D9Device()->GetViewport(&vp); //获取当前视口
mvp._11 = (vp.Width) / 2;
mvp._22 = (-vp.Height) / 2;
mvp._33 = vp.MaxZ - vp.MinZ;
mvp._41 = vp.X + (vp.Width)/2;
mvp._42 = vp.Y + (vp.Height)/2;
mvp._43 = vp.MinZ;
mvp._44 = 1.0f;

D3DXMATRIX mtemp; //综合变换矩阵

D3DXMatrixMultiply(&mtemp,g_Camera.GetMatrixCamera(),g_Camera.GetMatrixProj()); //先把视角矩阵和投影矩阵相乘

//下面不要再把viewport的变换乘进去
//D3DXMatrixMultiply(&mtemp,&mtemp,&mvp); //再乘视口变换矩阵

//直接用2次综合变换矩阵去变换vector
D3DXVec4Transform(&g_vec,&vec,&mtemp); //直接用综合变换矩阵去变换vec得到g_vec

//修改w值,使之为1。
g_vec.w = 1.0f;

//再进行视口变换
D3DXVec4Transform(&g_vec,&g_vec,&mvp);

这样结果就对了。

搞了我2天,真晕,希望这个帖子对大家能有用。

8,303

社区成员

发帖
与我相关
我的任务
社区描述
游戏开发相关内容讨论专区
社区管理员
  • 游戏开发
  • 呆呆敲代码的小Y
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧