1

わかりましたので、再帰関数を使用して 1 枚の地形をランダムに生成し、与えられた 4 つのポイントを分割する小さなプログラムを開発しています。地形はクワッドで構成されています。各再帰で見つかった中心点にはランダムな高さの値が与えられ、すべての頂点が配列に保存され、描画時にループされます。

もともと、スタックに頂点を格納するために使用する 2D 配列を定義していましたが、細分化の数によっては、これが非常に大きくなる可能性があります。たとえば、深度が 3 の場合、256 個の頂点が生成されます。そこで、この配列を可能な限りヒープ上に定義することで、より大きな地形のシートに対してより効率的になることにしました。Terrain クラスでは、次の float ポインターの配列を定義します。

float* vertices[]; 

次に、このクラスのコンストラクターで、ヒープ上のサイズを初期化するために次のことを行います。4 を与えられたサブディビジョンの数に 1 を乗じると、256 となり、作成される頂点の数になります。

int vertexCount = pow(4.0, subdivisions+1);
*vertices = new float[vertexCount];

次に、これらの各ポインターは、次のように定義されたヒープ内の float 値の配列を指します。

for(int i = 0; i<vertexCount + 1; i++)
{
    float* temp = new float[3];
    vertices[i] = temp;
    delete temp;
    temp = 0;
}

地形を作成するために使用される再帰関数は次のようになります。

void Terrain::Subdivide(float* a, float* b, float* c, float* d, int subdivisions, float      maxHeight)
{
if(subdivisions>0)
{
    float e[3];
    float f[3];
    float g[3];
    float h[3];
    float centre[3];
    int i = 0;

    srand(time(NULL));

    for(i = 0; i<3; i++) if(i!= 1) e[i] = (a[i]+b[i])/2;
    for(i = 0; i<3; i++) if(i!= 1) f[i] = (b[i]+c[i])/2;
    for(i = 0; i<3; i++) if(i!= 1) g[i] = (c[i]+d[i])/2;
    for(i = 0; i<3; i++) if(i!= 1) h[i] = (d[i]+a[i])/2;

    centre[0] = (h[0]+f[0])/2;
    centre[2] = (e[2]+g[2])/2;

    e[1] = 0;
    f[1] = 0;
    g[1] = 0;
    h[1] = 0;
    centre[1] = (float)rand()/((float)RAND_MAX/maxHeight);

    Subdivide(a, e, centre, h, subdivisions-1 , maxHeight);
    Subdivide(e, b, f, centre, subdivisions-1, maxHeight);
    Subdivide(centre, f, c, g, subdivisions-1, maxHeight);
    Subdivide(h, centre, g, d, subdivisions-1, maxHeight);
}
else
{
    int i = 0;
    for(i = 0; i<3; i++) {vertices[vertexCounter][i] = a[i];} vertexCounter++;
    for(i = 0; i<3; i++) {vertices[vertexCounter][i] = b[i];} vertexCounter++;
    for(i = 0; i<3; i++) {vertices[vertexCounter][i] = c[i];} vertexCounter++;
    for(i = 0; i<3; i++) {vertices[vertexCounter][i] = d[i];} vertexCounter++;
}

}

このコードは、頂点をスタック上の 2D 配列として定義するだけで動作するように見えますが (他の場所でいくつかの変更を加えています)、このコードは、メイン関数で glutInit を呼び出すときに、次の行の Glut.h で未処理の例外を生成します: (行486)

static void APIENTRY glutInit_ATEXIT_HACK(int *argcp, char **argv) {     __glutInitWithExit(argcp, argv, exit); }

私は何度もコードを調べましたが、どこに問題があるのか​​ わかりません.TerrainクラスではなくGlut.hから例外をスローする必要があることはさらに奇妙です. 誰かがここで何か間違ったことを見ることができますか?

4

1 に答える 1

1

ここにエラーがあります

for(int i = 0; i<vertexCount + 1; i++)

する必要があります

for(int i = 0; i<vertexCount; i++)

配列をゼロから数える理由は、配列を追加する必要がないためです。

ここでまた間違い

for(int i = 0; i<vertexCount; i++)
{
    float* temp = new float[3];
    vertices[i] = temp;
    delete temp;
    temp = 0;
}

する必要があります

for(int i = 0; i<vertexCount; i++)
{
    vertices[i] = new float[3];
}

なんらかの理由でメモリを割り当て、すぐに再度解放しますが、頂点配列で解放されたメモリを引き続き使用します。それは非常に速くクラッシュするでしょう。

3番目の(できれば最終的な)間違いは

*vertices = new float[vertexCount];

する必要があります

vertices = new float*[vertexCount];

2D 配列をシミュレートするために、最初の次元は float ポインターの配列です。それが固定コードが割り当てるものです。

于 2012-10-27T14:37:22.997 に答える