From 63ac19440c6e58e451453aa247cf66c5bebd4c58 Mon Sep 17 00:00:00 2001 From: Ethan Lafrenais <6609531+Francessco121@users.noreply.github.com> Date: Sun, 10 Jul 2022 19:01:02 -0400 Subject: [PATCH] Sprite Distort Instancing (#1630) * Implement instanced renderer for sprite distort * Clean up + formatting --- game/graphics/opengl_renderer/Shader.cpp | 1 + game/graphics/opengl_renderer/Shader.h | 1 + game/graphics/opengl_renderer/Sprite3.cpp | 343 +++++++++++++++--- game/graphics/opengl_renderer/Sprite3.h | 25 ++ .../shaders/sprite_distort_instanced.frag | 21 ++ .../shaders/sprite_distort_instanced.vert | 54 +++ 6 files changed, 398 insertions(+), 47 deletions(-) create mode 100644 game/graphics/opengl_renderer/shaders/sprite_distort_instanced.frag create mode 100644 game/graphics/opengl_renderer/shaders/sprite_distort_instanced.vert diff --git a/game/graphics/opengl_renderer/Shader.cpp b/game/graphics/opengl_renderer/Shader.cpp index ef54afad49..00931cc366 100644 --- a/game/graphics/opengl_renderer/Shader.cpp +++ b/game/graphics/opengl_renderer/Shader.cpp @@ -88,6 +88,7 @@ ShaderLibrary::ShaderLibrary() { at(ShaderId::COLLISION) = {"collision"}; at(ShaderId::MERC2) = {"merc2"}; at(ShaderId::SPRITE_DISTORT) = {"sprite_distort"}; + at(ShaderId::SPRITE_DISTORT_INSTANCED) = {"sprite_distort_instanced"}; for (auto& shader : m_shaders) { ASSERT_MSG(shader.okay(), "Shader compiled"); diff --git a/game/graphics/opengl_renderer/Shader.h b/game/graphics/opengl_renderer/Shader.h index b8f3f3021f..6b3692d7da 100644 --- a/game/graphics/opengl_renderer/Shader.h +++ b/game/graphics/opengl_renderer/Shader.h @@ -43,6 +43,7 @@ enum class ShaderId { COLLISION = 18, MERC2 = 19, SPRITE_DISTORT = 20, + SPRITE_DISTORT_INSTANCED = 21, MAX_SHADERS }; diff --git a/game/graphics/opengl_renderer/Sprite3.cpp b/game/graphics/opengl_renderer/Sprite3.cpp index 996a330dc9..425d2ad370 100644 --- a/game/graphics/opengl_renderer/Sprite3.cpp +++ b/game/graphics/opengl_renderer/Sprite3.cpp @@ -50,6 +50,18 @@ constexpr int SPRITE_RENDERER_MAX_DISTORT_SPRITES = Sprite3::Sprite3(const std::string& name, BucketId my_id) : BucketRenderer(name, my_id), m_direct(name, my_id, 1024) { + opengl_setup(); +} + +void Sprite3::opengl_setup() { + // Set up OpenGL for 'normal' sprites + opengl_setup_normal(); + + // Set up OpenGL for distort sprites + opengl_setup_distort(); +} + +void Sprite3::opengl_setup_normal() { glGenBuffers(1, &m_ogl.vertex_buffer); glGenVertexArrays(1, &m_ogl.vao); glBindVertexArray(m_ogl.vao); @@ -127,8 +139,11 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id) m_default_mode.set_ab(true); m_current_mode = m_default_mode; +} - // distort +void Sprite3::opengl_setup_distort() { + // Create framebuffer to snapshot current render to a texture that can be bound for the distort + // shader This will represent tex0 from the original GS data glGenFramebuffers(1, &m_distort_ogl.fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_distort_ogl.fbo); @@ -140,6 +155,7 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + // Texture clamping here matches the GS init data for distort glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -151,6 +167,8 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id) glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); + // Non-instancing + // ---------------------- glGenBuffers(1, &m_distort_ogl.vertex_buffer); glGenVertexArrays(1, &m_distort_ogl.vao); glBindVertexArray(m_distort_ogl.vao); @@ -165,7 +183,7 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id) glVertexAttribPointer(0, // location 0 in the shader 3, // 3 floats per vert GL_FLOAT, // floats - GL_TRUE, // normalized, ignored, + GL_FALSE, // don't normalize, ignored sizeof(SpriteDistortVertex), // (void*)offsetof(SpriteDistortVertex, xyz) // offset in array ); @@ -173,7 +191,7 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id) glVertexAttribPointer(1, // location 1 in the shader 2, // 2 floats per vert GL_FLOAT, // floats - GL_TRUE, // normalized, ignored, + GL_FALSE, // don't normalize, ignored sizeof(SpriteDistortVertex), // (void*)offsetof(SpriteDistortVertex, st) // offset in array ); @@ -193,6 +211,81 @@ Sprite3::Sprite3(const std::string& name, BucketId my_id) m_sprite_distorter_vertices.resize(distort_vert_buffer_len); m_sprite_distorter_indices.resize(distort_idx_buffer_len); m_sprite_distorter_frame_data.resize(SPRITE_RENDERER_MAX_DISTORT_SPRITES); + + // Instancing + // ---------------------- + glGenVertexArrays(1, &m_distort_instanced_ogl.vao); + glBindVertexArray(m_distort_instanced_ogl.vao); + + int distort_max_sprite_slices = 0; + for (int i = 3; i < 12; i++) { + // For each 'resolution', there can be that many slices + distort_max_sprite_slices += i; + } + + glGenBuffers(1, &m_distort_instanced_ogl.vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer); + + int distort_instanced_vert_buffer_len = distort_max_sprite_slices * 5; // 5 vertices per slice + glBufferData(GL_ARRAY_BUFFER, distort_instanced_vert_buffer_len * sizeof(SpriteDistortVertex), + nullptr, GL_STREAM_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, // location 0 in the shader + 3, // 3 floats per vert + GL_FLOAT, // floats + GL_FALSE, // don't normalize, ignored + sizeof(SpriteDistortVertex), // + (void*)offsetof(SpriteDistortVertex, xyz) // offset in array + ); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, // location 1 in the shader + 2, // 2 floats per vert + GL_FLOAT, // floats + GL_FALSE, // don't normalize, ignored + sizeof(SpriteDistortVertex), // + (void*)offsetof(SpriteDistortVertex, st) // offset in array + ); + + glGenBuffers(1, &m_distort_instanced_ogl.instance_buffer); + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer); + + int distort_instance_buffer_len = SPRITE_RENDERER_MAX_DISTORT_SPRITES; + glBufferData(GL_ARRAY_BUFFER, distort_instance_buffer_len * sizeof(SpriteDistortInstanceData), + nullptr, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, // location 2 in the shader + 4, // 4 floats per vert + GL_FLOAT, // floats + GL_FALSE, // normalized, ignored, + sizeof(SpriteDistortInstanceData), // + (void*)offsetof(SpriteDistortInstanceData, x_y_z_s) // offset in array + ); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, // location 3 in the shader + 4, // 4 floats per vert + GL_FLOAT, // floats + GL_FALSE, // normalized, ignored, + sizeof(SpriteDistortInstanceData), // + (void*)offsetof(SpriteDistortInstanceData, sx_sy_sz_t) // offset in array + ); + + glVertexAttribDivisor(2, 1); + glVertexAttribDivisor(3, 1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + m_sprite_distorter_vertices_instanced.resize(distort_instanced_vert_buffer_len); + + for (int i = 3; i < 12; i++) { + auto vec = std::vector(); + vec.resize(distort_instance_buffer_len); + + m_sprite_distorter_instances_by_res[i] = vec; + } } /*! @@ -224,13 +317,21 @@ void Sprite3::render_distorter(DmaFollower& dma, // Setup vertex data { auto prof_node = prof.make_scoped_child("setup"); - distort_setup(prof_node); + if (m_enable_distort_instancing) { + distort_setup_instanced(render_state, prof_node); + } else { + distort_setup(prof_node); + } } // Draw { auto prof_node = prof.make_scoped_child("drawing"); - distort_draw(render_state, prof_node); + if (m_enable_distort_instancing) { + distort_draw_instanced(render_state, prof_node); + } else { + distort_draw(render_state, prof_node); + } } } @@ -358,7 +459,7 @@ void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) { math::Vector2f vf03 = frame_data.st; math::Vector3f vf14 = frame_data.xyz; - // Each slice shares a center vertex, we can use this fact and cut out duplicate vertexes + // Each slice shares a center vertex, we can use this fact and cut out duplicate vertices u32 center_vert_idx = m_sprite_distorter_vertices.size(); m_sprite_distorter_vertices.push_back({vf14, vf03}); @@ -407,59 +508,93 @@ void Sprite3::distort_setup(ScopedProfilerNode& /*prof*/) { } } +/*! + * Sets up OpenGL data for rendering distort sprites using instanced rendering. + * + * A mesh is built once for each possible sprite resolution and is only re-built + * when the dimensions of the window are changed. These meshes are built just like + * the triangle strips in the VU program, but with the sprite-specific data removed. + * + * Required sprite-specific frame data is kept as is and is grouped by resolution. + */ +void Sprite3::distort_setup_instanced(SharedRenderState* render_state, + ScopedProfilerNode& /*prof*/) { + if (m_distort_instanced_ogl.last_window_width != render_state->window_width_px || + m_distort_instanced_ogl.last_window_height != render_state->window_height_px) { + m_distort_instanced_ogl.last_window_width = render_state->window_width_px; + m_distort_instanced_ogl.last_window_height = render_state->window_height_px; + + // Window dimensions changed, which means the aspect ratio may have changed, which means we have + // a new sine table + m_sprite_distorter_vertices_instanced.clear(); + + // Build a mesh for every possible distort sprite resolution + auto vf03 = math::Vector2f(0, 0); + auto vf14 = math::Vector3f(0, 0, 0); + + for (int res = 3; res < 12; res++) { + int entry_index = m_sprite_distorter_sine_tables.ientry[res - 3].x() - 352; + + for (int i = 0; i < res; i++) { + math::Vector3f vf06 = m_sprite_distorter_sine_tables.entry[entry_index++].xyz(); + math::Vector2f vf07 = m_sprite_distorter_sine_tables.entry[entry_index++].xy(); + math::Vector3f vf08 = m_sprite_distorter_sine_tables.entry[entry_index + 0].xyz(); + math::Vector2f vf09 = m_sprite_distorter_sine_tables.entry[entry_index + 1].xy(); + + // Normally, there would be a bunch of transformations here against the sprite data. + // Instead, we'll let the shader do it and just store the sine table specific parts here. + + m_sprite_distorter_vertices_instanced.push_back({vf06, vf07}); + m_sprite_distorter_vertices_instanced.push_back({vf08, vf09}); + m_sprite_distorter_vertices_instanced.push_back({vf06, vf07}); + m_sprite_distorter_vertices_instanced.push_back({vf08, vf09}); + m_sprite_distorter_vertices_instanced.push_back({vf14, vf03}); + } + } + + m_distort_instanced_ogl.vertex_data_changed = true; + } + + // Set up instance data for each sprite + m_distort_stats.total_tris = 0; + + for (auto& [res, vec] : m_sprite_distorter_instances_by_res) { + vec.clear(); + } + + for (int i = 0; i < m_distort_stats.total_sprites; i++) { + SpriteDistortFrameData frame_data = m_sprite_distorter_frame_data.at(i); + + // Shader just needs the position, tex coords, and scale + auto x_y_z_s = math::Vector4f(frame_data.xyz.x(), frame_data.xyz.y(), frame_data.xyz.z(), + frame_data.st.x()); + auto sx_sy_sz_t = math::Vector4f(frame_data.rgba.x(), frame_data.rgba.y(), frame_data.rgba.z(), + frame_data.st.y()); + + int res = frame_data.flag; + + m_sprite_distorter_instances_by_res[res].push_back({x_y_z_s, sx_sy_sz_t}); + + m_distort_stats.total_tris += res * 2; + } +} + /*! * Draws each distort sprite. */ void Sprite3::distort_draw(SharedRenderState* render_state, ScopedProfilerNode& prof) { // First, make sure the distort framebuffer is the correct size - if (m_distort_ogl.fbo_width != render_state->window_width_px || - m_distort_ogl.fbo_height != render_state->window_height_px) { - m_distort_ogl.fbo_width = render_state->window_width_px; - m_distort_ogl.fbo_height = render_state->window_height_px; - - glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0, - GL_RGB, GL_UNSIGNED_BYTE, NULL); - - glBindTexture(GL_TEXTURE_2D, 0); - } + distort_setup_framebuffer_dims(render_state); if (m_distort_stats.total_tris == 0) { // No distort sprites to draw, we can end early return; } - // The distort effect needs to read the current framebuffer, so copy what's been rendered so far - // to a texture that we can then pass to the shader - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo); + // Do common distort drawing logic + distort_draw_common(render_state, prof); - glBlitFramebuffer(render_state->window_offset_x_px, // srcX0 - render_state->window_offset_y_px, // srcY0 - render_state->window_width_px + render_state->window_offset_x_px, // srcX1 - render_state->window_height_px + render_state->window_offset_y_px, // srcY1 - 0, // dstX0 - 0, // dstY0 - m_distort_ogl.fbo_width, // dstX1 - m_distort_ogl.fbo_height, // dstY1 - GL_COLOR_BUFFER_BIT, // mask - GL_NEAREST // filter - ); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - // Set up OpenGL state - m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf - glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); // tex0 - m_current_mode.set_filt_enable(m_sprite_distorter_setup.tex1.mmag()); // tex1 - update_mode_from_alpha1(m_sprite_distorter_setup.alpha.data, m_current_mode); // alpha1 - // note: clamp and miptbp are skipped since that is set up ahead of time with the distort - // framebuffer texture - - setup_opengl_from_draw_mode(m_current_mode, GL_TEXTURE0, false); - - // Setup shader + // Set up shader auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT]; shader->activate(); @@ -498,6 +633,119 @@ void Sprite3::distort_draw(SharedRenderState* render_state, ScopedProfilerNode& glBindVertexArray(0); } +/*! + * Draws each distort sprite using instanced rendering. + */ +void Sprite3::distort_draw_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof) { + // First, make sure the distort framebuffer is the correct size + distort_setup_framebuffer_dims(render_state); + + if (m_distort_stats.total_tris == 0) { + // No distort sprites to draw, we can end early + return; + } + + // Do common distort drawing logic + distort_draw_common(render_state, prof); + + // Set up shader + auto shader = &render_state->shaders[ShaderId::SPRITE_DISTORT_INSTANCED]; + shader->activate(); + + Vector4f colorf = Vector4f(m_sprite_distorter_sine_tables.color.x() / 255.0f, + m_sprite_distorter_sine_tables.color.y() / 255.0f, + m_sprite_distorter_sine_tables.color.z() / 255.0f, + m_sprite_distorter_sine_tables.color.w() / 255.0f); + glUniform4fv(glGetUniformLocation(shader->id(), "u_color"), 1, colorf.data()); + + // Bind vertex array + glBindVertexArray(m_distort_instanced_ogl.vao); + + // Upload vertex data (if it changed) + if (m_distort_instanced_ogl.vertex_data_changed) { + m_distort_instanced_ogl.vertex_data_changed = false; + + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, + m_sprite_distorter_vertices_instanced.size() * sizeof(SpriteDistortVertex), + m_sprite_distorter_vertices_instanced.data(), GL_STREAM_DRAW); + } + + // Draw each resolution group + glBindBuffer(GL_ARRAY_BUFFER, m_distort_instanced_ogl.instance_buffer); + prof.add_tri(m_distort_stats.total_tris); + + int vert_offset = 0; + for (int res = 3; res < 12; res++) { + auto& instances = m_sprite_distorter_instances_by_res[res]; + int num_verts = res * 5; + + if (instances.size() > 0) { + // Upload instance data + glBufferData(GL_ARRAY_BUFFER, instances.size() * sizeof(SpriteDistortInstanceData), + instances.data(), GL_DYNAMIC_DRAW); + + // Draw + prof.add_draw_call(); + + glDrawArraysInstanced(GL_TRIANGLE_STRIP, vert_offset, num_verts, instances.size()); + } + + vert_offset += num_verts; + } + + // Done + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void Sprite3::distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& /*prof*/) { + // The distort effect needs to read the current framebuffer, so copy what's been rendered so far + // to a texture that we can then pass to the shader + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_distort_ogl.fbo); + + glBlitFramebuffer(render_state->window_offset_x_px, // srcX0 + render_state->window_offset_y_px, // srcY0 + render_state->window_width_px + render_state->window_offset_x_px, // srcX1 + render_state->window_height_px + render_state->window_offset_y_px, // srcY1 + 0, // dstX0 + 0, // dstY0 + m_distort_ogl.fbo_width, // dstX1 + m_distort_ogl.fbo_height, // dstY1 + GL_COLOR_BUFFER_BIT, // mask + GL_NEAREST // filter + ); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Set up OpenGL state + m_current_mode.set_depth_write_enable(!m_sprite_distorter_setup.zbuf.zmsk()); // zbuf + glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); // tex0 + m_current_mode.set_filt_enable(m_sprite_distorter_setup.tex1.mmag()); // tex1 + update_mode_from_alpha1(m_sprite_distorter_setup.alpha.data, m_current_mode); // alpha1 + // note: clamp and miptbp are skipped since that is set up ahead of time with the distort + // framebuffer texture + + setup_opengl_from_draw_mode(m_current_mode, GL_TEXTURE0, false); +} + +void Sprite3::distort_setup_framebuffer_dims(SharedRenderState* render_state) { + // Distort framebuffer must be the same dimensions as the default window framebuffer + if (m_distort_ogl.fbo_width != render_state->window_width_px || + m_distort_ogl.fbo_height != render_state->window_height_px) { + m_distort_ogl.fbo_width = render_state->window_width_px; + m_distort_ogl.fbo_height = render_state->window_height_px; + + glBindTexture(GL_TEXTURE_2D, m_distort_ogl.fbo_texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_distort_ogl.fbo_width, m_distort_ogl.fbo_height, 0, + GL_RGB, GL_UNSIGNED_BYTE, NULL); + + glBindTexture(GL_TEXTURE_2D, 0); + } +} + /*! * Handle DMA data that does the per-frame setup. * This should get the dma chain immediately after the call to sprite-draw-distorters. @@ -770,6 +1018,7 @@ void Sprite3::draw_debug_window() { ImGui::SameLine(); ImGui::Checkbox("3d", &m_3d_enable); ImGui::Checkbox("Distort", &m_distort_enable); + ImGui::Checkbox("Distort instancing", &m_enable_distort_instancing); } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/game/graphics/opengl_renderer/Sprite3.h b/game/graphics/opengl_renderer/Sprite3.h index af668cea3d..fd622e1321 100644 --- a/game/graphics/opengl_renderer/Sprite3.h +++ b/game/graphics/opengl_renderer/Sprite3.h @@ -19,12 +19,20 @@ class Sprite3 : public BucketRenderer { static constexpr int SPRITES_PER_CHUNK = 48; private: + void opengl_setup(); + void opengl_setup_normal(); + void opengl_setup_distort(); + void render_distorter(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof); void distort_dma(DmaFollower& dma, ScopedProfilerNode& prof); void distort_setup(ScopedProfilerNode& prof); + void distort_setup_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof); void distort_draw(SharedRenderState* render_state, ScopedProfilerNode& prof); + void distort_draw_instanced(SharedRenderState* render_state, ScopedProfilerNode& prof); + void distort_draw_common(SharedRenderState* render_state, ScopedProfilerNode& prof); + void distort_setup_framebuffer_dims(SharedRenderState* render_state); void handle_sprite_frame_setup(DmaFollower& dma); void render_3d(DmaFollower& dma); void render_2d_group0(DmaFollower& dma, @@ -90,6 +98,11 @@ class Sprite3 : public BucketRenderer { math::Vector2f st; }; + struct SpriteDistortInstanceData { + math::Vector4f x_y_z_s; // position, S-texture coord + math::Vector4f sx_sy_sz_t; // scale, T-texture coord + }; + struct { GLuint vao; GLuint vertex_buffer; @@ -100,6 +113,15 @@ class Sprite3 : public BucketRenderer { int fbo_height = 480; } m_distort_ogl; + struct { + GLuint vao; + GLuint vertex_buffer; // contains vertex data for each possible sprite resolution (3-11) + GLuint instance_buffer; // contains all instance specific data for each sprite per frame + int last_window_width = -1; + int last_window_height = -1; + bool vertex_data_changed = false; + } m_distort_instanced_ogl; + struct { int total_sprites; int total_tris; @@ -110,6 +132,8 @@ class Sprite3 : public BucketRenderer { SpriteDistorterSetup m_sprite_distorter_setup; // direct data SpriteDistorterSineTables m_sprite_distorter_sine_tables; std::vector m_sprite_distorter_frame_data; + std::vector m_sprite_distorter_vertices_instanced; + std::map> m_sprite_distorter_instances_by_res; u8 m_sprite_direct_setup[3 * 16]; SpriteFrameData m_frame_data; // qwa: 980 @@ -127,6 +151,7 @@ class Sprite3 : public BucketRenderer { int count_2d_grp1 = 0; } m_debug_stats; + bool m_enable_distort_instancing = true; bool m_enable_culling = true; bool m_2d_enable = true; diff --git a/game/graphics/opengl_renderer/shaders/sprite_distort_instanced.frag b/game/graphics/opengl_renderer/shaders/sprite_distort_instanced.frag new file mode 100644 index 0000000000..3170235a0b --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/sprite_distort_instanced.frag @@ -0,0 +1,21 @@ +#version 430 core + +out vec4 out_color; + +uniform sampler2D framebuffer_tex; + +in flat vec4 fragment_color; +in vec2 tex_coord; + +void main() { + vec4 color = fragment_color; + + // correct color + color *= 2; + + // correct texture coordinates + vec2 texture_coords = vec2(tex_coord.x, (1.0f - tex_coord.y) - (1 - (448.0/512.0)) / 2); + + // sample framebuffer texture + out_color = color * texture(framebuffer_tex, texture_coords); +} diff --git a/game/graphics/opengl_renderer/shaders/sprite_distort_instanced.vert b/game/graphics/opengl_renderer/shaders/sprite_distort_instanced.vert new file mode 100644 index 0000000000..d0d712a6df --- /dev/null +++ b/game/graphics/opengl_renderer/shaders/sprite_distort_instanced.vert @@ -0,0 +1,54 @@ +#version 430 core + +layout (location = 0) in vec3 xyz; // position from sine table +layout (location = 1) in vec2 st; // tex coord from sine table +layout (location = 2) in vec4 instance_xyz_s; // sprite position + texture S-coord +layout (location = 3) in vec4 instance_scale_t; // sprite scale + texture T-coord + +uniform vec4 u_color; + +out flat vec4 fragment_color; +out vec2 tex_coord; + +void main() { + // Pass on fragment color + fragment_color = u_color; + + // Adjust position and texture coordinates the same way as in the VU program + // + // The VU program operated on each "slice" of the sprite separately, which in + // this case is every 5 vertices. To know the exact transformation to apply + // for each vertex, we need to know which one it is. The order is always 2 + // vertices scaled by sizeX, 2 scaled by sizeY (sizeZ for tex coords), and + // finally the center vertex which isn't modified. + float slice_vert_id = mod(gl_VertexID, 5); + + vec2 texture_coord = vec2(instance_xyz_s.w, instance_scale_t.w); + if (slice_vert_id < 2) { + texture_coord += st * instance_scale_t.x; + } else if (slice_vert_id < 4) { + texture_coord += st * instance_scale_t.z; + } + + tex_coord = texture_coord; + + vec3 position = instance_xyz_s.xyz; + if (slice_vert_id < 2) { + position += xyz * instance_scale_t.x; + } else if (slice_vert_id < 4) { + position += xyz * instance_scale_t.y; + } + + vec4 transformed = vec4(position, 1.0); + + // correct xy offset + transformed.xy -= (2048.); + // correct z scale + transformed.z /= (8388608); + transformed.z -= 1; + // correct xy scale + transformed.x /= (256); + transformed.y /= -(128); + + gl_Position = transformed; +}