Начнём с того, что я обзавелся идеей оптимизировать процесс рисования для компьютеров без игровой видеокарты. Для встроенной карты не так важно сколько видеопамяти занимает приложение, сколько сама мощность этой карты. Насчет памяти для встроенной карты я кстати ещё не в курсе, но помню, что она может выделять её из 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; }
И вот результат.

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