I have a cube which is defined with the centre at 0,0,0 and the edges reaching out to -1/+1 (i.e. the cube has width, height, depth of 2).
I then setup the following matrices:
glm::mat4 modelMat4;
modelMat4 = glm::translate(modelMat4, 0.0f, 0.0f, 200.f);
modelMat4 = glm::scale(modelMat4, 200.0f, 200.0f, 200.0f);
glm::mat4 viewMat4;
viewMat4 = glm::lookAt(glm::vec3(0.0f, 0.0f, zNear),
glm::vec3(0.0f, 0.0f, zFar),
glm::vec3(0.0f, 1.0f, 0.0f));
// initialWidth = window width
// initialHeight = window height
// zNear = 1.0f
// zFar = 1000.0f
glm::mat4 projectionMat4;
projectionMat4 = glm::frustum(-initialWidth / 2.0f, initialWidth / 2.0f, -initialHeight / 2.0f, initialHeight / 2.0f, zNear, zFar);
But the middle of my object appears at the near z-plane (i.e. I can only see the back half of my cube, from the inside).
If I adjust the model transform to be:
glm::translate(modelMat4, 0.0f, 0.0f, 204.f);
Then I can see the front side of my cube as well.
If I change the model transform to be:
glm::translate(modelMat4, 0.0f, 0.0f, 250.f);
Then the cube only rasterises at approx 2x2x2 pixels.
What am I misunderstanding about model, view projection matrices? I was expecting the transform to be linear, but the z-plane disappears between 200 and 250. Even though the planes are defined between 1.0f and 1000.0f.
Edit: My shader code is below:
#version 100
layout(location = 0) in vec3 v_Position;
layout(location = 1) in vec4 v_Colour;
layout(location = 2) uniform mat4 v_ModelMatrix;
layout(location = 3) uniform mat4 v_ViewMatrix;
layout(location = 4) uniform mat4 v_ProjectionMatrix;
out vec4 f_inColour;
void main()
{
gl_Position = v_ProjectionMatrix * v_ViewMatrix * v_ModelMatrix * vec4(v_Position, 1.0);
f_inColour = v_Colour;
}
You didn't show how you are multiplying your matrices, but for what you describe, its seems that it could be doing wrong. Be sure to do in this order:
MVP = projectionMat4 * viewMat4 * modelMat4;
UPDATED
Looking more carefully into your code, it is seeming that you are lacking a multiplication to concanate your transformations:
modelMat4 = glm::translate(modelMat4, 0.0f, 0.0f, 200.f);
modelMat4 *= glm::scale(modelMat4, 200.0f, 200.0f, 200.0f); // <-- here
so, modelMat4 will be the results of a scale and then a translation
Related
i am trying to get the mouse cursor in world space pos from window space(-1,1 window width and height) using the viewprojectionmatrix.
This is how i calculate my projection matrix:
static mat4 Perspective4x4(float FOV, float AspectRatio, float FarC, float NearC)
{
// Positive x is right
// Positive y is up
// Positive z is forward into the screen
float Cotangent = 1.0f / tanf((FOV)*0.5f);
float Depth = NearC - FarC;
float A = (-FarC - NearC) / Depth;
float B = 2.0f * FarC * NearC / Depth;
return
{
Cotangent/AspectRatio, 0.0f, 0.0f, 0.0f,
0.0f, Cotangent, 0.0f, 0.0f,
0.0f, 0.0f, -A, -B,
0.0f, 0.0f, 1.0f, 0.0f,
};
}
...
float WidthOverHeight = ...;
mat4 ProjectionMatrix = Perspective4x4(DegreesToRadians(90.0f), WidthOverHeight, 50.0f, 0.1f);
This is how i calculate my view matrix:
static mat4 Translate4x4(vec3 V)
{
return
{
1.0f, 0.0f, 0.0f, V.X,
0.0f, 1.0f, 0.0f, V.Y,
0.0f, 0.0f, 1.0f, V.Z,
0.0f, 0.0f, 0.0f, 1.0f
};
}
...
mat4 ViewMatrix = Translate4x4(-CameraPosition);
This is how i calculate the cursor position in worldspace:
mat4 ProjectionMatrix = ...;
mat4 ViewMatrix = ...;
vec2 CursorP = GetBilateralCursorPos(Input); // Values between -1 and 1
v4_f32 WorldSpacePNear = Inverse(ProjectionMatrix) * V4F32(CursorP, -1.0f, 1.0f);
WorldSpacePNear /= WorldSpacePNear.W;
WorldSpacePNear = Inverse(ViewMatrix) * WorldSpacePNear;
v4_f32 WorldSpacePFar = Inverse(ProjectionMatrix) * V4F32(CursorP, 1.0f, 1.0f);
WorldSpacePFar /= WorldSpacePFar.W;
WorldSpacePFar = Inverse(ViewMatrix) * WorldSpacePFar;
WorldSpacePFar.Z *= -1.0f;
WorldSpacePNear.Z *= -1.0f;
EDIT: I also tried dividing by W at the end but it doesnt work properly either.
This is how i send the matrices to OpenGL (legacy):
// I send them transposed because my matrices are row-major
mat4 ProjectionMatrix = Transpose(...);
mat4 ViewMatrix = Transpose(...);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(ProjectionMatrix.E);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(ViewMatrix.E);
The resulting positions do not seem to take in account the view projection because a change in the camera position will offset them making them not accurate.
NOTES:
vec4, vec2, mat4 are custom float-based math types. (they are what you would expect)
I know legacy OpenGL is deprecated, in fact im going to switch to modern opengl very soon, i just want to get this working.
i fixed the issue calculating the cursor pos like this:
mat4 ProjectionMatrix = ...;
mat4 ViewMatrix = ...;
// Z Distance from camera pos
float WorldDistanceFromCameraZ = 1.0f;
vec2 CursorP = GetBilateralCursorPos(Input); // Values between -1 and 1
vec4 ProbeZ = V4F32(World->Camera.P - WorldDistanceFromCameraZ*World->Camera.P.Z, 1.0f);
ProbeZ = (ProjectionMatrix*ViewMatrix) * ProbeZ;
vec4 ClipP = V4F32(CursorP.X*ProbeZ.W, CursorP.Y*ProbeZ.W, ProbeZ.Z, ProbeZ.W);
vec4 WorldP = Inverse(ProjectionMatrix*ViewMatrix) * ClipP;
I am trying to add a paralax effect to an existing engine. So far the engine worked with an orthogonal projection. Objects are placed in pixel coordinates on the screen. The problem is that I can not figure out how to replicate the same projection with a perspective projection matrix ect. that I can add a Z coordinate for depth.
I tried various combinations of matrices and z coordinates already and the result was always a black screen.
The matrix I am trying to replace:
glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(1280.0f), static_cast<GLfloat>(720.0f), 0.0f, 0.0f, -100.0f);
The vertex shader:
// Shader code (I tested this while having identity matrices for view and model
#version 330 core
layout (location = 0) in vec2 vertex;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main() {
gl_Position = projection * view * model * vec4(vertex.xy, 1.0f, 1.0f);
}
The projection code I thought might work:
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-640, -310.0f, 0.0f));
model = glm::scale(model, glm::vec3(1.0f / 1280.0f, 1.0f / 720.0f, 1.0f));
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, -100.0f);
Expected Is that a rectangle gets still displayed at similar position (I can correct the details once something works) without having a black screen.
The specification of the Perspective projection matrix is wrong.
glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, -100.0f);
glm::perspective defines a Viewing frustum by an field of view angle along the y axis, an aspect ratio and a distance to the near and the far plane.
So the near and the far plane have to be positive values (> 0) and near has to be less than far:
0 < near < far
e.g.:
glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);
The geometry has to be in between the near and the far plane, else it is clipped.
The ration of the size of the projected area and the depth is linear and can be calculated. It depends on the field of view angle:
float fov_y = glm::radians(45.0f);
float ratio_size_depth = tan(fov_y / 2.0f) * 2.0f;
Note, if an object should appear with half the size in the projection on the viewport, the distance from the object to the camera (depth) has to be doubled.
So the corrected model translation matrix and required depth in the shader to have the coordinates match on the plane are as follows:
int width = 1280.0f;
int height = 720.0f;
glm::mat4 model = glm::mat4(1.0f);
model = glm::scale(model, glm::vec3(-1.0f / width, -1.0f / height, 1.0f));
model = glm::translate(model, glm::vec3(-((float)width / 2.0f), -((float)height / 2.0f), 0.0f));
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, 1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 1.0f, 0.1f, 100.0f);
Shader with Z-Value:
#version 330 core
layout (location = 0) in vec2 vertex;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main() {
gl_Position = projection * view * model * vec4(vertex.xy, 1.208f, 1.0f);
}
Which will be equivalent tho this orthogonal matrix:
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(this->width), static_cast<GLfloat>(this->height), 0.0f, 0.0f, -100.0f);
The matrices can also be multiplied together to have only one projection matrix you pass to the shader. This will make it easier to have an actual model matrix passed with the mesh ect.
I trying to implement an orthographic projection matrix - currently, all I am trying to do is draw a triangle to the screen, which works fine without the matrix, but as soon as multiply the coordinates by the matrix, the triangle doesn't fit on the page (hard to explain but one point is in the centre, and the other two are far off the page). I've tried it with a different matrix where there are negative coordinates (so the centre of the screen is the origin) and it works fine - am I doing something obviously wrong here? Relevant code is below:
GLfloat vertices[] =
{
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
};
glm::mat4 projectionMatrix = glm::ortho(0.0f, 960.0f, 640.0f, 0.0f, 1.0f, -1.0f);
glm::mat4 viewMatrix = glm::lookAt(glm::vec3(0,0,5),glm::vec3(0,0,0),glm::vec3(0,1,0));
glm::mat4 modelMatrix = glm::mat4(1.0f);
glm::mat4 PVM = projectionMatrix * viewMatrix * modelMatrix;
GLuint matrixID = glGetUniformLocation(shader.getShaderID(), "PVM");
And then the vertex shader:
#version 130
in vec4 vertexPosition;
uniform mat4 PVM;
out vec4 position;
void main()
{
gl_Position = vertexPosition * PVM;
position = vertexPosition;
}
I've just included the code I think is relevant.
Well, If you use Orthographics projection with screen resolution, as you probably did. The vertex coordinates become pixel coordinates. So your triangle is only 2pixels wide and half of it is offscreen.
So try to make it a bit bigger.
I've read several articles here at stackoverflow and in some books. But I can't find the error in my code. I have an deferred renderer and saved albedo, depth and normals (including spec) inside the g-buffer. Moreover created an shadow map from a light source from top of the scene.
It looks like this:
Albedo (R8G8B8A8):
Normal (R8G8B8A8):
Linear Depth (F32):
Shadow map from light source (linear depth) (500 meter above camera):
Ok. My shader code of the deferred with shadow mapping look like this:
I have uploaded row major matrices.
Vertex Shader:
layout(row_major) uniform UVSViewMatrix
{
mat4 m_ScreenMatrix;
};
layout(location = 0) in vec3 VertexPosition;
smooth out vec2 PSTexCoord;
void main()
{
vec4 Position = vec4(VertexPosition.xyz, 1.0f);
PSTexCoord = Position.xy;
gl_Position = Position * m_ScreenMatrix;
}
Fragment Shader:
#version 410
// -----------------------------------------------------------------------------
// Input from engine
// -----------------------------------------------------------------------------
layout(row_major) uniform UPSCameraProperties
{
mat4 m_ProjectionMatrix;
mat4 m_CameraView;
vec3 m_CameraPosition;
vec3 m_CameraDirection;
};
layout(row_major) uniform UPSLightProperties
{
mat4 m_LightProjection;
mat4 m_LightView;
vec4 m_LightPosition;
vec4 m_LightAmbientIntensity;
vec4 m_LightDiffuseIntensity;
vec4 m_LightSpecularIntensity;
};
uniform sampler2D PSTextureAlbedo;
uniform sampler2D PSTextureNormalSpecular;
uniform sampler2D PSTextureDepth;
uniform sampler2D PSTextureShadowMap;
// -----------------------------------------------------------------------------
// Input from vertex shader
// ----------------------------------------------------------------- ------------
smooth in vec2 PSTexCoord;
// -----------------------------------------------------------------------------
// Output to systembuffer
// -----------------------------------------------------------------------------
layout (location = 0) out vec4 PSOutput;
// -----------------------------------------------------------------------------
// Functions
// -----------------------------------------------------------------------------
vec3 GetViewSpacePositionFromDepth(float _Depth, vec2 _ScreenPosition, mat4 _InvertedProjectionMatrix)
{
// -----------------------------------------------------------------------------
// Information from:
// http://mynameismjp.wordpress.com/2009/03/10/reconstructing-position-from-depth/
// -----------------------------------------------------------------------------
vec4 ScreenPosition;
ScreenPosition.x = _ScreenPosition.x * 2.0f - 1.0f;
ScreenPosition.y = _ScreenPosition.y * 2.0f - 1.0f;
ScreenPosition.z = _Depth * 2.0f - 1.0f;
ScreenPosition.w = 1.0f;
// -----------------------------------------------------------------------------
// Transform by the inverse projection matrix
// -----------------------------------------------------------------------------
vec4 VSPosition = ScreenPosition * _InvertedProjectionMatrix;
// -----------------------------------------------------------------------------
// Divide by w to get the view-space position
// -----------------------------------------------------------------------------
return (VSPosition.xyz / VSPosition.w);
}
// -----------------------------------------------------------------------------
float GetShadowAtPosition(vec3 _WSPosition)
{
// -----------------------------------------------------------------------------
// Set worls space coord into light projection by multiply with light
// view and projection matrix;
// -----------------------------------------------------------------------------
vec4 LSPosition = vec4(_WSPosition, 1.0f) * m_LightView * m_LightProjection;
// -----------------------------------------------------------------------------
// Divide xyz by w to get the position in light view's clip space.
// -----------------------------------------------------------------------------
LSPosition.xyz /= LSPosition.w;
// -----------------------------------------------------------------------------
// Get uv texcoords for this position
// -----------------------------------------------------------------------------
vec3 ShadowCoord = LSPosition.xyz * 0.5f + 0.5f;
// -----------------------------------------------------------------------------
// Get final depth at this texcoord and compare it with the real
// position z value (do a manual depth test)
// -----------------------------------------------------------------------------
float DepthValue = texture( PSTextureShadowMap, vec2(ShadowCoord.x, ShadowCoord.y) ).r;
float Shadow = 1.0f;
if (ShadowCoord.z > DepthValue)
{
Shadow = 0.3f;
}
return Shadow;
}
// -----------------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------------
void main()
{
// -----------------------------------------------------------------------------
// Get informations from g-buffer
// -----------------------------------------------------------------------------
vec2 TexCoord = vec2(PSTexCoord.s, 1.0f - PSTexCoord.t);
vec4 AlbedoColor = texture(PSTextureAlbedo , TexCoord);
vec4 NormalSpec = texture(PSTextureNormalSpecular, TexCoord);
float Depth = texture(PSTextureDepth , TexCoord).r;
vec3 VSPosition = GetViewSpacePositionFromDepth(Depth, TexCoord, inverse(m_ProjectionMatrix));
vec3 Normal = normalize(NormalSpec.xyz);
float SpecularExponent = NormalSpec.w;
vec4 WSPosition = vec4(VSPosition, 1.0f) * inverse(m_CameraView);
// -----------------------------------------------------------------------------
// Compute lighting (Light Accumulation)
// -----------------------------------------------------------------------------
vec3 CameraPosition = m_CameraPosition.xyz;
vec3 LightPosition = m_LightPosition.xyz;
vec3 EyeDirection = WSPosition.xyz - CameraPosition;
vec3 LightDirection = normalize(LightPosition - WSPosition.xyz);
vec3 LightReflection = normalize(-reflect(LightDirection, Normal));
vec4 AmbientColor = m_LightAmbientIntensity;
vec4 DiffuseColor = clamp(m_LightDiffuseIntensity * max(dot(Normal, LightDirection), 0.0f), 0.0f, 1.0f);
vec4 SpecularColor = clamp(m_LightSpecularIntensity * pow(max(dot(LightReflection, EyeDirection), 0.0f), SpecularExponent), 0.0f, 1.0f);
float Shadow = GetShadowAtPosition(WSPosition.xyz);
// -----------------------------------------------------------------------------
// Final result
// -----------------------------------------------------------------------------
PSOutput = vec4((AlbedoColor * (AmbientColor + DiffuseColor) * Shadow).xyz, 1.0f);
}
At the end my result look like this:
Did anyone can see my mistake?
Some measurement results:
ShadowCoord.xy is always 0.5, 0.5
ShadowCoord.z seams to be 1.0f
Here are some variable values:
LightProjection
(
1.29903805f, 0.0f, 0.0, 0.0f,
0.0f, 1.73205066f, 0.0f, 0.0f,
0.0f, 0.0f, -1.00024426f, -1.0f,
0.0f, 0.0f, -1.00024426f, 0.0f
);
LightView
(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, -1500.0f, 1.0f
);
CameraProjection
(
1.29903805f, 0.0f, 0.0f, 0.0f,
0.0f, 1.73205066f, 0.0f, 0.0f,
0.0f, 0.0f, -1.00024426f, -1.0f,
0.0f, 0.0f, -1.00024426f, 0.0f
);
CameraView
(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, -1000.0f, 1.0f
);
One issue I can see is a difference between linear and hyperbolic depth. It looks like your "Linear Depth (F32)" G-buffer depth is actually hyperbolic, which is fine because the code expects this. However the light's depth is in fact linear but is then compared against the clip space depth after perspective divide.
It'd probably be easiest to just make the light buffer's depth hyperbolic (gl_FragCoord.z), but changing to compare against eye-space Z should work too:
float ShadowCoordZ = -(vec4(_WSPosition, 1.0f) * m_LightView).z;
The bit about "ShadowCoord.xy is always 0.5, 0.5" is confusing. The G-buffer texture coord seems to work. I can't really tell with the shadow, but do the lighting equations work? If so maybe something's wrong with the matrices? I'd also pass in the inverse of your matrices as uniforms so time isn't spent computing it in the shader.
I wrote a shadow map shader for my graphics engine. I followed these tutorials:
Part 1 and the following part.
Unfortunately, the results I get are quite a bit off. Here are some screenshots. They show what my scene normally looks like, the scene with enabled shadows and the content of the shadow map (please ignore the white stuff in the center, thats just the ducks's geometry).
This is how I compute the coordinates to sample the shadow map with in my fragment shader:
float calcShadowFactor(vec4 lightSpacePosition) {
vec3 projCoords = lightSpacePosition.xyz / lightSpacePosition.w;
vec2 uvCoords;
uvCoords.x = 0.5 * projCoords.x + 0.5;
uvCoords.y = 0.5 * projCoords.y + 0.5;
float z = 0.5 * projCoords.z + 0.5;
float depth = texture2D(shadowMapSampler, uvCoords).x;
if (depth < (z + 0.00001f))
return 0.0f;
else
return 1.0f;
}
The lightSpacePosition vector is computed by:
projectionMatrix * inverseLightTransformationMatrix
* modelTransformationMatrix * vertexPosition
The projection matrix is:
[1.0f / (tan(fieldOfView / 2) * (width / height)), 0.0f, 0.0f, 0.0f]
[0.0f, 1.0f / (tan(fieldOfView / 2), 0.0f, 0.0f]
[0.0f, 0.0f, (-zNear - zFar) / (zNear - zFar), 2.0f * zFar * zNear / (zNear - zFar)]
[0.0f, 0.0f, 1.0f, 0.0f]
My shadow map seems to be okay and I made sure the rendering pass uses the same lightSpacePosition vector as my shadow map pass. But I can't figure out what is wrong.
Although I do not understand this entirely, I think I found the bug:
I needed to transform the coordinates to NDC space and THEN multiply the matrices. My shadow coordinate computation now looks like this:
mat4 biasMatrix = mat4(
0.5f, 0.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f
);
vec4 shadowCoord0 = biasMatrix * light * vec4(vertexPosition, 1.0f);
shadowCoord = shadowCoord0.xyz / shadowCoord0.w;
where
light = projectionMatrix * inverseLightTransformationMatrix
* modelTransformationMatrix
Now the fragment shader's shadow factor computation is rather simple:
float shadowFactor = 1.0f;
if (texture(shadowMapSampler, shadowCoord.xy).z < shadowCoord.z - 0.0001f)
shadowFactor = 0.0f;