通过Skybox生成星星的程序 [英] Procedural generation of stars with skybox

查看:126
本文介绍了通过Skybox生成星星的程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图以程序方式在OpenGL中生成一个充满星星的背景.

I am attempting to procedurally generate a star-filled background in OpenGL.

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

The approach I am taking is to create a skybox with a cubemap texture. Each side of the cubemap texture essentially consists of a 2048x2048 black image with randomly selected texels set to White. Here is the result:

我不确定它在图像中有多明显,但是当移动非常明显的盒子形状时,可以将星星做成接近盒子边缘的形状显得更小,更靠近.我该如何预防呢?我是否需要放弃Skybox方法,而改用Skysphere之类的方法?

I'm not sure how obvious it is from the image, but when moving around a very distinct box shape can be made out as stars close to the edge of the box appear smaller and closer together. How can I prevent this? Do I need to abandon the skybox approach and use something like a skysphere instead?

这是我将立方体贴图映射到天空的方式.

here is how I am mapping the cubemap onto the sky.

// 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;
    }
}

这是用于渲染天空的绘制函数.

here is the draw function used to render the sky.

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. 以一定立方体积均匀地产生恒星

x=2.0*Random()-1.0; // <-1,+1>
y=2.0*Random()-1.0; // <-1,+1>
z=2.0*Random()-1.0; // <-1,+1>

  • 将它们投影在单位球上

    因此,只需计算向量(x,y,z)的长度,然后将其除以坐标即可.

    So just compute the length of vector (x,y,z) and divide the coordinates by it.

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

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

    Each side of cube is defined by the plane so find intersection of ray casted from (0,0,0) through Cartesian star position and the planes. Take the intersection with shortest distance to (0,0,0) and use that as final star position.

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

    看起来像它在这里预览的效果一样(混合球体/立方体贴图):

    Looks like it works as it should here preview (of the blended sphere/cube map):

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

    Although it looks like there are holes but there are none (it is may be some blend error) if I disable the sphere map render then there are no visible holes or distortions in the mapping.

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

    The sphere triangulation mesh obj used to test this is taken from here:

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

    我修复了代码...但是问题仍然存在.无关紧要的是,此映射是否正常工作,更新后的代码结果如下:

    I repaired the code ... but the problem persists anyway. does not matter this mapping is working as should here the updated code result:

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

    So just adapt the code to your texture generator ...

    [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个星标):

    Here Blend of booth (1000 stars):

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

    And Here only the cube-map (10000 stars)

    [Edit3]混合问题已解决

    这是由于Z格斗和投影过程中某些坐标的符号偶尔更改而引起的,原因是这里忘记了fabs此处的固定代码:

    It was caused by Z-fighting and occasional changing of sign for some coordinates during the projection due to forgotten fabs here fixed code:

        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)观看时,球体和立方体星完全重叠(白色):

    And here the Blend result finally the colors are as should be so the sphere and cube stars overlaps perfectly (white) while viewing from (0,0,0):

    这篇关于通过Skybox生成星星的程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆