From 517ffdf8b26772bfccd4d7bf95cb07be1bae650e Mon Sep 17 00:00:00 2001 From: Joseph Walton-Rivers <joseph@walton-rivers.uk> Date: Sat, 17 Jul 2021 23:22:43 +0100 Subject: [PATCH] first attempt at lighting --- build.sh | 3 ++ demo/data/normals_frag.glsl | 9 ++++++ demo/data/normals_geom.glsl | 30 +++++++++++++++++ demo/data/normals_vert.glsl | 18 +++++++++++ demo/data/phong_frag.glsl | 42 ++++++++++++++++++++++++ demo/data/phong_normals_frag.glsl | 41 ++++++++++++++++++++++++ demo/data/phong_vert.glsl | 34 ++++++++++++++++++++ demo/main.cpp | 53 ++++++++++++++++++++++++++++--- fggl/data/procedural.cpp | 53 +++++++++++++++++++++++++++++++ fggl/gfx/renderer.cpp | 8 +++++ fggl/gfx/shader.cpp | 2 +- 11 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 demo/data/normals_frag.glsl create mode 100644 demo/data/normals_geom.glsl create mode 100644 demo/data/normals_vert.glsl create mode 100644 demo/data/phong_frag.glsl create mode 100644 demo/data/phong_normals_frag.glsl create mode 100644 demo/data/phong_vert.glsl diff --git a/build.sh b/build.sh index 3c5eda8..b9b5e31 100755 --- a/build.sh +++ b/build.sh @@ -6,6 +6,9 @@ then mkdir build fi +# if doing shader development, disable the cache to make sure changes take affect +rm -rf /tmp/fggl/ + pushd build cmake .. make diff --git a/demo/data/normals_frag.glsl b/demo/data/normals_frag.glsl new file mode 100644 index 0000000..fe2187e --- /dev/null +++ b/demo/data/normals_frag.glsl @@ -0,0 +1,9 @@ +#version 330 core +// credit: https://learnopengl.com/Advanced-OpenGL/Geometry-Shader +out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0, 1.0, 0.0, 1.0); +} + diff --git a/demo/data/normals_geom.glsl b/demo/data/normals_geom.glsl new file mode 100644 index 0000000..6f1aa16 --- /dev/null +++ b/demo/data/normals_geom.glsl @@ -0,0 +1,30 @@ +#version 330 core +// credit: https://learnopengl.com/Advanced-OpenGL/Geometry-Shader + +layout (triangles) in; +layout (line_strip, max_vertices = 6) out; + +in VS_OUT { + vec3 normal; +} gs_in[]; + +const float MAGNITUDE = 0.4; + +uniform mat4 projection; + +void GenerateLine(int index) +{ + gl_Position = projection * gl_in[index].gl_Position; + EmitVertex(); + gl_Position = projection * (gl_in[index].gl_Position + + vec4(gs_in[index].normal, 0.0) * MAGNITUDE); + EmitVertex(); + EndPrimitive(); +} + +void main() +{ + GenerateLine(0); // first vertex normal + GenerateLine(1); // second vertex normal + GenerateLine(2); // third vertex normal +} diff --git a/demo/data/normals_vert.glsl b/demo/data/normals_vert.glsl new file mode 100644 index 0000000..36a36b7 --- /dev/null +++ b/demo/data/normals_vert.glsl @@ -0,0 +1,18 @@ +#version 330 core +// credit: https://learnopengl.com/Advanced-OpenGL/Geometry-Shader +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; + +out VS_OUT { + vec3 normal; +} vs_out; + +uniform mat4 view; +uniform mat4 model; + +void main() +{ + gl_Position = view * model * vec4(aPos, 1.0); + mat3 normalMatrix = mat3(transpose(inverse(view * model))); + vs_out.normal = normalize(vec3(vec4(normalMatrix * aNormal, 0.0))); +} diff --git a/demo/data/phong_frag.glsl b/demo/data/phong_frag.glsl new file mode 100644 index 0000000..0d4df5e --- /dev/null +++ b/demo/data/phong_frag.glsl @@ -0,0 +1,42 @@ +#version 330 core +// based on http://www.opengl-tutorial.org, WTFPL + + +uniform vec3 lightPos; + +in vec3 normal; + +in vec3 pos_ws; +in vec3 normal_cs; +in vec3 lightdir_cs; +in vec3 eyedir_cs; + +out vec4 FragColor; + +void main() +{ + vec3 lightColour = vec3( 1.0, 1.0, 1.0 ); + vec3 objColour = vec3(1.0f, 0.6f, 0.2f); + float lightPower = 200.0; + + // material colours + vec3 matDiff = vec3(1.0, 0.6, 0.2 ); + vec3 matAmb = vec3(0.1) * matDiff; + vec3 matSpec = vec3(0.3); + + vec3 n = normalize( normal_cs ); + vec3 l = normalize( lightdir_cs ); + float distance = length( lightPos - pos_ws ); + + vec3 e = normalize( eyedir_cs ); + vec3 r = reflect( -l, n ); + float cosAlpha = clamp( dot(e, r), 0, 1); + + float cosTheta = clamp( dot( n, l ), 0, 1 ); + vec3 colour = + ( matAmb ) + + ( matDiff * lightColour * lightPower * cosTheta / ( distance*distance ) ) + + ( matSpec * lightColour * lightPower * pow( cosAlpha, 5 ) / (distance*distance) ); + + FragColor = vec4(colour, 1); +} diff --git a/demo/data/phong_normals_frag.glsl b/demo/data/phong_normals_frag.glsl new file mode 100644 index 0000000..f39c8fc --- /dev/null +++ b/demo/data/phong_normals_frag.glsl @@ -0,0 +1,41 @@ +#version 330 core +// based on http://www.opengl-tutorial.org, WTFPL + +uniform vec3 lightPos; + +in vec3 normal; + +in vec3 pos_ws; +in vec3 normal_cs; +in vec3 lightdir_cs; +in vec3 eyedir_cs; + +out vec4 FragColor; + +void main() +{ + vec3 lightColour = vec3( 1.0, 1.0, 1.0 ); + vec3 objColour = vec3(1.0f, 0.6f, 0.2f); + float lightPower = 50.0; + + // material colours + vec3 matDiff = vec3(1.0, 0.6, 0.2 ); + vec3 matAmb = vec3(0.1) * matDiff; + vec3 matSpec = vec3(0.3); + + vec3 n = normalize( normal_cs ); + vec3 l = normalize( lightdir_cs ); + float distance = length( lightPos - pos_ws ); + + vec3 e = normalize( eyedir_cs ); + vec3 r = reflect( -l, n ); + float cosAlpha = clamp( dot(e, r), 0, 1); + + float cosTheta = clamp( dot( n, l ), 0, 1 ); +// vec3 colour = +// ( matAmb ) + +// ( matDiff * lightColour * lightPower * cosTheta / ( distance*distance ) ) + +// ( matSpec * lightColour * lightPower * pow( cosAlpha, 5 ) / (distance*distance) ); + + FragColor = vec4(normal, 1); +} diff --git a/demo/data/phong_vert.glsl b/demo/data/phong_vert.glsl new file mode 100644 index 0000000..c4a5f69 --- /dev/null +++ b/demo/data/phong_vert.glsl @@ -0,0 +1,34 @@ +#version 330 core +// based on http://www.opengl-tutorial.org, WTFPL + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform vec3 lightPos; + +out vec3 normal; + +out vec3 pos_ws; +out vec3 normal_cs; +out vec3 lightdir_cs; +out vec3 eyedir_cs; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + normal = aNormal; + + // world space + pos_ws = ( model * vec4( aPos, 1 ) ).xyz; + + // camera space + vec3 pos_cs = ( view * model * vec4(aPos, 1) ).xyz; + eyedir_cs = vec3(0, 0, 0) - pos_cs; + + vec3 light_cs = ( view * vec4( lightPos, 1) ).xyz; + lightdir_cs = light_cs + eyedir_cs; + normal_cs = ( view * model * vec4( aNormal, 0 ) ).xyz; +} diff --git a/demo/main.cpp b/demo/main.cpp index a5a4561..324c1ac 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -1,4 +1,5 @@ #include <filesystem> +#include <glm/trigonometric.hpp> #include <iostream> #include <fggl/gfx/window.hpp> @@ -61,28 +62,46 @@ int main(int argc, char* argv[]) { config.fragment = "unlit_frag.glsl"; auto shader = cache.load(config); + fggl::gfx::ShaderConfig configPhong; + configPhong.name = "phong"; + configPhong.vertex = configPhong.name + "_vert.glsl"; + configPhong.fragment = configPhong.name + "_frag.glsl"; +// configPhong.fragment = configPhong.name + "_normals_frag.glsl"; + auto shaderPhong = cache.load(configPhong); + + fggl::gfx::ShaderConfig configNormals; + configNormals.name = "normals"; + configNormals.hasGeom = true; + configNormals.vertex = configNormals.name + "_vert.glsl"; + configNormals.geometry = configNormals.name + "_geom.glsl"; + configNormals.fragment = configNormals.name + "_frag.glsl"; + auto shaderNormals = cache.load( configNormals ); + // create ECS fggl::ecs::ECS ecs; ecs.registerComponent<fggl::gfx::MeshToken>(); ecs.registerComponent<fggl::math::Transform>(); - for (int i=0; i<3; i++) { + int nCubes = 3; + int nSlopes = 3; + + for (int i=0; i<nCubes; i++) { // create an entity auto entity = ecs.createEntity(); // set the position auto result = ecs.addComponent<fggl::math::Transform>(entity); - result->origin( glm::vec3( 0.0f, 0.0f, i * -1.0f) ); + result->origin( glm::vec3( 0.0f, 0.0f, i * -1.0f) ); // in a supprise to no one it's a triangle auto mesh = fggl::data::make_cube(); auto token = meshRenderer.upload(mesh); - token.pipeline = shader; + token.pipeline = shaderNormals; ecs.addComponent<fggl::gfx::MeshToken>(entity, token); } constexpr float HALF_PI = M_PI / 2.0f; - for (int i=0; i<3; i++) { + for (int i=0; i<nSlopes; i++) { // create an entity auto entity = ecs.createEntity(); @@ -94,20 +113,44 @@ int main(int argc, char* argv[]) { // in a supprise to no one it's a triangle auto mesh = fggl::data::make_slope(); auto token = meshRenderer.upload(mesh); - token.pipeline = shader; + token.pipeline = shaderNormals; ecs.addComponent<fggl::gfx::MeshToken>(entity, token); } + float time = 0.0f; + float dt = 16.0f; while( !win.closeRequested() ) { ctx.pollEvents(); debug.frameStart(); // update step + time += dt; + +/* float amount = glm::radians( time / 2048.0f * 360.0f ); + auto spinners = ecs.getEntityWith<fggl::math::Transform>(); + for ( auto entity : spinners ) { + auto transform = ecs.getComponent<fggl::math::Transform>(entity); + transform->euler(glm::vec3(0.0f, amount, 0.0f)); + }*/ // render step ogl.clear(); + + // render using real shader + auto renderables = ecs.getEntityWith<fggl::gfx::MeshToken>(); + for ( auto renderable : renderables ) { + auto token = ecs.getComponent<fggl::gfx::MeshToken>(renderable); + token->pipeline = shaderPhong; + } meshRenderer.render(win, ecs, 16.0f); + // render using normals shader + /*for ( auto renderable : renderables ) { + auto token = ecs.getComponent<fggl::gfx::MeshToken>(renderable); + token->pipeline = shaderNormals; + } + meshRenderer.render(win, ecs, 16.0f);*/ + debug.draw(); win.swap(); } diff --git a/fggl/data/procedural.cpp b/fggl/data/procedural.cpp index e9a7e82..35c26de 100644 --- a/fggl/data/procedural.cpp +++ b/fggl/data/procedural.cpp @@ -1,9 +1,43 @@ #include "procedural.hpp" #include "model.hpp" +#include <glm/geometric.hpp> using namespace fggl::data; +// from https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal +static glm::vec3 calcSurfaceNormal(glm::vec3 a, glm::vec3 b, glm::vec3 c) { + glm::vec3 u = b - a; + glm::vec3 v = c - a; + return glm::normalize( glm::cross( u, v ) ); +} + +static void computeNormals( fggl::data::Mesh& mesh, const int* idx, const int* colIdx, int points) { + + // we're assuming all the normals are zero... + for (auto& vertex : mesh.vertexList() ) { + vertex.normal = glm::vec3(0.0f); + } + + // each vertex normal should be the sum of the triangles it's part of. + for (int i=0; i<points; i += 3) { + auto& v1 = mesh.vertex( colIdx[ idx[i] ] ); + auto& v2 = mesh.vertex( colIdx[ idx[i + 1] ] ); + auto& v3 = mesh.vertex( colIdx[ idx[i + 2] ] ); + + v1.normal += calcSurfaceNormal( v1.posititon, v2.posititon, v3.posititon ); + v2.normal += calcSurfaceNormal( v2.posititon, v3.posititon, v1.posititon ); + v3.normal += calcSurfaceNormal( v3.posititon, v1.posititon, v2.posititon ); +// v3.normal += calcSurfaceNormal( v3.posititon, v1.posititon, v2.posititon ); + } + + // each vertex should currently be the sum of every triangle it's part of + // so we need to normalize them + for (auto& vertex : mesh.vertexList() ) { + vertex.normal = glm::normalize( vertex.normal ); + } +} + fggl::data::Mesh fggl::data::make_triangle() { constexpr fggl::math::vec3 pos[]{ {-0.5f, -0.5f, 0.0f}, @@ -16,6 +50,7 @@ fggl::data::Mesh fggl::data::make_triangle() { for (auto po : pos) { Vertex vert{}; vert.posititon = po; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); mesh.push(vert); } @@ -45,6 +80,7 @@ fggl::data::Mesh fggl::data::make_quad_xy() { for (int i = 0; i < 4; ++i){ Vertex vert{}; vert.posititon = pos[i]; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); colIdx[ i ] = mesh.pushVertex(vert); } @@ -73,6 +109,7 @@ fggl::data::Mesh fggl::data::make_quad_xz() { for (int i = 0; i < 4; ++i){ Vertex vert{}; vert.posititon = pos[i]; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); colIdx[ i ] = mesh.pushVertex(vert); } @@ -119,13 +156,17 @@ fggl::data::Mesh fggl::data::make_cube() { for ( int i=0; i < 8; ++i ) { Vertex vert{}; vert.posititon = pos[i]; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); colIdx[ i ] = mesh.pushVertex( vert ); } + int tris = 0; for ( auto i : idx ){ mesh.pushIndex( colIdx[i] ); + tris++; } + computeNormals( mesh, idx, colIdx, tris ); return mesh; } @@ -161,13 +202,17 @@ fggl::data::Mesh fggl::data::make_slope() { for ( int i=0; i < 8; ++i ) { Vertex vert{}; vert.posititon = pos[i]; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); colIdx[ i ] = mesh.pushVertex( vert ); } + int tris = 0; for ( auto i : idx ){ mesh.pushIndex( colIdx[i] ); + tris++; } + computeNormals( mesh, idx, colIdx, tris ); return mesh; } @@ -205,13 +250,17 @@ fggl::data::Mesh fggl::data::make_ditch() { for ( int i=0; i < 8; ++i ) { Vertex vert{}; vert.posititon = pos[i]; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); colIdx[ i ] = mesh.pushVertex( vert ); } + int tris = 0; for ( auto i : idx ){ mesh.pushIndex( colIdx[i] ); + tris++; } + computeNormals( mesh, idx, colIdx, tris ); return mesh; } @@ -244,13 +293,17 @@ fggl::data::Mesh fggl::data::make_point() { for ( int i=0; i < 8; ++i ) { Vertex vert{}; vert.posititon = pos[i]; + vert.normal = glm::vec3(0.0f, 0.0f, 0.0f); vert.colour = fggl::math::vec3(1.0f, 1.0f, 1.0f); colIdx[ i ] = mesh.pushVertex( vert ); } + int tris = 0; for ( auto i : idx ){ mesh.pushIndex( colIdx[i] ); + tris++; } + computeNormals( mesh, idx, colIdx, tris ); return mesh; } diff --git a/fggl/gfx/renderer.cpp b/fggl/gfx/renderer.cpp index fe3e9d0..0a74213 100644 --- a/fggl/gfx/renderer.cpp +++ b/fggl/gfx/renderer.cpp @@ -74,6 +74,8 @@ void MeshRenderer::render(const Window& window, const fggl::ecs::ECS& ecs, float glEnable(GL_CULL_FACE); glCullFace(GL_BACK); + glEnable(GL_DEPTH_TEST); + // make sure the correct rendering context is active window.activate(); @@ -82,6 +84,7 @@ void MeshRenderer::render(const Window& window, const fggl::ecs::ECS& ecs, float glm::mat4 view = glm::lookAt( glm::vec3 ( 0.0f, 3.0f, 3.0f ), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f) ); glm::mat4 proj = glm::perspective( glm::radians(45.0f), 1280.0f/720.0f, 0.1f, 100.0f); + glm::vec3 lightPos(20.0f, 20.0f, 15.0f); // TODO better performance if grouped by vao first // TODO the nvidia performance presentation said I shouldn't use uniforms for large data @@ -99,6 +102,11 @@ void MeshRenderer::render(const Window& window, const fggl::ecs::ECS& ecs, float glUniformMatrix4fv( glGetUniformLocation(shader, "view"), 1, GL_FALSE, glm::value_ptr( view ) ); glUniformMatrix4fv( glGetUniformLocation(shader, "projection"), 1, GL_FALSE, glm::value_ptr( proj ) ); + // lighting + GLint lightID = glGetUniformLocation(shader, "lightPos"); + if ( lightID != -1 ) + glUniform3fv( lightID, 1, glm::value_ptr( lightPos ) ); + glBindVertexArray( mesh->vao ); glDrawElements( GL_TRIANGLES, mesh->idxSize, GL_UNSIGNED_INT, reinterpret_cast<void*>(mesh->idxOffset) ); } diff --git a/fggl/gfx/shader.cpp b/fggl/gfx/shader.cpp index 7aa478c..144bfa7 100644 --- a/fggl/gfx/shader.cpp +++ b/fggl/gfx/shader.cpp @@ -110,7 +110,7 @@ GLuint ShaderCache::load(const ShaderConfig& config) { GLuint geomShader; if ( config.hasGeom ) { geomShader = glCreateShader(GL_GEOMETRY_SHADER); - compileShader( config.fragment, geomShader ); + compileShader( config.geometry, geomShader ); glAttachShader(pid, geomShader); } -- GitLab