百科问答小站 logo
百科问答小站 font logo



如何还原被摄像机透视的图形? 第1页

  

user avatar   he-dao-38 网友的相关建议: 
      

最近在看[OpenGL的教程](

Tutorial 3 : Matrices

),有一些新的理解。补充一下

@韦易笑

大神的答案(主要是前面等式的由来)。

首先,在计算机图形学里面,经常使用4个数(齐次坐标)来表示一个3维空间的点,即使用(x,y,z,w) 来代替(x,y,z)。

This will be more clear soon, but for now, just remember this :

  • If w == 1, then the vector (x,y,z,1) is a position in space.
  • If w == 0, then the vector (x,y,z,0) is a direction.

(In fact, remember this forever.)

齐次坐标最直观的好处就是实现点的平移。例如,一个把点往x正方向平移1的矩阵:

一般地,可以把转换矩阵表示为

,其中R为旋转(Rotation)矩阵,T为平移(Translation)矩阵。注意到此类矩阵相乘之后,仍然是原来的形式。

下面简单介绍一下摄像机成像原理:

The engines don’t move the ship at all. The ship stays where it is and the engines move the universe around it.

Futurama

This is the single most important tutorial of the whole set. Be sure to read it at least eight times.

涉及三个坐标系:模型坐标,世界坐标,摄像机坐标。

模型坐标为以物体为参照物的坐标系:

世界坐标系以物体外的一点作为原点:

空间中的一个点,在不同坐标系下的表示是不同的,一般情况下,坐标系之间的变换是线性的(缩放->旋转->平移),可以用矩阵表示这个变换。以图上猴子的鼻尖为例,它在模型(model)坐标系下的坐标为

,在世界(world)坐标系下的坐标为:

至于

具体是怎样的由实际变换决定。

然后,摄像机坐标系:

由世界坐标系到摄像机坐标系的转换同样是线性的:

同样是待定的。

综上:

把两个矩阵相乘,得到:

上面的公式主要是为了说明线性变换的坐标系可以用矩阵表示。然后,通过选取合适的坐标系,可以大大简化计算,以题目为例:

1. 在模型坐标系里面,选取桌面左下角为坐标系的原点,往右为x轴,往上为y轴,整个桌面在z=0平面上。

2. 在摄像机坐标系里面,选取成像焦点为原点,z=1为投影面。使得点

投影为坐标


由于Zm为0,因此削掉第三列和第四行,顺便改一下符号

然后,可以接着看韦大的第5点了。

不过我看不懂为什么第6步里面可以设M22=1的。


user avatar   skywind3000 网友的相关建议: 
      

多年没碰图形,不知是否存在标准做法。有也无妨,我们可以从数学上推导一下:

三维坐标(x,y,z)和纹理坐标(u, v)承线性关系。根据题主问题,可以理解为已知四个点的屏幕投影坐标(xi,yi),和对应纹理坐标(u,v),求整个纹理坐标系到屏幕坐标系的反向映射过程,即根据(u,v)求解(xi,yi)。

1. 按照纹理隐射的原理,同平面纹理坐标与空间坐标存在线性关系,设 a1-a12为常数,有:

       a1 * x +  a2 * y +  a3 * z +  a4 = u ... 线性关系 a5 * x +  a6 * y +  a7 * z +  a8 = v ... 线性关系 a9 * x + a10 * y + a11 * z + a12 = 0 ... 平面方程     

2. 求解上面的方程组,可以得到类似下面的关系,其中b1-b9为常数:

       x = b1 * u + b2 * v + b3   y = b4 * u + b5 * v + b6  z = b7 * u + b8 * v + b9      

常数 b1-b9如果展开,就是9个关于a1-a12的等式,太长了,这里不展开,有兴趣可以自己求解。

3. 屏幕上投影坐标一般是:

                  x xi = D1 * --- + C1            z                    x yi = D2 * --- + C2            z     

因为同样一个透视投影矩阵下,能隐射成屏幕上同样形状纹理的平面,在空间中存在无穷多个,而且还存在不同透视投影矩阵下,同样屏幕投影的平面存在更多无穷多个。这里我们不用去求解每个平面,直接设置 D1 = D2 = 1 且 C1 = C2 = 0 有:

             x xi = ---       z               x yi = ---       z     

4. 展开等式:

              b1 * u + b2 * v + b3 xi =  ----------------------        b7 * u + b8 * v + b9         b4 * u + b5 * v + b6 yi =  ----------------------        b7 * u + b8 * v + b9     

改变一下,用矩阵 M的各个元素 m00 - m22来代替 b1-b9。

             m00 * u + m01 * v + m02 xi = -------------------------       m20 * u + m21 * v + m22        m10 * u + m11 * v + m12 yi = -------------------------       m20 * u + m21 * v + m22     

5. 于是整个问题很简单了,就是求矩阵 M。我们知道存在矩阵 M,使得:

       / m00 m01 m02    / u  | m10 m11 m12 | . | v | = [ uo, vo, wo ] = [ xi * wo, yi * wo, wo ]  m20 m21 m22 /    1 /                 m00 * u + m01 * v + m02 xi = uo / wo = ------------------------  .......(4)                m20 * u + m21 * v + m22                 m10 * u + m11 * v + m12 yi = uo / wo = ------------------------  .......(5)                m20 * u + m21 * v + m22     

6. 再此基础上,我们已知屏幕上四个端点的位置(x0, y0)-> (x3, y3), 也知道他们对应的纹理坐标,(u0, v0) -> (u3, v3)。继续设 m22 = 1,可以列出一个方程:

       / u0 v0  1  0  0  0 -u0*x0 -v0*x0  /m00 /x0 | u1 v1  1  0  0  0 -u1*x1 -v1*x1 | |m01| |x1| | u2 v2  1  0  0  0 -u2*x2 -v2*x2 | |m02| |x2| | u3 v3  1  0  0  0 -u3*x3 -v3*x3 |.|m10|=|x3| |  0  0  0 u0 v0  1 -u0*y0 -v0*y0 | |m11| |y0| |  0  0  0 u1 v1  1 -u1*y1 -v1*y1 | |m12| |y1| |  0  0  0 u2 v2  1 -u2*y2 -v2*y2 | |m20| |y2|   0  0  0 u3 v3  1 -u3*y3 -v3*y3 / m21/ y3/     

对于这个方程组,四个端点的 x, y是已知的,同时纹理坐标 u, v也是已知的。那么我们解个方程就能求出矩阵 M的各个元素。现在你已经有了 m00 - m22,有了式子4,和式子5:

           / m00 m01 m02   M = | m10 m11 m12 |   .....  已知      m20 m21 m22 /         m00 * u + m01 * v + m02 xi = -------------------------    ... (4)       m20 * u + m21 * v + m22        m10 * u + m11 * v + m12 yi = -------------------------    ... (5)       m20 * u + m21 * v + m22     

7. 在已知 M矩阵的各个元素,已知纹理坐标(u, v),你想求对应的屏幕坐标(x, y),就可以用公式(4)和公式(5)来求解了。假设纹理为 256x256大小,最终求解原纹理图片的代码应该是:

       for (int v = 0; v < 256; v++) {      for (int u = 0; u < 256; u++) {           int x = CalculateX(M, u, v);  // 带入式子(4)           int y = CalculateY(M, u, v);  // 带入式子(5)           texture[v * 256 + u] = GetPixel(x, y);      } }     

8. 优化一下,上面每个点的乘除法太多,我们需要找找关系,设:

       A = m00 * u + m01 * v + m02     B = m10 * u + m11 * v + m12     C = m20 * u + m21 * v + m22        

于是有:x = A / C, y = B / C,而随着 u坐标的均匀递增 A, B, C成线性变化,那么我们可以用差值来完成我们的优化:

       for (int v = 0; v < 256; v++) {  float A1 = m00 *   0 + m01 * v + m02;  float A2 = m00 * 256 + m01 * v + m02;  float B1 = m10 *   0 + m11 * v + m12;  float B2 = m10 * 256 + m11 * v + m12;  float C1 = m20 *   0 + m21 * v + m22;  float C2 = m20 * 256 + m21 * v + m22;   float dA = (A2 - A1) * (1 / 256.0);   // 计算步长  float dB = (B2 - B1) * (1 / 256.0);     float dC = (C2 - C1) * (1 / 256.0);     float A = A1 + 0.5;  // 方便取整时四舍五入  float B = B1 + 0.5;    float C = C1 + 0.5;   uint32_t *pixel = &texture[v * 256];   for (int u = 0; u < 256; u++) {   float i = 1.0 / C;    // 减少一次除法   int x = (int)(A * i);   int y = (int)(B * i);   pixel[0] = GetPixel(x, y);   pixel++;   A += dA;   B += dB;   C += dC;  } }     

整个程序基本上就是这个样子了,内循环已经足够紧凑。再次观察等式关系,可以发现其实:dA = m00, dB = m10, dC = m20。还可以继续进一步化解外层循环。是不是和透视纹理绘制方法很像?一个正过程,一个逆过程而已。

注意1:越界处理,简单判断下越界坐标。

注意2:如果还需要进一步提高性能,可以尝试定点数优化计算过程,尝试继续展开循环。

(完)




  

相关话题

  游戏画面的油腻感是从何而来的? 
  为什么中国做游戏引擎的公司没有像美国的unity unreal一样能跑的出来? 
  如何评价谷歌提出的 Pix2Seq:将目标检测看成语言建模,效果超过 DETR? 
  计算机图形学,下一步如何提高? 
  计算机图形学领域还有哪些没有啃到肉的问题? 
  OpenAI 发布文字生成图像工具 DALL·E 2,它的画作水平如何?从技术角度如何评价它的能力? 
  为什么时至今日仍有人质疑手机的图形性能? 
  有没有什么程序库使得我们可以比较方便的在windows下使用比较新版本的opengl的? 
  如何评价光子游戏工作室发布的《虚幻4》渲染短片? 
  OpenGL会淘汰吗? 

前一个讨论
如何看待很多女生要求男生一米八家境好上进心等等等等?
下一个讨论
如何看待苦难造就人这种逻辑?





© 2024-05-18 - tinynew.org. All Rights Reserved.
© 2024-05-18 - tinynew.org. 保留所有权利