Program Listing for File Noise.cpp

Return to documentation for file (Src/GraphicsEngineOpenGL/scene/atmospheric_effects/clouds/Noise.cpp)

#include "Noise.hpp"

#include <sstream>

Noise::Noise()
  :

    texture_dim_1(128), texture_dim_2(32)

{
    create_shader_programs();

    // we need 3d-voxel grids with different sizes for
    // creating different worley frequencies
    for (int i = 0; i < NUM_CELL_POSITIONS; i++) {
        num_cells_per_axis[i] = static_cast<GLuint>(pow(2, i + 1));
        generate_cells(num_cells_per_axis[i], i);
    }

    generate_textures();
}

void Noise::create_shader_programs()
{
    texture_1_shader_program = std::make_shared<ComputeShaderProgram>();
    texture_2_shader_program = std::make_shared<ComputeShaderProgram>();

    texture_1_shader_program->create_computer_shader_program_from_file("clouds/noise_texture_128_res.comp");
    texture_2_shader_program->create_computer_shader_program_from_file("clouds/noise_texture_32_res.comp");
}

void Noise::generate_textures()
{
    generate_num_cells_textures();
    generate_res128_noise_texture();
    generate_res32_noise_texture();
}

void Noise::generate_num_cells_textures()
{
    glGenTextures(NUM_CELL_POSITIONS, cell_ids);

    for (int i = 0; i < NUM_CELL_POSITIONS; i++) {
        glBindTexture(GL_TEXTURE_3D, cell_ids[i]);

        glTexImage3D(GL_TEXTURE_3D,
          0,
          GL_RGBA32F,
          num_cells_per_axis[i],
          num_cells_per_axis[i],
          num_cells_per_axis[i],
          0,
          GL_RGBA,
          GL_FLOAT,
          cell_data[i].data());

        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

        glBindTexture(GL_TEXTURE_3D, 0);
    }
}

void Noise::generate_res128_noise_texture()
{
    glGenTextures(1, &texture_1_id);

    glActiveTexture(GL_TEXTURE0 + NOISE_128D_TEXTURES_SLOT);
    glBindTexture(GL_TEXTURE_3D, texture_1_id);

    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA32F, texture_dim_1, texture_dim_1, texture_dim_1, 0, GL_RGBA, GL_FLOAT, NULL);

    glBindImageTexture(NOISE_128D_IMAGE_SLOT, texture_1_id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);

    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_3D, 0);
}

void Noise::generate_res32_noise_texture()
{
    glGenTextures(1, &texture_2_id);

    glActiveTexture(GL_TEXTURE0 + NOISE_32D_TEXTURES_SLOT);
    glBindTexture(GL_TEXTURE_3D, texture_2_id);

    glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA32F, texture_dim_2, texture_dim_2, texture_dim_2, 0, GL_RGBA, GL_FLOAT, NULL);

    glBindImageTexture(NOISE_32D_IMAGE_SLOT, texture_2_id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);

    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_3D, 0);
}

void Noise::print_comp_shader_capabilities()
{
    int work_grp_cnt[3];

    glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &work_grp_cnt[0]);
    glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &work_grp_cnt[1]);
    glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &work_grp_cnt[2]);

    printf("max global (total) work group counts x:%i y:%i z:%i\n", work_grp_cnt[0], work_grp_cnt[1], work_grp_cnt[2]);

    int work_grp_size[3];

    glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &work_grp_size[0]);
    glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &work_grp_size[1]);
    glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &work_grp_size[2]);

    printf("max local (in one shader) work group sizes x:%i y:%i z:%i\n",
      work_grp_size[0],
      work_grp_size[1],
      work_grp_size[2]);
}

void Noise::update()
{
    delete_textures();

    texture_1_shader_program->reload();
    texture_2_shader_program->reload();

    for (int i = 0; i < NUM_CELL_POSITIONS; i++) { generate_cells(num_cells_per_axis[i], i); }

    generate_textures();

    create_res128_noise();
    create_res32_noise();
}

void Noise::set_num_cells(GLuint num_cells_per_axis, GLuint index)
{
    this->num_cells_per_axis[index] = num_cells_per_axis;
}
void Noise::generate_cells(GLuint num_cells_per_axis, GLuint cell_index)
{
    cell_data[cell_index].reserve(num_cells_per_axis * num_cells_per_axis * num_cells_per_axis * 4);

    // guess which birthday this is ;)
    std::mt19937_64 gen64(25121995);
    std::uniform_real_distribution<float> dis(0, 1);

    // depth
    for (int i = 0; i < static_cast<int>(num_cells_per_axis); i++) {
        // height
        for (int k = 0; k < static_cast<int>(num_cells_per_axis); k++) {
            // width
            for (int m = 0; m < static_cast<int>(num_cells_per_axis); m++) {
                // from:
                // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage3D.xhtml
                // "The first element corresponds to the lower left corner of the
                // texture image. Subsequent elements progress left-to-right through the
                // remaining texels in the lowest row of the texture image, and then in
                // successively higher rows of the texture image. The final element
                // corresponds to the upper right corner of the texture image."

                const GLfloat random_offset[3] = { dis(gen64), dis(gen64), dis(gen64) };

                GLfloat position[3] = { (m + random_offset[0]), (k + random_offset[1]), (i + random_offset[2]) };

                cell_data[cell_index].push_back(position[0]);
                cell_data[cell_index].push_back(position[1]);
                cell_data[cell_index].push_back(position[2]);
                cell_data[cell_index].push_back(1.0f);

                // i leave this more c-style approach for my further me
                // to clearify things :)
                // GLuint index = (i + num_cells_per_axis * (k + m *
                // num_cells_per_axis)) * 4;
                /*GLfloat position[3] = {   (i + random_offset[0]),
                                                                                        (k + random_offset[1]),
                                                                                        (m + random_offset[2])};*/

                /*cell_data[cell_index][index]      = position[0];
                                        cell_data[cell_index][index + 1]    =
                   position[1]; cell_data[cell_index][index + 2]    = position[2];
                                        cell_data[cell_index][index + 3]
                   = 1.0f;*/
            }
        }
    }
}

void Noise::create_res128_noise()
{
    texture_1_shader_program->use_shader_program();

    texture_1_shader_program->setUniformInt(NOISE_128D_IMAGE_SLOT, "noise");

    std::stringstream ss;

    for (uint32_t i = 0; i < NUM_CELL_POSITIONS; i++) {
        ss << "cell_positions[" << i << "]";
        texture_1_shader_program->setUniformInt(NOISE_CELL_POSITIONS_SLOT + i, ss.str());
        ss.clear();
        ss.str(std::string());

        ss << "num_cells[" << i << "]";
        texture_1_shader_program->setUniformInt(num_cells_per_axis[i], ss.str());
        ss.clear();
        ss.str(std::string());

        glActiveTexture(GL_TEXTURE0 + NOISE_CELL_POSITIONS_SLOT + i);
        glBindTexture(GL_TEXTURE_3D, cell_ids[i]);
    }

    glDispatchCompute((GLuint)texture_dim_1, (GLuint)texture_dim_1, (GLuint)texture_dim_1);

    // make sure writing to image has finished before read
    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
    // glMemoryBarrier(GL_ALL_BARRIER_BITS);

    glBindTexture(GL_TEXTURE_3D, 0);
}

void Noise::create_res32_noise()
{
    texture_2_shader_program->use_shader_program();

    texture_2_shader_program->setUniformInt(NOISE_32D_IMAGE_SLOT, "noise");

    std::stringstream ss;

    for (uint32_t i = 0; i < NUM_CELL_POSITIONS; i++) {
        ss << "cell_positions[" << i << "]";
        texture_2_shader_program->setUniformInt(NOISE_CELL_POSITIONS_SLOT + i, ss.str());
        ss.clear();
        ss.str(std::string());

        ss << "num_cells[" << i << "]";
        texture_2_shader_program->setUniformInt(num_cells_per_axis[i], ss.str());
        ss.clear();
        ss.str(std::string());

        glActiveTexture(GL_TEXTURE0 + NOISE_CELL_POSITIONS_SLOT + i);
        glBindTexture(GL_TEXTURE_3D, cell_ids[i]);
    }

    glDispatchCompute((GLuint)texture_dim_2, (GLuint)texture_dim_2, (GLuint)texture_dim_2);

    // make sure writing to image has finished before read
    glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
    // glMemoryBarrier(GL_ALL_BARRIER_BITS);
    glBindTexture(GL_TEXTURE_3D, 0);
}

void Noise::read_res128_noise()
{
    GLuint texture_index = GL_TEXTURE0 + NOISE_128D_TEXTURES_SLOT;
    glActiveTexture((GLenum)texture_index);
    glBindTexture(GL_TEXTURE_3D, texture_1_id);
}

void Noise::read_res32_noise()
{
    GLuint texture_index = GL_TEXTURE0 + NOISE_32D_TEXTURES_SLOT;
    glActiveTexture((GLenum)texture_index);
    glBindTexture(GL_TEXTURE_3D, texture_2_id);
}

void Noise::delete_textures()
{
    glDeleteTextures(1, &texture_1_id);
    glDeleteTextures(1, &texture_2_id);

    glDeleteTextures(NUM_CELL_POSITIONS, cell_ids);
}

Noise::~Noise() { delete_textures(); }