用天空盒程序生成星星

Ale*_*wer 4 c++ opengl procedural-generation

我正在尝试在 OpenGL 中程序生成一个充满星星的背景。

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

程序生成的星星。我不确定从图像上看它有多明显,但是当在一个非常明显的盒子形状周围移动时,可以看出靠近盒子边缘的星星看起来更小而且更靠近。我怎样才能防止这种情况?我是否需要放弃天空盒方法并使用类似天空球的东西?

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

// 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);
Run Code Online (Sandbox Code Playgroud)

下面是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

Spe*_*tre 6

  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>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将它们投影到单位球体上

    所以只需计算向量的长度(x,y,z)并将坐标除以它。

  3. 将结果投影到立方体贴图上

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

    球体 2 立方体

实现可能是这样的 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);
Run Code Online (Sandbox Code Playgroud)

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

预览

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

仅立方体贴图

mesh obj用于测试的球体三角剖分取自此处:

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

我修复了代码......但问题仍然存在。这个映射是否正常工作并不重要,更新后的代码结果应该在这里:

预览

所以只需将代码调整为您的纹理生成器......

[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);
Run Code Online (Sandbox Code Playgroud)

这里混合展位(1000 颗星):

两个都

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

仅立方体

[Edit3] 混合问题解决了

这是由于忘记了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);
Run Code Online (Sandbox Code Playgroud)

在这里混合结果最终颜色应该是这样球体和立方体星星完美重叠(白色),同时从(0,0,0)以下位置查看:

在此处输入图片说明