Подтвердить что ты не робот

Рисование сферы в OpenGL без использования gluSphere()?

Существуют ли какие-либо учебные пособия, которые объясняют, как я могу нарисовать сферу в OpenGL без необходимости использования gluSphere()?

Многие учебники по 3D для OpenGL просто на кубах. Я искал, но большинство решений для рисования сферы заключается в использовании gluSphere(). Есть также сайт, на котором есть код для рисования сферы на этом сайте, но он не объясняет математику рисования сферы. У меня есть и другие версии того, как рисовать сферу в многоугольнике вместо четырехугольников в этой ссылке. Но опять же, я не понимаю, как сферы нарисованы с помощью кода. Я хочу иметь возможность визуализировать, чтобы я мог изменить сферу, если мне нужно.

4b9b3361

Ответ 1

Один из способов сделать это - начать с платонического твердого тела с треугольными сторонами - например, octahedron. Затем возьмите каждый треугольник и рекурсивно разложите его на более мелкие треугольники, например:

recursively drawn triangles

Как только у вас будет достаточное количество очков, вы нормализуете их векторы, чтобы они находились на постоянном расстоянии от центра тела. Это заставляет стороны выпячиваться в форму, напоминающую сферу, с повышением гладкости при увеличении количества очков.

Нормализация здесь означает перемещение точки так, чтобы ее угол относительно другой точки был одинаковым, но расстояние между ними различно. Вот двухмерный пример.

enter image description here

A и B разделены на 6 единиц. Но предположим, что мы хотим найти точку на линии AB, что 12 единиц от A.

enter image description here

Можно сказать, что C - нормализованная форма B относительно A с расстоянием 12. Мы можем получить C с таким кодом:

#returns a point collinear to A and B, a given distance away from A. 
function normalize(a, b, length):
    #get the distance between a and b along the x and y axes
    dx = b.x - a.x
    dy = b.y - a.y
    #right now, sqrt(dx^2 + dy^2) = distance(a,b).
    #we want to modify them so that sqrt(dx^2 + dy^2) = the given length.
    dx = dx * length / distance(a,b)
    dy = dy * length / distance(a,b)
    point c =  new point
    c.x = a.x + dx
    c.y = a.y + dy
    return c

Если мы выполняем этот процесс нормализации на множестве точек, все относительно одной и той же точки A и с тем же расстоянием R, то нормированные точки будут лежать на дуге окружности с центром A и радиусом R.

bulging line segment

Здесь черные точки начинаются на линии и "выпучиваются" в дугу.

Этот процесс может быть расширен в три измерения, и в этом случае вы получаете сферу, а не круг. Просто добавьте компонент dz в функцию нормализации.

normalized polygons

level 1 bulging octahedronlevel 3 bulging octahedron

Если вы посмотрите на сферу в Epcot, вы можете увидеть эту технику на работе. это додекаэдр с выпуклыми лицами, чтобы он выглядел круглым.

Ответ 2

Далее я объясню популярный способ создания сферы с использованием широты и долготы (другой путь, icospheres, был уже объяснен в самом популярном ответе на момент написания этой статьи.)

Сфера может быть выражена следующим параметрическим уравнением:

F (u, v) = [cos (u) * sin (v) * r, cos (v) * r, sin (u) * sin (v) * r]

Где:

  • r - радиус,
  • u - долгота, от 0 до 2 & pi;; и
  • v - широта, от 0 до & pi;.

Создание сферы затем включает в себя оценку параметрической функции с фиксированными интервалами.

Например, для генерации 16 строк долготы вдоль оси u будет 17 линий сетки, причем шаг & pi;/8 (2 & pi;/16) (17-я строка обертывается вокруг).

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

В псевдокоде ниже, UResolution - это число точек сетки вдоль оси U (здесь, линии долготы), а VResolution - количество точек сетки вдоль оси V (здесь, линии широты)

var startU=0
var startV=0
var endU=PI*2
var endV=PI
var stepU=(endU-startU)/UResolution // step size between U-points on the grid
var stepV=(endV-startV)/VResolution // step size between V-points on the grid
for(var i=0;i<UResolution;i++){ // U-points
 for(var j=0;j<VResolution;j++){ // V-points
 var u=i*stepU+startU
 var v=j*stepV+startV
 var un=(i+1==UResolution) ? EndU : (i+1)*stepU+startU
 var vn=(j+1==VResolution) ? EndV : (j+1)*stepV+startV
 // Find the four points of the grid
 // square by evaluating the parametric
 // surface function
 var p0=F(u, v)
 var p1=F(u, vn)
 var p2=F(un, v)
 var p3=F(un, vn)
 // NOTE: For spheres, the normal is just the normalized
 // version of each vertex point; this generally won't be the case for
 // other parametric surfaces.
 // Output the first triangle of this grid square
 triangle(p0, p2, p1)
 // Output the other triangle of this grid square
 triangle(p3, p1, p2)
 }
}

Ответ 3

Код в образце объясняется быстро. Вы должны взглянуть на функцию void drawSphere(double r, int lats, int longs):

void drawSphere(double r, int lats, int longs) {
    int i, j;
    for(i = 0; i <= lats; i++) {
        double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats);
        double z0  = sin(lat0);
        double zr0 =  cos(lat0);

        double lat1 = M_PI * (-0.5 + (double) i / lats);
        double z1 = sin(lat1);
        double zr1 = cos(lat1);

        glBegin(GL_QUAD_STRIP);
        for(j = 0; j <= longs; j++) {
            double lng = 2 * M_PI * (double) (j - 1) / longs;
            double x = cos(lng);
            double y = sin(lng);

            glNormal3f(x * zr0, y * zr0, z0);
            glVertex3f(r * x * zr0, r * y * zr0, r * z0);
            glNormal3f(x * zr1, y * zr1, z1);
            glVertex3f(r * x * zr1, r * y * zr1, r * z1);
        }
        glEnd();
    }
}

Параметры lat определяет, сколько горизонтальных строк, которые вы хотите иметь в своей сфере и lon том, сколько вертикальных линии. r - радиус вашей сферы.

В настоящее время существует двойная итерация над lat/lon и координаты вершин вычисляется, используя простую тригонометрию.

Рассчитанные вершины теперь отправляются в ваш графический процессор с помощью glVertex...() как GL_QUAD_STRIP, что означает, что вы отправляете каждые две вершины, которые образуют квад, с двумя ранее отправленными.

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

Ответ 4

Если вы хотите быть хитрым, как лиса, вы можете сделать половину кода от GLU. Проверьте исходный код MesaGL (http://cgit.freedesktop.org/mesa/mesa/).

Ответ 6

В моем примере, как использовать "треугольную полосу" для рисования "полярной" сферы, она состоит в рисовании точек парами:

const float PI = 3.141592f;
GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles        
GLfloat radius = 60.0f;
int gradation = 20;

for (alpha = 0.0; alpha < GL_PI; alpha += PI/gradation)
{        
    glBegin(GL_TRIANGLE_STRIP);
    for (beta = 0.0; beta < 2.01*GL_PI; beta += PI/gradation)            
    {            
        x = radius*cos(beta)*sin(alpha);
        y = radius*sin(beta)*sin(alpha);
        z = radius*cos(alpha);
        glVertex3f(x, y, z);
        x = radius*cos(beta)*sin(alpha + PI/gradation);
        y = radius*sin(beta)*sin(alpha + PI/gradation);
        z = radius*cos(alpha + PI/gradation);            
        glVertex3f(x, y, z);            
    }        
    glEnd();
}

Введенная первая точка (glVertex3f) является следующим параметрическим уравнением, а вторая сдвинута на один шаг альфа-угла (из следующей параллели).

Ответ 7

Хотя принятый ответ решает вопрос, в конце есть небольшое заблуждение. Додекаэдры являются (или могут быть) регулярным многогранником, где все грани имеют одинаковую площадь. Это похоже на Epcot (который, кстати, вообще не является додекаэдром). Поскольку решение, предложенное @Kevin, не дает этой характеристики, я думал, что могу добавить подход, который делает.

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

Обычные икосаэдры имеют 20 граней (12 вершин) и могут быть легко построены из 3 золотых прямоугольников; это просто вопрос о том, чтобы это было отправной точкой вместо октаэдра. Вы можете найти пример здесь.

Я знаю, что это немного не по теме, но я считаю, что это может помочь, если кто-то попадет сюда в поисках этого конкретного случая.

Ответ 8

Один из способов - создать квадрат, который обращен к камере, и написать вершинный и фрагментарный шейдер, который отображает нечто похожее на сферу. Вы можете использовать уравнения для круга/сферы, которые вы можете найти в Интернете.

Хорошо, что силуэт сферы выглядит одинаково под любым углом. Однако, если сфера не находится в центре перспективного вида, то она, по-видимому, скорее напоминает эллипс. Вы могли бы выработать уравнения для этого и поместить их в затенение фрагмента. Затем светлая затенение должна изменяться по мере продвижения игрока, если у вас действительно есть игрок, движущийся в трехмерном пространстве вокруг сферы.

Может кто-нибудь прокомментировать, если они пробовали это или если было бы слишком дорого, чтобы быть практичным?

Ответ 9

struct v3
{
    double x,y,z;
    v3(double _x=0, double _y=0, double _z=0){x=_x;y=_y;z=_z;  }
    v3   operator +  ( v3 v)     {return { x+v.x, y+v.y, z+v.z };}
    v3   operator *  ( double k) {return { x*k, y*k, z*k };}
    v3   operator /  ( double k) {return { x/k, y/k, z/k };}
    v3 normalize(){
       double L=sqrt( x*x+y*y+z*z);
       return { x/L , y/L , z/L };}
};

void draw_spheree(double r,int adim)
{

    //              z
    //              |
    //               __
    //             /|          
    //              |           
    //              |           
    //              |    *      \
    //              | _ _| _ _ _ |    _y
    //             / \c  |n     /                    a4 --- a3
    //            /   \o |i                           |     |
    //           /     \s|s      z=sin(v)            a1 --- a2
    //         |/__              y=cos(v) *sin(u)
    //                           x=cos(v) *cos(u) 
    //       /
    //      x
    //

    //glEnable(GL_LIGHTING);
    //glEnable(GL_LIGHT0);
    //glEnable(GL_TEXTURE_2D); 

    double pi=3.141592;
    double d=pi/adim;

    for(double u=-pi  ; u<pi  ; u+=d)   //horizonal  xy düzlemi     Longitude -180  -180
    for(double v=-pi/2; v<pi/2; v+=d)   //vertical   z aks          Latitude  -90     90
    {
        v3  a1 = {  cos(v)*cos(u)       ,cos(v)*sin(u)      ,sin(v)     },
            a2 = {  cos(v)*cos(u+d)     ,cos(v)*sin(u+d)    ,sin(v)     },
            a3 = {  cos(v+d)*cos(u+d)   ,cos(v+d)*sin(u+d)  ,sin(v+d)   },
            a4 = {  cos(v+d)*cos(u)     ,cos(v+d)*sin(u)    ,sin(v+d)   };

        v3 normal=(a1+a2+a3+a4)/4.0;   //normal vector

        a1=a1*r;
        a2=a2*r;
        a3=a3*r;
        a4=a4*r;

        double tu=(u+pi)  / (2*pi);  //0 to 1  horizonal
        double tv=(v+pi/2)/ pi;      //0 to 1  vertical

        double td=1.0/2./adim;

        glNormal3dv((double *)&normal);

        glBegin(GL_POLYGON);
            glTexCoord2d(tu    ,tv      ); glVertex3dv((double *) &a1);
            glTexCoord2d(tu+td ,tv      ); glVertex3dv((double *) &a2);
            glTexCoord2d(tu+td ,tv+2*td ); glVertex3dv((double *) &a3);
            glTexCoord2d(tu    ,tv+2*td ); glVertex3dv((double *) &a4);
        glEnd();                
    } 
 }