フラグメント シェーダー内からサーフェス法線 (フラグメントの平面に関連付けられた法線) にアクセスすることは可能ですか? それとも、頂点シェーダーでこれを行うことができますか?
関連するジオメトリのすべての知識は、シェーダー パイプラインを下ると失われますか? それとも、フラグメント シェーダーの頂点でその情報を回復するための巧妙な方法がありますか?
前もって感謝します。
乾杯、
ダグの
ツイッター:@dugla
表面法線ベクトルは、フラグメント シェーダーのビュー空間位置の偏導関数によって近似的に計算できます。偏導関数は、関数dFdx
およびdFdy
によって取得できます。これには、OpenGL es 3.0 またはOES_standard_derivatives
拡張機能が必要です。
in vec3 view_position;
void main()
{
vec3 normalvector = cross(dFdx(view_position), dFdy(view_position));
nv = normalize(normalvector * sign(normalvector.z));
.....
}
一般に、ジオメトリ シェーダーでサーフェスの法線ベクトルを計算することができます (OpenGL ES 3.2 以降)。たとえば、三角形を描画すると、ジオメトリ シェーダーで 3 つのポイントがすべて取得されます。3 つの点によって、法線ベクトルを計算できる平面が定義されます。ポイントが時計回りまたは反時計回りに配置されている場合は注意が必要です.
三角形の法線ベクトルは、三角形の頂点によって定義される 2 つのベクトルの正規化された外積です。反時計回りの三角形の次の例を参照してください。
#version 400
layout (location = 0) in vec3 inPos;
out vec3 vertPos;
uniform mat4 u_projectionMat44;
uniform mat4 u_modelViewMat44;
void main()
{
vec4 viewPos = u_modelViewMat44 * vec4( inPos, 1.0 );
vertPos = viewPos.xyz;
gl_Position = u_projectionMat44 * viewPos;
}
#version 400
layout( triangles ) in;
layout( triangle_strip, max_vertices = 3 ) out;
in vec3 vertPos[];
out vec3 geoPos;
out vec3 geoNV;
void main()
{
vec3 leg1 = vertPos[1] - vertPos[0];
vec3 leg2 = vertPos[2] - vertPos[0];
geoNV = normalize( cross( leg1, leg2 ) );
geoPos = vertPos[0];
EmitVertex();
geoPos = vertPos[1];
EmitVertex();
geoPos = vertPos[2];
EmitVertex();
EndPrimitive();
}
#version 400
in vec3 geoPos;
in vec3 geoNV;
void main()
{
// ...
}
もちろん、テッセレーション シェーダーでも法線ベクトルを計算できます (OpenGL ES 3.2 以降)。ただし、これは、他の理由で既にテッセレーション シェーダーが必要であり、さらに顔の法線ベクトルを計算する場合にのみ意味があります。
頂点シェーダーは上記と同じです。
#version 400
layout( vertices=3 ) out;
in vec3 vertPos[];
out vec3 tctrlPos[];
void main()
{
tctrlPos[gl_InvocationID] = vertPos[gl_InvocationID];
if ( gl_InvocationID == 0 )
{
gl_TessLevelOuter[0] = ;
gl_TessLevelOuter[1] = ;
gl_TessLevelOuter[2] = ;
gl_TessLevelInner[0] = ;
}
}
#version 400
layout(triangles, ccw) in;
in vec3 tctrlPos[];
out vec3 tevalPos;
out vec3 tevalNV;
void main()
{
vec3 leg1 = tctrlPos[1] - tctrlPos[0];
vec3 leg2 = tctrlPos[2] - tctrlPos[0];
tevalNV = normalize( cross( leg1, leg2 ) );
tevalPos = tctrlPos[0] * gl_TessCoord.x + tctrlPos[1] * gl_TessCoord.y + tctrlPos[2] * gl_TessCoord.z;
}
#version 400
in vec3 tevalPos;
in vec3 tevalNV;
void main()
{
// ...
}
「可変」(新しい OpenGL ではイン/アウトのみ) 変数を使用するだけで、頂点法線から補間されたピクセルごとの法線を取得できます。ただし、この法線を正規化することを忘れないでください。補間された法線の長さは、もはや 1 であってはなりません。これらの法線は、鋭いエッジでも悪い結果をもたらします。
より高い解像度でカスタム法線を使用する場合、一般的に使用される手法は法線マップです。オブジェクトの法線をベイクしたテクスチャを作成します。次に、テクスチャ ルックアップを使用してフラグメント テクスチャの法線にアクセスできます。
頂点法線を「可変」でフラグメント シェーダーに渡すと、補間されたフラグメント法線が得られます。
編集:アプリケーションで法線を計算し、それらを三角形の各頂点の属性としてシェーダーに渡す必要があります。
三角形の法線を計算する通常の方法は、外積を使用することです。
これにより、三角形の平面の法線が得られます。V2 は V1 の「左側」にある必要があります。そうしないと、法線が「外側」ではなく「内側」を指します。詳細については、クロス積に関するウィキペディアの記事を参照してください。
さらなる編集:そうです、私は今あなたの問題を理解しています。はい、共有頂点を使用すると、頂点ごとに複数の法線を実際に持つことはできません。
私が考えることができる唯一の他のことは、三角形の 3 つの頂点すべてが渡されるため、ジオメトリ シェーダーが役立つ可能性があるということです。私は彼らとの経験はありません。