I am fairly new to graphics programming, and I am working on a rudimentary game engine in C++. I had some old graphics code written in Java that I translated, but for some reason the shadows are not working. I have spent 5 hours staring at this code trying to figure out what's wrong.
My render code:
mat4 perspective(float fovy, float aspect, float n, float f)
{
float q = 1.0f / ((float) tan(util::toRadians(0.5f * fovy)));
float A = q / aspect;
float B = (n + f) / (n - f);
float C = (2.0f * n * f) / (n - f);
mat4 r(A, 0, 0, 0,
0, q, 0, 0,
0, 0, B, C,
0, 0, -1, 0);
return r;
}
mat4 lookAt(point3 eye, point3 target, vec3 y)
{
vec3 eyeV{eye};
vec3 targetV(target);
vec3 fwd = (targetV - eyeV).normalize();
vec3 side = (fwd.cross(y)).normalize();
vec3 up = (side.cross(fwd)).normalize();
mat4 look{
side[0], side[1], side[2], side.dot(eyeV * -1),
up[0], up[1], up[2], up.dot(eyeV * -1),
-fwd[0], -fwd[1], -fwd[2], (fwd * -1).dot(eyeV * -1),
0, 0, 0, 1
};
return look;
}
Renderer::Renderer(int _width, int _height, std::string _title) : width(_width), height(_height), title(_title),
pMat(perspective(60.0f, (float)_width/(float)_height, 0.1f, 1000.0f))
{
b = mat4{
.5, 0, 0, .5,
0, .5, 0, .5,
0, 0, .5, .5,
0, 0, 0, 1
};
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); // Note 5 is max value atm
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
status = GLFW_WINDOW_ERROR;
return;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to Initalize GLAD" << std::endl;
status = GLAD_INIT_ERROR;
return;
}
// TODO Shader error handling
Shader _passoneshader("GLSL\\pass1.vert");
pass1_shader = _passoneshader;
Shader _shader("GLSL\\basic.vert", "GLSL\\basic.frag");
basic_shader = _shader;
Shader _texshader("GLSL\\textured_pass2.vert", "GLSL\\textured_pass2.frag");
textured_shader = _texshader;
glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
StateManager& sm = StateManager::get();
std::vector<Object> objects = sm.get_objects();
std::vector<Object> tobjects = sm.get_textured_objects();
VBO = (GLuint*) malloc(objects.size() * sizeof(GLuint));
TVBO = (GLuint*) malloc(tobjects.size() * sizeof(GLuint));
TCBO = (GLuint*) malloc(tobjects.size() * sizeof(GLuint));
NBO = (GLuint*) malloc(tobjects.size() * sizeof(GLuint));
std::cout << "size: " << objects.size() << std::endl;
glGenBuffers(objects.size(), VBO);
glGenBuffers(tobjects.size(), TVBO);
glGenBuffers(tobjects.size(), TCBO);
glGenBuffers(tobjects.size(), NBO);
// for (int i = 0; i < objects.size(); ++i)
// {
// Object obj = objects[i];
// std::cout << VBO[i] << std::endl;
// glBindBuffer(GL_ARRAY_BUFFER, VBO[i]);
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// glEnableVertexAttribArray(0);
// std::vector<float> verts = obj.get_vertices();
// float* vertices = &verts[0];
// glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(float), vertices, GL_STATIC_DRAW);
// }
shadow_buffer = (GLuint*) malloc(sizeof(GLuint));
glGenFramebuffers(1, shadow_buffer);
shadow_tex = (GLuint*) malloc(sizeof(GLuint));
glGenTextures(1, shadow_tex);
// glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, shadow_tex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glBindFramebuffer(GL_FRAMEBUFFER, shadow_buffer[0]);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadow_tex[0], 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
textures = (unsigned int*) malloc(tobjects.size() * sizeof(unsigned int));
glGenTextures(tobjects.size(), textures);
for (int i = 0; i < tobjects.size(); ++i)
{
Object obj = tobjects[i];
int tex_width, tex_height, nrChannels;
std::cout << obj.get_tex_path() << std::endl;
unsigned char* data = stbi_load(obj.get_tex_path().c_str(), &tex_width, &tex_height, &nrChannels, 0);
glBindTexture(GL_TEXTURE_2D, textures[i]);
if (data)
{
std::cout << "got data" << std::endl;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
std::vector<float> verts = obj.get_vertices();
float* vertices = &verts[0];
std::vector<float> texcoords = obj.get_tex_coords();
float* tex = &texcoords[0];
std::vector<float> normals = obj.get_normals();
float* norms = &normals[0];
// glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, TVBO[i]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
glBufferData(GL_ARRAY_BUFFER, verts.size() * sizeof(float), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, TCBO[i]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(float), tex, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, NBO[i]);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(2);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), norms, GL_STATIC_DRAW);
}
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
timeValue = glfwGetTime();
lastTimeValue = timeValue;
status = INITIALIZED;
}
void Renderer::RenderLoop()
{
// input
processInput();
float depthClearVal[] = {1.0f};
// depthClearVal[0] = 1.0f;
glClearBufferfv(GL_DEPTH, 0, depthClearVal);
// rendering commands
glClearColor(.2f, .3f, .3f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glBindFramebuffer(GL_FRAMEBUFFER, shadow_buffer[0]);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadow_tex[0], 0);
glDrawBuffer(GL_NONE);
// glReadBuffer(GL_NONE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_POLYGON_OFFSET_FILL); // for reducing
glPolygonOffset(2.0f, 4.0f); // shadow artifacts
// std::cout << "before pass one" << std::endl;
passOne();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_POLYGON_OFFSET_FILL); // artifact reduction, continued
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, shadow_tex[0]);
glDrawBuffer(GL_FRONT);
// std::cout << "before pass two" << std::endl;
passTwo();
glfwSwapBuffers(window);
glfwPollEvents();
status = LOOP_SUCCESS; // TODO error handling on LOOP_FAILURE
}
void Renderer::passOne()
{
// basic_shader.use();
pass1_shader.use();
StateManager& sm = StateManager::get();
std::vector<Object> objects = sm.get_objects();
std::vector<Object> tobjects = sm.get_textured_objects();
PositionalLight light = sm.get_lights()[0]; // TODO allow more lights
point3 light_pos = light.get_location();
point3 target = light.get_target();
vec3 up = vec3{light_pos * -1}.normalize() * mat4::rotation(mat4::X, 90);
lightV_matrix = lookAt(light_pos, target, up);
lightP_matrix = pMat;
mat4 shadowMVP;
// glUniformMatrix4fv(pmat_loc, 1, GL_TRUE, pMat.getFloatArray());
int shadow_loc = glGetUniformLocation(pass1_shader.getID(), "shadowMVP");
// for (int i = 0; i < objects.size(); ++i)
// {
// Object obj = objects[i];
// mat4 mMat = obj.get_position().as_translation() * obj.get_rotation();
// shadowMVP = mMat * lightV_matrix * lightP_matrix;
//// mat4 mvMat = vMat*mMat;
// glUniformMatrix4fv(shadow_loc, 1, GL_TRUE, shadowMVP.getFloatArray());
// glBindVertexArray(VAO);
// glBindBuffer(GL_ARRAY_BUFFER, VBO[i]);
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// glEnableVertexAttribArray(0);
// glDrawArrays(GL_TRIANGLES, 0, obj.get_num_vertices() / 3);
//
// }
if (!tobjects.empty())
{
for (int i = 0; i < tobjects.size(); ++i)
{
Object obj = tobjects[i];
mat4 mMat = obj.get_position().as_translation() * obj.get_rotation() * obj.get_scale();
// mMat.print();
// mat4 mMat = obj.get_scale() * obj.get_rotation() * obj.get_position().as_translation();
// shadowMVP = mMat * lightV_matrix * lightP_matrix;
// shadowMVP.print();
shadowMVP = lightP_matrix * lightV_matrix * mMat;
glUniformMatrix4fv(shadow_loc, 1, GL_TRUE, shadowMVP.getFloatArray());
// glBindTexture(GL_TEXTURE_2D, textures[i]);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, TVBO[i]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
// glBindBuffer(GL_ARRAY_BUFFER, TCBO[i]);
// glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
// glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, obj.get_num_vertices() / 3);
}
}
}
void Renderer::passTwo()
{
// basic_shader.use();
// pass1_shader.use();
glClear(GL_DEPTH_BUFFER_BIT);
int pmat_loc = glGetUniformLocation(basic_shader.getID(), "proj_matrix");
int mv_loc = glGetUniformLocation(basic_shader.getID(), "mv_matrix");
//
StateManager& sm = StateManager::get();
//
// std::vector<Object> objects = sm.get_objects();
std::vector<Object> tobjects = sm.get_textured_objects();
Camera camera = sm.get_camera();
vMat = camera.get_transformation();
mat4 shadowMVP;
//
// glUniformMatrix4fv(pmat_loc, 1, GL_TRUE, pMat.getFloatArray());
//
// for (int i = 0; i < objects.size(); ++i)
// {
// Object obj = objects[i];
// mat4 mMat = obj.get_position().as_translation() * obj.get_rotation();
// mat4 mvMat = vMat*mMat;
// glUniformMatrix4fv(mv_loc, 1, GL_TRUE, mvMat.getFloatArray());
// glBindVertexArray(VAO);
// glBindBuffer(GL_ARRAY_BUFFER, VBO[i]);
// glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// glEnableVertexAttribArray(0);
// glDrawArrays(GL_TRIANGLES, 0, obj.get_num_vertices() / 3);
//
// }
if (!tobjects.empty())
{
textured_shader.use();
pmat_loc = glGetUniformLocation(textured_shader.getID(), "proj_matrix");
mv_loc = glGetUniformLocation(textured_shader.getID(), "mv_matrix");
int sloc = glGetUniformLocation(textured_shader.getID(), "shadowMVP");
glUniformMatrix4fv(pmat_loc, 1, GL_TRUE, pMat.getFloatArray());
for (int i = 0; i < tobjects.size(); ++i)
{
Object obj = tobjects[i];
mat4 mMat = obj.get_position().as_translation() * obj.get_rotation() * obj.get_scale();
// mat4 mMat = obj.get_scale() * obj.get_rotation() * obj.get_position().as_translation();
mat4 mvMat = vMat*mMat;
// mat4 mvMat = mMat*vMat;
installLights(textured_shader);
glUniformMatrix4fv(mv_loc, 1, GL_TRUE, mvMat.getFloatArray());
shadowMVP = mMat * lightV_matrix * lightP_matrix * b;
// shadowMVP = b * lightP_matrix * lightV_matrix * mMat;
glUniformMatrix4fv(sloc, 1, GL_TRUE, shadowMVP.getFloatArray());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, textures[i]);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, TVBO[i]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, TCBO[i]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, NBO[i]);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLES, 0, obj.get_num_vertices() / 3);
}
}
}
void Renderer::installLights(Shader current_shader)
{
StateManager& sm = StateManager::get();
PositionalLight currentLight = sm.get_lights()[0];
point3 lightP = currentLight.get_location();
point3 lightPv = lightP * vMat;
// float [] viewspaceLightPos = new float[] { (float) lightPv.getX(), (float) lightPv.getY(), (float) lightPv.getZ() };
std::array<float, 3> viewspaceLightPos = lightPv.get_array();
// set the current globalAmbient settings
int globalAmbLoc = glGetUniformLocation(textured_shader.getID(), "globalAmbient");
float globalAmbient[] = {.2f, .2f, .2f, 1.0f};
glUniform4fv(globalAmbLoc, 1, globalAmbient);
// get the locations of the light and material fields in the shader
int ambLoc = glGetUniformLocation(current_shader.getID(), "light.ambient");
int diffLoc = glGetUniformLocation(current_shader.getID(), "light.diffuse");
int specLoc = glGetUniformLocation(current_shader.getID(), "light.specular");
int posLoc = glGetUniformLocation(current_shader.getID(), "light.position");
int MambLoc = glGetUniformLocation(current_shader.getID(), "material.ambient");
int MdiffLoc = glGetUniformLocation(current_shader.getID(), "material.diffuse");
int MspecLoc = glGetUniformLocation(current_shader.getID(), "material.specular");
int MshiLoc = glGetUniformLocation(current_shader.getID(), "material.shininess");
std::array<float, 4> amb = currentLight.get_ambient().get_array();
std::array<float, 4> diff = currentLight.get_diffuse().get_array();
std::array<float, 4> spec = currentLight.get_specular().get_array();
// set the uniform light and material values in the shader
glUniform4fv(ambLoc, 1, &amb[0]);
glUniform4fv(diffLoc, 1, &diff[0]);
glUniform4fv(specLoc, 1, &spec[0]);
glUniform3fv(posLoc, 1, &viewspaceLightPos[0]);
// Values taken from graphicslib3d Material.SILVER
float matAmb[] = {0.1923f, 0.1923f, 0.1923f, 1.0f};
float matDiff[] = {0.5075f, 0.5075f, 0.5075f, 1.0f};
float matSpec[] = {0.5083f, 0.5083f, 0.5083f, 1.0f};
float matShine = 51.2;
glUniform4fv(MambLoc, 1, matAmb);
glUniform4fv(MdiffLoc, 1, matDiff);
glUniform4fv(MspecLoc, 1, matSpec);
glUniform1f(MshiLoc, matShine);
}
const bool Renderer::shouldLoop() const
{
return !glfwWindowShouldClose(window);
}
My pass 1 vertex shader:
#version 450 core
layout (location=0) in vec3 vertPos;
uniform mat4 shadowMVP;
void main(void)
{
gl_Position = shadowMVP * vec4(vertPos,1.0);
}
My pass 2 vertex shader:
#version 450 core
layout (location=0) in vec3 vertPos;
layout (location=1) in vec2 tex_coord;
layout (location=2) in vec3 vertNormal;
out vec3 vNormal, vLightDir, vVertPos, vHalfVec;
out vec4 shadow_coord;
out vec2 tc;
struct PositionalLight
{
vec4 ambient, diffuse, specular;
vec3 position;
};
struct Material
{
vec4 ambient, diffuse, specular;
float shininess;
};
uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 shadowMVP;
layout (binding=1) uniform sampler2DShadow shadowTex;
layout (binding=2) uniform sampler2D s;
void main(void)
{
//output the vertex position to the rasterizer for interpolation
vVertPos = (mv_matrix * vec4(vertPos,1.0)).xyz;
//get a vector from the vertex to the light and output it to the rasterizer for interpolation
vLightDir = light.position - vVertPos;
//get a vertex normal vector in eye space and output it to the rasterizer for interpolation
mat4 normalMat = transpose(inverse(mv_matrix));
vNormal = (normalMat * vec4(vertNormal,1.0)).xyz;
// calculate the half vector (L+V)
vHalfVec = (vLightDir-vVertPos).xyz;
shadow_coord = shadowMVP * vec4(vertPos,1.0);
tc = tex_coord;
gl_Position = proj_matrix * mv_matrix * vec4(vertPos,1.0);
}
My pass 2 fragment shader:
#version 450
in vec3 vNormal, vLightDir, vVertPos, vHalfVec;
in vec4 shadow_coord;
in vec2 tc;
out vec4 fragColor;
struct PositionalLight
{
vec4 ambient, diffuse, specular;
vec3 position;
};
struct Material
{
vec4 ambient, diffuse, specular;
float shininess;
};
uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 shadowMVP;
layout (binding=1) uniform sampler2DShadow shadowTex;
layout (binding=2) uniform sampler2D s;
void main(void)
{
vec3 L = normalize(vLightDir);
vec3 N = normalize(vNormal);
vec3 V = normalize(-vVertPos);
vec3 H = normalize(vHalfVec);
float inShadow = textureProj(shadowTex, shadow_coord);
vec4 texColor = texture(s, tc);
fragColor = texColor*(globalAmbient + light.ambient + light.diffuse * max(dot(L,N), 0.0));
// fragColor = vec4(0,0,0,0);
if (inShadow != 0.0) {
fragColor += light.diffuse * material.diffuse * max(dot(L, N), 0.0)
+ light.specular * material.specular
* pow(max(dot(H, N), 0.0), material.shininess*3.0);
}
}
When I open it up in renderdoc, the shadow texture appears to be all black, so I think it just isn't writing the texture properly, but I'm not sure. I appreciate any help.
Edit:
Render doc capture file: https://www.dropbox.com/s/zdxobai3ce86eod/capture.rdc?dl=0