SceneGraph(场景图) 简介

场景图介绍

该节内容翻译自gemedev的一篇文章 blog-SceneGraph Introduction

什么是场景图

场景图是一种将数据排序到层次结构中的方法,在层次结构中父节点影响子节点。你可能会说“这不是树吗?”你说得没错,场景图就是一棵n-tree。也就是说,它可以有任意多的孩子。但是场景图比一棵简单的树要复杂一些。它们表示在处理子对象之前要执行的某些操作。如果现在对这个概念不好理解,不用担心,这一切都会在后面的内容中给出解释。

为什么场景图有用

如果你还没有发现为什么场景图如此酷,那么让我来解释一下场景图的一些细节。假设你需要在你的游戏中模拟太阳系。这个系统里面,在中心有一颗恒星,带有两颗行星。每个行星也有两颗卫星。有两种方式可以实现这个功能。 我们可以为太阳系中的每个物体创建一个复杂的行为函数,但是如果设计师想要改变行星的位置,那么通过改变所有其他围绕它旋转的物体,就有很多工作要做。 另一个选择是创建一个场景图,让我们的生活变得简单。下图显示了如何创建场景图来表示对象:

SceneGraph(场景图) 简介

假设旋转节点保存当前世界矩阵,并将其与旋转相乘。这将影响其后渲染的所有其他对象。所以有了这个场景图,让我们看看这个场景图的逻辑流程。

  • 绘制Star
  • 保存当前的矩阵(star)
    • 执行旋转(star)
    • 绘制Planet 1
    • 保存当前的矩阵(planet1)
      • 执行旋转(planet1)
      • 绘制Moon A
      • 绘制Moon B
    • 恢复保存的矩阵(planet1)
    • 绘制Planet2
    • 保存当前的矩阵(Planet2)
      • 执行旋转(Planet2)
      • 绘制Moon C
      • 绘制Moon D
    • 恢复保存的矩阵(Planet2)
  • 恢复保存的矩阵(star)

这是一个非常简单的场景图的实现,你也应该发现为什么场景图是一个值得拥有的东西。但你可能会对自己说,这很容易做到,只要硬编码就可以了。场景图的优势在于场景图的显示方式可以不通过硬编码的方式实现,虽然对于你能想象到的节点,比如旋转,渲染等是硬编码实现的。基于这些知识,我们可以将上面的场景变得更加复杂,let's do it。让我们在太阳系中增加一些生命,让1号行星稍微摇晃一下。是的,1号行星被一颗大小行星撞击,现在正稍微偏离其轴旋转。不用担心,我们只需要创建一个抖动节点,并在绘制行星1之前设置它。

SceneGraph(场景图) 简介

但是行星1的摆动对我来说还不够真实,让我们继续这样做,让这两颗行星以不同的速度旋转。

SceneGraph(场景图) 简介

现在,这个场景图比最初呈现的要复杂得多,现在让我们来看看程序的逻辑流程。

  • 绘制Star
  • 保存当前的矩阵
    • 应用旋转
      • 保存当前的矩阵
        • 应用抖动
        • 绘制planet1
          • 保存当前的矩阵
            • 应用旋转
              • 绘制Moon A
              • 绘制Moon B
          • 恢复矩阵
      • 恢复矩阵
  • 恢复矩阵
  • 保存当前的矩阵
    • 应用旋转
      • 绘制planet2
      • 保存当前的矩阵
        • 应用旋转
        • 绘制Moon C
        • 绘制Moon D
      • 恢复矩阵
  • 恢复矩阵

真的!现在这只是一个简单的太阳系模型!想象一下,如果我们模仿这个级别的其他部分会发生什么。

简单实现示例

我认为这已经足够对场景图进行高层次的讨论了,让我们来谈谈我们将如何实现它们。为此,我们需要一个基类,以便从所有场景图节点派生。

class CSceneNode
{
public:
  // constructor
  CSceneNode() { }

  // destructor - calls destroy
  virtual ~CSceneNode() { Destroy(); }

  // release this object from memory
  void Release() { delete this; }

  // update our scene node
  virtual void Update()
  {
    // loop through the list and update the children
    for( std::list<CSceneNode*>::iterator i = m_lstChildren.begin();
         i != m_lstChildren.end(); i++ )
    {
      (*i)->Update();
    }
  }

  // destroy all the children
  void Destroy()
  {
    for( std::list<CSceneNode*>::iterator i = m_lstChildren.begin();
         i != m_lstChildren.end(); i++ )
      (*i)->Release();
  
    m_lstChildren.clear();
  }

  // add a child to our custody
  void AddChild( CSceneNode* pNode )
  {
    m_lstChildren.push_back(pNode);
  }

protected:
  // list of children
  std::list<CSceneNode*> m_lstChildren;
}

现在这已经超出了我们的方式,我们现在可以做一个我们享有的所有类型的节点的清单。这是我认为每个场景图都应该具有的节点列表。当然,如果你觉得合适的话,你可以添加新的类型。

  • Geometry Node
  • DOF(下面会有解释)
  • Rotation(animated)
  • Scaling(animated)
  • Translating(animated)
  • Animated DOF
  • Switch

对于一个基本的场景图引擎来说,这应该足够了。你总是可以在你的引擎里添加更多的东西,使它成为最好的新东西。

Geometry Node

会有一个没有图形的图形引擎么?这是不可能的。所以,现在介绍一下最重要的节点:

class CGeometryNode: public CSceneNode
{
public:
  CGeometryNode() { }
  ~CGeometryNode() { }

  void Update()
  {
  	// Draw our geometry here!

  	CSceneNode::Update();
  }
};

注意,上面的渲染代码上有点敷衍。你应该对于如何处理这个节点,是非常清楚的。先执行几何体的渲染(或将其发送到要渲染的位置),然后更新我们的子对象。

DOF

DOF节点通常称为变换。它们只不过是一个表示偏移、旋转或缩放的矩阵。如果不想将矩阵存储在Geometry Node中,这些选项非常有用。在下一个示例中,我们假设使用OpenGL进行渲染。

class CDOFNode: public CSceneNode
{
public:
  CDOFNode() { }
  ~CDOFNode() { }

  void Initialize( float m[4][4] )
  {
    for( int i = 0; i < 4; i++ )
      for( int j = 0; j < 4; j++ )
        m_fvMatrix[i][j] = m[i][j];
  }

  void Update()
  {
    glPushMatrix();
    glLoadMatrix( (float*)m_fvMatrix );

    CSceneNode::Update();

    glPopMatrix();
  }

private:
  float m_fvMatrix[4][4];
};

Switch Node

switch节点开始显示一些可以使用场景图执行的更复杂的操作。交换节点的作用就像铁路上的一个交叉点,只允许您选择以下路径之一(可以将它们更改为沿着两条路径,但这将由读者来完成)。让我们看一幅场景图,图中有一个开关节点。

SceneGraph(场景图) 简介

现在对于场景图的这一部分,开关表示赛车游戏中的车门。由于这辆车损坏了,我们想证明它正在损坏。当我们开始比赛时,我们希望赛车不会受到任何损坏,但随着赛车在水平面上的前进,受到的损坏越来越多,我们需要将路径切换到损坏更严重的车门上。我们甚至可以扩展这一范围,使受损更严重的身体部位在产生烟雾效应后附着粒子系统。你的想象力限制了这种可能性。

原文链接: https://www.cnblogs.com/grass-and-moon/p/13637387.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    SceneGraph(场景图) 简介

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/202544

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月12日 下午9:12
下一篇 2023年2月12日 下午9:12

相关推荐