Implement basic shadows

Still shadow acne, but working in principle
This commit is contained in:
karl 2020-10-27 21:13:07 +01:00
parent 5f30873f23
commit 0254b81882
11 changed files with 212 additions and 20 deletions

View File

@ -15,7 +15,7 @@ struct MouseLook {
glm::quat rotation;
bool is_active;
bool is_active = true;
};
#endif //ECSGAME_MOUSELOOK_H

View File

@ -14,7 +14,7 @@ struct Movement {
glm::vec3 velocity;
bool is_active;
bool is_active = true;
};
#endif //ECSGAME_MOVEMENT_H

View File

@ -35,7 +35,7 @@ struct PathMove {
PathMove(double speed, Path path, Views views) : speed(speed), path(path), views(views) {}
bool is_active;
bool is_active = false;
double speed;
float time_passed = 0.0;
int current_point_index = 0;

View File

@ -18,6 +18,38 @@
using namespace ECS;
// For debugging:
// renderQuad() renders a 1x1 XY quad in NDC
// -----------------------------------------
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad()
{
if (quadVAO == 0)
{
float quadVertices[] = {
// positions // texture Coords
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
// setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
class RenderSystem : public EntitySystem {
public:
struct RenderObject {
@ -36,9 +68,11 @@ public:
// 0 can't be a valid texture name, so we use it for meshes without textures here
if (texture_id != 0) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_id);
}
// TODO: Not always required (not when rendering shadows) - make functions separate?
shader.setFloat("diffuseStrength", material.diffuse);
shader.setFloat("specularStrength", material.specular);
@ -125,40 +159,113 @@ public:
return renderObjects;
}
void render(World *pWorld, Shader shader) {
RenderSystem() {
// Configure depth map
glGenFramebuffers(1, &depthMapFBO);
// create depth texture
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadow_width, shadow_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void render(World *pWorld, Shader normalShader, Shader shadowShader, Shader debugShader) {
pWorld->each<Camera, Transform>([&](Entity *ent, ComponentHandle<Camera> camera, ComponentHandle<Transform> cameraTransform) {
// Common
glClearColor(0.6f, 0.9f, 0.9f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use();
std::vector<std::vector<RenderObject>> allRenderObjects = getRenderObjects(pWorld, cameraTransform->get_origin());
std::vector<RenderObject> renderObjects = allRenderObjects[0];
std::vector<RenderObject> transparentRenderObjects = allRenderObjects[1];
// Calculate matrix for lighting
// Get light direction
// TODO: Currently only the last light is used!
glm::vec3 lightDirection;
pWorld->each<DirectionalLight>([&](Entity *ent, ComponentHandle<DirectionalLight> light) {
lightDirection = light->direction;
});
float near_plane = 1.0f, far_plane = 100.0f;
glm::mat4 lightProjection = glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f, near_plane, far_plane);
glm::mat4 lightView = glm::lookAt(lightDirection * 40.0f, -lightDirection, glm::vec3(0.0, 1.0, 0.0));
glm::mat4 lightSpaceMatrix = lightProjection * lightView;
// Render shadows
shadowShader.use();
shadowShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
glViewport(0, 0, shadow_width, shadow_height);
glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO);
for (const RenderObject &obj : renderObjects) {
obj.render(shadowShader);
}
for (const RenderObject &obj : transparentRenderObjects) {
obj.render(shadowShader);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// reset viewport
glViewport(0, 0, screen_width, screen_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render normal
normalShader.use();
// Lighting
// TODO: Currently only the last light is used!
pWorld->each<DirectionalLight>([&](Entity *ent, ComponentHandle<DirectionalLight> light) {
shader.setVec3("lightDirection", light->direction);
});
normalShader.setVec3("lightDirection", lightDirection);
normalShader.setMat4("lightSpaceMatrix", lightSpaceMatrix);
glm::vec3 cameraPos = cameraTransform->get_origin();
glm::mat4 view = cameraTransform->matrix;
view[3] = glm::vec4(cameraPos, 1.0);
shader.setMat4("projection", camera->projection);
shader.setMat4("view", glm::inverse(view));
shader.setVec3("cameraPosition", cameraTransform->get_origin());
normalShader.setMat4("projection", camera->projection);
normalShader.setMat4("view", glm::inverse(view));
normalShader.setVec3("cameraPosition", cameraTransform->get_origin());
std::vector<std::vector<RenderObject>> allRenderObjects = getRenderObjects(pWorld, cameraPos);
std::vector<RenderObject> renderObjects = allRenderObjects[0];
std::vector<RenderObject> transparentRenderObjects = allRenderObjects[1];
// Bind shadow texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthMap);
for (const RenderObject &obj : renderObjects) {
obj.render(shader);
obj.render(normalShader);
}
for (const RenderObject &obj : transparentRenderObjects) {
obj.render(shader);
obj.render(normalShader);
}
// Render the light's depth map to a quad for debugging
debugShader.use();
debugShader.setFloat("near_plane", near_plane);
debugShader.setFloat("far_plane", far_plane);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, depthMap);
//renderQuad(); // TODO: Add actual code switch instead of commenting
});
}
int screen_width = 1280;
int screen_height = 720;
int shadow_width = 1024;
int shadow_height = 1024;
unsigned int depthMap;
unsigned int depthMapFBO;
};
#endif //ECSGAME_RENDERSYSTEM_H

23
Shaders/debug-fragment.fs Normal file
View File

@ -0,0 +1,23 @@
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D depthMap;
uniform float near_plane;
uniform float far_plane;
// required when using a perspective projection matrix
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0; // Back to NDC
return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane));
}
void main()
{
float depthValue = texture(depthMap, TexCoords).r;
// FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0); // perspective
FragColor = vec4(vec3(depthValue), 1.0); // orthographic
}

12
Shaders/debug-vertex.vs Normal file
View File

@ -0,0 +1,12 @@
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}

View File

@ -4,14 +4,38 @@ out mediump vec4 FragColor;
in mediump vec2 TexCoord;
in mediump vec3 Normal;
in mediump vec3 FragPos;
in mediump vec4 FragPosLightSpace;
layout(binding=0) uniform sampler2D shadowMap;
layout(binding=1) uniform sampler2D tex;
uniform sampler2D tex;
uniform mediump vec3 lightDirection;
uniform mediump vec3 cameraPosition;
uniform mediump float diffuseStrength;
uniform mediump float specularStrength;
mediump float ShadowCalculation(vec4 fragPosLightSpace)
{
// perform perspective divide
mediump vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// If we're outside of [0.0, 1.0] in the coordinates, return 0
if (projCoords.x < 0.0 || projCoords.y < 0.0 || projCoords.x > 1.0 || projCoords.y > 1.0) return 0.0;
// get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
mediump float closestDepth = texture(shadowMap, projCoords.xy).r;
// get depth of current fragment from light's perspective
mediump float currentDepth = projCoords.z;
// check whether current frag pos is in shadow
mediump float shadow = currentDepth > closestDepth ? 1.0 : 0.0;
return shadow;
}
void main()
{
mediump vec4 texColor = texture(tex, TexCoord);
@ -33,8 +57,11 @@ void main()
mediump float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
// Shadow
mediump float shadow = ShadowCalculation(FragPosLightSpace);
// Total
mediump float light = min(diff * diffuseStrength + ambient + spec * specularStrength, 1.0);
mediump float light = min(diff * diffuseStrength + ambient + spec * specularStrength - shadow * 0.5 + 0.5, 2.0);
// Assign resulting color
mediump vec3 color = texColor.xyz * light;

View File

@ -6,10 +6,12 @@ layout (location = 2) in vec2 UV;
out vec2 TexCoord;
out vec3 Normal;
out vec3 FragPos;
out vec4 FragPosLightSpace;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 lightSpaceMatrix;
void main()
{
@ -17,4 +19,5 @@ void main()
TexCoord = UV;
Normal = NORMAL;
FragPos = vec3(model * vec4(aPos, 1.0));
FragPosLightSpace = lightSpaceMatrix * vec4(FragPos, 1.0);
}

View File

@ -0,0 +1,7 @@
#version 330 core
void main()
{
// This happens implicitly anyways
// gl_FragDepth = gl_FragCoord.z;
}

10
Shaders/shadow-vertex.vs Normal file
View File

@ -0,0 +1,10 @@
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 lightSpaceMatrix;
uniform mat4 model;
void main()
{
gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0);
}

View File

@ -112,6 +112,7 @@ int main() {
glm::angleAxis(glm::radians(180.f), glm::vec3(0.f, 1.f, 0.f))
})
);
player->get<Transform>()->set_origin(glm::vec3(0.0, 3.0, 4.0));
Entity *monkey = world->create();
monkey->assign<Transform>();
@ -172,6 +173,8 @@ int main() {
sun->assign<DirectionalLight>(glm::normalize(glm::vec3(1.0, 1.0, 1.0)));
Shader defaultShader("Shaders/default-vertex.vs", "Shaders/default-fragment.fs");
Shader shadowShader("Shaders/shadow-vertex.vs", "Shaders/shadow-fragment.fs");
Shader debugShader("Shaders/debug-vertex.vs", "Shaders/debug-fragment.fs");
double timeInLastFrame = glfwGetTime();
double elapsed_time = 0.0;
@ -184,7 +187,7 @@ int main() {
elapsed_time += delta;
world->tick(delta);
renderSystem->render(world, defaultShader);
renderSystem->render(world, defaultShader, shadowShader, debugShader);
/* Swap front and back buffers */
glfwSwapBuffers(window);