3

こんにちは、私は 3D モデリング アプリを作成しており、OpenGL でのレンダリングを高速化したいと考えています。現在、私は glBegin/glEnd を使用していますが、これは非常に遅く非推奨の方法です。非常に高速なフラット シェーディング モデルを描画する必要があります。フレームごとに CPU で法線を生成します。これは非常に遅いです。インデックス付きジオメトリで glDrawElements を使用しようとしましたが、法線が三角形レベルではなく頂点で指定されているため、法線生成に問題があります。
もう 1 つのアイデアは、GLSL を使用してジオメトリ シェーダーの GPU で法線を生成することでした。通常の生成用にこのコードを書きました:

#version 120 
#extension GL_EXT_geometry_shader4 : enable

vec3 NormalFromTriangleVertices(vec3 triangleVertices[3])
{
    // now is same as RedBook (OpenGL Programming Guide)
    vec3 u = triangleVertices[0] - triangleVertices[1];
    vec3 v = triangleVertices[1] - triangleVertices[2];
    return cross(v, u);
}

void main()
{
    // no change of position
    // computes normal from input triangle and front color for that triangle

    vec3 triangleVertices[3];
    vec3 computedNormal;

    vec3 normal, lightDir;
    vec4 diffuse;
    float NdotL;

    vec4 finalColor;

    for(int i = 0; i < gl_VerticesIn; i += 3)
    {
        for (int j = 0; j < 3; j++)
        {
            triangleVertices[j] = gl_PositionIn[i + j].xyz;
        }
        computedNormal = NormalFromTriangleVertices(triangleVertices);
        normal = normalize(gl_NormalMatrix * computedNormal);

        // hardcoded light direction 
        vec4 light = gl_ModelViewMatrix * vec4(0.0, 0.0, 1.0, 0.0);
        lightDir = normalize(light.xyz);

        NdotL = max(dot(normal, lightDir), 0.0);

        // hardcoded
        diffuse = vec4(0.5, 0.5, 0.9, 1.0);

        finalColor = NdotL * diffuse; 
        finalColor.a = 1.0; // final color ignores everything, except lighting

        for (int j = 0; j < 3; j++)
        { 
            gl_FrontColor = finalColor;
            gl_Position = gl_PositionIn[i + j];
            EmitVertex();
        }
    }
    EndPrimitive();
}

シェーダーをアプリケーションに統合しても、速度は向上しませんでした。以前よりもひどかった。私はGLSLとシェーダー全般の初心者なので、何が間違っていたのかわかりません。Geforce 9400M を搭載した MacBook でこのコードを試しました。

より明確にするために、これは私が置き換えたいコードです:

- (void)drawAsCommandsWithScale:(Vector3D)scale
{
    float frontDiffuse[4] = { 0.4, 0.4, 0.4, 1 };
    CGFloat components[4];
    [color getComponents:components];
    float backDiffuse[4];
    float selectedDiffuse[4] = { 1.0f, 0.0f, 0.0f, 1 };

    for (uint i = 0; i < 4; i++)
        backDiffuse[i] = components[i];

    glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse);

    Vector3D triangleVertices[3];

    float *lastDiffuse = frontDiffuse; 

    BOOL flip = scale.x < 0.0f || scale.y < 0.0f || scale.z < 0.0f;

    glBegin(GL_TRIANGLES);

    for (uint i = 0; i < triangles->size(); i++)
    {
        if (selectionMode == MeshSelectionModeTriangles) 
        {
            if (selected->at(i))
            {
                if (lastDiffuse == frontDiffuse)
                {
                    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, selectedDiffuse);
                    lastDiffuse = selectedDiffuse;
                }
            }
            else if (lastDiffuse == selectedDiffuse)
            {
                glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse);
                glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse);
                lastDiffuse = frontDiffuse;
            }
        }    
        Triangle currentTriangle = [self triangleAtIndex:i];
        if (flip)
            currentTriangle = FlipTriangle(currentTriangle);

        [self getTriangleVertices:triangleVertices fromTriangle:currentTriangle];
        for (uint j = 0; j < 3; j++)
        {
            for (uint k = 0; k < 3; k++)
            {
                triangleVertices[j][k] *= scale[k];
            }
        }
        Vector3D n = NormalFromTriangleVertices(triangleVertices);
        n.Normalize();
        for (uint j = 0; j < 3; j++)
        {
            glNormal3f(n.x, n.y, n.z);
            glVertex3f(triangleVertices[j].x, triangleVertices[j].y, triangleVertices[j].z);            
        }
    }

    glEnd();    
}

ご覧のとおり、非常に非効率的ですが、機能しています。triangles配列へのインデックスのvertices配列です。

このコードを描画に使用しようとしましたが、2 つではなく 1 つのインデックス配列 (頂点用に 1 つ、法線用に 2 つ目) を持つことはできません。

glEnableClientState(GL_VERTEX_ARRAY);

uint *trianglePtr = (uint *)(&(*triangles)[0]); 
float *vertexPtr = (float *)(&(*vertices)[0]);

glVertexPointer(3, GL_FLOAT, 0, vertexPtr);
glDrawElements(GL_TRIANGLES, triangles->size() * 3, GL_UNSIGNED_INT, trianglePtr);
glDisableClientState(GL_VERTEX_ARRAY);

さて、いくつかの頂点が異なる三角形によって共有されているため、それらの法線が異なる場合、法線へのポインタを指定するにはどうすればよいですか?

4

3 に答える 3

2

それで、ようやくレンダリング速度を上げることができました。頂点または三角形が変更された場合にのみ、CPU で法線を再計算します。これは、シーン全体ではなく 1 つのメッシュで作業している場合にのみ発生します。私が望んでいた解決策ではありませんが、実際には以前のアプローチよりも優れています。
ジオメトリ全体を個別の法線配列と頂点配列にキャッシュします。フラット シェーディングが必要なため、インデックス付きの描画は使用できません (3ds max のスムージング グループと同様の問題)。
私は頂点シェーダーを照らすために単純なものを使用しますglDrawArrays。これは、三角形モードで、選択した三角形と選択されていない三角形に別の色が必要であり、マテリアルの配列がないためです(何も見つかりませんでした)。

于 2009-12-27T14:49:22.263 に答える
1

あなたの質問は私に法線のないこの法線のブログ投稿を思い出させます。

于 2009-12-26T22:18:35.560 に答える
1

通常、フレームごとに法線を計算するのではなく、ジオメトリが変更された場合にのみ計算します。三角形ごとに 1 つの法線を設定するには、三角形の各頂点に同じ法線を設定するだけです。これは、メッシュ内の隣接する三角形間で頂点を共有できないことを意味しますが、この種のことではまったく珍しいことではありません。

于 2009-12-26T21:57:05.183 に答える