Determining cascaded shadow map split ranges - c++

I'm doing cascaded shadow maps, and I believe I have a problem concerning the way I do the split comparison to select the proper shadow map. As it stands, the shadow mapping works overall but in a few cases at certain angles it does not work.
Currently the lighting shader stage looks like this:
"#version 420
const float DEPTH_BIAS = 0.00005;
layout(std140) uniform UnifDirLight
{
mat4 mVPMatrix[4];
mat4 mCamViewMatrix;
vec4 mSplitDistance;
vec4 mLightColor;
vec4 mLightDir;
vec4 mGamma;
vec2 mScreenSize;
} UnifDirLightPass;
layout (binding = 2) uniform sampler2D unifPositionTexture;
layout (binding = 3) uniform sampler2D unifNormalTexture;
layout (binding = 4) uniform sampler2D unifDiffuseTexture;
layout (binding = 6) uniform sampler2DArrayShadow unifShadowTexture;
out vec4 fragColor;
void main()
{
vec2 texcoord = gl_FragCoord.xy / UnifDirLightPass.mScreenSize;
vec3 worldPos = texture(unifPositionTexture, texcoord).xyz;
vec3 normal = normalize(texture(unifNormalTexture, texcoord).xyz);
vec3 diffuse = texture(unifDiffuseTexture, texcoord).xyz;
vec4 camPos = UnifDirLightPass.mCamViewMatrix * vec4(worldPos, 1.0); // legit way of determining the split?
int index = 3;
if (camPos .z > UnifDirLightPass.mSplitDistance.x)
index = 0;
else if (camPos .z > UnifDirLightPass.mSplitDistance.y)
index = 1;
else if (camPos .z > UnifDirLightPass.mSplitDistance.z)
index = 2;
vec4 projCoords = UnifDirLightPass.mVPMatrix[index] * vec4(worldPos, 1.0);
projCoords.w = projCoords.z - DEPTH_BIAS;
projCoords.z = float(index);
float visibilty = texture(unifShadowTexture, projCoords);
float angleNormal = clamp(dot(normal, UnifDirLightPass.mLightDir.xyz), 0, 1);
fragColor = vec4(diffuse, 1.0) * visibilty * angleNormal * UnifDirLightPass.mLightColor;
}
And the "mSplitDistance", each component is the center fardistance of the frustrum for that split, multiplied by the main cameras view matrix
Vec4 camFarDistCenter;
CameraFrustrum cameraFrustrum = CalculateCameraFrustrum(nearDistArr[cascadeIndex], farDistArr[cascadeIndex], lighting.mCameraPosition, lighting.mCameraDirection, camFarDistCenter);
.....
camFarDistCenter = lighting.mCameraViewMatrix * camFarDistCenter;
splitDistances[cascadeIndex] = camFarDistCenter.z;
Here's how I create the camera frustrum for each split, if its of interest, I believe this is a pretty common alghorithm:
CameraFrustrum CalculateCameraFrustrum(const float minDist, const float maxDist, const Vec3& cameraPosition, const Vec3& cameraDirection, Vec4& camFarZ)
{
CameraFrustrum ret = { Vec4(-1.0f, -1.0f, 1.0f, 1.0f), Vec4(-1.0f, -1.0f, -1.0f, 1.0f), Vec4(-1.0f, 1.0f, 1.0f, 1.0f), Vec4(-1.0f, 1.0f, -1.0f, 1.0f),
Vec4(1.0f, -1.0f, 1.0f, 1.0f), Vec4(1.0f, -1.0f, -1.0f, 1.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f), Vec4(1.0f, 1.0f, -1.0f, 1.0f) };
const Vec3 forwardVec = glm::normalize(cameraDirection);
const Vec3 rightVec = glm::normalize(glm::cross(forwardVec, Vec3(0.0f, 0.0f, 1.0f)));
const Vec3 upVec = glm::normalize(glm::cross(rightVec, forwardVec));
const Vec3 nearCenter = cameraPosition + forwardVec * minDist;
const Vec3 farCenter = cameraPosition + forwardVec * maxDist;
camFarZ = Vec4(farCenter, 1.0);
const float nearHeight = tan(glm::radians(70.0f) / 2.0f) * minDist;
const float nearWidth = nearHeight * 1920.0f / 1080.0f;
const float farHeight = tan(glm::radians(70.0f) / 2.0f) * maxDist;
const float farWidth = farHeight * 1920.0f / 1080.0f;
ret[0] = Vec4(nearCenter - (upVec * nearHeight) - (rightVec * nearWidth), 1.0);
ret[1] = Vec4(nearCenter + (upVec * nearHeight) - (rightVec * nearWidth), 1.0);
ret[2] = Vec4(nearCenter + (upVec * nearHeight) + (rightVec * nearWidth), 1.0);
ret[3] = Vec4(nearCenter - (upVec * nearHeight) + (rightVec * nearWidth), 1.0);
ret[4] = Vec4(farCenter - upVec * farHeight - rightVec * farWidth, 1.0);
ret[5] = Vec4(farCenter + upVec * farHeight - rightVec * farWidth, 1.0);
ret[6] = Vec4(farCenter + upVec * farHeight + rightVec * farWidth, 1.0);
ret[7] = Vec4(farCenter - upVec * farHeight + rightVec * farWidth, 1.0);
return ret;
}
Is it sound to do the split comparison in camera space like I do? Is that a potential problem?

void CalculateCameraFrustrum(glm::mat4& projectionMatrix,
glm::mat4 viewMatrix, // viewMatrix = light POV
glm::vec3 camera // camera = eye position + eye direction
float zNear,
float zFar,
glm::vec4 point[4]) // point[4] = shadow map boundaries
glm::mat4 shadMvp = projectionMatrix * (viewMatrix * glm::translate(camera));
glm::vec3 transf;
float maxX = zNear, minX = zFar,
maxY = zNear, minY = zFar;
int i = -1;
while (++i < 4)
{
transf = shadMvp * point[i];
transf.x /= transf.w;
transf.y /= transf.w;
if(transf.x > maxX)
maxX = transf.x;
if(transf.x < minX)
minX = transf.x;
if(transf.y > maxY)
maxY = transf.y;
if(transf.y < minY)
minY = transf.y;
}
float scaleX = 2.0f / (maxX - minX),
scaleY = 2.0f / (maxY - minY),
offsetX = -0.5f * (maxX + minX) * scaleX,
offsetY = -0.5f * (maxY + minY) * scaleY;
shadMvp = glm::mat4(1); // Identity matrix
shadMvp[0][0] = scaleX;
shadMvp[1][1] = scaleY;
shadMvp[0][3] = offsetX;
shadMvp[1][3] = offsetY;
projectionMatrix *= shadMvp;
} // No need to calculate view frustum splitting,
// only the boundaries of the shadow maps levels are needed (glm::ortho(...)).
// :)

Related

Orbiting an object around another object, but it doesn't move

I'm trying to make a visual "simulation" of the solar system using OpenGL and am using this function to orbit a planet around the sun (in a circular orbit).
glm::vec3 application::orbit(glm::vec3 thisPlanet, glm::vec3 otherPlanet, float rotSpeed, const time &dt)
{
float radius = glm::distance(thisPlanet, otherPlanet);
float angle = acosf((thisPlanet.x - otherPlanet.x) / radius) - atanf(1.0f) * 4.0f;
angle += dt.as_seconds() * rotSpeed;
if (angle > 2 * atanf(1.0f) * 4.0f)
angle -= 2 * atanf(1.0f) * 4.0f;
float x = otherPlanet.x + cosf(angle) * radius;
float z = otherPlanet.z + sinf(angle) * radius;
return glm::vec3(x, thisPlanet.y, z);
}
The function is called every frame like this:
void application::tick(const time &dt)
{
if (m_keyboard.key_released(GLFW_KEY_ESCAPE)) {
m_running = false;
}
m_controller.update(m_keyboard, m_mouse, dt);
m_cube_rotation += dt.as_seconds();
m_mercury_position = orbit(m_mercury_position, m_sun_position, 2.0f, dt);
// glm::mat4 world = glm::translate(glm::mat4(1.0f), m_cube_position)
// * glm::rotate(glm::mat4(1.0f), m_cube_rotation, glm::normalize(glm::vec3(1.0f, 1.0f, -1.0f)));
glm::mat4 sun = glm::translate(glm::scale(glm::mat4(1.0f), glm::vec3(2.0f, 2.0f, 2.0f)), m_sun_position)
* glm::rotate(glm::mat4(1.0f), m_cube_rotation, glm::normalize(glm::vec3(1.0f, 1.0f, -1.0f)));
glm::mat4 mercury = glm::translate(glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, 1.0f)), m_mercury_position)
* glm::rotate(glm::mat4(1.0f), m_cube_rotation, glm::normalize(glm::vec3(1.0f, 1.0f, -1.0f)));
//m_crate.set_transform(world);
m_sun.set_transform(sun);
m_mercury.set_transform(mercury);
const int frames_per_second = int(1.0f / dt.as_seconds());
const int frame_timing_ms = int(dt.as_milliseconds());
m_overlay.pre_frame(m_width, m_height);
m_overlay.push_line("FPS: %d (%dms)", frames_per_second, frame_timing_ms);
}
Why doesn't the planet move?
This is how I would construct the model matrix:
mat4 model_mat;
const vec3 n_forward = dir.x;
const vec3 n_up = vec3(0, 1, 0);
const vec3 n_left = dir.z;
// construct a basis and a translation
model_mat[0] = normalize(vec4(n_left, 0.0f));
model_mat[1] = normalize(vec4(n_forward, 0.0f));
model_mat[2] = normalize(vec4(n_up, 0.0f));
model_mat[3] = vec4(p, 1.0f);
The position (e.g. p) is given by the parametric circle equation p = r cos(t) + r sin(t), where t is the elapsed time and r is the circular orbit radius. Also see: https://www.mathopenref.com/coordparamcircle.html
Does this help at all?

Directional Light Shadow Mapping Issues

So I've been trying to re-implement shadow mapping in my engine using directional lights, but I have to throw shade on my progress so far (see what I did there?).
I had it working in a previous commit a while back but refactored my engine and I'm trying to redo some of the shadow mapping. Wouldn't say I'm the best in terms of drawing shadows so thought I'd try and get some help.
Basically my issue seems to stem from the calculation of the light space matrix (seems a lot of people have the same issue). Initially I had a hardcoded projection matrix and simple view matrix for the light like this
void ZLight::UpdateLightspaceMatrix()
{
// …
if (type == ZLightType::Directional) {
auto lightDir = glm::normalize(glm::eulerAngles(Orientation()));
glm::mat4 lightV = glm::lookAt(lightDir, glm::vec3(0.f), WORLD_UP);
glm::mat4 lightP = glm::ortho(-50.f, 50.f, -50.f, 50.f, -100.f, 100.f);
lightspaceMatrix_ = lightP * lightV;
}
// …
}
This then gets passed unmodified as a shader uniform, with which I multiply the vertex world space positions by. A few months ago this was working but with the recent refactor I did on the engine it no longer shows anything. The output to the shadow map looks like this
And my scene isn't showing any shadows, at least not where it matters
Aside from this, after hours of scouring posts and articles about how to implement a dynamic frustrum for the light that will encompass the scene's contents at any given time, I also implemented a simple solution based on transforming the camera's frustum into light space, using an NDC cube and transforming it with the inverse camera VP matrix, and computing a bounding box from the result, which gets passed in to glm::ortho to make the light's projection matrix
void ZLight::UpdateLightspaceMatrix()
{
static std::vector <glm::vec4> ndcCube = {
glm::vec4{ -1.0f, -1.0f, -1.0f, 1.0f },
glm::vec4{ 1.0f, -1.0f, -1.0f, 1.0f },
glm::vec4{ -1.0f, 1.0f, -1.0f, 1.0f },
glm::vec4{ 1.0f, 1.0f, -1.0f, 1.0f },
glm::vec4{ -1.0f, -1.0f, 1.0f, 1.0f },
glm::vec4{ 1.0f, -1.0f, 1.0f, 1.0f },
glm::vec4{ -1.0f, 1.0f, 1.0f, 1.0f },
glm::vec4{ 1.0f, 1.0f, 1.0f, 1.0f }
};
if (type == ZLightType::Directional) {
auto activeCamera = Scene()->ActiveCamera();
auto lightDir = normalize(glm::eulerAngles(Orientation()));
glm::mat4 lightV = glm::lookAt(lightDir, glm::vec3(0.f), WORLD_UP);
lightspaceRegion_ = ZAABBox();
for (const auto& corner : ndcCube) {
auto invVPMatrix = glm::inverse(activeCamera->ProjectionMatrix() * activeCamera->ViewMatrix());
auto transformedCorner = lightV * invVPMatrix * corner;
transformedCorner /= transformedCorner.w;
lightspaceRegion_.minimum.x = glm::min(lightspaceRegion_.minimum.x, transformedCorner.x);
lightspaceRegion_.minimum.y = glm::min(lightspaceRegion_.minimum.y, transformedCorner.y);
lightspaceRegion_.minimum.z = glm::min(lightspaceRegion_.minimum.z, transformedCorner.z);
lightspaceRegion_.maximum.x = glm::max(lightspaceRegion_.maximum.x, transformedCorner.x);
lightspaceRegion_.maximum.y = glm::max(lightspaceRegion_.maximum.y, transformedCorner.y);
lightspaceRegion_.maximum.z = glm::max(lightspaceRegion_.maximum.z, transformedCorner.z);
}
glm::mat4 lightP = glm::ortho(lightspaceRegion_.minimum.x, lightspaceRegion_.maximum.x,
lightspaceRegion_.minimum.y, lightspaceRegion_.maximum.y,
-lightspaceRegion_.maximum.z, -lightspaceRegion_.minimum.z);
lightspaceMatrix_ = lightP * lightV;
}
}
What results is the same output in my scene (no shadows anywhere) and the following shadow map
I've checked the light space matrix calculations over and over, and tried tweaking values dozens of times, including all manner of lightV matrices using the glm::lookAt function, but I never get the desired output.
For more reference, here's my shadow vertex shader
#version 450 core
#include "Shaders/common.glsl" //! #include "../common.glsl"
layout (location = 0) in vec3 position;
layout (location = 5) in ivec4 boneIDs;
layout (location = 6) in vec4 boneWeights;
layout (location = 7) in mat4 instanceM;
uniform mat4 P_lightSpace;
uniform mat4 M;
uniform mat4 Bones[MAX_BONES];
uniform bool rigged = false;
uniform bool instanced = false;
void main()
{
vec4 pos = vec4(position, 1.0);
if (rigged) {
mat4 boneTransform = Bones[boneIDs[0]] * boneWeights[0];
boneTransform += Bones[boneIDs[1]] * boneWeights[1];
boneTransform += Bones[boneIDs[2]] * boneWeights[2];
boneTransform += Bones[boneIDs[3]] * boneWeights[3];
pos = boneTransform * pos;
}
gl_Position = P_lightSpace * (instanced ? instanceM : M) * pos;
}
my soft shadow implementation
float PCFShadow(VertexOutput vout, sampler2D shadowMap) {
vec3 projCoords = vout.FragPosLightSpace.xyz / vout.FragPosLightSpace.w;
if (projCoords.z > 1.0)
return 0.0;
projCoords = projCoords * 0.5 + 0.5;
// PCF
float shadow = 0.0;
float bias = max(0.05 * (1.0 - dot(vout.FragNormal, vout.FragPosLightSpace.xyz - vout.FragPos.xzy)), 0.005);
for (int i = 0; i < 4; ++i) {
float z = texture(shadowMap, projCoords.xy + poissonDisk[i]).r;
shadow += z < (projCoords.z - bias) ? 1.0 : 0.0;
}
return shadow / 4;
}
...
...
float shadow = PCFShadow(vout, shadowSampler0);
vec3 color = (ambient + (1.0 - shadow) * (diffuse + specular)) + materials[materialIndex].emission;
FragColor = vec4(color, albd.a);
and my camera view and projection matrix getters
glm::mat4 ZCamera::ProjectionMatrix()
{
glm::mat4 projectionMatrix(1.f);
auto scene = Scene();
if (!scene) return projectionMatrix;
if (cameraType_ == ZCameraType::Orthographic)
{
float zoomInverse_ = 1.f / (2.f * zoom_);
glm::vec2 resolution = scene->Domain()->Resolution();
float left = -((float)resolution.x * zoomInverse_);
float right = -left;
float bottom = -((float)resolution.y * zoomInverse_);
float top = -bottom;
projectionMatrix = glm::ortho(left, right, bottom, top, -farClippingPlane_, farClippingPlane_);
}
else
{
projectionMatrix = glm::perspective(glm::radians(zoom_),
(float)scene->Domain()->Aspect(),
nearClippingPlane_, farClippingPlane_);
}
return projectionMatrix;
}
glm::mat4 ZCamera::ViewMatrix()
{
return glm::lookAt(Position(), Position() + Front(), Up());
}
Been trying all kinds of small changes but I still don't get correct shadows. Don't know what I'm doing wrong here. The closest I've gotten is by scaling lightspaceRegion_ bounds by a factor of 10 in the light space matrix calculations (only in X and Y) but the shadows are still no where near correct.
The camera near and far clipping planes are set at reasonable values (0.01 and 100.0, respectively), camera zoom is 45.0 degrees and scene→Domain()→Aspect() just returns the width/height aspect ratio of the framebuffer's resolution. My shadow map resolution is set to 2048x2048.
Any help here would be much appreciated. Let me know if I left out any important code or info.

Modern OpenGL cube rotation not working as expected

I'm attempting to rotate a cube around an axis and it's definitely behaving incorrectly. I'm assuming the problem lies in my matrix rotation code as everything else seems to be working. I can translate the model correctly along the x, y or z axis, as well as scale. My camera view matrix is working as expected as well and so is my projection matrix. If I remove the view matrix and or the projection matrix implementations the problem remains.
If you wish to see what result I'm getting, it's the exact same output as the gif shown on this stackoverflow post: Rotating a cube in modern opengl... looks strange
The cube appears to fold in on itself while rotating, then returns to normal after a full rotation and seems to rotate fine for about 20 degrees until folding in on itself again and repeating. My issue is the same as that in the linked to article, however my matrix class is not the same, so my problem, though the same, seemingly has a different solution.
Here's my stripped matrix declaration with possibly relevant operators
math.h
typedef struct matrix4x4
{
//Elements stored in ROW MAJOR ORDER
GLfloat matrix[16];
void translate(Vector3f translation);
void rotateX(GLfloat angle);
void rotateY(GLfloat angle);
void rotateZ(GLfloat angle);
void rotate(Vector3f angles);
void scale(Vector3f scales);
void scale(GLfloat scale);
inline matrix4x4& operator*=(const matrix4x4& rhs)
{
this->matrix[0] = this->matrix[0] * rhs.matrix[0] + this->matrix[1] * rhs.matrix[4] + this->matrix[2] * rhs.matrix[8] + this->matrix[3] * rhs.matrix[12];
this->matrix[1] = this->matrix[0] * rhs.matrix[1] + this->matrix[1] * rhs.matrix[5] + this->matrix[2] * rhs.matrix[9] + this->matrix[3] * rhs.matrix[13];
this->matrix[2] = this->matrix[0] * rhs.matrix[2] + this->matrix[1] * rhs.matrix[6] + this->matrix[2] * rhs.matrix[10] + this->matrix[3] * rhs.matrix[14];
this->matrix[3] = this->matrix[0] * rhs.matrix[3] + this->matrix[1] * rhs.matrix[7] + this->matrix[2] * rhs.matrix[11] + this->matrix[3] * rhs.matrix[15];
this->matrix[4] = this->matrix[4] * rhs.matrix[0] + this->matrix[5] * rhs.matrix[4] + this->matrix[6] * rhs.matrix[8] + this->matrix[7] * rhs.matrix[12];
this->matrix[5] = this->matrix[4] * rhs.matrix[1] + this->matrix[5] * rhs.matrix[5] + this->matrix[6] * rhs.matrix[9] + this->matrix[7] * rhs.matrix[13];
this->matrix[6] = this->matrix[4] * rhs.matrix[2] + this->matrix[5] * rhs.matrix[6] + this->matrix[6] * rhs.matrix[10] + this->matrix[7] * rhs.matrix[14];
this->matrix[7] = this->matrix[4] * rhs.matrix[3] + this->matrix[5] * rhs.matrix[7] + this->matrix[6] * rhs.matrix[11] + this->matrix[7] * rhs.matrix[15];
this->matrix[8] = this->matrix[8] * rhs.matrix[0] + this->matrix[9] * rhs.matrix[4] + this->matrix[10] * rhs.matrix[8] + this->matrix[11] * rhs.matrix[12];
this->matrix[9] = this->matrix[8] * rhs.matrix[1] + this->matrix[9] * rhs.matrix[5] + this->matrix[10] * rhs.matrix[9] + this->matrix[11] * rhs.matrix[13];
this->matrix[10] = this->matrix[8] * rhs.matrix[2] + this->matrix[9] * rhs.matrix[6] + this->matrix[10] * rhs.matrix[10] + this->matrix[11] * rhs.matrix[14];
this->matrix[11] = this->matrix[8] * rhs.matrix[3] + this->matrix[9] * rhs.matrix[7] + this->matrix[10] * rhs.matrix[11] + this->matrix[11] * rhs.matrix[15];
this->matrix[12] = this->matrix[12] * rhs.matrix[0] + this->matrix[13] * rhs.matrix[4] + this->matrix[14] * rhs.matrix[8] + this->matrix[15] * rhs.matrix[12];
this->matrix[13] = this->matrix[12] * rhs.matrix[1] + this->matrix[13] * rhs.matrix[5] + this->matrix[14] * rhs.matrix[9] + this->matrix[15] * rhs.matrix[13];
this->matrix[14] = this->matrix[12] * rhs.matrix[2] + this->matrix[13] * rhs.matrix[6] + this->matrix[14] * rhs.matrix[10] + this->matrix[15] * rhs.matrix[14];
this->matrix[15] = this->matrix[12] * rhs.matrix[3] + this->matrix[13] * rhs.matrix[7] + this->matrix[14] * rhs.matrix[11] + this->matrix[15] * rhs.matrix[15];
return *this;
}
}matrix4x4;
matrix4x4 createTransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale);
matrix4x4 createPerspectiveProjectionMatrix(GLfloat width, GLfloat height, GLfloat fov, GLfloat nearPlane, GLfloat farPlane);
matrix4x4 createViewMatrix(Vector3f cameraPosition, GLfloat cameraPitch, GLfloat cameraYaw, GLfloat cameraRoll);
and it's relevant implementations
math.cpp
matrix4x4::matrix4x4(GLfloat elements[])
{
//Elements stored in ROW MAJOR ORDER
for (unsigned int i = 0; i <= elementCount; i++)
{
matrix[i] = elements[i];
}
}
void matrix4x4::setIdentity()
{
std::fill(matrix, matrix + sizeof(matrix) / sizeof(GLfloat), 0.0f);
matrix[0] = 1;
matrix[5] = 1;
matrix[10] = 1;
matrix[15] = 1;
}
/*/////////////////////////////////////////////////////
math
/////////////////////////////////////////////////////*/
void matrix4x4::translate(Vector3f translation)
{
GLfloat transformElements[16] =
{
1.0f, 0.0f, 0.0f, translation.x,
0.0f, 1.0f, 0.0f, translation.y,
0.0f, 0.0f, 1.0f, translation.z,
0.0f, 0.0f, 0.0f, 1.0f
};
matrix4x4 transform = matrix4x4(transformElements);
*this *= transform;
}
void matrix4x4::rotateX(GLfloat angle)
{
angle = degreesToRadians(angle);
GLfloat transformElements[16] =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, std::cos(-angle), -std::sin(-angle), 0.0f,
0.0f, std::sin(-angle), std::cos(-angle), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
matrix4x4 transform = matrix4x4(transformElements);
*this *= transform;
}
void matrix4x4::rotateY(GLfloat angle)
{
angle = degreesToRadians(angle);
GLfloat transformElements[16] =
{
std::cos(-angle), 0.0f, std::sin(-angle), 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-std::sin(-angle), 0.0f, std::cos(-angle), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
matrix4x4 transform = matrix4x4(transformElements);
*this *= transform;
}
void matrix4x4::rotateZ(GLfloat angle)
{
angle = degreesToRadians(angle);
GLfloat transformElements[16] =
{
std::cos(-angle), -std::sin(-angle), 0.0f, 0.0f,
std::sin(-angle), std::cos(-angle), 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
matrix4x4 transform = matrix4x4(transformElements);
*this *= transform;
}
void matrix4x4::rotate(Vector3f angles)
{
matrix4x4 transform = matrix4x4();
transform.setIdentity();
transform.rotateX(angles.x);
transform.rotateY(angles.y);
transform.rotateZ(angles.z);
*this *= transform;
}
void matrix4x4::scale(Vector3f scales)
{
GLfloat transformElements[16] =
{
scales.x, 0.0f, 0.0f, 0.0f,
0.0f, scales.y, 0.0f, 0.0f,
0.0f, 0.0f, scales.z, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
matrix4x4 transform = matrix4x4(transformElements);
*this *= transform;
}
matrix4x4 createTransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale)
{
matrix4x4 transformationMatrix;
transformationMatrix.setIdentity();
//I've tried changing the order of these around, as well as only
//doing one operation (skipping translate and scale, or everything but a single axis rotation
transformationMatrix.translate(translation);
transformationMatrix.rotate(rotation);
transformationMatrix.scale(scale);
return transformationMatrix;
}
matrix4x4 createPerspectiveProjectionMatrix(GLfloat width, GLfloat height, GLfloat fov, GLfloat nearPlane, GLfloat farPlane)
{
matrix4x4 projectionMatrix;
projectionMatrix.setIdentity();
GLfloat aspectRatio = width / height;
projectionMatrix.matrix[0] = (1.0f / std::tan((degreesToRadians(fov)) / 2.0f) / aspectRatio);
projectionMatrix.matrix[5] = 1.0f / std::tan((degreesToRadians(fov)) / 2.0f);
projectionMatrix.matrix[10] = (farPlane + nearPlane) / (nearPlane - farPlane);
projectionMatrix.matrix[11] = (2.0f * farPlane * nearPlane) / (nearPlane - farPlane);
projectionMatrix.matrix[14] = -1.0f;
return projectionMatrix;
}
I know my matrix/vector implementations are quick and dirty, but I'm just trying to get something set up. I've got plans to make the math methods (scale, translate, etc) static methods that don't affect the contents of the matrix, but instead accept a matrix as input and return a new one... but that's not the issue right now.
Here's my vertex shader
#version 330 core
//declare inputs
in vec3 position;
in vec2 textureCoords;
//declare output
out vec2 pass_textureCoords;
//uniforms
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
void main(void)
{
//tell OpenGL where to render the vertex on screen
gl_Position = projectionMatrix * viewMatrix * transformationMatrix * vec4(position.x, position.y, position.z, 1.0);
pass_textureCoords = textureCoords;
}
My render method...
void Renderer::render(Entity entity, Shader* shader)
{
...
RawModel* rawModel = texturedModel->getRawModel();
glBindVertexArray(rawModel->getVaoID());
...
matrix4x4 transformationMatrix = createTransformationMatrix(entity.getPosition(), entity.getRotation(), entity.getScale());
shader->loadTransformationMatrix(transformationMatrix);
...
glDrawElements(GL_TRIANGLES, rawModel->getVertexCount(), GL_UNSIGNED_INT, 0);
...
}
And finally the relevant pieces from my main. The cube definitions and so on
//This is a simple cube
std::vector<GLfloat> vertices =
{
-0.5f,0.5f,-0.5f,
-0.5f,-0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
0.5f,0.5f,-0.5f,
-0.5f,0.5f,0.5f,
-0.5f,-0.5f,0.5f,
0.5f,-0.5f,0.5f,
0.5f,0.5f,0.5f,
0.5f,0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
0.5f,-0.5f,0.5f,
0.5f,0.5f,0.5f,
-0.5f,0.5f,-0.5f,
-0.5f,-0.5f,-0.5f,
-0.5f,-0.5f,0.5f,
-0.5f,0.5f,0.5f,
-0.5f,0.5f,0.5f,
-0.5f,0.5f,-0.5f,
0.5f,0.5f,-0.5f,
0.5f,0.5f,0.5f,
-0.5f,-0.5f,0.5f,
-0.5f,-0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
0.5f,-0.5f,0.5f
};
std::vector<GLfloat> textureCoords =
{
...
};
std::vector<GLuint> indices =
{
0,1,3,
3,1,2,
4,5,7,
7,5,6,
8,9,11,
11,9,10,
12,13,15,
15,13,14,
16,17,19,
19,17,18,
20,21,23,
23,21,22
};
//parameters are (model, pos, rotation, scale)
Entity entity = Entity(&texturedModel, Vector3f(0.0f, 0.0f, -2.0f), Vector3f(0.0f, 0.0f, 0.0f), 1.0f);
//SHADER STUFF
Shader textureShader = Shader("uniformVarTextureShader");
textureShader.loadProjectionMatrix(display.getProjectionMatrix());
Camera cam;
//draw in wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
while (display.checkForClose() == 0)
{
glfwPollEvents();
//TO DO: update logic here
//entity.varyPosition(+0.005f, 0.0f, -0.002f); //this works, as does scaling and camera movement
//entity.varyRotation(0.25f, 0.18f, 0.0f);
entity.setYRotation(entity.getYRotation() + 0.25f); //any sort of rotation operation ends up with the strange behaivor
//rendering commands here
display.prepare();
textureShader.bind();
textureShader.loadViewMatrix(cam);
display.render(entity, &textureShader);
textureShader.stop();
display.swapBuffers();
}
So, to recap; I'm not having any issues with translating, scaling, "camera movement" and the projection matrix appears to work as well. Any time I attempt to rotate however, I get the exact same behavior as the linked to article above.
Final notes: I have depth testing enabled and clear the depth buffer each frame. I also pass GL_TRUE to transpose any matrix data I give to glUniformMatrix4fv. I've checked the locations of each of the uniforms and they are passing correctly; 0, 1 and 2 respectively. No -1.
I'm stumped, any help would be appreciated. I can post more code if need be, but I'm pretty sure this covers the entirety of where the problem most likely lies. Thanks again
The major issue is the matrix multipolication operation.
Since you manipulate the matrix (you read from the matrix and you write to it), are some elements already manipulated, before you read it.
e.g. In the first line this->matrix[0] is written to
this->matrix[0] = this->matrix[0] * rhs.matrix[0] + this->matrix[1] * rhs.matrix[4] + this->matrix[2] * rhs.matrix[8] + this->matrix[3] * rhs.matrix[12];
and in the second line this->matrix[0] is read again:
this->matrix[1] = this->matrix[0] * rhs.matrix[1] + this->matrix[1] * rhs.matrix[5] + this->matrix[2] * rhs.matrix[9] + this->matrix[3] * rhs.matrix[13];
Copy the matrix array to a local variable, to solve the issue:
matrix4x4& operator*=(const matrix4x4& rhs)
{
matrix4x4 act( this->matrix );
this->matrix[0] = act.matrix[0] * rhs.matrix[0] + act.matrix[1] * rhs.matrix[4] + act.matrix[2] * rhs.matrix[8] + act.matrix[3] * rhs.matrix[12];
this->matrix[1] = act.matrix[0] * rhs.matrix[1] + act.matrix[1] * rhs.matrix[5] + act.matrix[2] * rhs.matrix[9] + act.matrix[3] * rhs.matrix[13];
....
return *this;
}
By the way, since you multiply a vector to the matrix from the right, in the shader
gl_Position = projectionMatrix * viewMatrix * transformationMatrix * vec4(position.x, position.y, position.z, 1.0);
the matrix has to be initilized in column major order:
mat4 m44 = mat4(
vec4( Xx, Xy, Xz, 0.0),
vec4( Yx, Xy, Yz, 0.0),
vec4( Zx Zy Zz, 0.0),
vec4( Tx, Ty, Tz, 1.0) );
Note your matrices are initialized in row major order e.g. matrix4x4::translate:
GLfloat transformElements[16] =
{
1.0f, 0.0f, 0.0f, translation.x,
0.0f, 1.0f, 0.0f, translation.y,
0.0f, 0.0f, 1.0f, translation.z,
0.0f, 0.0f, 0.0f, 1.0f
};
So you have to transpose the matrix when you set it to the uniform glUniformMatrix4fv:
glUniformMatrix4fv( ..., ..., GL_TRUE, ... );

Vertex attribute is not being interpolated at all

I assign each vertex a unique set of coordinates and i am expecting the fragment in fragment shader to have interpolated value for that varying variable. Problem is, in fragment shader value always remains 0.0. How can i fix that. I am using opengles 2.0 with glsl version 1.2.
Vertex shader:
attribute vec4 Position;
attribute vec4 SourceColor;
attribute vec3 BaryCentricCoord;
varying vec4 DestinationColor;
uniform mat4 Projection;
uniform mat4 Modelview;
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
varying vec3 vBC;
void main() {
vBC = BaryCentricCoord;
DestinationColor = SourceColor;
gl_Position = Projection * Modelview * Position;
TexCoordOut = TexCoordIn;
};
Fragment shader:
varying vec4 DestinationColor;
varying vec2 TexCoordOut;
varying vec3 vBC;
uniform sampler2D Texture;
uniform int TexEnabled;
float edgeFactor();
void main() {
if (TexEnabled == 1) {
gl_FragColor = texture2D(Texture, TexCoordOut) * DestinationColor;
} else{
gl_FragColor = vec4(DestinationColor.xyz, edgeFactor());
}
}
float edgeFactor() {
vec3 d = fwidth(vBC);
vec3 a3 = smoothstep(vec3(0.0), d*1.5, vBC);
return min(min(a3.x, a3.y), a3.z);
};
Vertex data:
float vertices[] {
p1.x - deltaX, p1.y + deltaY, 0.0f, //top-left
p2.x - deltaX, p2.y + deltaY, 0.0f, //top-right
p2.x + deltaX, p2.y - deltaY, 0.0f, //bottom-right
p2.x + deltaX, p2.y - deltaY, 0.0f, //bottom-right
p1.x + deltaX, p1.y - deltaY, 0.0f, //bottom-left
p1.x - deltaX, p1.y + deltaY, 0.0f //top-left
};
Barycentric coordinates i am passing (each set of 3 corresponds to a vertex):
// R
data[0] = 1.0;
data[1] = 0.0;
data[2] = 0.0;
// G
data[3] = 0.0;
data[4] = 1.0;
data[5] = 0.0;
// B
data[6] = 0.0;
data[7] = 0.0;
data[8] = 1.0;
// B
data[9] = 0.0;
data[10] = 0.0;
data[11] = 1.0;
// G
data[12] = 0.0;
data[13] = 1.0;
data[14] = 0.0;
// R
data[15] = 1.0;
data[16] = 0.0;
data[17] = 0.0;
Enabled :
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_BLEND );
Passing data as: glDrawArrays(GL_TRIANGLES, 0, 6);

OpenGL GLSL - Projection matrix not working

I've got a basic OpenGL application and I want to use my projection matrix.
This is my matrix:
WorldCoordinates.m[0][0] = 2.0f / Width - 1.0f; WorldCoordinates.m[0][1] = 0; WorldCoordinates.m[0][2] = 0, WorldCoordinates.m[0][3] = 0;
WorldCoordinates.m[1][0] = 0; WorldCoordinates.m[1][1] = 2.0f / Height - 1.0f; WorldCoordinates.m[1][2] = 0, WorldCoordinates.m[1][3] = 0;
WorldCoordinates.m[2][0] = 0; WorldCoordinates.m[2][1] = 0; WorldCoordinates.m[2][2] = 0, WorldCoordinates.m[2][3] = 0;
WorldCoordinates.m[3][0] = 0; WorldCoordinates.m[3][1] = 0; WorldCoordinates.m[3][2] = 0, WorldCoordinates.m[3][3] = 0;
(WorldCoordinates is the Matrix4 struct that contains just a variable called m that is a float[4][4])(Width and Height are two ints).
I then apply this coordinates to my vertex shader using this:
shader.Bind();
glUniformMatrix4fv(glGetUniformLocation(shader.GetProgramID(), "worldCoordinates"), 1, GL_TRUE, &WorldCoordinates.m[0][0]);
(Shader is a class and has got a Bind() method that is just glUseProgram).
This is my Vertex Shader GLSL
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;
out vec3 Color;
out vec2 TexCoord;
uniform mat4 worldCoordinates;
void main()
{
gl_Position = worldCoordinates * vec4(position, 1.0f);
Color = color;
TexCoord = texCoord;
}
Using this, it doesn't work. But changing the gl_Position call to this:
gl_Position = vec4(vec3(position.x * 1/400 -1, position.y * 1/300 -1, 1.0f), 1.0f);
it renders as expected. Why is that?
This is how you build a orthogonal projection matrix :
static void
mat4_ortho(mat4_t m, float left, float right, float bottom, float top, float near, float far)
{
float rl = right - left;
float tb = top - bottom;
float fn = far - near;
mat4_zero(m);
m[ 0] = 2.0f / rl;
m[ 5] = 2.0f / tb;
m[10] = -2.0f / fn;
m[12] = -(left + right) / rl;
m[13] = -( top + bottom) / tb;
m[14] = -( far + near) / fn;
m[15] = 1.0f;
}
For you case, you'd set left=0, right=width, bottom=0, top=height, near and far don't matter, just set -1.0 and 1.0 for instance.
With such a matrix, the vertex coordinates you use for drawing will map 1:1 with the pixels on screen.