Начнём с того, что я обзавелся идеей оптимизировать процесс рисования для компьютеров без игровой видеокарты. Для встроенной карты не так важно сколько видеопамяти занимает приложение, сколько сама мощность этой карты. Насчет памяти для встроенной карты я кстати ещё не в курсе, но помню, что она может выделять её из CPU.
Так вот. Я хотел рендерить сцену один раз с двумя источниками света и применять запечённые текстуры уже в обычном рендеринге без просчета света каждый раз. Если добавиться новый источник света, то просчитаем опять везде свет и дальше рисуем запечённые текстуры.
Итак начнём. У меня движок ещё сырой, так что не ругайте за код, так как я бывает многое переписываю, потому что не учитывал некоторые аспекты того, что требуется.
Первым делом, чтобы проверить, что всё точно работает, я написал вот такой код.
for (int indx_z = 0; indx_z < height; indx_z++) {
for (int indx_x = 0; indx_x < width; indx_x++) {
struct obj_model *obj = obj_model_init (game, scene, RES_OBJECTS, TILE_OBJECT, SHADER_BACKED_TEXTURE_MODEL);
obj->model->texture = sprite_init_with_texture_for_object_optimized (game, RES_SPRITES, FLOOR_RHOMB_PNG_TEXTURE);
#if 1
obj_model_set_optimized_shader (obj, SHADER_TEXTURE_WITH_LIGHT_POINT_OPTIMIZED);
В цикле создается модель. Приставку назвал optimized, так как не нашел для неё другого лучшего названия. Здесь мы создаем модель и присваиваем ей обычный шейдер SHADER_BACKED_TEXTURE_MODEL, а в отпмизации указываем SHADER_TEXTURE_WITH_LIGHT_POINT_OPTIMIZED.
Сам шейдер оптимизации выглядит так.
#version 450 core
layout (location = 0) in vec3 dpos;
layout (location = 1) in vec2 dtex_coord;
layout (location = 2) in vec3 dnormal;
out vec2 tex_coord;
out vec3 normal;
out vec3 frag_pos;
uniform mat4 model;
uniform mat4 transform;
void main ()
{
vec4 texture_coord = vec4 (dtex_coord.x, dtex_coord.y, 1.0, 1.0);
gl_Position = model * texture_coord;
tex_coord = dtex_coord;
normal = dnormal;
frag_pos = vec3 (transform * vec4 (dpos, 1.0));
}
~
~
~
~
Как вы можете видеть, вместо того, чтобы рисовать модель по вершинам, я создаю точку вершины у текстуры и рисую её на экран. Для неё нужен ortho, который находится в model такой.
ortho (sp->ortho, 0.f, g->screen->aspect / 2.f, 1.f, 0.f, -1.0, 10.f);
Я поставил источник света и вот что получилось. Там горят два источника света.

Как видно, я применил glViewport (0, 0, 1024, 1024) так как размер текстуры именно должен быть таким. Куда падает свет тоже видно. Теперь уберём один источник света.

Так как это сработало, то я теперь могу считать буфер экрана в массив и загрузить этот массив в текстуру этой модели. Посмотрим что будет.
Текстура у меня перевернута оказалась, так что я меня в шейдере (1.0 - dtex_coord.y) и получаем.

Теперь стало больше похоже на правду.
В инициализации я создал фреймбуфер.
off_screen = framebuffer_init (1024, 1024, LINEAR_INTERPOLATION, 4);
Теперь осталось нарисовать в этот фреймбуфер
Я сделал такой код, чтобы рисовать, но вышло не то, что хотел.
if (is_once) {
glBindFramebuffer (GL_FRAMEBUFFER, off_screen->fbo);
uint32_t tex_width = 1024;
glDisable (GL_DEPTH_TEST);
glViewport (0, 0, tex_width, tex_width);
uint32_t total_bytes = total_floor_tiles * 4;
if (buf == NULL) {
buf = malloc (tex_width * tex_width * 4);
}
for (int i = 0; i < total_floor_tiles; i++) {
obj_model_render_optimized (scene, tile[i], NULL, NULL, light);
glReadPixels (0, 0, tex_width, tex_width, GL_RGBA, GL_UNSIGNED_BYTE, buf);
graphics_fill_texture_by_buf (tile[i]->model->texture, 1024, buf);
}
glEnable (GL_DEPTH_TEST);
glBindFramebuffer (GL_FRAMEBUFFER, 0);
is_once = 0;
glViewport (0, 0, game->screen->w, game->screen->h);
}
В graphics_fill_texture_by_buf вот такой код.
void
graphics_fill_texture_by_buf (struct sprite *sp0, uint32_t width, uint8_t *buf)
{
glBindTexture (GL_TEXTURE_2D, sp0->textures0[0]);
glTexSubImage2D (GL_TEXTURE_2D,
0,
0,
0,
sp0->tex_width,
sp0->tex_height,
GL_RGBA,
GL_UNSIGNED_BYTE,
buf
);
glBindTexture (GL_TEXTURE_2D, 0);
}
Текстура перевёрнута. Значит надо её теперь повернуть. В шейдере меняем на вот так.
#version 450 core
layout (location = 0) in vec3 dpos;
layout (location = 1) in vec2 dtex_coord;
layout (location = 2) in vec3 dnormal;
out vec2 tex_coord;
out vec3 normal;
out vec3 frag_pos;
uniform mat4 model;
uniform mat4 transform;
void main ()
{
vec4 texture_coord = vec4 (dtex_coord.x, dtex_coord.y, 1.0, 1.0);
gl_Position = model * texture_coord;
tex_coord = dtex_coord;
normal = dnormal;
frag_pos = vec3 (transform * vec4 (dpos, 1.0));
}
Почти всё сработало, но почему-то выглядит как-то скошенным, как будто текстура неполностью нарисована.

Я поменял ширину ortho в текстуре, которую рисуем на экран.
struct sprite *
sprite_init_with_texture_for_object_optimized (struct game *g, uint32_t type, uint32_t texture)
{
struct sprite *sp = malloc (sizeof (struct sprite));
memset (sp, 0, sizeof (struct sprite));
mat4x4_diag (sp->model, 1.f);
mat4x4_diag (sp->scale, 1.f);
translate (sp->transform, 0.f, 0.f, 0.f);
ortho (sp->ortho, 0.f, 1.f, 1.f, 0.f, -1.0, 10.f);
sp->shader = NULL;
sp->textures0 = NULL;
sp->g = g;
sprite_set_texture (sp, type, texture);
return sp;
}
И вот результат.

Теперь у нас каждая текстура имеет свой набор света и это работает. Я показал, что текстуры теперь можно запекать. Далее можно оптимизировать и сделать один пол, а не так как у меня. У меня каждый тайл пола рисуется отдельно, это я буду менять. Главное, что теперь такое можно запустить компьютере без игровой видеокарты и запечь текстуру один раз. А если добавить ещё один источник света, то заного просто перезапечь текстуры и всё. А потом рисовать запечёнными текстурами.