OpenGL教程翻译 第十一课 Concatenating Transformations

OpenGL教程翻译 第十一课 Concatenating Transformations

原文地址:http://ogldev.atspace.co.uk/(源码请从原文主页下载)

Background

在前几章中我们学习了一些变换,通过它们我们能将3D世界中的物体灵活的移动到任何位置。之后我们还要学习两个变换(相机控制和透视投影),但是正如你可能已经猜到的,我们需要一个变换的组合。在很多情况下,你需要缩放物体以适应你的3D世界,将其旋转到需要的方向,平移到某处等等。直到现在,我们一直在运用每次仅进行一个单一的变换。为了实现上述一系列的变换,我们需要用顶点坐标与第一个变换矩阵相乘,然后将前面的结果与下一个的变换矩阵相乘。如此持续到所有的转换矩阵都应用到顶点上面。做此事一个普遍的方法是向着色器提供所有的变换矩阵并依次让他们做相乘运算。然而,这种方法非常低效,因为对于所有的顶点来说变换矩阵都是一样的,而只有顶点位置在改变而已。幸运地是,线性代数提供了一套规则使得我们的处理变得简单。这个规则告诉我们,给定一组矩阵M0...Mn和一个向量V,以下式子恒成立:

Mn * Mn-1 * ... * M0 * V = (Mn* Mn-1 * ... * M0) * V

所以如果我们计算:

N = Mn * Mn-1 * ... * M0

然后:

Mn * Mn-1 * ... * M0 * V = N * V

这意味着我们可以计算N一次,然后将其作为一致变量传递到着色器和每个顶点相乘。这就要求GPU每个顶点进行一次矩阵或者向量相乘。

当我们生成N时你是怎么安排矩阵顺序的?你需要牢记得第一件事情是向量需要首先和最右边的矩阵相乘(本例中是M0)。然后从右向左,向量依次被每个矩阵变换。在3D图形中,你常常想先缩放物体然后旋转、平移,然后应用相机变换,最后将它投射到2D上。让我们看看当你先旋转再平移后发生了什么:

现在我们再看看先平移再旋转发生了什么:


正如你看到的,在世界坐标系中,当你先平移物体后很难设置它的位置,因为如果你将其从原点移开然后再旋转,它会围绕着原点旋转,这实际上等价于你再次平移了这个物体。这第二次的平移是你想避免的。通过先旋转然后平移,你断开了这两种操作的依赖关系。这就是为什么建模应围绕原点越对称越好。这样当你缩放或者旋转物体时都没有副作用,被旋转或缩放的物体仍然保持对称。

既然我们开始处理多个矩阵的变换,那么我们必须丢弃直接更新渲染函数中的矩阵的习惯。这个方法效果不怎么好还容易出错。取而代之我们引进了管线类。这个类隐藏了简单API背后为改变平移、旋转等而进行的矩阵运算的具体细节。在设置了其内部所有的参数后,提取结合了所有变换的最终矩阵。这个矩阵可直接被送到着色器中。

Code Walkthru

#defineToRadian(x) ((x) * M_PI / 180.0f)
#define ToDegree(x) ((x) *180.0f / M_PI)

在这章我们开始使用角度的实际值。碰巧的是,标准C语言库里面使用弧度作为参数。上面的宏命令将角度和弧度互换。

 

inline Matrix4foperator*(const Matrix4f& Right) const
{
    Matrix4fRet;
    for(unsigned int i = 0 ; i < 4 ; i++) {
       for (unsigned int j = 0 ; j < 4 ; j++) {


           Ret.m[i][j] = m[i][0] * Right.m[0][j] +
                  m[i][1]* Right.m[1][j] +
                  m[i][2] * Right.m[2][j]+
                  m[i][3] * Right.m[3][j];


      }
    }

    returnRet;
}

这个矩阵的便利操作类处理了矩阵相乘。正如你看到的,在结果矩阵中的每个条目是左边矩阵本行和右边矩阵本列的数量积。这个操作在管线类的实现上至关重要。

 

class Pipeline

{

       public:

              Pipeline()

              { ...  }

              void Scale(floatScaleX, float ScaleY, float ScaleZ)

              { ... }

              void WorldPos(float x,float y, float z)

              { ... }

              void Rotate(floatRotateX, float RotateY, float RotateZ)

              { ... }

              const Matrix4f*GetTrans();

       private:

              Vector3f m_scale;

              Vector3f m_worldPos;

              Vector3f m_rotateInfo;

              Matrix4fm_transformation;

};

管道类抽象了将一个物体所需的所有的转换相结合的细节。这里有三个私有成员向量来储存缩放、世界坐标位置和绕哪个轴的旋转。另外还有API来设置他们,以及一个用来得到代表这些变换的最终矩阵的函数。

const Matrix4f*Pipeline::GetTrans()

{

       Matrix4f ScaleTrans, RotateTrans, TranslationTrans;

       InitScaleTransform(ScaleTrans);

       InitRotateTransform(RotateTrans);

       InitTranslationTransform(TranslationTrans);

       m_transformation = TranslationTrans * RotateTrans *ScaleTrans;

       return &m_transformation;

}

这个函数初始化了三个互不相关的矩阵,作为匹配当前配置的变换。它把他们一个一个相乘起来,返回最后的结果。请注意这个顺序是硬编码的,遵循上面的描述。如果你需要一些灵活性,你可以使用一个位掩码来指明顺序。也需要注意它总是将最后的变换储存下来作为成员。如果上次调用过此函数后配置没有发生变化,你可以尝试通过检查一个不讨好的标志返回储存的矩阵来优化这个函数。

这个函数使用私有方法,根据我们在前面几章所学的,来生成不同的变换。接下来的章节中,这个类将被扩展来处理相机控制和透视投影。

 

Pipeline p;

p.Scale(sinf(Scale * 0.1f),sinf(Scale * 0.1f), sinf(Scale * 0.1f));

p.WorldPos(sinf(Scale),0.0f, 0.0f);

p.Rotate(sinf(Scale) *90.0f, sinf(Scale) * 90.0f, sinf(Scale) * 90.0f);

glUniformMatrix4fv(gWorldLocation,1, GL_TRUE, (const GLfloat*)p.GetTrans());

这些是渲染函数的变化。我们指定一个管线类对象,配置它并且将结果变换传递到着色器。修改这些参数,在最后的图像中来看看它们的效果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行程实践、外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值