首先,如果您想了解 GLM lookAt 算法的解释,请查看针对此问题提供的答案:https://stackoverflow.com/a/19740748/1525061
mat4x4 lookAt(vec3 const & eye, vec3 const & center, vec3 const & up)
{
vec3 f = normalize(center - eye);
vec3 u = normalize(up);
vec3 s = normalize(cross(f, u));
u = cross(s, f);
mat4x4 Result(1);
Result[0][0] = s.x;
Result[1][0] = s.y;
Result[2][0] = s.z;
Result[0][1] = u.x;
Result[1][1] = u.y;
Result[2][1] = u.z;
Result[0][2] =-f.x;
Result[1][2] =-f.y;
Result[2][2] =-f.z;
Result[3][0] =-dot(s, eye);
Result[3][1] =-dot(u, eye);
Result[3][2] = dot(f, eye);
return Result;
}
现在我要告诉你为什么我似乎对这个算法有概念上的问题。这个 View 矩阵有两个部分,平移和旋转。平移进行正确的逆变换,将相机位置带到原点,而不是相机的原点位置。同样,您希望相机定义的旋转在被放入此 View 矩阵之前也被反转。我在这里看不到这种情况,这是我的问题。
考虑前向 vector ,这是您的相机所注视的位置。因此,这个前向 vector 需要映射到 -Z 轴,这是 openGL 使用的前向方向。这个 View 矩阵的工作方式是通过在 View 矩阵的列中创建一个正交基,所以当你在这个矩阵的右侧乘以一个顶点时,你实际上只是将它的坐标转换为不同轴的坐标.
当我在脑海中播放由于这种变换而发生的旋转时,我看到的旋转不是相机的反向旋转,就像假设发生的那样,而是我看到的是非反向旋转。也就是说,我没有找到映射到 -Z 轴的前向相机,而是发现映射到前向相机的 -Z 轴。
如果您不明白我的意思,请考虑此处发生的同一类型事物的二维示例。假设正向 vector 是 (sqr(2)/2 , sqr(2)/2) 或 45 度的 sin/cos,我们还假设此 2D 相机的侧向 vector 是 -45 度的 sin/cos。我们想将这个前向 vector 映射到 (0,1),即正 Y 轴。正 Y 轴可以被认为是 openGL 空间中的 -Z 轴的类比。让我们考虑一个与前向 vector 方向相同的顶点,即 (1,1)。通过使用 GLM.lookAt 的逻辑,我们应该能够通过使用由第一列中的前向 vector 和第二列中的侧向 vector 组成的 2x2 矩阵将 (1,1) 映射到 Y 轴。这是该计算的等效计算 http://www.wolframalpha.com/input/?i=%28sqr%282%29%2F2+%2C+sqr%282%29%2F2%29++1+%2B+%28sqr%282%29%2F2%2C+-sqr%282%29%2F2+%29+1 .
请注意,您没有像您希望的那样将 (1,1) 顶点映射到正 Y 轴,而是将它映射到正 X 轴。如果应用此变换,您可能还会考虑正 Y 轴上的顶点发生了什么。果然转化为前向 vector 。
因此,GLM 算法似乎发生了一些非常可疑的事情。但是,我怀疑这个算法是不正确的,因为它是如此流行。我错过了什么?
最佳答案
查看 Mesa 中的 GLU 源代码:http://cgit.freedesktop.org/mesa/glu/tree/src/libutil/project.c
首先在 gluPerspective 的实现中,注意 -1 使用索引 [2][3] 和 -2 * zNear * zFar/(zFar - zNear) 正在使用 [3][2]。这意味着索引是 [column][row]。
现在在gluLookAt的实现中,第一行设置为side,下一行设置为up,最后一行设置为-转发。这将为您提供旋转矩阵,该矩阵乘以将眼睛带到原点的平移。
GLM 似乎使用相同的 [column][row] 索引(来自代码)。您刚刚为 lookAt 发布的文章与更标准的 gluLookAt 一致(包括翻译部分)。所以至少 GLM 和 GLU 同意。
然后让我们逐步推导完整的构造。注意 C 中心位置和 E 眼睛位置。
移动整个场景以将眼睛位置放在原点,即应用 -E 的平移。
旋转场景以将相机的轴与标准的(x, y, z) 轴对齐。
2.1 计算相机的正正交基:
f = normalize(C - E) (pointing towards the center)
s = normalize(f x u) (pointing to the right side of the eye)
u = s x f (pointing up)
据此,(s, u, -f) 是相机的正正交基。
2.2 找到将 (s, u, -f) 轴与标准轴 (x, y, z) 对齐的旋转矩阵 。逆旋转矩阵 R R^-1 做相反的事情,将标准轴与相机轴对齐,根据定义,这意味着:
(sx ux -fx)
R^-1 = (sy uy -fy)
(sz uz -fz)
因为 R^-1 = R^T,我们有:
( sx sy sz)
R = ( ux uy uz)
(-fx -fy -fz)
将平移与旋转相结合。点 M 通过“查看”变换映射到 R (M - E) = R M - R E = R M + t。所以最终“看”的 4x4 变换矩阵确实是:
( sx sy sz tx ) ( sx sy sz -s.E )
L = ( ux uy uz ty ) = ( ux uy uz -u.E )
(-fx -fy -fz tz ) (-fx -fy -fz f.E )
( 0 0 0 1 ) ( 0 0 0 1 )
所以当你写的时候:
That is, instead of finding the camera forward being mapped to the -Z axis, I find the -Z axis being mapped to the camera forward.
这非常令人惊讶,因为根据构造,“观察”变换将相机前向轴映射到 -z 轴。这种“观察”变换应该被认为是移动整个场景以将相机与标准原点/轴对齐,它确实是这样做的。
使用您的 2D 示例:
By using the logic of GLM.lookAt, we should be able to map (1,1) to the Y axis by using a 2x2 matrix that consists of the forward vector in the first column and the side vector in the second column.
相反,按照我描述的构造,您需要一个 2x2 矩阵,其中正向和行 vector 作为 rows 而不是列来映射 (1, 1) 和另一个 vector 到 y和 x 轴。要使用矩阵系数的定义,您需要通过变换获得标准基 vector 的图像。这直接给出了矩阵的列。但是由于您要寻找的是相反的(将 vector 映射到标准基 vector ),因此您必须反转转换(转置,因为它是旋转)。然后你的引用 vector 变成行而不是列。
关于c++ - 我对 GLM lookAt 函数的理解不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21152556/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio