add correct interleaving of opaque and transparent sprites

Thu, 21 Mar 2024 22:23:00 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 21 Mar 2024 22:23:00 +0100
changeset 41
df81d493716e
parent 40
6c438be1a1fd
child 42
cc912686f663

add correct interleaving of opaque and transparent sprites

shader/sprite_vtx.glsl file | annotate | diff | comparison | revisions
src/ascension/datatypes.h file | annotate | diff | comparison | revisions
src/ascension/scene.h file | annotate | diff | comparison | revisions
src/ascension/shader.h file | annotate | diff | comparison | revisions
src/camera.c file | annotate | diff | comparison | revisions
src/scene.c file | annotate | diff | comparison | revisions
src/shader.c file | annotate | diff | comparison | revisions
src/text.c file | annotate | diff | comparison | revisions
src/window.c file | annotate | diff | comparison | revisions
--- a/shader/sprite_vtx.glsl	Thu Mar 21 20:48:18 2024 +0100
+++ b/shader/sprite_vtx.glsl	Thu Mar 21 22:23:00 2024 +0100
@@ -5,8 +5,12 @@
 
 uniform mat4 projection;
 uniform mat4 model;
+uniform float depth;
 
 void main(void) {
-    gl_Position = projection*model*vec4(position, 0.0, 1.0);
+    vec4 pos = projection*model*vec4(position.x, position.y, 0, 1.0);
+    // apply depth
+    pos.z = depth / -256.0;
+    gl_Position = pos;
     texcoord = vec2(model[0].x, model[1].y)*position;
 }
--- a/src/ascension/datatypes.h	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/ascension/datatypes.h	Thu Mar 21 22:23:00 2024 +0100
@@ -138,14 +138,17 @@
         float left,
         float right,
         float bottom,
-        float top
+        float top,
+        float near,
+        float far
 ) {
     memset(mat, 0, sizeof(float) * 16);
     mat[asc_mat4_index(0,0)] = 2.f / (right - left);
     mat[asc_mat4_index(1,1)] = 2.f / (top - bottom);
-    mat[asc_mat4_index(2,2)] = -1;
+    mat[asc_mat4_index(2,2)] = -2.f / (far - near);
     mat[asc_mat4_index(3,0)] = -(right + left) / (right - left);
     mat[asc_mat4_index(3,1)] = -(top + bottom) / (top - bottom);
+    mat[asc_mat4_index(3,2)] = -(far + near) / (far - near);
     mat[asc_mat4_index(3,3)] = 1;
 }
 
--- a/src/ascension/scene.h	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/ascension/scene.h	Thu Mar 21 22:23:00 2024 +0100
@@ -49,8 +49,8 @@
 };
 
 enum AscRenderGroup {
-    ASC_RENDER_GROUP_NONE,
-    ASC_RENDER_GROUP_FONTS
+    ASC_RENDER_GROUP_SPRITE_OPAQUE,
+    ASC_RENDER_GROUP_SPRITE_BLEND
 };
 
 struct AscSceneNode {
@@ -63,6 +63,7 @@
     asc_scene_update_func update_func;
     asc_scene_update_func transform_update_func;
     asc_scene_draw_func draw_func;
+    unsigned depth;
     asc_transform world_transform;
     asc_transform local_transform;
     asc_transform final_transform;
@@ -87,8 +88,8 @@
     AscSceneNode *root;
     asc_recti viewport;
     AscCamera cameras[ASC_SCENE_CAMERAS_MAX];
-    CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_none);
-    CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_fonts);
+    CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_sprites_opaque);
+    CX_ARRAY_DECLARE(struct asc_render_group_entry, rg_sprites_blended);
 } AscScene;
 
 /**
--- a/src/ascension/shader.h	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/ascension/shader.h	Thu Mar 21 22:23:00 2024 +0100
@@ -42,6 +42,7 @@
 typedef struct AscShaderSprite {
     AscShaderProgram base;
     int surface;
+    int depth;
 } AscShaderSprite;
 
 
--- a/src/camera.c	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/camera.c	Thu Mar 21 22:23:00 2024 +0100
@@ -32,7 +32,7 @@
     float right = left + (float) camera->rect.size.width;
     float top = (float) camera->rect.pos.y;
     float bottom = top + (float) camera->rect.size.height;
-    asc_mat4f_ortho(camera->projection, left, right, bottom, top);
+    asc_mat4f_ortho(camera->projection, left, right, bottom, top, -1, 1);
 }
 
 void asc_camera_ortho(AscCamera *camera, asc_recti rect) {
--- a/src/scene.c	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/scene.c	Thu Mar 21 22:23:00 2024 +0100
@@ -65,8 +65,8 @@
     scene->root = asc_scene_node_empty();
 
     // initialize the render groups
-    cx_array_initialize(scene->rg_none, 8);
-    cx_array_initialize(scene->rg_fonts, 8);
+    cx_array_initialize(scene->rg_sprites_opaque, 8);
+    cx_array_initialize(scene->rg_sprites_blended, 8);
 }
 
 void asc_scene_destroy(AscScene *scene) {
@@ -83,10 +83,16 @@
         rg[i].draw(rg[i].node); \
     } (void)0
 
+#define asc_scene_draw_render_group_reversed(rg) \
+    for(size_t i = rg##_size ; i > 0 ; i--) { \
+        rg[i-1].draw(rg[i-1].node); \
+    } (void)0
+
 void asc_scene_draw(AscScene *scene) {
     // reset render groups
-    scene->rg_none_size = 0;
-    scene->rg_fonts_size = 0;
+    // TODO: avoid recalculating the groups, if possible
+    scene->rg_sprites_opaque_size = 0;
+    scene->rg_sprites_blended_size = 0;
 
     // skip the root node deliberately, we know it's just the container
     CxTreeIterator iter = asc_scene_node_iterator(scene->root, false);
@@ -94,6 +100,8 @@
 
     // update the children and add them to the render groups
     cx_foreach(AscSceneNode*, node, iter) {
+        node->depth = iter.depth;
+
         // execute behaviors, first
         AscBehaviorNode *behavior = node->behaviors;
         while (behavior) {
@@ -122,11 +130,11 @@
                     node->draw_func, node
             };
             switch (node->render_group) {
-                case ASC_RENDER_GROUP_NONE:
-                    cx_array_simple_add(scene->rg_none, entry);
+                case ASC_RENDER_GROUP_SPRITE_OPAQUE:
+                    cx_array_simple_add(scene->rg_sprites_opaque, entry);
                     break;
-                case ASC_RENDER_GROUP_FONTS:
-                    cx_array_simple_add(scene->rg_fonts, entry);
+                case ASC_RENDER_GROUP_SPRITE_BLEND:
+                    cx_array_simple_add(scene->rg_sprites_blended, entry);
                     break;
             }
         }
@@ -139,6 +147,7 @@
             scene->viewport.size.width,
             scene->viewport.size.height
     );
+    glClear(GL_COLOR_BUFFER_BIT);
 
     // -----------------------------------------
     // process the render groups for each camera
@@ -149,21 +158,25 @@
         if (camera->update == NULL) continue;
         camera->update(camera);
 
-        // for the NONE group, the draw func is expected to do everything
+        // 2D Elements
+        // ===========
         glEnable(GL_DEPTH_TEST);
-        glDisable(GL_BLEND);
-        asc_scene_draw_render_group(scene->rg_none);
+        glClear(GL_DEPTH_BUFFER_BIT);
 
-        // draw the FONTS group
-        glDisable(GL_DEPTH_TEST);
-        glEnable(GL_BLEND);
-        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        // Sprites
+        // -------
         // TODO: see if we can really always ignore the view matrix
-        // TODO: compute render order for alpha blending to work correctly
         glUseProgram(ASC_SHADER_SPRITE.base.id);
         glUniformMatrix4fv(ASC_SHADER_SPRITE.base.projection, 1,
                            GL_FALSE, camera->projection);
-        asc_scene_draw_render_group(scene->rg_fonts);
+
+        // render opaque sprites from front to back
+        glDisable(GL_BLEND);
+        asc_scene_draw_render_group_reversed(scene->rg_sprites_opaque);
+        // render sprites with alpha value from back to front
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        asc_scene_draw_render_group(scene->rg_sprites_blended);
     }
 }
 
--- a/src/shader.c	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/shader.c	Thu Mar 21 22:23:00 2024 +0100
@@ -139,6 +139,7 @@
 void asc_shader_initialize_predefined(void) {
     ASC_SHADER_SPRITE.base = asc_shader_compile_link_discard("shader/sprite_vtx.glsl", "shader/sprite_frag.glsl");
     ASC_SHADER_SPRITE.surface = glGetUniformLocation(ASC_SHADER_SPRITE.base.id, "surface");
+    ASC_SHADER_SPRITE.depth = glGetUniformLocation(ASC_SHADER_SPRITE.base.id, "depth");
 }
 
 void asc_shader_destroy_predefined(void) {
--- a/src/text.c	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/text.c	Thu Mar 21 22:23:00 2024 +0100
@@ -46,6 +46,9 @@
     glBindTexture(GL_TEXTURE_RECTANGLE, node->tex_id);
     glUniform1i(ASC_SHADER_SPRITE.surface, 0);
 
+    // Apply depth
+    glUniform1f(ASC_SHADER_SPRITE.depth, (float)(node->base.depth));
+
     // Draw mesh
     asc_primitives_draw_plane();
 }
@@ -83,7 +86,7 @@
     }
     node->dimension.width = surface->w;
     node->dimension.height = surface->h;
-    asc_node_update_transform(node);
+    asc_node_update_transform((AscSceneNode *) node);
 
     // Transfer Image Data
     // TODO: move the image data transfer to a separate function - we will need it more often
@@ -104,7 +107,7 @@
         return NULL;
     }
 
-    node->base.render_group = ASC_RENDER_GROUP_FONTS;
+    node->base.render_group = ASC_RENDER_GROUP_SPRITE_BLEND;
     node->base.free_func = (asc_scene_free_func) asc_text_free;
     node->base.update_func = (asc_scene_update_func) asc_text_update;
     node->base.transform_update_func = (asc_scene_update_func) asc_text_update_transform;
--- a/src/window.c	Thu Mar 21 20:48:18 2024 +0100
+++ b/src/window.c	Thu Mar 21 22:23:00 2024 +0100
@@ -116,6 +116,7 @@
         GLenum err = glewInit();
         if (err == GLEW_OK) {
             SDL_GL_SetSwapInterval(settings->vsync);
+            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
             glEnable(GL_DEBUG_OUTPUT);
             glDebugMessageCallback(asc_gl_debug_callback, NULL);
 
@@ -183,10 +184,6 @@
         asc_window_activate(window);
     }
 
-    // Clear for new frame
-    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
     // Draw the UI
     asc_scene_draw(&window->ui);
 

mercurial