用skybox程序生成星星

时间:2016-06-27 19:11:24

标签: c++ opengl procedural-generation

我试图在程序上在OpenGL中生成星形背景。

我正在采取的方法是创建一个带有立方体贴图纹理的天空盒。立方体贴图纹理的每一面基本上由2048x2048黑色图像组成,其中随机选择的纹理像素设置为白色。结果如下:

Procedurally generated stars.我不确定图像有多么明显,但是当移动时,可以看到一个非常独特的盒子形状,因为靠近盒子边缘的星星看起来更小,更靠近。我怎么能阻止这个?我是否需要放弃天空盒方法并使用像skysphere这样的东西?

编辑:这是我如何将立方体贴图映射到天空。

// Create and bind texture.
glGenTextures(1, &texture_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture_);

for (unsigned int i = 0; i < 6; ++i) {
    std::vector<std::uint8_t> image = generateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, TEXTURE_WIDTH, TEXTURE_HEIGHT,
                 0, GL_RGB, GL_UNSIGNED_BYTE, image.data());
}

// Set texture parameters.
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

以下是generateTexture函数的定义:

std::vector<std::uint8_t> Stars::generateTexture(GLsizei width, GLsizei height) {
    std::vector<std::uint8_t> image(static_cast<std::size_t>(3 * width * height));

    add_stars(image, NUM_STARS);

    return image;
}

void Stars::add_stars(std::vector<std::uint8_t>& image, unsigned int nStars) {
    std::default_random_engine eng;
    std::uniform_int_distribution<std::size_t> dist(0, image.size() / 3 - 1);

    while (nStars--) {
        std::size_t index = 3 * dist(eng);

        image[index++] = 255;
        image[index++] = 255;
        image[index++] = 255;
    }
}

EDIT2:这是用于渲染天空的绘制函数。

void Stars::draw(const Camera& camera) const {
    // Skybox will be rendered last. In order to ensure that the stars are rendered at the back of
    // the scene, the depth buffer is filled with values of 1.0 for the skybox -- this is done in
    // the vertex shader. We need to make sure that the skybox passes the depth te3t with values
    // less that or equal to the depth buffer.
    glDepthFunc(GL_LEQUAL);

    program_.enable();

    // Calculate view-projection matrix and set the corresponding uniform. The view matrix must be
    // stripped of translation components so that the skybox follows the camera.
    glm::mat4 view       = glm::mat4(glm::mat3(camera.viewMatrix()));
    glm::mat4 projection = camera.projectionMatrix();

    glm::mat4 VP = projection * view;
    glUniformMatrix4fv(program_.uniformLocation("VP"), 1, GL_FALSE, glm::value_ptr(VP));

    // Bind buffer objects and texture to current context and draw.
    glBindVertexArray(vao_);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texture_);

    glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(INDICES.size()), GL_UNSIGNED_INT,
                   reinterpret_cast<GLvoid *>(0));

    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
    program_.disable();

    glDepthFunc(GL_LESS);
}

1 个答案:

答案 0 :(得分:4)

  1. 在一些立方体积中均匀生成星星

    x=2.0*Random()-1.0; // <-1,+1>
    y=2.0*Random()-1.0; // <-1,+1>
    z=2.0*Random()-1.0; // <-1,+1>
    
  2. 在单位范围内投影

    所以只需计算向量(x,y,z)的长度并用它来划分坐标。

  3. 将结果投影到多维数据集地图

    立方体的每一边都由平面定义,因此找到从(0,0,0)通过笛卡尔星位置和平面投射的光线的交点。取最短距离到(0,0,0)的交点,并将其用作最终的星位。

    sphere 2 cube

  4. 实现可能类似于OpenGL和C ++代码:

        glClearColor(0.0,0.0,0.0,0.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        int i,n=10000;
        float a,b,x,y,z;
        //RandSeed=8123456789;
        n=obj.pnt.num;  // triangulated sphere point list
    
        glDepthFunc(GL_LEQUAL);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE,GL_ONE);
    
        glPointSize(2.0);
        glBegin(GL_POINTS);
        for (i=0;i<n;i++)
            {
            // equidistant points instead of random to test this
            x=obj.pnt[i].p[0];
            y=obj.pnt[i].p[1];
            z=obj.pnt[i].p[2];
    /*
            // random star spherical position
            a=2.0*M_PI*Random();
            b=M_PI*(Random()-0.5);
            // spherical 2 cartessian r=1;
            x=cos(a)*cos(b);
            y=sin(a)*cos(b);
            z=       sin(b);
    */
            // redish sphere map
            glColor3f(0.6,0.3,0.0); glVertex3f(x,y,z);
            // cube half size=1 undistort // using similarities like: yy/xx = y/x
                 if ((fabs(x)>=fabs(y))&&(fabs(x)>=fabs(z))){ y/=x; z/=x; if (x>=0) x=1.0; else x=-1.0; }
            else if ((fabs(y)>=fabs(x))&&(fabs(y)>=fabs(z))){ x/=y; z/=y; if (y>=0) y=1.0; else y=-1.0; }
            else if ((fabs(z)>=fabs(x))&&(fabs(z)>=fabs(y))){ x/=z; y/=z; if (z>=0) z=1.0; else z=-1.0; }
            // bluish cube map
            glColor3f(0.0,0.3,0.6); glVertex3f(x,y,z);
            }
        glEnd();
        glPointSize(1.0);
        glDisable(GL_BLEND);
        glFlush();
        SwapBuffers(hdc);
    

    看起来它的工作原理应该在这里预览(混合球体/立方体贴图):

    preview

    虽然看起来有漏洞但没有漏洞(可能是某些混合错误)如果我禁用了球体贴图渲染,那么贴图中就没有可见的漏洞或扭曲。

    cube map only

    用于测试此问题的球体三角测量mesh obj取自此处:

    [Edit1]是的,有一个愚蠢的混合错误

    我修复了代码......但问题仍然存在。无关紧要,这个映射的工作原理应该是更新的代码结果:

    preview

    所以只需将代码调整到纹理生成器......

    [Edit2]随机明星

    glClearColor(0.0,0.0,0.0,0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    int i;
    float x,y,z,d;
    RandSeed=8123456789;
    
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE,GL_ONE);
    
    glPointSize(2.0);
    glBegin(GL_POINTS);
    for (i=0;i<1000;i++)
        {
        // uniform random cartesian stars inside cube
        x=(2.0*Random())-1.0;
        y=(2.0*Random())-1.0;
        z=(2.0*Random())-1.0;
        // project on unit sphere
        d=sqrt((x*x)+(y*y)+(z*z));
        if (d<1e-3) { i--; continue; }
        d=1.0/d;
        x*=d; y*=d; z*=d;
        // redish sphere map
        glColor3f(0.6,0.3,0.0); glVertex3f(x,y,z);
        // cube half size=1 undistort using similarities like: y/x = y'/x'
             if ((fabs(x)>=fabs(y))&&(fabs(x)>=fabs(z))){ y/=x; z/=x; if (x>=0) x=1.0; else x=-1.0; }
        else if ((fabs(y)>=fabs(x))&&(fabs(y)>=fabs(z))){ x/=y; z/=y; if (y>=0) y=1.0; else y=-1.0; }
        else if ((fabs(z)>=fabs(x))&&(fabs(z)>=fabs(y))){ x/=z; y/=z; if (z>=0) z=1.0; else z=-1.0; }
        // bluish cube map
        glColor3f(0.0,0.3,0.6); glVertex3f(x,y,z);
        }
    glEnd();
    glPointSize(1.0);
    glDisable(GL_BLEND);
    glFlush();
    SwapBuffers(hdc);
    

    这里摊位(1000颗星):

    both

    这里只有立方体图(10000颗星)

    cube only

    [Edit3] Blend问题已解决

    由于忘记了fabs这里的固定代码,这是由于Z-fighting和偶尔在投影过程中某些坐标的符号更改造成的:

        glClearColor(0.0,0.0,0.0,0.0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        int i;
        float x,y,z,d;
        RandSeed=8123456789;
    
        glDepthFunc(GL_ALWAYS);
    //  glDepthFunc(GL_LEQUAL);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE,GL_ONE);
    
        glPointSize(2.0);
        glBegin(GL_POINTS);
        for (i=0;i<25000;i++)
            {
            // uniform random cartesian stars inside cube
            x=(2.0*Random())-1.0;
            y=(2.0*Random())-1.0;
            z=(2.0*Random())-1.0;
            // project on unit sphere
            d=sqrt((x*x)+(y*y)+(z*z));
            if (d<1e-3) { i--; continue; }
            d=1.0/d;
            x*=d; y*=d; z*=d;
            // redish sphere map
            glColor3f(0.6,0.3,0.0); glVertex3f(x,y,z);
            // cube half size=1 undistort using similarities like: y/x = y'/x'
                 if ((fabs(x)>=fabs(y))&&(fabs(x)>=fabs(z))){ y/=fabs(x); z/=fabs(x); if (x>=0) x=1.0; else x=-1.0; }
            else if ((fabs(y)>=fabs(x))&&(fabs(y)>=fabs(z))){ x/=fabs(y); z/=fabs(y); if (y>=0) y=1.0; else y=-1.0; }
            else if ((fabs(z)>=fabs(x))&&(fabs(z)>=fabs(y))){ x/=fabs(z); y/=fabs(z); if (z>=0) z=1.0; else z=-1.0; }
            // bluish cube map
            glColor3f(0.0,0.3,0.6); glVertex3f(x,y,z);
            }
        glEnd();
        glPointSize(1.0);
        glDisable(GL_BLEND);
        glFlush();
        SwapBuffers(hdc);
    

    这里的Blend结果最终颜色应该是这样,所以当从(0,0,0)观看时,球体和立方体星星完全重叠(白色):

    enter image description here