質問

Uniで2年目を終えたばかりのゲームコースをやっていますが、これは常に数学とゲームのプログラミングがどのように関連しているかを私に悩ませてきました。今まで私は使用していました Vectors, Matrices, 、 と Quaternions ゲームでは、これらがゲームにどのように適合するかを理解できます。

これは 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 私たちはこれを、「縫う」ことができる小さな塊で景観を生成する方法として学びました。これはゲームで起こることですか?手続き的な方法などで使用されているのはこれを聞いたことがありません(私の知識は非常に限られています)。私がこれまでに行ったことには、処理される頂点情報の配列が含まれます。

これが話題から外れている場合は申し訳ありませんが、ここのコミュニティは、このちょっとしたことでスポットになっているようです。

ありがとう。

役に立ちましたか?

解決

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
}

xに対してfの導関数を計算するためにC ++関数を書き込むことが可能になりました。 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. 。素晴らしいエクササイズは、規則を解決して、減算、分割、トリグ機能などを可能にすることです。

他のヒント

2)デリバティブと積分は通常、リアルタイムで大規模なデータセットで計算されず、高すぎます。代わりに、それらは事前に計算されます。たとえば、(私の頭の上部で)単一の散布媒体をレンダリングするためのBo Sun et al。多くの代数的ショートカットで構成される「Airlightモデル」を使用して、事前に計算されたルックアップテーブルを取得します。

3)大規模なデータセットのストリーミングは、特に地形では大きなトピックです。

ゲームで遭遇する数学の多くは、非常に具体的な問題を解決することであり、通常は単純に保たれます。線形代数は、どの計算よりもはるかに多く使用されています。グラフィックス(私はこれが最も好きです)では、多くのアルゴリズムはアカデミアで行われた研究から来ており、ゲームプログラマーによってスピードのために修正されます。学術研究でさえ、最近の目標をスピードしています。

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)
}

しかし、私たちはまだ関数の派生物の方法を自分自身の方法を作成する必要がありますが、少なくとも間違ったものを誤って呼び出すつもりはありません。

現在、他の人が述べているように、ゲームは速度を好む傾向があるため、多くの数学が単純化されています:補間、事前に計算されたテーブルなど。

ゲームの数学のほとんどは、可能な限り安価に計算できるように設計されており、精度を超えて取引されています。たとえば、数のクランチの多くは、ダブルではなく整数または単一の精度のフロートを使用します。

具体的な例についてはわかりませんが、事前にデリバティブの安価な(計算)式を定義できる場合は、その場で物事を計算する方が望ましいです。

ゲームでは、パフォーマンスが最重要です。視覚的な忠実度の顕著な増加につながる場合を除き、静的に行うことができるときに動的に行われるものは何も見つかりません。

時間の象徴的な差別化をコンパイルすることに興味があるかもしれません。これは(原則として)C ++テンプレートで実行できます。ゲームが実際にこれを行うかどうかについては考えていません(シンボリック差別化はプログラムするには高すぎる可能性があり、そのような広範なテンプレートの使用はコンパイル時間では高すぎるかもしれませんが、私にはわかりません)。

しかし、このトピックの議論が興味深いと思うかもしれないと思いました。グーグル「C ++テンプレートシンボリック派生」は、いくつかの記事を提供します。

象徴的な計算と誘導体の計算に興味がある場合、多くの素晴らしい答えがあります。

ただし、正気のチェックと同様に、この種の象徴的な(分析的)計算は、ゲームのコンテキストでリアルタイムで行うことは実用的ではありません。

私の経験(ゲームよりもコンピュータービジョンの3Dジオメトリです)では、3Dジオメトリの計算と数学のほとんどは、事前にオフラインで物事を計算してから、この数学を実装するためにコーディングすることによってもたらされます。この方法で、その場で物事を象徴的に計算してから、オンザフライ分析式を取得する必要があることはめったにありません。

ゲームプログラマーは検証できますか?

1), 2)

Maclaurin/Taylorシリーズ(1)は、いずれにしても誘導体(2)から構築されています。

はい、実行時にこれらのいずれかを象徴的に計算する必要はほとんどありませんが、確かに user207442あなたがそれを必要とするなら、の答えは素晴らしいです。

あなたが見つけたのは、数学的な計算を実行する必要があり、合理的な時間に、または時には非常に速くそれを行う必要があることです。これを行うには、他のソリューションを再利用しても、基本的な分析を理解する必要があります。

自分で問題を解決する必要がある場合、利点は、概算のみが必要なだけであることが多いということです。これは、たとえば、シリーズタイプの拡張により、複雑な関数を単純な線形または二次に減らすことができることを意味します。これは非常に高速です。

積分の場合、結果を数値的に計算することができますが、常に 多くの 分析ソリューションよりも遅い。違いは、実用的であるかどうかの違いである可能性があります。

要するに、はい、あなたは数学を学ぶ必要がありますが、プログラムにプログラムにそれをさせるのではなく、プログラムを書くために学ぶ必要があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top