题
我刚刚在Uni完成游戏课程的第二年,这总是让我烦恼数学和游戏编程如何相关。到现在为止我一直在使用 Vectors
, Matrices
, , 和 Quaternions
在游戏中,我可以忍受它们如何适应游戏。
这是一个 General Question
关于数学与实时图形编程之间的关系,我对数学的动态有多好奇。是所有公式和衍生物都是预定义的(半定义)的情况吗?
实时计算衍生物/积分甚至可行吗?
这些是我看不到它们如何适合编程/数学的一些事情。
MacLaurin/Talor Series
我可以看到这很有用,但是您必须通过功能及其衍生词,还是可以传递单个功能并为您处理衍生物?MacLaurin(sin(X)); or MacLaurin(sin(x), cos(x), -sin(x));
Derivatives /Integrals
这与第一点有关。计算y'
在运行时动态完成的函数,或者这是在设置函数内部使用变量的静态完成的操作。f = derive(x); or f = derivedX;
Bilnear Patches
我们了解到这是一种可能在可能“缝制”的小块中产生景观的一种方式,这是游戏中发生的事情吗?我从未听说过(授予我的知识非常有限)与程序方法或其他方式一起使用。到目前为止,我所做的涉及正在处理的顶点信息的数组。
对不起,如果这是一个脱离话题,但是这里的社区似乎在这方面存在。
谢谢。
解决方案
从字面上采取时,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 ++函数来计算相对于X的F的导数。这是一个完整的独立程序,用于计算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;
}
它打印结果 6
和 4
正如您所期望的。尝试其他功能 f
. 。一个不错的练习是尝试制定规则以减法,除法,触发功能等。
其他提示
2)通常不会在大型数据集上实时计算衍生物和积分,这太昂贵了。相反,它们是预先计算的。例如(在我的头顶)渲染单个散点媒体Bo Sun等。使用他们的“空中型号”,其中包括许多代数快捷方式来获取预定的查找表。
3)流式传输大数据集是一个大主题,尤其是在地形。
您将在游戏中遇到的许多数学是解决非常具体的问题,并且通常保持简单。线性代数的使用远远超过任何微积分。在图形(我最喜欢的)中,许多算法来自在学术界完成的研究,然后通过游戏程序员进行了速度修改:尽管即使学术研究也可以迅速进行目标。
我建议两本书实时碰撞检测和实时渲染,其中包含游戏引擎编程中使用的大多数数学和概念的胆量。
我认为您对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)。
另外,您必须确保当您想要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
};
并创建我们的特定数学功能:
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几何形状中的大多数微积分和数学都通过提前计算出离线的方式,然后进行编码以实现此数学。很少有人需要象征性地计算东西,然后以这种方式获得即时的分析公式。
任何游戏程序员都可以验证吗?
1), 2)
麦克洛林/泰勒系列(1)均由衍生物(2)构建。
是的,您不太可能在运行时象征性地计算其中的任何一个 - 但可以肯定 用户207442如果您需要的话,答案很棒。
您发现的是,您需要执行数学计算,并且需要在合理的时间或有时很快进行数学计算。为此,即使您重复使用其他人的解决方案,也需要了解基本分析。
如果您确实必须自己解决问题,那么好处是您通常只需要一个大致的答案即可。这意味着,例如,串联类型的扩展很可能使您可以将复杂的功能简化为简单的线性或二次,这将非常快。
对于积分,您通常可以通过数值计算结果,但是它永远是 很多 比分析解决方案慢。差异很可能是实用与否之间的区别。
简而言之:是的,您需要学习数学,但是为了编写程序而不是为您提供程序。