Создание 3D-сферы в Opengl с использованием Visual C ++ - PullRequest
25 голосов
/ 13 мая 2011

Я не могу создать простую трехмерную сферу, используя библиотечную функцию OpenGL glutSolidSphere () в C ++.

Вот что я пробовал:

#include<GL/glu.h> 
void display() 
{ 
    glClear(GL_COLOR_BUFFER_BIT); 
    glColor3f(1.0,0.0,0.0); 
    glLoadIdentity(); 
    glutSolidSphere( 5.0, 20.0, 20.0); 
    glFlush(); 
} 

void myInit() 
{
    glClearColor(1.0,1.0,1.0,1.0); 
    glColor3f(1.0,0.0,0.0); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    gluOrtho2D(0.0,499.0,0.0,499.0); 
    glMatrixMode(GL_MODELVIEW); 
} 

void main(int argc,char **argv) 
{ 
    qobj = gluNewQuadric(); 
    glutInit(&argc,argv); 
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); 
    glutInitWindowSize(500,500); 
    glutCreateWindow("pendulum");         
    glutDisplayFunc(display); 
    myInit(); 
    glutMainLoop(); 
}

Ответы [ 6 ]

73 голосов
/ 13 мая 2011

В OpenGL вы не создаете объекты, вы просто рисуете их.Как только они нарисованы, OpenGL больше не заботится о том, какую геометрию вы отправили.

glutSolidSphere просто отправляет команды рисования в OpenGL.Однако в этом нет ничего особенного.И поскольку он привязан к GLUT, я бы не стал его использовать.Вместо этого, если вам действительно нужна какая-то сфера в вашем коде, как насчет создания if для себя?

#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>

// your framework of choice here

class SolidSphere
{
protected:
    std::vector<GLfloat> vertices;
    std::vector<GLfloat> normals;
    std::vector<GLfloat> texcoords;
    std::vector<GLushort> indices;

public:
    SolidSphere(float radius, unsigned int rings, unsigned int sectors)
    {
        float const R = 1./(float)(rings-1);
        float const S = 1./(float)(sectors-1);
        int r, s;

        vertices.resize(rings * sectors * 3);
        normals.resize(rings * sectors * 3);
        texcoords.resize(rings * sectors * 2);
        std::vector<GLfloat>::iterator v = vertices.begin();
        std::vector<GLfloat>::iterator n = normals.begin();
        std::vector<GLfloat>::iterator t = texcoords.begin();
        for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
                float const y = sin( -M_PI_2 + M_PI * r * R );
                float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
                float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

                *t++ = s*S;
                *t++ = r*R;

                *v++ = x * radius;
                *v++ = y * radius;
                *v++ = z * radius;

                *n++ = x;
                *n++ = y;
                *n++ = z;
        }

        indices.resize(rings * sectors * 4);
        std::vector<GLushort>::iterator i = indices.begin();
        for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
                *i++ = r * sectors + s;
                *i++ = r * sectors + (s+1);
                *i++ = (r+1) * sectors + (s+1);
                *i++ = (r+1) * sectors + s;
        }
    }

    void draw(GLfloat x, GLfloat y, GLfloat z)
    {
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glTranslatef(x,y,z);

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
        glNormalPointer(GL_FLOAT, 0, &normals[0]);
        glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
        glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
        glPopMatrix();
    }
};

SolidSphere sphere(1, 12, 24);

void display()
{
    int const win_width  = …; // retrieve window dimensions from
    int const win_height = …; // framework of choice here
    float const win_aspect = (float)win_width / (float)win_height;

    glViewport(0, 0, win_width, win_height);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45, win_aspect, 1, 10);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

#ifdef DRAW_WIREFRAME
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
    sphere.draw(0, 0, -5);

    swapBuffers();
}

int main(int argc, char *argv[])
{
    // initialize and register your framework of choice here
    return 0;
}
20 голосов
/ 29 декабря 2012

Не похоже, чтобы кто-то до сих пор обращался к реальной проблеме с вашим исходным кодом, поэтому я подумал, что сделаю это, даже если вопрос довольно старый на данный момент.

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

Сначала была создана ортогональная проекция с использованием вызова

gluOrtho2D(0.0, 499.0, 0.0, 499.0);

, что "эквивалентно вызову glOrtho с ближним = -1 и дальним = 1."Это означает, что просмотр усеченного имеет глубину 2. Таким образом, сфера с радиусом чего-либо больше 1 (диаметр =2) не будет полностью вписываться в область просмотра.

Затем используются вызовы

glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);

, которые загружают матрицу тождественности матрицы вида модели и затем " [r] вводит сферу, центрированную в начале координат координат моделирования указанного радиуса."Значение, сфера отображается в начале координат, (x, y, z) = (0, 0, 0) и с радиусомиз 5.

Теперь проблема состоит из трех частей:

  1. Так как окно имеет размер 500x500 пикселей, а ширина и высота области просмотра составляет почти 500 (499,0),малый радиус сферы (5.0) делает только ее проектируемую площадьчуть более одной пятидесятой (2 * 5/499) размера окна в каждом измерении.Это означает, что видимый размер сферы будет примерно 1/2500-й (на самом деле pi*5^2/499^2, что ближе к 1/3170-й) всего окна, поэтому может быть трудно увидеть .Это при условии, что весь круг нарисован в пределах области окна.Однако это не так, как мы увидим в пункте 2.
  2. Поскольку область обзора имеет левую плоскость при x = 0 и нижнюю плоскость при y = 0, сфера будет визуализирована с геометрическим центром вв самом нижнем левом углу окна, так что будет виден только один квадрант проецируемой сферы!Это означает, что то, что будет видно, будет еще меньше, примерно 1/10 000-й (на самом деле pi*5^2/(4*499^2), что ближе к 1/12 682-й) от размера окна.Это сделает еще труднее увидеть .Тем более, что сфера отображается так близко к краям / углу экрана, где вы можете и не подумать смотреть.
  3. Поскольку глубина поля зрения значительно меньше диаметра сферы (меньше половины), только осколок сферы будет внутри области просмотра, отображая только эту часть.Таким образом, вы получите больше похожий на полый круг на экране, чем на сплошную сферу / круг.Так получилось, что толщина этого осколка может составлять менее 1 пикселя на экране, что означает, что мы можем даже увидеть ничего на экране, даже если часть сферы действительно находится в пределах области просмотра.

Решение заключается в том, чтобы просто изменить угол обзора и радиус сферы.Например,

gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);

отображает следующее изображение.

r = 5.0

Как видите, вокруг "экватора" видна лишь небольшая частьсфера с радиусом 5. (Я изменил проекцию, чтобы заполнить окно сферой.) Другой пример,

gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);

отображает следующее изображение.

r = 1.1

На изображении выше показано больше сферы внутри усеченной области обзора, но сфера все еще на 0,2 глубины больше, чем усеченная область просмотра.Как видите, «ледяные шапки» сферы отсутствуют как на севере, так и на юге.Итак, если мы хотим, чтобы вся сфера вписывалась в усеченный зрачок с глубиной 2, мы должны сделать радиус меньше или равным 1.

gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);

отображает следующее изображение.

r = 1.0

Надеюсь, это кому-то помогло.Береги себя!

8 голосов
/ 12 декабря 2012

Я не понимаю, как генерация индекса datenwolf может быть правильной.Но все же я нахожу его решение довольно ясным.Это то, что я получаю после некоторых размышлений:

inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
    int curRow = r * sectors;
    int nextRow = (r+1) * sectors;

    indices.push_back(curRow + s);
    indices.push_back(nextRow + s);
    indices.push_back(nextRow + (s+1));

    indices.push_back(curRow + s);
    indices.push_back(nextRow + (s+1));
    indices.push_back(curRow + (s+1));
}

void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
             float radius, unsigned int rings, unsigned int sectors)
{
    float const R = 1./(float)(rings-1);
    float const S = 1./(float)(sectors-1);

    for(int r = 0; r < rings; ++r) {
        for(int s = 0; s < sectors; ++s) {
            float const y = sin( -M_PI_2 + M_PI * r * R );
            float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
            float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

            texcoords.push_back(vec2(s*S, r*R));
            vertices.push_back(vec3(x,y,z) * radius);
            push_indices(indices, sectors, r, s);
        }
    }
}
2 голосов
/ 22 января 2016

Мне нравится ответ монеты. Это просто понять и работает с треугольниками. Однако индексы его программы иногда выходят за границы. Поэтому я публикую здесь его код с двумя небольшими исправлениями:

inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
    int curRow = r * sectors;
    int nextRow = (r+1) * sectors;
    int nextS = (s+1) % sectors;

    indices.push_back(curRow + s);
    indices.push_back(nextRow + s);
    indices.push_back(nextRow + nextS);

    indices.push_back(curRow + s);
    indices.push_back(nextRow + nextS);
    indices.push_back(curRow + nextS);
}

void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
                  float radius, unsigned int rings, unsigned int sectors)
{
    float const R = 1./(float)(rings-1);
    float const S = 1./(float)(sectors-1);

    for(int r = 0; r < rings; ++r) {
        for(int s = 0; s < sectors; ++s) {
            float const y = sin( -M_PI_2 + M_PI * r * R );
            float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
            float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

            texcoords.push_back(vec2(s*S, r*R));
            vertices.push_back(vec3(x,y,z) * radius);
            if(r < rings-1)
                push_indices(indices, sectors, r, s);
        }
    }
}
2 голосов
/ 10 ноября 2014

Код Datanewolf почти прав. Мне пришлось поменять местами намотку и нормали, чтобы она работала правильно со стационарным трубопроводом. Ниже корректно работает при включенном или выключенном cull:

std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;

float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;

vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
    float const y = sin( -M_PI_2 + M_PI * r * R );
    float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
    float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );

    *t++ = s*S;
    *t++ = r*R;

    *v++ = x * radius;
    *v++ = y * radius;
    *v++ = z * radius;

    *n++ = -x;
    *n++ = -y;
    *n++ = -z;
}

indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++)
    for(s = 0; s < sectors-1; s++) {
       /* 
        *i++ = r * sectors + s;
        *i++ = r * sectors + (s+1);
        *i++ = (r+1) * sectors + (s+1);
        *i++ = (r+1) * sectors + s;
        */
         *i++ = (r+1) * sectors + s;
         *i++ = (r+1) * sectors + (s+1);
        *i++ = r * sectors + (s+1);
         *i++ = r * sectors + s;

}

Edit: Возник вопрос о том, как нарисовать это ... в моем коде я инкапсулирую эти значения в классе G3DModel. Это мой код для настройки рамки, рисования модели и ее завершения:

void GraphicsProvider3DPriv::BeginFrame()const{
        int win_width;
        int win_height;// framework of choice here
        glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
        float const win_aspect = (float)win_width / (float)win_height;
        // set lighting
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_DEPTH_TEST);
        GLfloat lightpos[] = {0, 0.0, 0, 0.};
        glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
        GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
        // set up world transform
        glClearColor(0.f, 0.f, 0.f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        gluPerspective(45, win_aspect, 1, 10);

        glMatrixMode(GL_MODELVIEW);

    }


    void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
        G3DModelPriv* privModel = (G3DModelPriv *)model;
        glPushMatrix();
        glLoadMatrixf(transform.GetOGLData());

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
        glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
        glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);

        glEnable(GL_TEXTURE_2D);
        //glFrontFace(GL_CCW);
        glEnable(GL_CULL_FACE);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, privModel->texname);

        glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
        glPopMatrix();
        glDisable(GL_TEXTURE_2D);

    }

    void GraphicsProvider3DPriv::EndFrame()const{
        /* Swap front and back buffers */
        glDisable(GL_LIGHTING);
        glDisable(GL_LIGHT0);
        glDisable(GL_CULL_FACE);
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }
2 голосов
/ 26 мая 2013

Вот код:

glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...