5

我刚刚在 Uni 完成了第二年的游戏课程,这一直困扰着我数学和游戏编程之间的关系。到目前为止,我一直在使用VectorsMatricesQuaternions在游戏中,我可以理解这些如何融入游戏。

这是General Question关于数学和实时图形编程之间的关系,我很好奇数学的动态性。是否所有公式和导数都是预定义(半定义)的?

实时计算导数/积分是否可行?

这些是一些我看不到它们如何适合编程/数学的东西作为例子。

  1. MacLaurin/Talor Series我可以看到这很有用,但是您是否必须传递您的函数及其派生类,或者您可以传递一个函数并让它为您计算派生类?

    MacLaurin(sin(X)); or MacLaurin(sin(x), cos(x), -sin(x));
    
  2. Derivatives /Integrals这与第一点有关。计算y'在运行时动态完成的函数的计算,或者这是静态完成的事情,可能使用集合函数内的变量。

    f = derive(x); or f = derivedX;
    
  3. Bilnear Patches我们了解到这是一种可以生成可以“缝合”在一起的小块景观的方法,这在游戏中会发生吗?我从来没有听说过这个(当然我的知识非常有限)与程序方法或其他方法一起使用。到目前为止,我所做的涉及处理顶点信息的数组。

抱歉,如果这是题外话,但这里的社区似乎在这件事上很到位。

谢谢。

4

8 回答 8

5

从字面上看,Skizz 的回答是正确的,但只需要进行很小的更改即可计算 C++ 函数的导数。我们将skizz的函数修改f

template<class Float> f (Float x)
{
  return x * x + Float(4.0f) * x + Float(6.0f); // f(x) = x^2 + 4x + 6
}

现在可以编写一个 C++ 函数来计算 f 对 x 的导数。这是一个完整的自包含程序来计算 f 的导数。它是精确的(对于机器精度),因为它没有使用像有限差分这样的不准确方法。我在我写的一篇论文中解释了它是如何工作的。它推广到更高的导数。请注意,大部分工作是由编译器静态完成的。如果您进行优化,并且您的编译器内联得体,它应该与您可以为简单函数手动编写的任何东西一样快。(有时更快!特别是,它非常擅长同时计算 f 和 f' 的成本,因为与为 f 和 f' 编写单独的函数相比,它使编译器更容易发现公共子表达式消除。)

using namespace std;

template<class Float>
Float f(Float x)
{
  return x * x + Float(4.0f) * x + Float(6.0f);
}

struct D
{
  D(float x0, float dx0 = 0) : x(x0), dx(dx0) { }
  float x, dx;
};

D operator+(const D &a, const D &b)
{
  // The rule for the sum of two functions.
  return D(a.x+b.x, a.dx+b.dx);
}

D operator*(const D &a, const D &b)
{
  // The usual Leibniz product rule.
  return D(a.x*b.x, a.x*b.dx+a.dx*b.x);
}

// Here's the function skizz said you couldn't write.
float d(D (*f)(D), float x) {
  return f(D(x, 1.0f)).dx;
}

int main()
{
  cout << f(0) << endl;
  // We can't just take the address of f. We need to say which instance of the
  // template we need. In this case, f<D>.
  cout << d(&f<D>, 0.0f) << endl;
}

它会打印结果64正如您所期望的那样。试试其他功能f。一个很好的练习是尝试制定规则以允许减法、除法、三角函数等。

于 2011-04-28T18:06:13.000 回答
2

2)导数和积分通常不是在大型数据集上实时计算的,它太昂贵了。相反,它们是预先计算的。例如(在我的头顶)渲染单个散射媒体 Bo Sun 等人。使用包含许多代数快捷方式的“airlight 模型”来获得预先计算的查找表。

3) 流式处理大型数据集是一个很大的话题,尤其是在地形方面。

你在游戏中会遇到的很多数学都是为了解决非常具体的问题,而且通常很简单。线性代数的使用远远超过任何微积分。在图形(我最喜欢这个)中,很多算法都来自学术界的研究,然后游戏程序员为了速度而对其进行了修改:尽管如今即使是学术研究也将速度作为他们的目标。

我推荐两本书实时碰撞检测和实时渲染,其中包含了游戏引擎编程中使用的大部分数学和概念。

于 2011-04-28T11:34:52.733 回答
2

我认为您对 C++ 语言本身的理解存在根本问题。C++ 中的函数与数学函数不同。因此,在 C++ 中,您可以定义一个函数(我现在将调用方法以避免混淆)来实现一个数学函数:

float f (float x)
{
  return x * x + 4.0f * x + 6.0f; // f(x) = x^2 + 4x + 6
}

在 C++ 中,除了获取给定 x 的 f(x) 值外,没有其他方法可以使用方法 f 做任何事情。数学函数 f(x) 可以很容易地转换,例如 f'(x),在上面的示例中是 f'(x) = 2x + 4。要在 C++ 中执行此操作,您需要定义一个方法df (x):

float df (float x)
{
  return 2.0f * x + 4.0f; //  f'(x) = 2x + 4
}

你不能这样做:

get_derivative (f(x));

并让方法get_derivative为您转换方法 f(x)。

此外,您必须确保当您想要调用方法 df 的 f 的导数时。如果您不小心调用了 g 的导数方法,您的结果将是错误的。

然而,对于给定的 x,我们可以近似 f(x) 的导数:

float d (float (*f) (float x), x) // pass a pointer to the method f and the value x
{
  const float epsilon = a small value;
  float dy = f(x+epsilon/2.0f) - f(x-epsilon/2.0f);
  return epsilon / dy;
}

但这是非常不稳定且非常不准确的。

现在,在 C++ 中,您可以在这里创建一个类来提供帮助:

class Function
{
public:
  virtual float f (float x) = 0; // f(x)
  virtual float df (float x) = 0; // f'(x)
  virtual float ddf (float x) = 0; // f''(x)
  // if you wanted further transformations you'd need to add methods for them
};

并创建我们特定的数学函数:

class ExampleFunction : Function
{
  float f (float x) { return x * x + 4.0f * x + 6.0f; } // f(x) = x^2 + 4x + 6 
  float df (float x) { return 2.0f * x + 4.0f; } //  f'(x) = 2x + 4
  float ddf (float x) { return 2.0f; } //  f''(x) = 2
};

并将此类的实例传递给系列扩展例程:

float Series (Function &f, float x)
{
   return f.f (x) + f.df (x) + f.ddf (x); // series = f(x) + f'(x) + f''(x)
}

但是,我们仍然需要自己为函数的导数创建一个方法,但至少我们不会意外调用错误的方法。

现在,正如其他人所说,游戏倾向于速度,所以很多数学都被简化了:插值、预先计算的表格等。

于 2011-04-28T11:54:24.407 回答
1

游戏中的大多数数学计算都是为了尽可能便宜地计算,以速度而不是准确性为代价。例如,大部分数字运算使用整数或单精度浮点数而不是双精度数。

不确定您的具体示例,但是如果您可以事先为导数定义一个便宜的(计算)公式,那么这比动态计算更可取。

于 2011-04-28T11:15:57.480 回答
1

在游戏中,性能是最重要的。当可以静态完成时,您将找不到任何动态完成的内容,除非它导致视觉保真度显着提高。

于 2011-04-28T11:34:23.397 回答
0

您可能对编译时符号微分感兴趣。这可以(原则上)使用 c++ 模板来完成。不知道游戏在实践中是否这样做(符号区分可能太昂贵而无法正确编程,而且如此广泛的模板使用在编译时可能太昂贵,我不知道)。

但是,我认为您可能会发现这个主题的讨论很有趣。谷歌搜索“c++ 模板符号导数”给出了几篇文章。

于 2011-04-28T13:35:07.960 回答
0

如果您对符号计算和导数计算感兴趣,这里有很多很好的答案。

然而,作为一种健全性检查,这种符号(分析)演算在游戏环境中实时执行是不切实际的。

根据我的经验(计算机视觉中的 3D 几何比游戏更多),3D 几何中的大多数微积分和数学都是通过提前离线计算事物然后编码来实现这个数学的。您很少需要动态地进行符号计算,然后以这种方式获取动态分析公式。

任何游戏程序员都可以验证吗?

于 2011-04-28T14:32:09.820 回答
0

1), 2)

MacLaurin/Taylor 级数 (1) 在任何情况下都是由导数 (2) 构成的。

是的,您不太可能需要在运行时象征性地计算其中任何一个 - 但如果您需要,可以肯定user207442的答案非常好。

你确实发现你需要进行数学计算,而且你需要在合理的时间内完成,或者有时非常快。为此,即使您重复使用其他解决方案,您也需要了解基本分析。

如果你必须自己解决问题,好处是你通常只需要一个大概的答案。这意味着,例如,级数类型的展开很可能允许您将复杂函数简化为简单的线性或二次函数,这将非常快。

对于积分,您通常可以用数值计算结果,但它总是解析解慢得多。差异很可能是实用与否之间的差异。

简而言之:是的,你需要学习数学,但是为了编写程序而不是让程序为你做。

于 2011-04-29T01:24:43.717 回答