Improve batch rendering: Use multiple OpenGL texture units

- Instead of creating a seperate mesh for every texture, each mesh now uses multiple texture bound to different units to reduce the total number of meshes.
- Use OpenGL version to 4.6
- Use OpenGL alpha testing instead of manual discard in fragment shader
master
Elias Fleckenstein 2021-08-23 18:06:57 +02:00
parent 8bbd0f4492
commit c26c61c4d8
17 changed files with 135 additions and 73 deletions

View File

@ -1,11 +1,12 @@
#version 330 core
#version 460 core
in float fragmentTextureIndex;
in vec2 fragmentTextureCoords;
in vec3 fragmentColor;
out vec4 outColor;
uniform sampler2D texture0;
uniform sampler2D textures[8]; // ToDo: Replace 8 by max_texture_units
vec3 hsv2rgb(vec3 c)
{
@ -16,7 +17,5 @@ vec3 hsv2rgb(vec3 c)
void main()
{
outColor = texture(texture0, fragmentTextureCoords) * vec4(hsv2rgb(vec3(fragmentColor)), 1.0);
if (outColor.a == 0.0)
discard;
outColor = texture(textures[int(fragmentTextureIndex)], fragmentTextureCoords) * vec4(hsv2rgb(vec3(fragmentColor)), 1.0);
}

View File

@ -1,9 +1,11 @@
#version 330 core
#version 460 core
layout(location = 0) in vec3 vertexPosition;
layout(location = 1) in vec2 vertexTextureCoords;
layout(location = 2) in vec3 vertexColor;
layout(location = 1) in float vertexTextureIndex;
layout(location = 2) in vec2 vertexTextureCoords;
layout(location = 3) in vec3 vertexColor;
out float fragmentTextureIndex;
out vec2 fragmentTextureCoords;
out vec3 fragmentColor;
@ -14,6 +16,8 @@ uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(vertexPosition, 1.0);
fragmentTextureIndex = vertexTextureIndex;
fragmentTextureCoords = vertexTextureCoords;
fragmentColor = vertexColor;
}

View File

@ -1,4 +1,4 @@
#version 330 core
#version 460 core
in vec2 fragmentTextureCoords;
@ -10,6 +10,4 @@ uniform vec3 textColor;
void main()
{
outColor = vec4(1.0, 1.0, 1.0, texture(texture0, fragmentTextureCoords).r) * vec4(textColor, 1.0);
if (outColor.a == 0.0)
discard;
}

View File

@ -1,4 +1,4 @@
#version 330 core
#version 460 core
layout(location = 0) in vec2 vertexPosition;
layout(location = 1) in vec2 vertexTextureCoords;

View File

@ -1,4 +1,4 @@
#version 330 core
#version 460 core
in vec2 fragmentTextureCoords;
@ -9,6 +9,4 @@ uniform sampler2D texture0;
void main()
{
outColor = texture(texture0, fragmentTextureCoords);
if (outColor.a == 0.0)
discard;
}

View File

@ -1,4 +1,4 @@
#version 330 core
#version 460 core
layout(location = 0) in vec2 vertexPosition;
layout(location = 1) in vec2 vertexTextureCoords;

View File

@ -2,52 +2,52 @@
Vertex3D cube_vertices[6][6] = {
{
{{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, -0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, -0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, -0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, -0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, +0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
},
{
{{-0.5, -0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, +0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, +0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, +0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, +0.5, +0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
},
{
{{-0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, +0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
},
{
{{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
},
{
{{-0.5, -0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, -0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, -0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, +0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, -0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
},
{
{{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{+0.5, +0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
{{-0.5, +0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, -0.5}, +0.0, {+1.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{+0.5, +0.5, +0.5}, +0.0, {+1.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, +0.5, +0.5}, +0.0, {+0.+0.0, +0.0}, {+1.+0.0, +0.+0.0, +1.0}},
{{-0.5, +0.5, -0.5}, +0.0, {+0.+0.0, +1.0}, {+1.+0.0, +0.+0.0, +1.0}},
},
};

View File

@ -144,10 +144,13 @@ Font *font_create(char *text)
};
Mesh *mesh = fnt->meshes[i] = mesh_create();
mesh->texture = ch->texture->id;
mesh->layout = &vertex_layout;
mesh->textures = &ch->texture->id;
mesh->textures_count = 1;
mesh->free_textures = false;
mesh->vertices = vertices;
mesh->vertices_count = 6;
mesh->free_vertices = false;
mesh->layout = &vertex_layout;
mesh_configure(mesh);
offset += ch->advance >> 6;

View File

@ -36,7 +36,7 @@ static void game_loop(Client *client)
while (! glfwWindowShouldClose(window.handle) && client->state != CS_DISCONNECTED && ! interrupted) {
clock_gettime(CLOCK_REALTIME, &ts);
f64 dtime = (f64) (ts.tv_sec - ts_old.tv_sec) + (f64) (ts.tv_nsec - ts_old.tv_nsec) / 1000000000.0;
f64 dtime = (f64) (ts.tv_sec - ts_old.tv_sec) + (f64) (ts.tv_nsec - ts_old.tv_nsec) / 1.0e9;
ts_old = ts;
if ((fps_update_timer -= dtime) <= 0.0) {
@ -50,9 +50,11 @@ static void game_loop(Client *client)
frames++;
glEnable(GL_DEPTH_TEST);
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glEnable(GL_MULTISAMPLE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glAlphaFunc(GL_GREATER, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.52941176470588f, 0.8078431372549f, 0.92156862745098f, 1.0f);

View File

@ -97,8 +97,12 @@ bool hud_init()
hud.elements = list_create(NULL);
hud.image_mesh = mesh_create();
hud.image_mesh->textures = NULL;
hud.image_mesh->textures_count = 1;
hud.image_mesh->free_textures = false;
hud.image_mesh->vertices = image_vertices;
hud.image_mesh->vertices_count = 6;
hud.image_mesh->free_vertices = false;
hud.image_mesh->layout = &image_vertex_layout;
return true;
@ -186,7 +190,7 @@ void hud_render()
case HUD_IMAGE:
glUseProgram(hud.image_prog);
glUniformMatrix4fv(hud.image_loc_model, 1, GL_FALSE, element->transform[0]);
hud.image_mesh->texture = element->def.type_def.image.texture->id;
hud.image_mesh->textures = &element->def.type_def.image.texture->id;
mesh_render(hud.image_mesh);
break;

View File

@ -6,6 +6,7 @@ Mesh *mesh_create()
{
Mesh *mesh = malloc(sizeof(Mesh));
mesh->VAO = mesh->VBO = 0;
mesh->free_textures = false;
mesh->free_vertices = false;
return mesh;
@ -13,6 +14,9 @@ Mesh *mesh_create()
void mesh_delete(Mesh *mesh)
{
if (mesh->textures && mesh->free_textures)
free(mesh->textures);
if (mesh->vertices && mesh->free_vertices)
free(mesh->vertices);
@ -51,7 +55,9 @@ void mesh_render(Mesh *mesh)
if (mesh->vertices)
mesh_configure(mesh);
glBindTexture(GL_TEXTURE_2D, mesh->texture);
for (GLuint i = 0; i < mesh->textures_count; i++)
glBindTextureUnit(i, mesh->textures[i]);
glBindVertexArray(mesh->VAO);
glDrawArrays(GL_TRIANGLES, 0, mesh->vertices_count);

View File

@ -9,7 +9,9 @@
typedef struct
{
GLuint VAO, VBO;
GLuint texture;
GLuint *textures;
GLuint textures_count;
bool free_textures;
GLvoid *vertices;
GLuint vertices_count;
bool free_vertices;

View File

@ -1,14 +1,21 @@
#include <stdlib.h>
#include "client/object.h"
#include "client/scene.h"
#define OBJECT_VERTEX_ATTRIBUTES 4
static VertexAttribute vertex_attributes[3] = {
static VertexAttribute vertex_attributes[OBJECT_VERTEX_ATTRIBUTES] = {
// position
{
.type = GL_FLOAT,
.length = 3,
.size = sizeof(Vertex3DPosition),
},
// textureIndex
{
.type = GL_FLOAT,
.length = 1,
.size = sizeof(Vertex3DTextureIndex),
},
// textureCoordinates
{
.type = GL_FLOAT,
@ -26,7 +33,7 @@ static VertexAttribute vertex_attributes[3] = {
static VertexLayout vertex_layout = {
.attributes = vertex_attributes,
.count = 3,
.count = OBJECT_VERTEX_ATTRIBUTES,
.size = sizeof(Vertex3D),
};
@ -80,22 +87,30 @@ static int qsort_compare_faces(const void *f1, const void *f2)
return ((ObjectFace *) f1)->texture - ((ObjectFace *) f2)->texture;
}
static void add_mesh(Array *meshes, Array *vertices, GLuint texture)
static void add_mesh(Array *meshes, Array *vertices, Array *textures)
{
if (vertices->siz > 0) {
Mesh *mesh = mesh_create();
mesh->vertices = vertices->ptr;
mesh->vertices_count = vertices->siz;
mesh->free_vertices = true;
mesh->texture = texture;
mesh->free_textures = true;
mesh->layout = &vertex_layout;
size_t textures_count;
array_copy(textures, (void *) &mesh->textures, &textures_count);
mesh->textures_count = textures_count;
free(textures->ptr);
array_append(meshes, &mesh);
}
*vertices = array_create(sizeof(Vertex3D));
*textures = array_create(sizeof(GLuint));
}
#include <assert.h>
bool object_add_to_scene(Object *obj)
{
if (obj->faces.siz == 0)
@ -105,23 +120,38 @@ bool object_add_to_scene(Object *obj)
qsort(obj->faces.ptr, obj->faces.siz, sizeof(ObjectFace), &qsort_compare_faces);
GLuint max_texture_units = scene_get_max_texture_units();
Array meshes = array_create(sizeof(Mesh *));
Array vertices = array_create(sizeof(Vertex3D));
GLuint texture = 0;
Array textures = array_create(sizeof(GLuint));
GLuint texture_id = 0;
GLuint texture_index = max_texture_units;
for (size_t f = 0; f < obj->faces.siz; f++) {
ObjectFace *face = &((ObjectFace *) obj->faces.ptr)[f];
if (face->texture != texture) {
add_mesh(&meshes, &vertices, texture);
texture = face->texture;
if (face->texture != texture_id) {
if (++texture_index >= max_texture_units) {
add_mesh(&meshes, &vertices, &textures);
texture_index = 0;
}
texture_id = face->texture;
array_append(&textures, &texture_id);
}
for (size_t v = 0; v < face->vertices.siz; v++) {
Vertex3D *vertex = &((Vertex3D *) face->vertices.ptr)[v];
vertex->textureIndex = texture_index;
array_append(&vertices, vertex);
}
for (size_t v = 0; v < face->vertices.siz; v++)
array_append(&vertices, &((Vertex3D *) face->vertices.ptr)[v]);
free(face->vertices.ptr);
}
add_mesh(&meshes, &vertices, texture);
add_mesh(&meshes, &vertices, &textures);
free(obj->faces.ptr);
array_copy(&meshes, (void *) &obj->meshes, &obj->meshes_count);

View File

@ -16,6 +16,8 @@ typedef struct {
GLfloat x, y, z;
} __attribute__((packed)) Vertex3DPosition;
typedef GLfloat Vertex3DTextureIndex;
typedef struct {
GLfloat s, t;
} __attribute__((packed)) Vertex3DTextureCoordinates;
@ -27,6 +29,7 @@ typedef struct {
typedef struct
{
Vertex3DPosition position;
Vertex3DTextureIndex textureIndex;
Vertex3DTextureCoordinates textureCoordinates;
Vertex3DColor color;
} __attribute__((packed)) Vertex3D;

View File

@ -17,6 +17,7 @@ static struct
GLint loc_model;
GLint loc_view;
GLint loc_projection;
GLint max_texture_units;
mat4x4 projection;
f32 fov;
f32 render_distance;
@ -36,6 +37,14 @@ bool scene_init()
scene.loc_view = glGetUniformLocation(scene.prog, "view");
scene.loc_projection = glGetUniformLocation(scene.prog, "projection");
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &scene.max_texture_units);
GLint texture_indices[scene.max_texture_units];
for (GLint i = 0; i < scene.max_texture_units; i++)
texture_indices[i] = i;
glProgramUniform1iv(scene.prog, glGetUniformLocation(scene.prog, "textures"), scene.max_texture_units, texture_indices);
scene.fov = 86.1f;
scene.render_distance = 1000.0f;
@ -67,8 +76,6 @@ void scene_render()
camera_enable(scene.loc_view);
glUniformMatrix4fv(scene.loc_projection, 1, GL_FALSE, scene.projection[0]);
glActiveTexture(GL_TEXTURE0);
for (ListPair **pairptr = &scene.objects.first; *pairptr != NULL; ) {
ListPair *pair = *pairptr;
Object *obj = pair->key;
@ -89,3 +96,8 @@ void scene_on_resize(int width, int height)
{
mat4x4_perspective(scene.projection, scene.fov / 180.0f * M_PI, (float) width / (float) height, 0.01f, scene.render_distance);
}
GLuint scene_get_max_texture_units()
{
return scene.max_texture_units;
}

View File

@ -8,5 +8,6 @@ void scene_deinit();
void scene_add_object(Object *obj);
void scene_render();
void scene_on_resize(int width, int height);
GLuint scene_get_max_texture_units();
#endif

View File

@ -57,8 +57,8 @@ bool window_init(int width, int height)
}
glfwWindowHint(GLFW_SAMPLES, 8);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window.handle = glfwCreateWindow(width, height, "Dragonblocks", NULL, NULL);