Создание сферы (используя osg::Geometry) в OpenSceneGraph

Я потратил довольно много времени, чтобы заставить это работать, но моя сфера просто не отображается.
Использовал следующий код для создания своей функции:
Создание 3D-сферы в Opengl с помощью Visual C++

А все остальное — это простой OSG с osg::Geometry.
(Примечание: не с помощью ShapeDrawable, так как с его помощью нельзя реализовать пользовательские фигуры.)
Добавлены вершины, нормали, texcoords в VecArrays.

Во-первых, я подозреваю, что что-то не так, поскольку мой сохраненный объект наполовину пуст.
Есть ли способ преобразовать существующее описание в OSG?
Причина? Я хочу понять, как создавать объекты позже.
Действительно, это связано с более поздним заданием, но сейчас я просто готовлюсь заранее.

Примечание: так как я должен сделать это без индексов, я их не использовал.
Но мой цилиндр прекрасно отображается без них.


person Apache    schedule 06.12.2013    source источник
comment
Здравствуйте, не могли бы вы опубликовать небольшой образец кода? Это поможет нам помочь вам :)   -  person GMasucci    schedule 10.12.2013
comment
@GMasucci Я думал об этом, но потом отказался от этой идеи, поскольку у OSG очень простой синтаксис. Завтра добавлю код.   -  person Apache    schedule 11.12.2013
comment
Вы собирались поделиться с нами кодом?   -  person Joe Z    schedule 12.12.2013
comment
@JoeZ - вот образец. trac.openscenegraph.org/projects/osg//wiki/Support/ Tutorials/ (я только что сделал функцию, которая помещала данные в массивы, а затем отображала четырехугольник, большое ничто.)   -  person Apache    schedule 12.12.2013
comment
@Shiki: У вас была возможность взглянуть и попробовать мой обновленный код?   -  person Joe Z    schedule 15.12.2013
comment
@JoeZ: Я посмотрю на это довольно скоро. Я перезапущу награду, как только она упадет. Сейчас я понимаю, что задал вопрос слишком рано, так как тогда я был занят, и у меня не было времени на это. Но я это сделаю, и извините всех за то, что не смогли предоставить обратную связь.   -  person Apache    schedule 16.12.2013
comment
Для всех, кому интересно: нашел отличный способ генерировать фигуры, просто используя их параметрические уравнения. Я опубликую свой код как свой собственный ответ. Надеюсь, это поможет людям узнать что-то новое.   -  person Apache    schedule 16.01.2014


Ответы (3)


Предостережение: я не эксперт OSG. Но я провел небольшое исследование.

OSG требует, чтобы все грани были определены в порядке против часовой стрелки, чтобы отбраковка обратной грани могла отклонить грани, которые «обращены в сторону». Код, который вы используете для создания сферы, не создает все грани в порядке против часовой стрелки.

Вы можете подойти к этому несколькими способами:

  1. Отрегулируйте, как код генерирует лица, вставляя лица против часовой стрелки.
  2. Удвойте свою модель и вставьте каждую грань дважды, один раз с вершинами на каждой грани в их текущем порядке и один раз с вершинами в обратном порядке.

Вариант 1 выше ограничит общее количество полигонов до необходимого. Вариант 2 даст вам сферу, видимую как снаружи, так и внутри сферы.

Чтобы реализовать вариант 2, вам просто нужно изменить этот цикл из кода, на который вы ссылались:

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

Удвойте набор четырехугольников следующим образом:

    indices.resize(rings * sectors * 8);
    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;
        }

Однако это действительно решение «большого молота».

Лично мне трудно понять, почему оригинального цикла недостаточно; Интуитивно пробираясь через геометрию, мне кажется, что он уже генерирует грани против часовой стрелки, потому что каждое последующее кольцо находится над предыдущим, и каждый последующий сектор идет против часовой стрелки вокруг поверхности сферы из предыдущего. Таким образом, сам исходный порядок должен быть против часовой стрелки по отношению к лицу, ближайшему к зрителю.


РЕДАКТИРОВАНИЕ Используя код OpenGL, который вы связали ранее, и учебник OSG, который вы связали сегодня, я собрал то, что я считаю правильной программой для создания osg::Geometry / osg::Geode для сферы. У меня нет возможности протестировать следующий код, но при проверке он выглядит правильным или, по крайней мере, в значительной степени правильным.

#include <vector>

class SolidSphere
{
protected:

    osg::Geode      sphereGeode;
    osg::Geometry   sphereGeometry;
    osg::Vec3Array  sphereVertices;
    osg::Vec3Array  sphereNormals;
    osg::Vec2Array  sphereTexCoords;

    std::vector<osg::DrawElementsUInt> spherePrimitiveSets;

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;

        sphereGeode.addDrawable( &sphereGeometry );

        // Establish texture coordinates, vertex list, and normals
        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 );

                sphereTexCoords.push_back( osg::Vec2(s*R, r*R) );

                sphereVertices.push_back ( osg::Vec3(x * radius,
                                                     y * radius,
                                                     z * radius) );

                sphereNormals.push_back  ( osg::Vec3(x, y, z) );

            }

        sphereGeometry.setVertexArray  ( &spehreVertices  );
        sphereGeometry.setTexCoordArray( &sphereTexCoords );

        // Generate quads for each face.  
        for(r = 0; r < rings-1; r++)
            for(s = 0; s < sectors-1; s++)
            {
                spherePrimitiveSets.push_back(
                    DrawElementUint( osg::PrimitiveSet::QUADS, 0 )
                );

                osg::DrawElementsUInt& face = spherePrimitiveSets.back();

                // Corners of quads should be in CCW order.
                face.push_back( (r + 0) * sectors + (s + 0) );
                face.push_back( (r + 0) * sectors + (s + 1) );
                face.push_back( (r + 1) * sectors + (s + 1) );
                face.push_back( (r + 1) * sectors + (s + 0) );

                sphereGeometry.addPrimitveSet( &face );
            }
    }

    osg::Geode     *getGeode()     const { return &sphereGeode;     }
    osg::Geometry  *getGeometry()  const { return &sphereGeometry;  }
    osg::Vec3Array *getVertices()  const { return &sphereVertices;  }
    osg::Vec3Array *getNormals()   const { return &sphereNormals;   }
    osg::Vec2Array *getTexCoords() const { return &sphereTexCoords; }

};

Вы можете использовать методы getXXX для получения различных частей. Я не видел, как привязать нормали поверхности к чему-либо, но я сохраняю их в массиве Vec2Array. Если они вам нужны, они вычисляются и сохраняются и ждут, когда их к чему-то привяжут.

person Joe Z    schedule 11.12.2013
comment
Проблема в том, что мы не используем (AFAIK) индексы в OSG. Например, получается пирамида: trac.openscenegraph.org/projects/ osg//wiki/Support/Tutorials/ . Эта форма также опирается только на вершины... Как мне формировать фигуры? - person Apache; 12.12.2013
comment
Так что дело в том, что мы используем Vertices (и Vec3Array для этого), так что вы не можете туда даже 4 индекса впихнуть. Может быть, я полностью упускаю суть. Если это так, пожалуйста, объясните. - person Apache; 13.12.2013
comment
В примере показано размещение индексов в вершинах. Это то, что делает pyramidBase->push_back(). Они проталкивают несколько вершин, а затем начинают определять грани в терминах индексов в этих вершинах. - person Joe Z; 13.12.2013
comment
@Shiki: я отредактировал свой пост, чтобы предоставить код OSG. Дайте мне знать, как это происходит. - person Joe Z; 13.12.2013
comment
Вот как выглядит экспортированный .obj: pastebin.com/GVAkLM5n | А вот и полусферический результат. Я не стал создавать класс, так как в этом коде появилось еще больше проблем, которые необходимо исправить. Итак, вот оно: i.imgur.com/a0p2fGN.jpg - person Apache; 20.12.2013
comment
@Shiki: Если я понял лица в строках f в OBJ, то это треугольники, а не четырехугольники. Вы генерируете четырехугольники в своем коде или вам нужны треугольники? Если вам нужны треугольники, то вам нужно нажать оба треугольника, связанные с квадроциклом. Это определенно похоже на то, что вы неправильно интерпретируете четырехъядерные вершины как треугольники. (Там есть некоторые формы галстука-бабочки, которые, я думаю, из-за этого.) - person Joe Z; 20.12.2013
comment
@Shiki: Был ли загруженный вами OBJ полным? Он имеет только 512 граней и относится только к 1536 вершинам, но на самом деле имеет 3750 вершин. На самом деле 512 лиц кажутся любопытным ограничением. - person Joe Z; 20.12.2013
comment
@Shiki: я написал быстрый и грязный perl-скрипт для создания правильных quad граней для вершин, которые уже были в вашем файле .OBJ. Кажется, он создал правильную сферу. Вы можете увидеть мой Perl-скрипт здесь: spatula-city.org/~im14u2c/dl/ huge_sphere_vertex_gen.txt И вы можете получить исправленный OBJ здесь: spatula-city .org/~im14u2c/dl/huge_sphere_fixed.txt Что-то не так с тем, как вы вставляете лица в свою сферу. То, что вы поместили в pastebin, имело треугольники, а не четырехугольники, и порядок вершин был просто неправильным. - person Joe Z; 20.12.2013
comment
@Shiki: ..continuing: вам нужно гнездо цикла, подобное последнему гнезду цикла в моем коде выше, в котором говорится «Создать четырехугольники для каждой грани». И если вам нужны треугольники вместо четырехугольников, это несложно сделать, но скажите об этом, если это требование. OSG позволяет толкать квадроциклы. - person Joe Z; 20.12.2013
comment
Ладно, сегодня займусь кодом и отчитаюсь. Спасибо, что потратили столько времени на вопрос/проблему. - person Apache; 21.12.2013
comment
Ну, честно говоря, код просто непригоден для использования. Я потратил на это еще пару часов, но это никуда не денется. ЕСЛИ вы их поднимете, они просто не будут отображаться, так как находятся на одном и том же месте. - person Apache; 08.01.2014
comment
@Shiki Как все прошло? Код в порядке? Я спрашиваю, потому что вижу, что вы дали награду, но не приняли ответ. - person Adri C.S.; 30.10.2014
comment
@AdriC.S.: Да, извини за это. Принял ваш ответ. - person Apache; 31.10.2014
comment
@Шики Мой ответ? Вы ошибаетесь! хаха. Я попробовал код, пришлось исправить некоторые ошибки в нем, но он работал нормально. Сфера, которую он создает, очень хороша. - person Adri C.S.; 31.10.2014
comment
@AdriC.S.: В конце концов я использовал совершенно другой подход. Так или иначе. Я просто захожу на сайт, чтобы отметить ответы/удалить/сообщить о минах. Сайт получает все больше и больше устаревших / устаревших вопросов день ото дня. В наше время это практически бесполезно. :/ - person Apache; 01.11.2014
comment
@Shiki Я использовал код ответа, но должен был его исправить. Он показывает красивую сферу. - person Adri C.S.; 03.11.2014
comment
@JoeZ Я опубликовал ответ с исправленным OSG. Спасибо за Ваш ответ!! - person Adri C.S.; 03.11.2014

Этот код вызывает glutSolidSphere() для рисования сферы, но нет смысла вызывать его, если ваше приложение не использует GLUT для отображения окна с трехмерным контекстом.

Есть еще один способ легко нарисовать сферу, используя gluSphere() (вероятно, у вас установлен GLU):

void gluSphere(GLUquadric* quad, GLdouble радиус, GLint срезы, GLint < em>стеки);

Параметры

quad — указывает объект квадрика (созданный с помощью gluNewQuadric).

radius – указывает радиус сферы.

срезы – определяет количество делений по оси Z (аналогично линиям долготы).

стеки – указывает количество подразделений по оси Z (аналогично линиям широты).

Использование:

// If you also need to include glew.h, do it before glu.h
#include <glu.h>

GLUquadric* _quadratic = gluNewQuadric();
if (_quadratic == NULL)
{
    std::cerr << "!!! Failed gluNewQuadric" << std::endl;
    return;
}

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glTranslatef(0.0, 0.0, -5.0);
glColor3ub(255, 97, 3);
gluSphere(_quadratic, 1.4f, 64, 64);

glFlush();

gluDeleteQuadric(_quadratic);

Вероятно, будет разумнее переместить вызов gluNewQuadric() в конструктор вашего класса, поскольку его нужно выделить только один раз, а вызов gluDeleteQuadric() переместить в деструктор класса.

person karlphillip    schedule 16.12.2013
comment
Вы не можете экспортировать такую ​​геометрию в OSG (насколько я знаю), и речь идет о чистом (настолько чистом, насколько это возможно) OSG. Тем не менее, ответ весьма полезен для тех, кто может забрести в земли OSG. - person Apache; 20.12.2013
comment
Вы можете иметь вещи GLU в OSG, наследуя от osg::Drawable и переопределяя метод drawImplementation(). На самом деле вы можете иметь там все, что захотите. Это сделано в примере NURBS Surface в OpenSceneGraph 3 Cookbook. Однако в этом конкретном случае это действительно медленно. - person Patrick K; 14.10.2014

Ответ @JoeZ отличный, но в коде OSG есть некоторые ошибки/плохие практики. Вот обновленный код. Он был протестирован, и он показывает очень хорошую сферу.

    osg::ref_ptr<osg::Geode> buildSphere( const double radius,
                                          const unsigned int rings,
                                          const unsigned int sectors )
    {
        osg::ref_ptr<osg::Geode>      sphereGeode = new osg::Geode;
        osg::ref_ptr<osg::Geometry>   sphereGeometry = new osg::Geometry;
        osg::ref_ptr<osg::Vec3Array>  sphereVertices = new osg::Vec3Array;
        osg::ref_ptr<osg::Vec3Array>  sphereNormals = new osg::Vec3Array;
        osg::ref_ptr<osg::Vec2Array>  sphereTexCoords = new osg::Vec2Array;

        float const R = 1. / static_cast<float>( rings - 1 );
        float const S = 1. / static_cast<float>( sectors - 1 );

        sphereGeode->addDrawable( sphereGeometry );

        // Establish texture coordinates, vertex list, and normals
        for( unsigned int r( 0 ); r < rings; ++r ) {
            for( unsigned 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 );

                sphereTexCoords->push_back( osg::Vec2( s * R, r * R ) );

                sphereVertices->push_back ( osg::Vec3( x * radius,
                                                       y * radius,
                                                       z * radius) )
                ;
                sphereNormals->push_back  ( osg::Vec3( x, y, z ) );

            }
        }

        sphereGeometry->setVertexArray  ( sphereVertices  );
        sphereGeometry->setTexCoordArray( 0, sphereTexCoords );

        // Generate quads for each face.
        for( unsigned int r( 0 ); r < rings - 1; ++r ) {
            for( unsigned int s( 0 ); s < sectors - 1; ++s ) {

                osg::ref_ptr<osg::DrawElementsUInt> face =
                        new osg::DrawElementsUInt( osg::PrimitiveSet::QUADS,
                                                   4 )
                ;
                // Corners of quads should be in CCW order.
                face->push_back( ( r + 0 ) * sectors + ( s + 0 ) );
                face->push_back( ( r + 0 ) * sectors + ( s + 1 ) );
                face->push_back( ( r + 1 ) * sectors + ( s + 1 ) );
                face->push_back( ( r + 1 ) * sectors + ( s + 0 ) );

                sphereGeometry->addPrimitiveSet( face );
            }
        }

        return sphereGeode;
    }

Изменения:

  • Элементы OSG, используемые в коде, теперь являются интеллектуальными указателями1. Кроме того, такие классы, как Geode и Geometry, имеют защищенные деструкторы, поэтому единственный способ создать их экземпляры — это динамическое размещение.

  • Удален spherePrimitiveSets, так как он не нужен в текущей версии кода.

  • Я поместил код в бесплатную функцию, так как мне не нужен класс Sphere в моем коде. Я пропустил getters и защищенные атрибуты. Они не нужны: если вам нужно получить доступ, скажем, к геометрии, вы можете получить ее через: sphereGeode->getDrawable(...). То же самое касается остальных атрибутов.

[1] См. Правило №1 здесь. Это немного устарело, но совет сохраняется.

person Adri C.S.    schedule 03.11.2014