全景图剪切程序Sphere2Cube(一张全景生成六张立方体天空盒图Cubmap)

1.介绍

  将一张全景图片贴在立方体天空盒上,需要uv进行映射,分别对应六个面的纹理; 

2.实现

 查阅了网上,发现大概有如下几种实现方式;

  • 基于opencv进行分割,  保存出6张图片
  • 基于opengl,通过改变LookAt摄像机方向,获取6张texture通过FBO,再通过glreadpixel读回数据进行保存;

由于直接需要将数据做cubmap,所以我实现的方式是:将全景图片分别映射到6块内存,每块内存通过

glTexImage2D()直接送到GPU。

这样使用Cubmap直接渲染,无需再保存以及读取6张图片的过程;

3.代码


void SphereCubeTexture::create_spherecube_texture()
{

    std::thread prc[6];
    int index = _resource_name.rfind(".");
    if (index == std::string::npos)
    {
        LOG_ERR("invalid cube texture file name");
        return;
    }
    std::string prefix = _resource_name.substr(0, index);
    std::string extend = _resource_name.substr(index);

    _texture_data = new TextureData(GL_TEXTURE_CUBE_MAP, GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE,
                                    GL_CLAMP_TO_EDGE);
    if (_miplevel)
    {
        _texture_data->set_sample(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE,
                                  GL_CLAMP_TO_EDGE);
    }

    int width = 0;
    int height = 0;
    int n = 0;
    unsigned char *image = nullptr;
    std::string pic_name;
    const short face_szie = 6;
    //
    pic_name = prefix + extend;
    image = stbi_load(pic_name.c_str(), &width, &height, &n);

    {
        for (int lx = 0; lx < face_szie; lx++)
        {
            {
                int sphere_height = height, sphere_width = width;

                unsigned char *im = new unsigned char[_tile_size * _tile_size * n];
                for (int tile_y = 0; tile_y < _tile_size; tile_y++)
                {
                    for (int tile_x = 0; tile_x < _tile_size; tile_x++)
                    {
                        float theta, phi;
                        std::tie(theta, phi) = (this->face_func[lx])(*this, tile_y, tile_x);
                        int sp_x = this->phi2width(sphere_width, phi);
                        int sp_y = this->theta2height(sphere_height, theta);

                        for (int k = 0; k < 4; ++k)
                        {
                            im[(_tile_size * tile_y + tile_x) * n + k] = image[(width * sp_y + sp_x) * n + k];
                        }
                    }
                }

                if (im)
                {
                    _memory += _tile_size * _tile_size * n;

                    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + lx, 0,
                                 _texture_info.components, _tile_size, _tile_size, 0,
                                 _texture_info.format, _texture_info.type, im);
                    delete[] im;
                    im = nullptr;
                }
            }
     
        }

         set_width(width);
         set_height(height);
    }
    _available = true;
    stbi_image_free(image);
    image = nullptr;
    return;
}

float SphereCubeTexture::update_phi(float phi, int major_dir, int minor_dir, float major_m, float major_p, float minor_m, float minor_p) const
{
    if (major_dir < _half_size)
    {
        return phi + major_m;
    }
    else if (major_dir > _half_size)
    {
        return phi + major_p;
    }
    else if (minor_dir < _half_size)
    {
        return minor_m;
    }
    else
    {
        return minor_p;
    }
}

vec2f SphereCubeTexture::func_up(int tile_y, int tile_x)
{
    float theta = _cache_zp[tile_y][tile_x];
    float phi = _cache_phi[tile_x][tile_y];
    phi = update_phi(phi, tile_y, tile_x, pi, 0, -half_pi, half_pi);
    return vec2f(theta, phi);
}

vec2f SphereCubeTexture::func_front(int tile_y, int tile_x)
{
    float theta = _cache_xypm[_tile_size - tile_y - 1][_tile_size - tile_x - 1];
    float phi = _cache_phi[tile_x][_tile_size - 1];
    phi = update_phi(phi, tile_y, tile_x, 0, 0, -half_pi, half_pi);
    return vec2f(theta, phi);
}

vec2f SphereCubeTexture::func_right(int tile_y, int tile_x)
{
    float theta, phi;
    std::tie(theta, phi) = func_front(tile_y, tile_x);
    phi += half_pi;
    if (phi > doub_pi)
    {
        phi -= doub_pi;
    }
    return vec2f(theta, phi);
}

vec2f SphereCubeTexture::func_back(int tile_y, int tile_x)
{
    float theta, phi;
    std::tie(theta, phi) = func_front(tile_y, tile_x);
    phi += 2 * half_pi;
    if (phi > doub_pi)
    {
        phi -= doub_pi;
    }
    return vec2f(theta, phi);
}

vec2f SphereCubeTexture::func_left(int tile_y, int tile_x)
{
    float theta, phi;
    std::tie(theta, phi) = func_front(tile_y, tile_x);
    phi += 3 * half_pi;
    if (phi > doub_pi)
    {
        phi -= doub_pi;
    }
    return vec2f(theta, phi);
}

vec2f SphereCubeTexture::func_down(int tile_y, int tile_x)
{
    float theta = _cache_zm[tile_y][tile_x];
    float phi = _cache_phi[tile_x][_tile_size - tile_y - 1];
    phi = update_phi(phi, tile_y, tile_x, 0, pi, -half_pi, half_pi);
    return vec2f(theta, phi);
}

float SphereCubeTexture::phi2width(int width, float phi) const
{
    float x = 0.5 * width * (phi * inv_pi + 1);
    if (x < 1)
    {
        return x + width;
    }
    else if (x > width)
    {
        return x - width;
    }
    else
    {
        return x;
    }
}

float SphereCubeTexture::theta2height(int height, float theta) const
{
    return height * theta * inv_pi;
}

4. 改进

   大量的数据在cpu进行运算,运算量比较大,可以考虑将整个过程放进GPU进行运算;Like this:

  

const float isqrt2 = 0.70710676908493042;

vec3 cubify(const in vec3 s)
{
float xx2 = s.x * s.x * 2.0;
float yy2 = s.y * s.y * 2.0;

vec2 v = vec2(xx2 – yy2, yy2 – xx2);

float ii = v.y – 3.0;
ii *= ii;

float isqrt = -sqrt(ii – 12.0 * xx2) + 3.0;

v = sqrt(v + isqrt);
v *= isqrt2;

return sign(s) * vec3(v, 1.0);
}

vec3 sphere2cube(const in vec3 sphere)
{
vec3 f = abs(sphere);

bool a = f.y >= f.x && f.y >= f.z;
bool b = f.x >= f.z;

return a ? cubify(sphere.xzy).xzy : b ? cubify(sphere.yzx).zxy : cubify(sphere);
}

5.参考

6. 可执行工程下载

   稍后上传

猜你喜欢

转载自blog.csdn.net/jaccen/article/details/81222203