Процедурное поколение звезд с скайбоксом

Я пытаюсь сгенерировать звездный фон в OpenGL.

Подход, который я использую, заключается в создании скайбокса с текстурой кубической карты. Каждая сторона текстуры кубической карты по существу состоит из черного изображения 2048×2048 со случайно выбранными текселями, установленными на Белое. Вот результат:

Процессуально сгенерированные звезды. Я не уверен, насколько это очевидно из изображения, но при перемещении вокруг очень четкой формы прямоугольника можно разглядеть, поскольку звезды, расположенные близко к краю рамки, кажутся меньше и ближе друг к другу. Как я могу предотвратить это? Нужно ли мне отказаться от подхода «скайбокс» и использовать вместо этого что-то вроде небесной сферы?

РЕДАКТИРОВАТЬ: вот как я отображаю кубическую карту на небо.

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

2

Решение

  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) и использовать это как окончательное положение звезды.

    сфера 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);

Похоже, что он работает так, как и должно быть здесь (предварительный просмотр смешанной карты сферы / куба):

предварительный просмотр

Хотя, похоже, что есть дыры, но их нет (это может быть ошибка смешения), если я отключу рендеринг карты сфер, то в отображении не будет видимых дыр или искажений.

только кубическая карта

Сфера триангуляции 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] Проблема смешивания решена

Это было вызвано Z-боем и случайным изменением знака для некоторых координат во время проекции из-за забытого fabs здесь исправлен код:

    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):

введите описание изображения здесь

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]