diff --git a/README.md b/README.md index 4091dc3..5a30cee 100644 --- a/README.md +++ b/README.md @@ -6,21 +6,31 @@ a random game engine thing. Now finally with ECS! # TO DO LIST -Add support for multiple lights. +## High priority (tomorrow) -Support dynamic shadow mapping (probably with cascades) +- Support dynamic shadow mapping (at least for shadows cast from the scene's sun) -Support animations. - - skinned meshes / morph targets +- Sort out LOG_X calls and ensure DEBUG, TRACE, INFO, etc are being used appropriately -At some point, add game controller support. Make sure it works well with the -InputManager class. +## Medium priority (next week) -(Was implemented in the past) -For font rendering, put all ASCII characters in one large texture and use -'instancing' (and uniform buffer objects?) to reduce draw calls. +- UI generator exposed at the Application level (Scene UI Systems could use this to draw menus every frame) -Sort out LOG_X calls and ensure DEBUG, TRACE, INFO, etc are being used appropriately +- Proper IBL with an irradiance map + +- Multiple lights (dynamic and static; do not need to be shadow casting) + +- More accurate raycast collision detection (Perhaps only sphere, capsule, OBB colliders. Mesh collision is probably unneccesary.) + +## Low priority (next month) + +- Explicit post processing pass exposed by the GFXDevice that can be used for bloom, AA, etc + +- Audio!! + +- Support animations (skinned meshes / morph targets) + +- Game controller support (controller detection, input, feedback etc in window.cpp; integration with input_manager.cpp) # DONE @@ -47,4 +57,4 @@ Added the BVH AABB tree made in Summer to start a much better Collision system. The CameraControllerSystem now uses raycasting to enable FPS-style player movement. -Added support for simple shadow mapping. \ No newline at end of file +Added static soft shadows. \ No newline at end of file diff --git a/res/engine/shaders/fancy.frag b/res/engine/shaders/fancy.frag index 22c9442..5cc8122 100644 --- a/res/engine/shaders/fancy.frag +++ b/res/engine/shaders/fancy.frag @@ -10,14 +10,15 @@ layout(set = 2, binding = 0) uniform sampler2D materialSetAlbedoSampler; layout(set = 2, binding = 1) uniform sampler2D materialSetNormalSampler; layout(set = 2, binding = 2) uniform sampler2D materialSetOcclusionRoughnessMetallic; -layout(location = 0) in vec2 fragUV; -layout(location = 1) in vec3 fragPosTangentSpace; -layout(location = 2) in vec3 fragViewPosTangentSpace; -layout(location = 3) in vec3 fragLightPosTangentSpace; -layout(location = 4) in vec3 fragNormWorldSpace; -layout(location = 5) in vec3 fragViewPosWorldSpace; -layout(location = 6) in vec3 fragPosWorldSpace; -layout(location = 7) in vec4 fragPosLightSpace; +layout(location = 0) in vec2 fragUV; // for looking up textures +layout(location = 1) in vec3 fragPosTangentSpace; // finding view vector +layout(location = 2) in vec3 fragViewPosTangentSpace; // finding view vector +layout(location = 3) in vec3 fragLightDirTangentSpace; // directional light +layout(location = 4) in vec3 fragNormWorldSpace; // for skybox reflection lookup +layout(location = 5) in vec3 fragViewPosWorldSpace; // for skybox reflection lookup +layout(location = 6) in vec3 fragPosWorldSpace; // for skybox reflection lookup +layout(location = 7) in vec4 fragPosLightSpace; // for shadow map lookup +layout(location = 8) in vec4 fragPosScreenSpace; // for shadow map randomness layout(location = 0) out vec4 outColor; @@ -32,6 +33,19 @@ float GGXDist(float alpha_2, float N_dot_H) { return num / den; } +vec2 VogelDiskSample(int sampleIndex, int samplesCount, float phi) { + const float GoldenAngle = 2.4; + float r = sqrt(sampleIndex + 0.5) / sqrt(samplesCount); + float theta = sampleIndex * GoldenAngle + phi; + return vec2(r * cos(theta), r * sin(theta)); +} + +float InterleavedGradientNoise(vec2 position_screen) +{ + const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f); + return magic.z * dot(position_screen, magic.xy); +} + void main() { const vec4 albedo_alpha = texture(materialSetAlbedoSampler, fragUV); @@ -48,18 +62,14 @@ void main() { const float roughness_2 = roughness * roughness; - vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4 * 4.0; - float light_distance = length(fragLightPosTangentSpace - fragPosTangentSpace); - float attenuation = 1.0 / (1.0 + 0.09 * light_distance + - 0.032 * (light_distance * light_distance)); - //light_colour *= 5.0 * attenuation; + vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4 * 2.0; const vec3 emission = vec3(0.0, 0.0, 0.0); const vec3 N = GetNormal(); const vec3 V = normalize(fragViewPosTangentSpace - fragPosTangentSpace); - const vec3 L = normalize(fragLightPosTangentSpace /* - fragPosTangentSpace */ ); + const vec3 L = normalize(fragLightDirTangentSpace); //const vec3 L = normalize(vec3(5.0, 0.0, 3.0)); const vec3 H = normalize(V + L); @@ -92,27 +102,27 @@ void main() { vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords.x = projCoords.x * 0.5 + 0.5; projCoords.y = projCoords.y * 0.5 + 0.5; - //float closestDepth = texture(globalSetShadowmap, projCoords.xy).r; const float currentDepth = max(projCoords.z, 0.0); - //const float bias = max(0.01 * (1.0 - L_dot_N), 0.005); float shadow = 0.0; - vec2 texelSize = 1.0 / textureSize(globalSetShadowmap, 0); - for(int x = -2; x <= 2; ++x) + vec2 texelSize = 2.0 / textureSize(globalSetShadowmap, 0); + const int samples = 16; + const float phi = InterleavedGradientNoise(fragPosScreenSpace.xy / fragPosScreenSpace.w) * 2.0 * PI; + //const float phi = 0.0; + for(int i = 0; i < samples; ++i) { - for(int y = -2; y <= 2; ++y) - { - float pcfDepth = texture(globalSetShadowmap, projCoords.xy + vec2(x, y) * texelSize).r; - shadow += currentDepth > pcfDepth ? 1.0 : 0.0; - } + float depth = texture(globalSetShadowmap, projCoords.xy + VogelDiskSample(i, samples, phi) * texelSize).r; + shadow += currentDepth > depth ? 1.0 : 0.0; } - shadow /= 25.0; - shadow = shadow < 0.25 ? 0.0 : shadow; + shadow /= float(samples); + //shadow = shadow < 0.25 ? 0.0 : shadow; lighting *= (1.0 - shadow); const vec3 ambient_light = vec3(0.09082, 0.13281, 0.18164) * 2.4; lighting += mix(ambient_light, texture(globalSetSkybox, R).rgb, metallic) * ao * diffuse_brdf; // this is NOT physically-based, it just looks cool - outColor = vec4(min(emission + lighting, 1.0), 1.0); - //outColor = vec4(vec3(shadow), 1.0); + // tone mapping + const vec3 hdr_color = min(emission + lighting, 1.0); + outColor = vec4(hdr_color / (hdr_color + 1.0), 1.0); + //outColor = vec4(vec3(1.0 - shadow), 1.0); } diff --git a/res/engine/shaders/fancy.vert b/res/engine/shaders/fancy.vert index 6636c45..87a79e6 100644 --- a/res/engine/shaders/fancy.vert +++ b/res/engine/shaders/fancy.vert @@ -21,11 +21,12 @@ layout(location = 3) in vec2 inUV; layout(location = 0) out vec2 fragUV; layout(location = 1) out vec3 fragPosTangentSpace; layout(location = 2) out vec3 fragViewPosTangentSpace; -layout(location = 3) out vec3 fragLightPosTangentSpace; +layout(location = 3) out vec3 fragLightDirTangentSpace; layout(location = 4) out vec3 fragNormWorldSpace; layout(location = 5) out vec3 fragViewPosWorldSpace; layout(location = 6) out vec3 fragPosWorldSpace; layout(location = 7) out vec4 fragPosLightSpace; +layout(location = 8) out vec4 fragPosScreenSpace; void main() { vec4 worldPosition = constants.model * vec4(inPosition, 1.0); @@ -39,14 +40,14 @@ void main() { fragUV = inUV; fragPosTangentSpace = worldToTangentSpace * vec3(worldPosition); fragViewPosTangentSpace = worldToTangentSpace * vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0)); - fragLightPosTangentSpace = worldToTangentSpace * vec3(-0.4278,0.7923,0.43502); // directional light - //fragLightPosTangentSpace = worldToTangentSpace * vec3(10.0, 0.0, 10.0); + fragLightDirTangentSpace = worldToTangentSpace * vec3(-0.4278,0.7923,0.43502); // directional light fragNormWorldSpace = N; fragViewPosWorldSpace = vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0)); fragPosWorldSpace = worldPosition.xyz; fragPosLightSpace = globalSetUniformBuffer.lightSpaceMatrix * vec4(worldPosition.xyz, 1.0); + fragPosScreenSpace = gl_Position; gl_Position.y *= -1.0; } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 543e24d..93f5361 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -1351,9 +1351,9 @@ gfx::Pipeline* GFXDevice::CreatePipeline(const gfx::PipelineInfo& info) if (info.depth_attachment_only) { // use depth bias if only rendering to a depth attachment rasterizer.depthBiasEnable = VK_TRUE; - rasterizer.depthBiasConstantFactor = 1.25f; + rasterizer.depthBiasConstantFactor = 2.0f;//1.25f; rasterizer.depthBiasClamp = 0.0f; - rasterizer.depthBiasSlopeFactor = 1.75f; + rasterizer.depthBiasSlopeFactor = 3.5f;//1.75f; } else { rasterizer.depthBiasEnable = VK_FALSE; diff --git a/test/src/camera_controller.cpp b/test/src/camera_controller.cpp index 5d47ee9..29a993a 100644 --- a/test/src/camera_controller.cpp +++ b/test/src/camera_controller.cpp @@ -63,14 +63,14 @@ void CameraControllerSystem::OnUpdate(float ts) c->vel.z += CameraControllerComponent::kGravAccel * dt; // jumping - if (scene_->app()->input_manager()->GetButtonPress("jump") && c->grounded) { + if (scene_->app()->input_manager()->GetButtonPress("jump") && (c->grounded || c->noclip)) { c->vel.z += CameraControllerComponent::kJumpHeight; // m/s } // update position with velocity: // check horizontal collisions first as otherwise the player may be teleported above a wall instead of colliding against it - if (c->vel.x != 0.0f || c->vel.y != 0.0f) { // just in case, to avoid a ray with direction = (0,0,0) + if ((c->vel.x != 0.0f || c->vel.y != 0.0f) && !c->noclip) { // just in case, to avoid a ray with direction = (0,0,0) std::array raycasts{}; engine::Raycast* chosen_cast = nullptr; // nullptr means no hit at all @@ -113,45 +113,47 @@ void CameraControllerSystem::OnUpdate(float ts) } // check falling collisions - if (c->vel.z < 0.0f) { // if falling - engine::Ray fall_ray{}; - fall_ray.origin = t->position; - fall_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight; - fall_ray.direction = {0.0f, 0.0f, -1.0f}; // down - const engine::Raycast fall_raycast = scene_->GetSystem()->GetRaycast(fall_ray); - if (fall_raycast.hit) { // there is ground below player - // find how far the player will move (downwards) if velocity is applied without collision - const float mag_dz = fabsf(c->vel.z * dt); - // check if the player will be less than 'height' units above the collided ground - if (mag_dz > fall_raycast.distance - CameraControllerComponent::kMaxStairHeight) { - // push player up to ground level and set as grounded - t->position.z = fall_raycast.location.z + CameraControllerComponent::kPlayerHeight; - c->vel.z = 0.0f; - c->grounded = true; + if (!c->noclip) { + if (c->vel.z < 0.0f) { // if falling + engine::Ray fall_ray{}; + fall_ray.origin = t->position; + fall_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight; + fall_ray.direction = { 0.0f, 0.0f, -1.0f }; // down + const engine::Raycast fall_raycast = scene_->GetSystem()->GetRaycast(fall_ray); + if (fall_raycast.hit) { // there is ground below player + // find how far the player will move (downwards) if velocity is applied without collision + const float mag_dz = fabsf(c->vel.z * dt); + // check if the player will be less than 'height' units above the collided ground + if (mag_dz > fall_raycast.distance - CameraControllerComponent::kMaxStairHeight) { + // push player up to ground level and set as grounded + t->position.z = fall_raycast.location.z + CameraControllerComponent::kPlayerHeight; + c->vel.z = 0.0f; + c->grounded = true; + } + else { // there is ground below player, however they are not grounded + c->grounded = false; + } } - else { // there is ground below player, however they are not grounded + else { // there is no ground below player (THEY ARE FALLING INTO THE VOID) c->grounded = false; } } - else { // there is no ground below player (THEY ARE FALLING INTO THE VOID) - c->grounded = false; - } - } - else if (c->vel.z > 0.0f) { - c->grounded = false; // they are jumping - // check if intersection with ceiling - engine::Ray jump_ray{}; - jump_ray.origin = t->position; - jump_ray.direction = { 0.0f, 0.0f, 1.0f }; - const engine::Raycast jump_raycast = scene_->GetSystem()->GetRaycast(jump_ray); - if (jump_raycast.hit) { - // find how far the player will move (upwards) if velocity is applied without collision - const float mag_dz = fabsf(c->vel.z * dt); - // check if the player will be higher than the collided ground - if (mag_dz > jump_raycast.distance - CameraControllerComponent::kPlayerCollisionRadius) { - // push player below ceiling - t->position.z = jump_raycast.location.z - CameraControllerComponent::kPlayerCollisionRadius; - c->vel.z = 0.0f; + else if (c->vel.z > 0.0f) { + c->grounded = false; // they are jumping + // check if intersection with ceiling + engine::Ray jump_ray{}; + jump_ray.origin = t->position; + jump_ray.direction = { 0.0f, 0.0f, 1.0f }; + const engine::Raycast jump_raycast = scene_->GetSystem()->GetRaycast(jump_ray); + if (jump_raycast.hit) { + // find how far the player will move (upwards) if velocity is applied without collision + const float mag_dz = fabsf(c->vel.z * dt); + // check if the player will be higher than the collided ground + if (mag_dz > jump_raycast.distance - CameraControllerComponent::kPlayerCollisionRadius) { + // push player below ceiling + t->position.z = jump_raycast.location.z - CameraControllerComponent::kPlayerCollisionRadius; + c->vel.z = 0.0f; + } } } } diff --git a/test/src/camera_controller.hpp b/test/src/camera_controller.hpp index 6ba54ad..23b6a1e 100644 --- a/test/src/camera_controller.hpp +++ b/test/src/camera_controller.hpp @@ -28,6 +28,8 @@ struct CameraControllerComponent { static constexpr float kGravAccel = -9.81f; static constexpr float kMaxDistanceFromOrigin = 1000.0f; + bool noclip = false; + float yaw = 0.0f; float pitch = glm::half_pi(); glm::vec3 vel{0.0f, 0.0f, 0.0f}; diff --git a/test/src/game.cpp b/test/src/game.cpp index e31b560..3d8935e 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -67,9 +67,12 @@ void PlayGame(GameSettings settings) /* as of right now, the entity with tag 'camera' is used to build the view * matrix */ + engine::Entity sponza = engine::util::LoadGLTF(*start_scene, app.GetResourcePath("models/tree.glb"), true); + start_scene->GetPosition(sponza).z += 90.0f; + start_scene->RegisterComponent(); start_scene->RegisterSystem(); - start_scene->AddComponent(camera); + start_scene->AddComponent(camera)->noclip = true; } engine::Scene* main_scene = app.scene_manager()->CreateEmptyScene();