一个拥有3A游戏梦的不正经大学生|

shadow_lr

园龄:4年10个月粉丝:41关注:1

回顾Games101图形学(一)几何变换中一些公式的推导

回顾Games101 chapter 1 - 6

前言

本文只写回顾后重新加深认识的知识

透视除法的意义

经过MVP矩阵之后,将模型空间下某点的坐标,转换成了裁剪空间下的坐标,此时因为裁剪空间的范围是x[W/2W/2]y[H/2H/2],所以经过以下两个变换,其中除以pz就是透视除法

透视矫正意义和做法:https://stackoverflow.com/questions/24441631/how-exactly-does-opengl-do-perspectively-correct-linear-interpolation
一:

12·(pxpz·near)w112·(pypz·near)h1

二:

[xyzw][100001000010ΔxΔyΔz1]=[x+Δxwy+Δywz+Δzww]

只有当W=1,这个三维坐标转换是等价的,才能保证位移的量是正确的,W=0时,则没有位移

只有当W=1时,三维坐标点转换成四维齐次坐标点才是等价的

坐标系变换和矩阵推导

坐标系变换理解不直观,倾向于101中闫老师所说的理解坐标系的转换通过矩阵进行的线性变换,将A坐标系下的点P,乘上矩阵得出B坐标系下的点P',以下是抛开常见的变换(如透视投影变换、正交投影变换等)如何得出变换矩阵M,通过矩阵变换(下文着重说明)

已知坐标系A和坐标系B

BxyzAuxuyvz0

(vx,vy,vz,0)(wx,wy,wz,0),坐标系B的原点在坐标系A下表示为(Qx,Qy,Qz,1

img

则将坐标系B中一点P从坐标系B变换到坐标系A的变换矩阵为:(注意此处的例子是将源坐标系A变换到目标坐标系B下)

M=[uxuyuz0vxvyvz0wxwywz0QxQyQz1]

如之前所说,变换过程中点p在空间中的绝对位置没有发生改变,只是参考坐标系发生了改变,从B坐标系变到A坐标系。(缩放,旋转,平移变换只有在同一坐标系下才有意义)

矩阵变换是基于基向量组的结果

  • 矩阵变换之于同一个坐标系,可以理解为坐标系不变,点的位置改变
  • 矩阵变换之于不同坐标系,可以理解为点的绝对位置不变,坐标系改变

[xy]=B[xy][xy]=B1[xy]B=[b1b2],且b1b2是坐标系B的基向量

其中,矩阵B的各个列向量分别对应B坐标系的各个基向量,[xy]是向量OP或者说点P在B坐标系的表示,[xy]则是向量OP或者点P在A坐标系中的表示

这里写图片描述

以图中的两个向量b1b2为基确定一个坐标系B,显然在B坐标系中b1B=[10]b2B=[01],接下来,将b1b2定位到A坐标系中,得到b1A=[21]b2A=[11]

OP=2b1+2b2

OP在B坐标系中的表示为[22],现在,将OP用A坐标系描叙:
OP=2b1+2b2=2b1A+2b2A=[b1Ab2A][22]=[24]
现在,令矩阵B=[b1Ab2A],P点是用B坐标系表示的任意一点(x,y)
于是OP在A坐标系中的表示[xy]=B[xy],显然,B是可逆的,于是就有了之前的结论
那么在这个例子当中,当我们需要知道某点在转换坐标系后的新坐标时,通过该例子也可以加深印象,比如在B坐标系下有点Q(3,4),即OQ=(3,4),跟据刚才的例子可以看出它转换在A坐标系下的点

OQ=2b1+2b2=2b1A+2b2A=[b1Ab2A][34]=[2111][54]=[69]

即转换到A坐标系下的点Q的坐标为Q(6,9)

虽然这里的讨论是基于二维的,但是,结论可以扩展到任意维度

阐述结论:

将B坐标系的基向量定位到A坐标系,然后将定位之后的基向量作为矩阵B的列向量,用矩阵B对B坐标系中的点P的坐标进行矩阵变换,将得到点P在A坐标系中的坐标。这个过程,就是从坐标系B到坐标系A的一个追溯过程

View/Camera Transformation

先将相机移到原点,然后进行分别对坐标轴进行旋转,用矩阵表示则是Mview=RviewTview

  • 将相机移回原点

Tview=[100xe010ye001ze0001]

  • RotategtoZ,ttoY,(g×t)ToX
    g是相机看的方向(lookAt),t是相机向上的方向(Up),也就是相机的-Z轴和Y轴,两个向量叉积就是另一个坐标轴

Rview1=[xg^×t^xtxg0yg^×t^ytyg0zg^×t^ztzg00001]

旋转矩阵是正交矩阵,所以旋转矩阵的逆就是旋转矩阵的转置

Rview=[xg^×t^yg^×t^zg^×t^0xtytyt0xgygzg00001]

正交投影矩阵

无论是正交投影还是透视投影,都是要将x、y、z移到-1到1的范围内,先将中心点移到原点,然后缩放

Mortho=(2rl00002tb00002nf00001)(100r+l2010t+b2001n+f20001)

透视投影矩阵推导

首先先将frustum 转变为cuboid(n -> n,f -> f)(Mpersp>ortho)
然后再做正交投影

整个投影变换包括两部分

  • v = P(矩阵)*p
  • v=vvw=vpz透视除法


以上大概推出等式这一步,接下来用公式展示更为直观

(m00m01m02m03m10m11m12m13m20m21m22m23m30m31m32m33)(xyz1)=(xzaspecttan(fov2)yztan(fov2)z1)

m00x+m01y+m02z+m03=xzaspecttan(fov2)

将右边的四维列向量表示的坐标每一项乘以z,所以有

(m00m01m02m03m10m11m12m13m20m21m22m23m30m31m32m33)(xyz1)=(xaspecttan(fov2)ytan(fov2)zzz)

所以求得矩阵为

(1aspecttan(fov2)00001tan(fov2)0000m22m230010)

m22z+m23=zzm22+m23z=z

因为z=zNear时,z''=-1;z=zFar时,z''=1所以有以下等式

m22+m23zNear=1m22+m23zFar=1

联立求得:

m22=zFarzNearzNearzFarm23=2zFarzNearzNearzFar

最后求得投影矩阵为

(1aspecttan(fov2)00001tan(fov2)0000zFarzNearzNearzFar2zNearzFarzNearzFar0010)

将这样得矩阵乘以视锥体内的一个顶点坐标,得到一个新的向量,再将这个向量的每个分量除以第四个分量(此步骤也被称为透视除法)(w),这样就可以得到顶点映射到规则立方观察体后的新的坐标

注意:z坐标的映射方式的获得,最后我们是为了方便矩阵乘法的操作方向求得了z坐标与cvv中的z坐标的映射方式:

m22+m23z=z

此时的映射并不是线性的,当z越大时,z的变化对z''的扰动越小

Canonical Cube to Screen

  • Irrelevant to z
  • Transform in xy plane : [-1, 1] to [0, width] × [0, height]
  • Viewport transform matrix:

视口矩阵

Mviewport=[width200width20height20height200100001]

深度z的计算

前言
3D光栅化发生在图元被变换到Screen space之后,因为这里的Screen space与2D的Screen Space完全一致,所以2D的光栅化算法在这里依然适用。

然而由于图元经过了投影变换,且投影变换为非线性变换,所以不能用简单的线性插值获取fragment的属性

投影变换不会保持相对距离不变性
如上图所示,view space中的线段v0v1上两点p0(p0x,p0y,p0z,1)p1(p1x,p1y,p1z,1)在near plane上的投影为点s0(s0x,s0y)s1(s1x,s1y)p0p1中间一点v(vx,vy,vz,1)在near plane上的投影为点q(qx,qy)。从图中可以看出点v到p0,p1的距离比值与点q到s0,s1的距离比值完全不同,投影变换不保持距离不变。

为了执行z-buffer算法,需要通过点q获取到v的深度值(z)
v的深度值可以通过如下方法插值得到:

vz=1cp1z+(1c)p0z

以下是推导的过程:

手写版:

文字版:
由于点q为点v在near plane上的投影,因此点q与点v的关系为:

  • qx=vx·nearvz
    v位于p0p1之间,则
  • vz=p0z+t·(p1zp0z)=vx·nearqx
    由点vp0p1之间,点qs0s1之间则有
  • vx=p0x·(1t)+p1x·t=p0x+t·(p1xp0x)
  • qx=s0x·(1c)+s1x·c=s0x+c·(s1xs0x)
    代入式(1)可得
    vz=vx·nearqx=(p0x+t·(p1xp0x))·nears0x+c·(s1xs0x)式(2)
    又s0和s1分别为p0和p1在near plane上的投影,则:
  • s0x=p0x·nearp1z
  • s1x=p1x·nearp1z
    代入式(2)可得:

vz=(p0x·s0xnear+t·(p1x·s1xnearp0x·s0xnear))·nears0x+c·(s1xs0x)

vz=(p0x·s0xnear+t·(p1x·s1xnearp0x·s0xnear))·nears0x+c·(s1xs0x)vz=(p0x·s0x+t·(p1x·s1xp0x·s0x))s0x+c·(s1xs0x)p0z+t·(p1zp0z)=(p0x·s0x+t·(p1x·s1xp0x·s0x))s0x+c·(s1xs0x)(p0z+t·(p1zp0z))·(s0x+c·(s1xs0x))=p0x·s0x+t·(p1x·s1xp0x·s0x)p0z·s0x+p0z·c·(s1xs0x)+t·(p1zp0z)·s0x+t·c·(p1zp0z)·(s1xs0x)=p0x·s0x+t·(p1x·s1xp0x·s0x)

化简得:

t·(p1zc·(p1zp0z))=c·p0z

则:

t=c·p0zc·p0z+(1c)·p1z

代入式(1)可得

vz=p0z+t·(p1zp0z)

vz=p0z+c·p0zc·p0z+(1c)·p1z·(p1zp0z)

vz=1cp1z+(1c)p0z

若View Space中三角形v0v1v2,变换到Screen Space后为三角形s0s1s2v0v1v2内一点v在Screen Space的投影点s0s1s2内的点q,对三角形s0s1s2内的点(fragment)q,可以通过如下方法取得fragmentq在View Space中对应的深度值:

q.z=v.z=1λ0v0.z+λ1v1.z+λ2v2.z

λ0,λ1,λ2为点p在三角形s0s1s2内的重心坐标
引入结论:
对Screen Space三角形s0,s1,s2内一点p的任意属性插值的公式为:

Atribute(p)=z·(λ0·Atribute(v0)z0+λ1·Atribute(v1)z1+λ2·Atribute(v2)z2)

λ0,λ1,λ2为点p的重心坐标,z0,z1,z2,z分别为s0,s1,s2,p在view space中对应点的深度值,可以用这个方法插值得到p在NDC Space内对应点的深度值
此处贴一下Games101作业框架中关于深度的计算,与上述公式对应z=zinterpolatedwreciprocal

    auto[alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
    float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
    float z_interpolated =
    alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
    z_interpolated *= w_reciprocal;

    if (depth_buf[get_index(x, y)] > z_interpolated) {
        depth_buf[get_index(x, y)] = z_interpolated;
        Eigen::Vector3f point;
        point << static_cast<float>(x), static_cast<float>(y), z_interpolated;
        set_pixel(point, t.getColor());
    }

罗德里格斯旋转公式

指定任意轴k旋转α角得出旋转矩阵
字写得不好,在爬了...
手写版:

文字版:
首先先将k处理成单位向量,这点很重要,关乎着下一步等式是否成立,有些博文写这里不需要处理单位向量,这是错的

v·k=|v|·|k|·cos<v,k>=|v|·cos<v,k>

可得

v||=|v|·cos<v,k>·kv=v+v||v=vv||=v(v·k)k

k做旋转时,向下做垂线,可看作底部经过了类似半圆的旋转
要求得vrot=v||+vrot,将vrot作正交分解有vrot=a+b,易得|w|=|v|,则有w=k×v=k×[vv||]=k×vk×v||=k×v0=k×v
接下来求ab

|a|=|vrot|·cos(θ90)=|vrot|·sin(θ)a=w|w|·|a|=w|vrot|·|vrot|·sin(θ)=w·sin(θ)

|b|=|vrot|·cos(180θ)=|vrot|·cos(θ)b=v|v|·|b|=v|v|·|vrot|·cos(θ)=v·cos(θ)注意|v|=|vrot|

vrot=a+b=w·sin(θ)+v·cos(θ)=sin(θ)·(k×v)+cos(θ)(v(v·k)k)vrot=v||+vrot=(v·k)k+sin(θ)·(k×v)+cos(θ)(v(v·k)k)=cos(θ)v+(1cos(θ)(v·k)k)+sin(θ)·(k×v)

kv分别写为列向量

k=(kxkykz)

v=(vxvyvz)

vrot=R·v
两个式子

(v·k)k=k(v·k)=k(kT·v)

k×v=[kyvzkzvykzvxkxvzkxvykyvx]=[0kzkykz0kxkykx0][vxvyvz]

结合以上两个式子可得,其中I为3×3的单位矩阵

R=Icos(θ)+(1cos(θ))(kxkykz)(kxkykz)+sin(θ)(0kzkykz0kxkykx0)

以下是比较通用的表示方式

R(n,α)=cos(α)I+(1cos(α))nnT+sin(α)(0nznynz0nxnynx0)

部分引用的博文

https://blog.csdn.net/unclerunning/article/details/70948696#齐次坐标系与平移

https://zhuanlan.zhihu.com/p/45757899

本文作者:shadow_lr

本文链接:https://www.cnblogs.com/shadow-lr/p/Games101-MyNote1.html

版权声明:本作品采用shadow-lr许可协议进行许可。

posted @   shadow_lr  阅读(914)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起