From 3b791f79bf53e481d053ea4516eedce8be3423bf Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 1 Nov 2022 19:12:04 +0000 Subject: Switch to a deferred lighting based render pipeline Tidy-up required. --- gfx/gl/shaders/basicShader.fs | 23 +++++---- gfx/gl/shaders/basicShader.vs | 18 ++++--- gfx/gl/shaders/landmassShader.fs | 46 ++++++++++-------- gfx/gl/shaders/landmassShader.vs | 16 +++--- gfx/gl/shaders/lightingShader.fs | 62 ++++++++++++++++++++++++ gfx/gl/shaders/lightingShader.vs | 13 +++++ gfx/gl/shaders/waterShader.fs | 21 +++++--- gfx/gl/shaders/waterShader.vs | 23 +++++---- ui/gameMainWindow.cpp | 9 ++-- ui/gameMainWindow.h | 3 +- ui/sceneRenderer.cpp | 102 +++++++++++++++++++++++++++++++++++++++ ui/sceneRenderer.h | 18 +++++++ ui/window.cpp | 2 +- 13 files changed, 292 insertions(+), 64 deletions(-) create mode 100644 gfx/gl/shaders/lightingShader.fs create mode 100644 gfx/gl/shaders/lightingShader.vs create mode 100644 ui/sceneRenderer.cpp create mode 100644 ui/sceneRenderer.h diff --git a/gfx/gl/shaders/basicShader.fs b/gfx/gl/shaders/basicShader.fs index 0bd42b4..9c4945b 100644 --- a/gfx/gl/shaders/basicShader.fs +++ b/gfx/gl/shaders/basicShader.fs @@ -1,15 +1,20 @@ #version 330 core -in vec2 texCoord0; -in vec3 normal0; +in vec3 FragPos; +in vec2 TexCoords; +in vec3 Normal; -uniform sampler2D sampler; -uniform vec3 lightDirection; -uniform vec3 lightColor; -uniform vec3 ambientColor; +out vec4 gPosition; +out vec4 gNormal; +out vec4 gAlbedoSpec; -void main() +uniform sampler2D texture0; + +void +main() { - gl_FragColor = texture(sampler, texCoord0); - gl_FragColor.rgb *= clamp(ambientColor + (dot(-lightDirection, normal0) * lightColor), 0.0, 1.0); + float clear = round(texture(texture0, TexCoords).a); + gPosition = vec4(FragPos, clear); + gNormal = vec4(Normal, clear); + gAlbedoSpec = texture(texture0, TexCoords); } diff --git a/gfx/gl/shaders/basicShader.vs b/gfx/gl/shaders/basicShader.vs index 8f2ab3e..bc7ea5d 100644 --- a/gfx/gl/shaders/basicShader.vs +++ b/gfx/gl/shaders/basicShader.vs @@ -4,15 +4,21 @@ in vec3 position; in vec2 texCoord; in vec3 normal; -out vec2 texCoord0; -out vec3 normal0; +out vec3 FragPos; +out vec2 TexCoords; +out vec3 Normal; uniform mat4 viewProjection; uniform mat4 model; -void main() +void +main() { - gl_Position = viewProjection * model * vec4(position, 1.0); - texCoord0 = texCoord; - normal0 = (model * vec4(normal, 0.0)).xyz; + vec4 worldPos = model * vec4(position, 1.0); + + FragPos = worldPos.xyz; + TexCoords = texCoord; + Normal = (model * vec4(normal, 0.0)).xyz; + + gl_Position = viewProjection * worldPos; } diff --git a/gfx/gl/shaders/landmassShader.fs b/gfx/gl/shaders/landmassShader.fs index aa368dc..7c08e8e 100644 --- a/gfx/gl/shaders/landmassShader.fs +++ b/gfx/gl/shaders/landmassShader.fs @@ -1,13 +1,14 @@ #version 330 core -in vec2 texCoord0; -in vec3 normal0; -in float height; +in vec3 FragPos; +in vec2 TexCoords; +in vec3 Normal; -uniform sampler2D sampler; -uniform vec3 lightDirection; -uniform vec3 lightColor; -uniform vec3 ambientColor; +layout(location = 0) out vec4 gPosition; +layout(location = 1) out vec4 gNormal; +layout(location = 2) out vec4 gAlbedoSpec; + +uniform sampler2D texture0; const vec3 grass = vec3(.1, .4, .05); const vec3 slope = vec3(.6, .6, .4); @@ -32,36 +33,39 @@ mixBetween(vec3 colA, vec3 colB, float blend, float low, float high) void main() { - vec4 tex = texture(sampler, texCoord0); - gl_FragColor = tex; - gl_FragColor.rgb *= clamp(ambientColor + (dot(-lightDirection, normal0) * lightColor), 0.0, 1.0); + vec3 color = texture(texture0, TexCoords).rgb; + float height = FragPos.z; if (height < beachline) { // Sandy beach - gl_FragColor.rgb *= sand; + color *= sand; } - else if (normal0.z < slope_max) { // Dark rocky face - gl_FragColor.rgb *= rock; + else if (Normal.z < slope_max) { // Dark rocky face + color *= rock; } else { // Colour by lesser slope - if (normal0.z < slope_mid) { - gl_FragColor.rgb *= mixBetween(rock, slope, normal0.z, slope_max, slope_mid); + if (Normal.z < slope_mid) { + color *= mixBetween(rock, slope, Normal.z, slope_max, slope_mid); } - else if (normal0.z < slope_min) { - gl_FragColor.rgb *= mixBetween(slope, grass, normal0.z, slope_mid, slope_min); + else if (Normal.z < slope_min) { + color *= mixBetween(slope, grass, Normal.z, slope_mid, slope_min); } else { - gl_FragColor.rgb *= grass; + color *= grass; } // Add a snow covering if (height > snowline_low) { - vec3 tsnow = tex.rgb * snow; + vec3 tsnow = color * snow; if (height > snowline_high) { - gl_FragColor.rgb = tsnow; + color = tsnow; } else { - gl_FragColor.rgb = mixBetween(gl_FragColor.rgb, tsnow, height, snowline_low, snowline_high); + color = mixBetween(color, tsnow, height, snowline_low, snowline_high); } } } + + gPosition = vec4(FragPos, 1); + gNormal = vec4(Normal, 1); + gAlbedoSpec = vec4(color, 1); } diff --git a/gfx/gl/shaders/landmassShader.vs b/gfx/gl/shaders/landmassShader.vs index bc9a947..6bf39b0 100644 --- a/gfx/gl/shaders/landmassShader.vs +++ b/gfx/gl/shaders/landmassShader.vs @@ -4,16 +4,18 @@ in vec3 position; in vec2 texCoord; in vec3 normal; -out vec2 texCoord0; -out vec3 normal0; -out float height; +out vec3 FragPos; +out vec2 TexCoords; +out vec3 Normal; uniform mat4 viewProjection; -void main() +void +main() { + FragPos = position; + TexCoords = texCoord; + Normal = normal; + gl_Position = viewProjection * vec4(position, 1.0); - texCoord0 = texCoord; - normal0 = normal; - height = position.z; } diff --git a/gfx/gl/shaders/lightingShader.fs b/gfx/gl/shaders/lightingShader.fs new file mode 100644 index 0000000..b0ca20a --- /dev/null +++ b/gfx/gl/shaders/lightingShader.fs @@ -0,0 +1,62 @@ +#version 330 core +#extension GL_ARB_shading_language_420pack : enable + +out vec3 FragColor; + +in vec2 TexCoords; + +layout(binding = 0) uniform sampler2D gPosition; +layout(binding = 1) uniform sampler2D gNormal; +layout(binding = 2) uniform sampler2D gAlbedoSpec; + +// struct Light { +// vec3 Position; +// vec3 Color; + +// float Linear; +// float Quadratic; +// float Radius; +//}; +// const int NR_LIGHTS_MAX = 12; +// uniform Light lights[NR_LIGHTS_MAX]; +// uniform vec3 viewPos; + +uniform vec3 lightDirection = normalize(vec3(1, 0, -1)); +uniform vec3 lightColor = vec3(0.6, 0.6, 0.6); +uniform vec3 ambientColor = vec3(0.5, 0.5, 0.5); + +void +main() +{ + // retrieve data from gbuffer + // const vec3 FragPos = texture(gPosition, TexCoords).rgb; + const vec3 Normal = texture(gNormal, TexCoords).rgb; + const vec3 Diffuse = texture(gAlbedoSpec, TexCoords).rgb; + // float Specular = texture(gAlbedoSpec, TexCoords).a; + + // then calculate lighting as usual + // vec3 lighting = Diffuse; + const vec3 lighting = ambientColor + clamp((dot(-lightDirection, Normal) * lightColor), 0, 0.5); + // vec3 viewDir = normalize(viewPos - FragPos); + // for (int i = 0; i < NR_LIGHTS_MAX; ++i) { + // calculate distance between light source and current fragment + // float distance = length(lights[i].Position - FragPos); + // if (distance < lights[i].Radius) { + // diffuse + // vec3 lightDir = normalize(lights[i].Position - FragPos); + // vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Diffuse * lights[i].Color; + // specular + // vec3 halfwayDir = normalize(lightDir + viewDir); + // float spec = pow(max(dot(Normal, halfwayDir), 0.0), 16.0); + // vec3 specular = lights[i].Color * spec * Specular; + // attenuation + // float attenuation = 1.0 / (1.0 + lights[i].Linear * distance + lights[i].Quadratic * distance * distance); + // diffuse *= attenuation; + // specular *= attenuation; + // lighting += diffuse + specular; + //} + //} + // FragColor = vec4(lighting, 1.0); + + FragColor = vec3(Diffuse * lighting); +} diff --git a/gfx/gl/shaders/lightingShader.vs b/gfx/gl/shaders/lightingShader.vs new file mode 100644 index 0000000..933a46a --- /dev/null +++ b/gfx/gl/shaders/lightingShader.vs @@ -0,0 +1,13 @@ +#version 330 core + +in vec3 aPos; +in vec2 aTexCoords; + +out vec2 TexCoords; + +void +main() +{ + TexCoords = aTexCoords; + gl_Position = vec4(aPos, 1.0); +} diff --git a/gfx/gl/shaders/waterShader.fs b/gfx/gl/shaders/waterShader.fs index abfa532..d242566 100644 --- a/gfx/gl/shaders/waterShader.fs +++ b/gfx/gl/shaders/waterShader.fs @@ -1,13 +1,22 @@ #version 330 core +#extension GL_ARB_shading_language_420pack : enable -in vec2 texCoord0; -in float depth; +in vec3 FragPos; +in vec2 TexCoords; +in vec3 Normal; -uniform sampler2D sampler; +out vec4 gPosition; +out vec4 gNormal; +out vec4 gAlbedoSpec; + +uniform sampler2D texture0; uniform vec3 waves; -void main() +void +main() { - gl_FragColor = texture(sampler, texCoord0); - gl_FragColor.a *= clamp(-depth * .7, .1, 1.0); + gPosition = vec4(FragPos, 1); + gNormal = vec4(Normal, 1); + gAlbedoSpec = texture(texture0, TexCoords); + gAlbedoSpec.a *= clamp(-FragPos.z * .7, .1, 1.0); } diff --git a/gfx/gl/shaders/waterShader.vs b/gfx/gl/shaders/waterShader.vs index bc4b7fc..fe9206f 100644 --- a/gfx/gl/shaders/waterShader.vs +++ b/gfx/gl/shaders/waterShader.vs @@ -4,19 +4,22 @@ in vec3 position; in vec2 texCoord; in vec3 normal; -out vec2 texCoord0; -out float depth; +out vec3 FragPos; +out vec2 TexCoords; +out vec3 Normal; uniform mat4 viewProjection; uniform vec3 waves; -void main() +void +main() { - vec3 wpos = vec3( - position.x + cos(waves.x), - position.y + cos(waves.x * waves.y / 2), - cos(waves.x + position.x + (position.y / 8)) * .3); - gl_Position = viewProjection * vec4(wpos, 1.0); - texCoord0 = texCoord; - depth = position.z; + vec4 wpos = vec4(position.x + cos(waves.x), position.y + cos(waves.x * waves.y / 2), + cos(waves.x + position.x + (position.y / 8)) * .3, 1.0); + + FragPos = vec3(wpos.xy, position.z); + TexCoords = texCoord; + Normal = normal; + + gl_Position = viewProjection * wpos; } diff --git a/ui/gameMainWindow.cpp b/ui/gameMainWindow.cpp index 84d0ca6..c08588d 100644 --- a/ui/gameMainWindow.cpp +++ b/ui/gameMainWindow.cpp @@ -30,7 +30,8 @@ public: }; GameMainWindow::GameMainWindow(size_t w, size_t h) : - Window {w, h, "I Like Trains"}, camera {{-1250.0F, -1250.0F, 35.0F}, quarter_pi, rdiv(w, h), 0.1F, 10000.0F} + Window {w, h, "I Like Trains"}, SceneRenderer {size}, camera {{-1250.0F, -1250.0F, 35.0F}, quarter_pi, rdiv(w, h), + 0.1F, 10000.0F} { uiComponents.create(glm::vec2 {-1150, -1150}); auto gms = uiComponents.create(&camera, glm::vec2 {w, h}); @@ -51,8 +52,10 @@ GameMainWindow::tick(TickDuration) void GameMainWindow::render() const { - glEnable(GL_DEPTH_TEST); - gameState->world.apply(&Renderable::render, shader); + SceneRenderer::render([this] { + gameState->world.apply(&Renderable::render, shader); + uiComponents.apply(&WorldOverlay::render, shader); + }); Window::render(); } diff --git a/ui/gameMainWindow.h b/ui/gameMainWindow.h index 9cc72ab..2c2c501 100644 --- a/ui/gameMainWindow.h +++ b/ui/gameMainWindow.h @@ -3,10 +3,11 @@ #include "chronology.hpp" #include "gfx/gl/camera.h" #include "gfx/gl/shader.h" +#include "sceneRenderer.h" #include "window.h" #include -class GameMainWindow : public Window { +class GameMainWindow : public Window, SceneRenderer { public: GameMainWindow(size_t w, size_t h); diff --git a/ui/sceneRenderer.cpp b/ui/sceneRenderer.cpp new file mode 100644 index 0000000..5cd5097 --- /dev/null +++ b/ui/sceneRenderer.cpp @@ -0,0 +1,102 @@ +#include "sceneRenderer.h" +#include +#include + +SceneRenderer::SceneRenderer(glm::ivec2 size) : lighting {lightingShader_vs.compile(), lightingShader_fs.compile()} +{ + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + const auto configuregdata + = [size](const GLuint data, const GLint format, const GLenum type, const GLenum attachment) { + glBindTexture(GL_TEXTURE_2D, data); + glTexImage2D(GL_TEXTURE_2D, 0, format, size.x, size.y, 0, GL_RGBA, type, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, data, 0); + }; + configuregdata(gPosition, GL_RGBA16F, GL_FLOAT, GL_COLOR_ATTACHMENT0); + configuregdata(gNormal, GL_RGBA16F, GL_FLOAT, GL_COLOR_ATTACHMENT1); + configuregdata(gAlbedoSpec, GL_RGBA, GL_UNSIGNED_BYTE, GL_COLOR_ATTACHMENT2); + static constexpr std::array attachments { + GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2}; + glDrawBuffers(attachments.size(), attachments.data()); + + glBindRenderbuffer(GL_RENDERBUFFER, depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.x, size.y); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth); + + // finally check if framebuffer is complete + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + throw std::runtime_error("Framebuffer not complete!"); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +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), nullptr); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast(3 * sizeof(float))); + } + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); +} + +void +SceneRenderer::render(std::function content) const +{ + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + // Geometry pass + glBindFramebuffer(GL_FRAMEBUFFER, gBuffer); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + content(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Lighting pass + glDisable(GL_BLEND); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glUseProgram(lighting.m_program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, gPosition); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, gNormal); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, gAlbedoSpec); + // TODO Configure lights + renderQuad(); +} diff --git a/ui/sceneRenderer.h b/ui/sceneRenderer.h new file mode 100644 index 0000000..2e1c372 --- /dev/null +++ b/ui/sceneRenderer.h @@ -0,0 +1,18 @@ +#pragma once + +#include "gfx/gl/shader.h" +#include "lib/glArrays.h" +#include + +class SceneRenderer { +public: + SceneRenderer(glm::ivec2 size); + + void render(std::function content) const; + +private: + glFrameBuffer gBuffer; + glTexture gPosition, gNormal, gAlbedoSpec; + glRenderBuffer depth; + ProgramHandleBase lighting; +}; diff --git a/ui/window.cpp b/ui/window.cpp index 419abc3..82f4d40 100644 --- a/ui/window.cpp +++ b/ui/window.cpp @@ -94,7 +94,7 @@ Window::refresh() const void Window::render() const { - uiComponents.apply(&WorldOverlay::render, getShader()); + glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); uiComponents.apply(&UIComponent::render, uiShader, UIComponent::Position {}); } -- cgit v1.2.3