【问题标题】:Procedural generation of stars with skybox带有天空盒的恒星程序生成
【发布时间】:2016-06-27 19:11:24
【问题描述】:

我正在尝试在 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);

这里是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);
}

【问题讨论】:

  • 你为什么贴一个黑色的矩形?如此浪费内存/存储空间。
  • @ThomasMatthews ?图片中应该有星星。
  • 不清楚您是如何将立方体贴图映射到天空的。请提供minimal reproducible example
  • 在立方体面上生成星星可能看起来是均匀分布的,但是一旦添加了透视变换,立方体的形状就会很明显,如您所见。你需要做一些更复杂的事情并将它们映射到你的立方体上。
  • 很难让立方体上的纹理看起来均匀。一般来说,你可以做三件事之一。 1) 使立方体更矩形(在 Y-up 世界中的 XZ 平面中),这样天空更近,角落不那么明显(假设场景中有物体)。 2) 使用球坐标在随机方向上投射光线,并通过恒星的反距离来缩放恒星的大小。 3) 我建议使用球形贴图而不是立方体贴图。

标签: c++ opengl procedural-generation


【解决方案1】:
  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),并将其作为最终的星位。

实现可能类似于以下 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);

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

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

用于测试的球体三角剖分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);

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

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

[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);

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

【讨论】:

猜你喜欢
  • 2017-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-06
  • 2012-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多