From 54c65d1745157837d4e2dc283672bd1172fd4730 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Tue, 1 Feb 2022 12:36:21 -0500 Subject: [PATCH] implement buffer backed lighting uniform structure --- data/full_lighting.frag | 81 +++++++++---------------- src/main.cpp | 130 +++++++++++++++++++++++++++++----------- src/shader.cpp | 15 +++-- src/shader.h | 20 +++---- src/tangerine.cpp | 44 +++++++++----- src/tangerine.h | 21 +++++-- 6 files changed, 188 insertions(+), 123 deletions(-) diff --git a/data/full_lighting.frag b/data/full_lighting.frag index 4e973bf..30d29d0 100644 --- a/data/full_lighting.frag +++ b/data/full_lighting.frag @@ -6,75 +6,50 @@ in vec2 frag_uv; out vec4 color; -layout (std140) uniform matrices -{ - mat4 view_xform; - mat4 proj_xform; - mat4 normal_xform; -}; - -uniform mat4 node_xform; uniform sampler2D sampler; -struct PointLight -{ - vec3 pos; - vec3 color; - uint intensity; -}; - -struct DirectionalLight -{ - vec3 direction; - vec3 color; - uint intensity; -}; - -// FIXME: probably want a buffer-backed UBO for lights in case we have more -// than one shader that does lighting calculations -//uniform PointLight lights[32]; -// FIXME: hard-coded values for testing -const DirectionalLight dl1 = - DirectionalLight(vec3(-2, 1, 3), vec3(1,1,1), 1u); const vec4 ambient_color = vec4(0.1, 0.1, 0.1, 1); - -const PointLight pl1 = PointLight(vec3(-20, 10, 30), vec3(1, 1, 1), 800u); - -/// - - const uint NUM_LIGHTS = 32u; layout (std140) uniform lights { - uint active_p_lights; + uint buf_size; uint max_p_lights; - uint active_d_lights; + uint active_p_lights; uint max_d_lights; - PointLight p_lights[NUM_LIGHTS]; - DirectionalLight d_lights[NUM_LIGHTS]; + uint active_d_lights; + + vec4 pl_positions[NUM_LIGHTS]; + vec4 pl_colors[NUM_LIGHTS]; + uint pl_intensities[NUM_LIGHTS]; // NOTE: 16 bytes * NUM_LIGHTS + + vec4 dl_directions[NUM_LIGHTS]; // FIXME: should be directions + vec4 dl_colors[NUM_LIGHTS]; + uint dl_intensities[NUM_LIGHTS]; // NOTE: 16 bytes * NUM_LIGHTS }; void main() { - for (uint i = 0u; i < NUM_LIGHTS; i++) { - if (p_lights[i].pos.x > 0) - color.r = 128; + // NOTE: directional lights + vec4 diffuse_color = vec4(0); + + for (uint i = 0u; i < active_d_lights; i++) { + vec4 light_direction = normalize(dl_directions[i]); + float diffuse_factor = + clamp(dot(vec4(frag_normal, 1), light_direction), 0, 1); + diffuse_color = diffuse_color + + dl_intensities[i] * diffuse_factor * dl_colors[i]; } -#if 1 // directional light - vec3 light_direction = normalize(dl1.direction); - float diffuse_factor = clamp(dot(frag_normal, light_direction), 0, 1); - vec4 diffuse_color = dl1.intensity * vec4(dl1.color * diffuse_factor, 1); -#else // point light - vec3 light_direction = pl1.pos - frag_pos; - float len = length(light_direction); - light_direction = normalize(light_direction); - float diffuse_factor = dot(frag_normal, light_direction) / pow(len, 2); - diffuse_factor = clamp(diffuse_factor, 0, 1); - vec4 diffuse_color = pl1.intensity * vec4(pl1.color * diffuse_factor, 1); -#endif +//#else // point light +// vec4 light_direction = pl1.pos - frag_pos; +// float len = length(light_direction); +// light_direction = normalize(light_direction); +// float diffuse_factor = dot(frag_normal, light_direction) / pow(len, 2); +// diffuse_factor = clamp(diffuse_factor, 0, 1); +// vec4 diffuse_color = pl1.intensity * vec4(pl1.color * diffuse_factor); +//#endif color = (ambient_color + diffuse_color) * texture(sampler, frag_uv.st); } diff --git a/src/main.cpp b/src/main.cpp index 8efd81e..174746d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,57 @@ #include "tangerine.h" +bool +initLights(RenderState* rs) +{ + // NOTE: add a directional light + LightsBuffer* lb = rs->lights_buf; + u32 idx = lb->active_d_lights++; + lb->dl_directions[idx] = glm::vec4(-2, 1, 3, 1); + lb->dl_colors[idx] = glm::vec4(1, 0.8, 0.6, 1); + lb->dl_intensities[idx] = glm::uvec4(1, 0, 0, 0); + + // FIXME: we need an interface function to get uniforms by name + GLBuffer* lights_ubo = nullptr; + for (u32 i = 0; i < rs->gl_ctx->num_ubos; i++) { + GLBuffer* ubo = &rs->gl_ctx->uniform_buffers[i]; + + // FIXME: we need a utilty function for cstring comparison that takes + // into account the length of both strings so we don't match a longer + // string with a matching prefix + if (strncmp(ubo->name, "lights", 8)) + lights_ubo = ubo; + } + + // FIXME: we need an interface function to update lights + glBufferSubData(lights_ubo->target, 0, lb->buf_size, lb); + + // FIXME: this only makes sense for point lights +#if 0 + // NOTE: add a debug mesh to view the light source + RenderGroup* debug_group = getFreeRenderGroup(rs); + assert(debug_group); + ShaderProgram* s_debug = getShaderByName("debug", rs->gl_ctx); + assert(s_debug); + initRenderGroup(debug_group, rs->rg_arena, s_debug, 256, "debug_lights"); + Entity* e = getFreeEntity(debug_group); + assert(e); + if (!initEntity(e, + rs->gl_ctx, + rs->rg_arena, + &rs->assets.models[0], // tex_cube from loadScene() + s_debug->num_vertex_attribs, + s_debug->attrib_mappings)) + { + LOGF(Error, "Error initializing debug entity for light\n"); + return false; + } + + setEntityPosition(e, glm::vec3(lb->dl_directions[idx])); +#endif + return true; +} + bool loadScene(RenderState* rs) { @@ -56,39 +107,11 @@ loadScene(RenderState* rs) scaleEntity(e, 3); } - // NOTE: add a point light - PointLight* l1 = &rs->lights[rs->num_lights++]; - l1->position = glm::vec3(-20, 10, 30); - l1->color = glm::vec3(1, 1, 1); - l1->intensity = 256; - - // NOTE: add a debug mesh to view the light source - RenderGroup* debug_group = getFreeRenderGroup(rs); - assert(debug_group); - ShaderProgram* s_debug = getShaderByName("debug", rs->gl_ctx); - assert(s_debug); - initRenderGroup(debug_group, rs->rg_arena, s_debug, 256, "debug_lights"); - - for (u32 i = 0; i < rs->num_lights; i++) { - PointLight* l = &rs->lights[i]; - Entity* e = getFreeEntity(debug_group); - assert(e); - if (!initEntity(e, - rs->gl_ctx, - rs->rg_arena, - tex_cube, - s_debug->num_vertex_attribs, - s_debug->attrib_mappings)) - { - return false; - } - - setEntityPosition(e, l->position); - } - - return true; + return initLights(rs); } +#include + void render_cb_pre(RenderState* rs) { @@ -103,6 +126,45 @@ render_cb_pre(RenderState* rs) } } + // NOTE: wiggle camera + + static glm::vec3 cam_pos = glm::vec3(0, 0, 50); + static glm::vec3 cam_mov = glm::vec3(0.3, 0, 0); + static glm::vec3 look_pos = glm::vec3(0, 0, 0); + static float direction = -1; + + if (cam_pos.x < -20 || cam_pos.x > 20) { + direction *= -1; + cam_mov.x *= direction; + } + + cam_pos += cam_mov; + rs->xforms->view_xform = glm::lookAt(cam_pos, look_pos, glm::vec3(0, 1, 0)); + +#if 1 + // TODO: make helper function for getting UBO by name in shader.h + static GLBuffer* xform_ubo = nullptr; + u64 h_search = utilFNV64a_str("matrices"); + + if (!xform_ubo) { + for (u32 i = 0; i < rs->gl_ctx->num_ubos; i++) { + GLBuffer* buf = &rs->gl_ctx->uniform_buffers[i]; + + if (buf->name != nullptr) { + u64 hash = utilFNV64a_str(buf->name); + if (hash == h_search) + xform_ubo = buf; + } + } + + if (!xform_ubo) + LOGF(Error, "matrices ubo not found\n"); + } + + updateCameraTransforms(rs->xforms, xform_ubo); + //// +#endif +#if 0 for (u32 i = 0; i < rs->num_render_groups; i++) { RenderGroup& rg = rs->render_groups[i]; @@ -114,17 +176,17 @@ render_cb_pre(RenderState* rs) direction * (float) M_PI / (3 * 60)); } } +#endif } int main() { RenderState* rs = initRenderState(); - rs->clear_col = {0.2, 0.2, 0.2, 1}; if (rs) { if (!loadScene(rs)) { - LOG(Error) << "error loading scene\n"; + LOGF(Error, "error loading scene\n"); return 1; } @@ -134,7 +196,7 @@ main() return 0; } - std::cout << "error loading scene\n"; + LOGF(Error, "error loading scene\n"); return 1; } diff --git a/src/shader.cpp b/src/shader.cpp index b6cdd45..8e9d239 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -513,18 +513,23 @@ parseUniform(MemoryArena* arena, ShaderProgram* s, u32 uniform_idx) GLUniform unif = {0}; GLchar unif_name[256] = {0}; GLsizei name_len = 0; + unif.idx = uniform_idx; glGetActiveUniform(s->prog_id, uniform_idx, sizeof(unif_name), &name_len, &unif.num_elements, - &unif.data_type, + &unif.uniform_type, unif_name); - glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, - GL_UNIFORM_BLOCK_INDEX, &unif.block_idx); - unif.idx = uniform_idx; + glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_BLOCK_INDEX, + &unif.block_idx); + glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_ARRAY_STRIDE, + &unif.array_stride); + glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, GL_UNIFORM_OFFSET, + &unif.uniform_offset); + unif.name = arenaCopyCStr(arena, unif_name); unif.location = glGetUniformLocation(s->prog_id, unif.name); @@ -712,7 +717,7 @@ parseShader(MemoryArena* arena, GLContext* gl_ctx, ShaderProgram* s) return true; } - LOGF(Error, "%Error parsing shader\n"); + LOGF(Error, "Error parsing shader\n"); return false; } diff --git a/src/shader.h b/src/shader.h index 3eac3b6..5a52652 100644 --- a/src/shader.h +++ b/src/shader.h @@ -17,9 +17,16 @@ struct GLUniform GLuint idx; GLint location; GLint block_idx; - GLenum data_type; - GLint data_size; - GLint num_elements; + + GLenum uniform_type; // NOTE: GL_UNSIGNED_INT, GL_FLOAT_VEC4 + GLint num_elements; // NOTE: 1 unless uniform is an array of base types + + // FIXME: we can't actually get this from opengl introspection API, we have + // to infer it from the uniform type... + GLint data_size; // NOTE: total size in bytes... + GLint array_stride; // NOTE: bytes between array elements + GLint uniform_offset; // NOTE: byte offset from beginning of uniform + char* name; }; @@ -142,13 +149,6 @@ struct GLMesh struct Animation; -struct PointLight -{ - glm::vec3 position; - glm::vec3 color; - u32 intensity; -}; - struct Transforms { glm::mat4 view_xform; diff --git a/src/tangerine.cpp b/src/tangerine.cpp index 1e8daa2..44baf73 100644 --- a/src/tangerine.cpp +++ b/src/tangerine.cpp @@ -10,7 +10,7 @@ bool initGraphics(SDLHandles* handles); -LightsBuffer initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights); +LightsBuffer* initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights); // interface @@ -290,17 +290,35 @@ initGraphics(SDLHandles* handles) return handles->window != nullptr; } -LightsBuffer +LightsBuffer* initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights) { // FIXME: revisit for 'Scene' abstraction - LightsBuffer lights_buf; - lights_buf.active_p_lights = 0; - lights_buf.active_d_lights = 0; - lights_buf.max_p_lights = max_lights; - lights_buf.max_d_lights = max_lights; - lights_buf.p_lights = ARENA_ALLOC(arena, PointLight, max_lights); - lights_buf.d_lights = ARENA_ALLOC(arena, PointLight, max_lights); + + // TODO: we need a way to do this automatically somehow + u32 buf_size = 8 * sizeof(u32) // NOTE: 'header' + + 6 * max_lights * sizeof(glm::vec4); // NOTE: vector arrays + + LOGF(Debug, "buf_size: %d\n", buf_size); + + LightsBuffer* lb = (LightsBuffer*) ARENA_ALLOC(arena, u8, buf_size); + lb->buf_size = buf_size; + lb->max_p_lights = max_lights; + lb->max_d_lights = max_lights; + + // NOTE: set offsets for array pointers + lb->pl_positions = (glm::vec4*) arenaGetAddressOffset(lb, 8 * sizeof(u32)); + u32 arr_size = max_lights * sizeof(glm::vec4); + lb->pl_colors = + (glm::vec4*) arenaGetAddressOffset(lb->pl_positions, arr_size); + lb->pl_intensities = + (glm::uvec4*) arenaGetAddressOffset(lb->pl_colors, arr_size); + lb->dl_directions = + (glm::vec4*) arenaGetAddressOffset(lb->pl_intensities, arr_size); + lb->dl_colors = + (glm::vec4*) arenaGetAddressOffset(lb->dl_directions, arr_size); + lb->dl_intensities = + (glm::uvec4*) arenaGetAddressOffset(lb->dl_colors, arr_size); GLBuffer* lights_ubo = getFreeUBO(gl_ctx); assert(lights_ubo); @@ -311,20 +329,16 @@ initLights(MemoryArena* arena, GLContext* gl_ctx, u32 max_lights) assert((GLint) gl_ctx->binding_count < gl_ctx->max_binding_points); lights_ubo->binding_idx = gl_ctx->binding_count++; - // FIXME: need to pad any vec3s to vec4 to match layout std140 - u32 buf_size = sizeof(LightsBuffer) - + max_lights * sizeof(PointLight) - + max_lights * sizeof(PointLight); glGenBuffers(1, &lights_ubo->id); glBindBuffer(lights_ubo->target, lights_ubo->id); glBufferData(lights_ubo->target, buf_size, - &lights_buf, + lb, GL_DYNAMIC_DRAW); glBindBufferBase(lights_ubo->target, lights_ubo->binding_idx, lights_ubo->id); - return lights_buf; + return lb; } diff --git a/src/tangerine.h b/src/tangerine.h index 94942fe..1d7359e 100644 --- a/src/tangerine.h +++ b/src/tangerine.h @@ -109,13 +109,22 @@ struct GLClearColor // NOTE: structure to match the layout of the 'lights' uniform in a shader struct LightsBuffer { - u32 active_p_lights; + u32 buf_size; u32 max_p_lights; - u32 active_d_lights; + u32 active_p_lights; u32 max_d_lights; - // FIXME: do we need any padding here for layout(std 140)? - PointLight* p_lights; - PointLight* d_lights; // TODO: directional lights + u32 active_d_lights; + + // NOTE: padding for std140, vec4 need to be aligned at 16 bytes + u8 padding[12]; + + glm::vec4* pl_positions; + glm::vec4* pl_colors; + glm::uvec4* pl_intensities; + + glm::vec4* dl_directions; + glm::vec4* dl_colors; + glm::uvec4* dl_intensities; }; struct RenderState @@ -136,7 +145,7 @@ struct RenderState // could have render groups, lights, camera // could match up with gltf scene graph where everyting is part of a node // TODO: will also need other types of lights: directional, spotlight - LightsBuffer lights_buf; + LightsBuffer* lights_buf; #if 0 u32 num_lights; u32 max_lights;