Implement basic shadows
Still shadow acne, but working in principle
This commit is contained in:
parent
5f30873f23
commit
0254b81882
@ -15,7 +15,7 @@ struct MouseLook {
|
||||
|
||||
glm::quat rotation;
|
||||
|
||||
bool is_active;
|
||||
bool is_active = true;
|
||||
};
|
||||
|
||||
#endif //ECSGAME_MOUSELOOK_H
|
||||
|
@ -14,7 +14,7 @@ struct Movement {
|
||||
|
||||
glm::vec3 velocity;
|
||||
|
||||
bool is_active;
|
||||
bool is_active = true;
|
||||
};
|
||||
|
||||
#endif //ECSGAME_MOVEMENT_H
|
||||
|
@ -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;
|
||||
|
@ -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
23
Shaders/debug-fragment.fs
Normal 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
12
Shaders/debug-vertex.vs
Normal 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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
7
Shaders/shadow-fragment.fs
Normal file
7
Shaders/shadow-fragment.fs
Normal 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
10
Shaders/shadow-vertex.vs
Normal 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);
|
||||
}
|
5
main.cpp
5
main.cpp
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user