C++ 电子游戏编程中的数学

C++ 电子游戏编程中的数学,c++,math,video-game-consoles,C++,Math,Video Game Consoles,我刚刚在大学完成了第二年的游戏课程,这一直困扰着我数学和游戏编程的关系。到目前为止,我一直在游戏中使用向量,矩阵,和四元数,我可以理解这些如何适合游戏 这是一个关于数学和实时图形编程之间关系的一般性问题,我很好奇数学的动态性。是否所有公式和导数都是预定义的(半定义的) 实时计算导数/积分是否可行 这些都是我不认为它们如何适合编程/数学的例子 MacLaurin/Talor Series我可以看出这很有用,但是在这种情况下,你必须传递函数及其导数,还是可以传递一个函数,让它为你算出导数 MacLa

我刚刚在大学完成了第二年的游戏课程,这一直困扰着我数学和游戏编程的关系。到目前为止,我一直在游戏中使用
向量
矩阵
,和
四元数
,我可以理解这些如何适合游戏

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

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

这些都是我不认为它们如何适合编程/数学的例子

  • MacLaurin/Talor Series
    我可以看出这很有用,但是在这种情况下,你必须传递函数及其导数,还是可以传递一个函数,让它为你算出导数

    MacLaurin(sin(X)); or MacLaurin(sin(x), cos(x), -sin(x));
    
  • 导数/积分
    这与第一点有关。计算在运行时动态完成的函数的
    y'
    ,或者这是静态完成的,可能是使用集合函数中的变量

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

  • 抱歉,如果这是离题的话,但是这里的社区似乎在这类事情上很到位


    谢谢。

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


    不确定你的具体例子,但如果你能事先为一个导数定义一个便宜的(计算)公式,那么这比在运动中计算东西要好。

    在游戏中,性能是最重要的。如果可以静态地进行,你将找不到任何动态地进行的操作,除非它能显著提高视觉保真度。

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

    3) 流式传输大数据集是一个大主题,特别是在地形中

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


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

    < P>我认为你对C++语言本身的理解存在一个根本性的问题。C++中的函数与数学函数不一样。所以,在C++中,您可以定义一个函数(我现在称之为避免混淆的方法)来实现数学函数:

    float f (float x)
    {
      return x * x + 4.0f * x + 6.0f; // f(x) = x^2 + 4x + 6
    }
    
    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
    };
    
    <>在C++中,除了获得给定x的f(x)值外,没有其他方法可以做任何事情。数学函数f(x)可以很容易地进行变换,例如f’(x),在上面的例子中是f’(x)=2x+4。要在C++中这样做,您需要定义一个方法DF(x):

    你不能这样做:

    get_derivative (f(x));
    
    并让方法
    get_导数
    为您转换方法f(x)

    此外,还必须确保当需要f的导数时,调用方法df。如果你偶然调用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
    };
    
    并创建特定的数学函数:

    float f (float x)
    {
      return x * x + 4.0f * x + 6.0f; // f(x) = x^2 + 4x + 6
    }
    
    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)
    }
    
    但是,我们仍然需要自己为函数的导数创建一个方法,但至少我们不会意外地调用错误的方法


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

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


    然而,我想你可能会发现这个话题的讨论很有趣。谷歌搜索“c++模板符号导数”会给出一些文章。

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

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

    根据我的经验(计算机视觉中的3D几何比游戏更重要),3D几何中的大部分微积分和数学都是通过提前离线计算,然后编码来实现这个数学。很少有人需要在飞行中象征性地计算事物,然后以这种方式获得飞行中的分析公式


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

    Skizz的答案从字面上看是正确的,但仅限于
    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;
    }