🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Major confusion with Ortho projection matrix and Z Near / Z Far values

Started by
9 comments, last by Alberth 4 years ago

I'm getting back into OpenGL graphics programming and I'm having trouble with projection matrices when it comes to the z near and z far values. They don't work how I thought they would, so I think I'm doing something wrong.

Starting off simple I have a default out of the box OpenGL 3.3 context. I am drawing a red square using a vertex buffer / index buffer and I am passing a single matrix setup as a orthographic projection to my shaders that acts as the MVP matrix.

The red square does show on the screen. BUT I have some major confusion about the z near and z far values needed in the ortho matrix and their relationship to vertices.

When I create my ortho matrix I set the z near as 0.0f and the z far as 1.0f

mvp.toOrtho(800.0f, 600.0f, 0.0f, 1.0f);

So I expect that my “camera” is at x: 0, y: 0, z: 0 and is looking down the Z axis. Where Z gets more negative as you go further (Right Hand Coord System). I also expect anything outside of a 0.0f to 1.0f range (0.0f to -1.0f z index) will not be visible on the screen,

Now, when my red square's vertices have the Z index set to a value between 0.0f and -1.0f it will show on the screen. If the Z index is a value of -2.0f and lower nothing shows on the screen. Which makes sense, because the direction of the “camera” is looking down the Z axis where further out is a more negative Z value.

However, if I set the Z index value of my vertices to a 1.0f value I still see the red square on the screen. Why is this happening? Wouldn't the red square be “off screen” or behind the camera because it has a Z index of 1.0f?

Just as a FYI, setting the value to 2.0f, so 100% outside of the z near / z far range, the red square does not show.

What am I doing wrong? Or what am I missing?

Implementation of my Ortho Matrix

void Matrix4::toOrtho(float viewWidth, float viewHeight, float zNear, float zFar) {

	//Col 1
	data[0] = 2.0f / viewWidth;
	data[1] = 0.0f;
	data[2] = 0.0f;
	data[3] = 0.0f;

	//Col 2
	data[4] = 0.0f;
	data[5] = 2.0f / viewHeight;
	data[6] = 0.0f;
	data[7] = 0.0f;

	//Col 3
	data[8] = 0.0f;
	data[9] = 0.0f;
	data[10] = 1.0f / (zNear - zFar);
	data[11] = 0;

	//Col 4
	data[12] = 0.0f;
	data[13] = 0.0f;
	data[14] = zNear / (zNear - zFar);
	data[15] = 1.0f;
}

Data in my buffer

//With Index bufffer
float size = 64.0f;
float zIndex = 1.0f;
vertices[0] = 0.0f;
vertices[1] = 0.0f;
vertices[2] = zIndex;

vertices[3] = size;
vertices[4] = 0.0f;
vertices[5] = zIndex;

vertices[6] = 0.0f;
vertices[7] = size;
vertices[8] = zIndex;

vertices[9] = size;
vertices[10] = size;
vertices[11] = zIndex;

indices[0] = 0;
indices[1] = 1;
indices[2] = 2;

indices[3] = 1;
indices[4] = 3;
indices[5] = 2;

Vertex Shader

#version 330 core
layout(location = 0) in vec3 pos;

uniform mat4 MVP;
void main() {
	gl_Position = MVP * vec4(pos.x, pos.y, pos.z, 1.0);
}
Advertisement

No good idea of what you are doing, but standard C++ doesn't do column-oriented matrices like Fortran. Would your results make sense if you view your settings as rows rather than columns?

If so, that would explain the problem.

@Alberth

From what I understand it doesn't matter how the data of a matrix is layed out in memory. I think what you are asking is what happens if I transpose my matrix. So I tried that and the red square is very distorted or not even visible. So I don't think its a data layout issue.

I added my vertex shader code just in case

There is no camera in OpenGL. You see what is in a 1x1x1 cube that is flattened to a 1x1 square. I like to think of the ‘camera’ as being bolted onto the front of the cube.

Also, the NDC cube that OpenGL uses is left handed. The only reason OpenGL is known to be RH is because everyone inverts the Z in the projection matrix. Kinda dumb.

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

@noodlebowl : how ortho matrices are built and their use can be read up anywhere. It looks like you're building them not correctly, but i haven't checked any details. Here are too examples that definitely.do.work with opengl, they're right-handed. A matrix is made from an array of 4 vec4s that are an array of 4Ts each. The constructor with the 1 as argument just returns an identity matrix. Ignore the syntax sugar, i just copied them out ithout further ado. Hth a bit.

template<typename T>
inline static mat4_t<T> ortho( const T &left, const T &right, const T &bottom, const T &top ) {
	mat4_t<T> result{ static_cast<T>(1) };
	result[0][0] = static_cast<T>(2) / (right - left);
	result[1][1] = static_cast<T>(2) / (top - bottom);
	result[2][2] = - static_cast<T>(1);
	result[3][0] = - (right + left) / (right - left);
	result[3][1] = - (top + bottom) / (top - bottom);
	return result;
}

template<typename T>
inline static mat4_t<T> ortho( const T &left, const T &right, const T &bottom, const T &atop,
		const T &zNear, const T &zFar ) {
	mat4_t<T> result{ static_cast<T>(1) };
	result[0][0] = static_cast<T>(2) / (right - left);
	result[1][1] = static_cast<T>(2) / (top - bottom);
	result[2][2] = - static_cast<T>(2) / (zFar - zNear);
	result[3][0] = - (right + left) / (right - left);
	result[3][1] = - (top + bottom) / (top - bottom);
	result[3][2] = - (zFar + zNear) / (zFar - zNear);
	return result;
}

I'm not editing my last post because on save the code parts will be messed up with &amps …

@noodlebowl try inverting the near/far parts. And you will want the left and bottom parts as well as the right and top to be able to draw only over a part of a window.

@Green_Baron

So I think my main problem was that I was basing / using projection matrix calculations intended for direct x and not open gl. I'm not really sure what direct x is doing when it comes to their z index mapping, but it definitely doesn't match open gl's.

After swapping out my projection calculations intended for open gl things work as expected. If anyone could explain or has a clue / reason why the direct x version of the formula doesn't work in open gl that would be awesome. I honestly thought it would be ok because of NDC, but maybe those don't apply to z index

For ref the ortho formula I was using:

https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthorh

This link would have been handy, sorry about the time you lost:

https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml

Here's my perspective projection code, to compare:

https://github.com/sjhalayka/blind_poker/blob/master/blind%20poker/bp/matrix_utils.mm

noodleBowl said:

@Green_Baron

So I think my main problem was that I was basing / using projection matrix calculations intended for direct x and not open gl. I'm not really sure what direct x is doing when it comes to their z index mapping, but it definitely doesn't match open gl's.

After swapping out my projection calculations intended for open gl things work as expected. If anyone could explain or has a clue / reason why the direct x version of the formula doesn't work in open gl that would be awesome. I honestly thought it would be ok because of NDC, but maybe those don't apply to z index

For ref the ortho formula I was using:

https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthorh

@noodlebowl i had a similar problen then i found this :

For programming purposes, OpenGL matrices are 16-value arrays with base vectors laid out contiguously in memory. The translation components occupy the 13th, 14th, and 15th elements of the 16-element matrix, where indices are numbered from 1 to 16 as described in section 2.11.2 of the OpenGL 2.1 Specification.

Column-major versus row-major is purely a notational convention. Note that post-multiplying with column-major matrices produces the same result as pre-multiplying with row-major matrices. The OpenGL Specification and the OpenGL Reference Manual both use column-major notation. You can use any notation, as long as it's clearly stated.

Sadly, the use of column-major format in the spec and blue book has resulted in endless confusion in the OpenGL programming community. Column-major notation suggests that matrices are not laid out in memory as a programmer would expect.

i have resolved this issue doing all the matrices and vectors calculations using standard notation and transposing the final matrices obtained before giving them to opengl .

Hope this help.

Column-major format is standard in the numeric community, probably because Fortran picked that format.

It is just that C++ uses row-major, perhaps because the numeric community indexes a matrix as (row, column), and c++ matrix[row][col] notation then makes it row-major.

This topic is closed to new replies.

Advertisement