2

GLSL 頂点シェーダーで頂点を正しく表示するようになってから数日後、ライティングに取り掛かりました。openGL ライティング/法線についての私の理解は、想像力を駆使してもあまりよくないので、ご容赦ください。法線を正しく表示するには、法線にどのような変換を適用する必要があるかわかりません。ライトを設定するアプリケーション コードは次のとおりです。

    final float diffuseIntensity = 0.9f;
    final float ambientIntensity = 0.5f;

    final float position[] = { 0f, 0f, 25000f, 0f};
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, position, 0);
    final float diffuse[] = { 0, diffuseIntensity, 0, 1f};
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuse, 0);
    final float ambient[] = { ambientIntensity, ambientIntensity, ambientIntensity, 1f};
    gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, ambient, 0);

これまでのところかなり標準的なもの。アプリケーションの要件により、(やや奇妙な) 頂点シェーダーを次に示します。

void main()
{   
// P is the camera matrix, model_X_matrices are the relative translation/rotation etc of the model currently being rendered.
vec4 pos = gl_ProjectionMatrix * P * modelTranslationMatrix * modelRotationMatrix * modelScaleMatrix * gl_Vertex;

gl_Position = pos;

gl_TexCoord[0] = gl_MultiTexCoord0;

gl_FrontColor = gl_Color;           
}

gl_Normalを世界座標に変換する必要があることは私の理解です。私のシェーダーの場合、これは次のようになると思います。

vec4 normal = modelTranslationMatrix * modelRotationMatrix * modelScaleMatrix * vec4(gl_Normal);

次に、ライトの位置を取得する必要があります (ワールド空間のアプリケーション コードで既に宣言されています)。私はこれを次のように行うと思います:

vec3 light_position = gl_LightSource[0].position.xyz;

次に、法線とライトの位置のドット積を見つけて、ライトの拡散値を見つけます。

さらに、フラグメント シェーダーでは、色にこの拡散値を乗算するだけで、すべて機能するはずです。通常の座標を正しく変換する方法が本当にわかりません。私の仮定は正しいですか、それとも私は完全にボールから外れていますか?

編集:gl_NormalMatrix法線行列 ( ) が行列の 3x3 の逆数である ことを読んだ後gl_ModelView、ワールド空間で法線を計算する正しい方法は、gl_Normalに の逆数を掛けることだとmodelTranslationMatrix * modelRotationMatrix * modelScaleMatrix思います。Pこれに行列を掛ける必要がありますか、それとも通常の計算には関係ありませんか?

4

2 に答える 2

2

事前に乗算modelTranslationMatrix * modelRotationMatrix * modelScaleMatrixし、それを単一のユニフォームとしてのみ渡す必要があります。

法線は、モデル ビュー マトリックスの逆転置によって乗算されます。詳細については、こちらの優れた記事で説明されています http://www.lighthouse3d.com/tutorials/glsl-tutorial/the-normal-matrix/

GLSL では、これを行うコードは次のようになります。

mat4 modelView = view * model;
mat4 mvp = projection * modelView;
mat4 normalMvp = projection * transpose(inverse(modelView));

ただし、GLSL を使用してこれを実装しないでください。 代わりに、ホスト コードで計算を行い、行列をユニフォームで渡す必要があります。行列と行列の乗算は O(n³) であるため、かなりコストがかかります。この計算を GPU 側で行うと、各 GPU スレッドで並行して多くの計算負荷が発生します。私たちはそれが起こることを望んでいません。代わりに、linmath.h、GLM、Eigen、または同等のものを使用して、CPU で 1 回計算します。

于 2011-04-19T15:02:31.843 に答える
0

これは1年前の質問だと思いますが、私はこれに苦労していたので、答えを共有したいと思いました.

OpenGL のベクトルは、点 (つまり、軸ごとに 1 つの座標) と同義で表されます。つまり、その方向は、原点からそれらの座標への変換によって定義されるということです。概念的な数学的ベクトルを変換しても、その方向は変わりません。Originを翻訳せずに OpenGL ベクトルを翻訳すると、翻訳されます。

そして、モデルビューマトリックス(またはカスタムマトリックススタックが何であれ)によって法線ベクトルを変換することについて間違っているのは、一般的に言えば、回転と変換が含まれます(問題ではスケーリングですが、それはここでもそこでもありません)。平行移動を適用すると、法線の方向が変わります。簡単に言うと、頂点から離れるほど、法線はカメラ頂点ベクトルに平行になります。

ではなく、エルゴ

vec4 normal = modelTransformMatrix * vec4(gl_Normal);

あなたが実際に欲しい

vec3 normal = mat3(modelTransformMatrix) * gl_Normal;

したがって、翻訳用語は除外されますが、回転、スケール、せん断は保持されます。

カメラ マトリックスの使用に関しては、何をしたいかによって異なります。重要なことは、方程式のすべての値が同じ座標空間にあるということです。そうは言っても、カメラ投影を掛けると問題が発生する可能性があります。これは、(おそらく) カメラに対する 3D ワールド座標から画面座標 + 深度に投影するように設定されているためです。一般に、ワールド スペースでライティングを計算します。つまり、モデルの移動を乗算しますが、カメラの投影は乗算しません。

于 2012-04-11T23:22:43.023 に答える