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

Я поставил источник света и вот что получилось. Там горят два источника света.

вариант 1
вариант 1

Как видно, я применил 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;
}

И вот результат.

Результат
Результат

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

Комментарии (0)