私は教育的追求のためのエンジンの始まりに取り組んでおり、理解していると思っていた OpenGL の概念に出くわしましたが、私が観察してきた動作を説明することはできません。問題は深度バッファにあります。また、問題を修正したことを理解してください。投稿の最後で、問題を修正した理由を説明しますが、私のソリューションで問題が修正された理由がわかりません。まず、GLUT と GLEW を初期化します。
//Initialize openGL
glutInit(&argc, argv);
//Set display mode and window attributes
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
//Size and position attributes can be found in constants.h
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitWindowPosition(WINDOW_XPOS, WINDOW_YPOS);
//Create window
glutCreateWindow("Gallagher");
// Initialize GLEW
glewExperimental = true;
glewInit();
//Initialize Graphics program
Initialize();
次に、プログラムを初期化します (読みやすさと関連性の欠如のためにセグメントを除外します)。
//Remove cursor
glutSetCursor(GLUT_CURSOR_NONE);
//Enable depth buffering
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LESS);
glDepthRange(0.0f, 1.0f)
//Set back color
glClearColor(0.0,0.0,0.0,1.0);
//Set scale and dimensions of orthographic viewport
//These values can be found in constants.h
//Program uses a right handed coordinate system.
glOrtho(X_LEFT, X_RIGHT, Y_DOWN, Y_UP, Z_NEAR, Z_FAR);
それ以降は、さまざまなエンジン コンポーネントの初期化、.obj ファイルの読み込み、ModularGameObject クラスのインスタンスの初期化、それらへのメッシュのアタッチが含まれます。ただし、先に進む前に、次の値を指定することが重要な場合があります。
X_LEFT = -1000;
X_RIGHT = 1000;
Y_DOWN = -1000;
Y_UP = 1000;
Z_NEAR = -0.1;
Z_FAR = -1000;
これにより、ビューポートは右手の座標系に従います。問題に関与していると思われるコードの最後の部分は、私の頂点シェーダーです。
#version 330 core
//Position of vertices in attribute 0
layout(location = 0) in vec4 _vertexPosition;
//Vertex Normals in attribute 1
layout(location = 1) in vec4 _vertexNormal;
//Model transformations
//Uniform location of model transformation matrix
uniform mat4 _modelTransformation;
//Uniform location of camera transformations
//Camera transformation matrix
uniform mat4 _cameraTransformation;
//Camera perspective matrix
uniform mat4 _cameraPerspective;
//Uniform location of inverse screen dimensions
//This is used because GLSL normalizes viewport from -1 to 1
//So any vector representing a position in screen space must be multiplied by this vector before display
uniform vec4 _inverseScreenDimensions;
//Output variables
//Indicates whether a vertex is valid or not, non valid vertices will not be drawn.
flat out int _valid; // 0 = valid vertex
//Normal to be sent to fragment shader
smooth out vec4 _normal;
void main()
{
//Initiate transformation pipeline
//Transform to world space
vec4 vertexInWorldSpace = vec4(_modelTransformation *_vertexPosition);
//Transform to camera space
vec4 vertexInCameraSpace = vec4(_cameraTransformation * vertexInWorldSpace);
//Project to screen space
vec4 vertexInScreenSpace = vec4(_cameraPerspective * vertexInCameraSpace);
//Transform to device coordinates and store
vec4 vertexInDeviceSpace = vec4(_inverseScreenDimensions * vertexInScreenSpace);
//Store transformed vertex
gl_Position = vertexInScreenSpace;
}
このコードにより、すべての変換と通常の計算 (含まれていない) が正しく実行されますが、モデルの各面は常に他の面よりも優れているように戦っています。問題がないのは、描画されている最初のモデルの内側に立っているときだけで、ちらつきはなく、スザンヌの頭の内側を本来のように見ることができます。
考えられることを何週間も試した後、最終的に、わずか2行のコードを変更/追加することで解決策にたどり着きました。まず、頂点シェーダーのメイン関数の最後に次の行を追加しました。
gl_Position.z = 0.0001+vertexInScreenSpace.z;
このコード行の追加により、Z ファイティングのすべてのビットがなくなりましたが、深度バッファーが完全に後方になり、遠くにある頂点が前方の頂点の上に確実に描画されていました。これが私の最初の質問です。なぜこのコード行がより信頼性の高い動作を引き起こすのでしょうか?
信頼できる動作が得られ、深度ファイティングがなくなったので、描画順序を逆にするだけで済みました。そのため、glDepthRange への呼び出しを次のように変更しました。
glDepthRange(1.0f, 0.0f);
私は、glDepthRange(0.0f, 1.0f) が Z_NEAR (-0.1) に近いオブジェクトを 0 に近づけ、Z_FAR(-1000) に近いオブジェクトを 1 に近づけると仮定していました。 Depth Test を GL_LESS に設定することは完全に理にかなっています。実際のところ、私の Z_NEAR と Z_FAR が何であるかに関係なく、glDepthRange が値をマップする方法が間違っていなければ、これは当てはまります。
ただし、この行の変更は、私に近いオブジェクトが深度バッファーに 1 に近い値を格納し、さらにオブジェクトが後方描画順序をレンダリングする 0 の値を持つことを意味するため、間違っているに違いありませんが、確かにそれは魅力。
誰かが私の仮定が間違っている理由と、gsl と深度バッファリングの理解を考慮に入れていない可能性があることの方向性を教えてくれれば。エンジンの基礎の機能を完全に理解するまで、エンジンの進歩に進みたくありません。
編集: 私の _cameraPerspective マトリックスの内容は次のとおりです: 透視マトリックス図
AspectX 0 0 0
0 AspectY 0 0
0 0 1 0
0 0 1/focalLength 0
AspectX は 16、AspectY は 9 です。焦点距離のデフォルトは 70 ですが、実行時にこれを変更するためのコントロールが追加されました。
derhass によって指摘されましたが、これは glOrtho() に渡された情報がシェーダーによってどのように考慮されるかを説明していません。標準のパイプラインとマトリックス スタックを使用していないため、ビューポートのサイズは _inverseScreenDimensions で考慮されます。これは [1/X_RIGHT, 1/Y_UP, 1/Z_Far, 1] を含む vec4 です。または、変数名がない場合は、[1/1000、1/1000、-1/1000、1]。
頂点シェーダーでこれをスクリーン座標ベクトルに掛けると、X 値は -1 から 1 の間、Y 値は -1 から 1 の間、Z 値は 0 から 1 の間になります (オブジェクトがカメラの前にあった場合、最初は負の z 座標)、W は 1 です。
私が間違っていなければ、これが「デバイス座標」に到達するための最後のステップであり、続いてメッシュを描画します。
元の質問を心に留めておいてください: 私はこれが合理化されていないことを知っています.GLMやこれに最もよく使用されるすべてのライブラリを使用していないことは知っています. 私の質問は、私が行った変更によってこれが修正されたのはなぜですか?