web-dev-qa-db-ja.com

Visual C ++を使用してOpenglで3D球体を作成する

C++でOpenGLライブラリ関数glutSolidSphere()を使用して単純な3D球体を作成することはできません。

ここに私が試したものがあります:

#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(); 
}
26
Lloyd

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

元のコードの実際の問題をこれまでに解決した人はいないようですので、この時点ではかなり古い質問ですが、私はそれを行うと思いました。

問題はもともと、球の半径と位置に関連する投影に関係していました。問題はそれほど複雑ではないことがわかると思います。プログラムは実際に正しく動作します。描画されているものが見にくいというだけです。

最初に、コールを使用して正射影が作成されました

_gluOrtho2D(0.0, 499.0, 0.0, 499.0);
_

which " near = -1およびfar = 1でglOrthoを呼び出すのと同じです "これは viewing frustum の深さが2であることを意味します。 1(直径= 2)を超えるものは、視錐台内に完全には収まりません。

その後、呼び出し

_glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
_

モデルビューマトリックスの単位マトリックスを読み込んでから、「 [r]指定された半径のモデリング座標原点を中心とする球体をレンダリングします。 "意味、球体は原点、(x、y、z)=(0、0、0)、および半径5。

現在、問題は3つあります。

  1. ウィンドウは500x500ピクセルで、視錐台の幅と高さはほぼ500(499.0)であるため、球体の小さな半径(5.0)により、投影面積はサイズの50分の1(2 * 5/499)をわずかに超えます。各次元のウィンドウの。これは、球体の見かけのサイズがウィンドウ全体の約1/2,500(実際には_pi*5^2/499^2_、約1/3170に近い)になるため、見にくい。これは、円全体がウィンドウの領域内に描画されることを前提としています。ただし、ポイント2で見るように、そうではありません。
  2. 視錐台の左面はx = 0、底面はy = 0であるため、球体は幾何学的中心がウィンドウの左下隅にレンダリングされ、投影された球体の1つの象限のみが表示されます。 !これは、表示されるものがウィンドウサイズの約1/10,000(実際にはpi*5^2/(4*499^2)、これは1/12,682ndに近い)より小さいことを意味します。これにより、見づらくなります。特に、球体は画面のエッジ/コーナーに非常に近い場所にレンダリングされるため、見た目が思わないかもしれません。
  3. 視錐台の深さは球体の直径よりもかなり小さいため(半分未満)、球体のスライバーのみが視錐台内にあり、その部分のみをレンダリングします。したがって、画面上では、中実の球/円というよりも、中空の円のようになります。それが起こると、そのスライバーの厚さは画面上で1ピクセル未満を表す可能性があります。つまり、たとえ一部であっても、画面上にnothing球体は実際に視錐台内にあります。

解決策は、単に視錐台と球体の半径を変更することです。例えば、

_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

これが誰かの助けになることを願っています。世話をする!

21
Victor Zamanian

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);
        }
    }
}
9
coin

コードは次のとおりです。

glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
3
sarah

コインの答えが好きです。理解するのは簡単で、三角形を使用します。しかし、彼のプログラムのインデックスは時々限界を超えています。そこで、ここに2つの小さな修正を加えたコードを投稿します。

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);
        }
    }
}
3
Harald

Datanewolfのコードはほぼ正しいです。固定パイプラインで適切に動作させるために、巻線と法線の両方を逆にする必要がありました。以下は、カリングをオンまたはオフにして正しく動作します:

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;

}

編集:これを描画する方法について質問がありました...私のコードでは、これらの値を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();
    }
3
user430788