C++ 在 OpenGL 中绘制球体而不使用 gluSphere()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7687148/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:21:29  来源:igfitidea点击:

Drawing Sphere in OpenGL without using gluSphere()?

c++openglgeometry

提问by Carven

Are there any tutorials out there that explain how I can draw a sphere in OpenGL without having to use gluSphere()?

是否有任何教程可以解释如何在 OpenGL 中绘制球体而无需使用gluSphere()

Many of the 3D tutorials for OpenGL are just on cubes. I have searched but most of the solutions to drawing a sphere are to use gluSphere(). There is also a site that has the code to drawing a sphere at this sitebut it doesn't explain the math behind drawing the sphere. I have also other versions of how to draw the sphere in polygon instead of quads in that link. But again, I don't understand how the spheres are drawn with the code. I want to be able to visualize so that I could modify the sphere if I need to.

许多 OpenGL 的 3D 教程只是关于立方体。我已经搜索过,但大多数绘制球体的解决方案都是使用gluSphere(). 还有一个站点在站点上有绘制球体的代码,但它没有解释绘制球体背后的数学原理。我还有其他版本的如何在该链接中以多边形而不是四边形绘制球体。但同样,我不明白球体是如何用代码绘制的。我希望能够进行可视化,以便在需要时可以修改球体。

回答by Kevin

One way you can do it is to start with a platonic solid with triangular sides - an octahedron, for example. Then, take each triangle and recursively break it up into smaller triangles, like so:

一种方法是从具有三角形边的柏拉图立体开始 -例如八面体。然后,将每个三角形递归地分解成更小的三角形,如下所示:

recursively drawn triangles

递归绘制的三角形

Once you have a sufficient amount of points, you normalize their vectors so that they are all a constant distance from the center of the solid. This causes the sides to bulge out into a shape that resembles a sphere, with increasing smoothness as you increase the number of points.

一旦你有足够数量的点,你就可以对它们的向量进行归一化,使它们与实体中心的距离保持不变。这会导致侧面凸出成类似于球体的形状,随着点数的增加,平滑度也会增加。

Normalization here means moving a point so that its angle in relation to another point is the same, but the distance between them is different. Here's a two dimensional example.

这里的归一化是指移​​动一个点,使其相对于另一个点的角度相同,但它们之间的距离不同。这是一个二维示例。

enter image description here

在此处输入图片说明

A and B are 6 units apart. But suppose we want to find a point on line AB that's 12 units away from A.

A 和 B 相距 6 个单位。但是假设我们想在 AB 线上找到一个距离 A 12 个单位的点。

enter image description here

在此处输入图片说明

We can say that C is the normalized form of B with respect to A, with distance 12. We can obtain C with code like this:

我们可以说 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

If we do this normalization process on a lot of points, all with respect to the same point A and with the same distance R, then the normalized points will all lie on the arc of a circle with center A and radius R.

如果我们在很多点上进行这个归一化过程,所有点都相对于相同的点 A 和相同的距离 R,那么归一化的点将全部位于以 A 为中心、半径为 R 的圆弧上。

bulging line segment

凸出线段

Here, the black points begin on a line and "bulge out" into an arc.

在这里,黑点从一条线上开始并“凸出”成弧形。

This process can be extended into three dimensions, in which case you get a sphere rather than a circle. Just add a dz component to the normalize function.

这个过程可以扩展到三个维度,在这种情况下,你会得到一个球体而不是一个圆。只需在 normalize 函数中添加一个 dz 组件即可。

normalized polygons

标准化多边形

level 1 bulging octahedronlevel 3 bulging octahedron

1级凸出八面体level 3 bulging octahedron

If you look at the sphere at Epcot, you can sort of see this technique at work. it's a dodecahedron with bulged-out faces to make it look rounder.

如果您查看Epcot上的球体,您可以看到这种技术在起作用。它是一个十二面体,带有凸出的面,使其看起来更圆。

回答by Peter O.

I'll further explain a popular way of generating a sphere using latitude and longitude (another way, icospheres, was already explained in the most popular answer at the time of this writing.)

我将进一步解释使用纬度和经度生成球体的一种流行方式(另一种方式,icospheres,在撰写本文时已经在最流行的答案中进行了解释。)

A sphere can be expressed by the following parametric equation:

球体可以用以下参数方程表示:

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

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

Where:

在哪里:

  • ris the radius;
  • uis the longitude, ranging from 0 to 2π; and
  • vis the latitude, ranging from 0 to π.
  • r是半径;
  • u是经度,范围从 0 到 2π;和
  • v是纬度,范围从 0 到 π。

Generating the sphere then involves evaluating the parametric function at fixed intervals.

生成球体然后涉及以固定间隔评估参数函数。

For example, to generate 16 lines of longitude, there will be 17 grid lines along the uaxis, with a step of π/8 (2π/16) (the 17th line wraps around).

例如,要生成 16 条经线,沿u轴将有 17 条网格线,步长为 π/8 (2π/16)(第 17 条线环绕)。

The following pseudocode generates a triangle mesh by evaluating a parametric function at regular intervals (this works for anyparametric surface function, not just spheres).

以下伪代码通过定期评估参数函数来生成三角形网格(这适用于任何参数表面函数,而不仅仅是球体)。

In the pseudocode below, UResolutionis the number of grid points along the U axis (here, lines of longitude), and VResolutionis the number of grid points along the V axis (here, lines of latitude)

在下面的伪代码中,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)
 }
}

回答by Constantinius

The code in the sample is quickly explained. You should look into the function void drawSphere(double r, int lats, int longs):

快速解释了示例中的代码。你应该看看这个函数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();
    }
}

The parameters latdefines how many horizontal lines you want to have in your sphere and lonhow many vertical lines. ris the radius of your sphere.

这些参数lat定义了您希望在球体中拥有多少条水平线以及lon多少条垂直线。r是球体的半径。

Now there is a double iteration over lat/lonand the vertex coordinates are calculated, using simple trigonometry.

现在对lat/进行两次迭代,lon并使用简单的三角函数计算顶点坐标。

The calculated vertices are now sent to your GPU using glVertex...()as a GL_QUAD_STRIP, which means you are sending each two vertices that form a quad with the previously two sent.

计算出的顶点现在glVertex...()作为 a发送到您的 GPU GL_QUAD_STRIP,这意味着您每发送两个顶点,与之前发送的两个顶点形成一个四边形。

All you have to understand now is how the trigonometry functions work, but I guess you can figure it out easily.

您现在需要了解的只是三角函数的工作原理,但我想您可以轻松搞清楚。

回答by blockchaindev

If you wanted to be sly like a fox you could half-inch the code from GLU. Check out the MesaGL source code (http://cgit.freedesktop.org/mesa/mesa/).

如果你想像狐狸一样狡猾,你可以从 GLU 中取出半英寸的代码。查看 MesaGL 源代码 (http://cgit.freedesktop.org/mesa/mesa/)。

回答by Walter

See the OpenGL red book: http://www.glprogramming.com/red/chapter02.html#name8It solves the problem by polygon subdivision.

看OpenGL红皮书:http: //www.glprogramming.com/red/chapter02.html#name8 是通过多边形细分来解决问题的。

回答by bloody

My example how to use 'triangle strip' to draw a "polar" sphere, it consists in drawing points in pairs:

我的示例如何使用“三角带”绘制“极坐标”球体,它包括成对绘制点:

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

First point entered (glVertex3f) is as follows the parametric equation and the second one is shifted by a single step of alpha angle (from next parallel).

输入的第一个点 (glVertex3f) 如下参数方程,第二个点移动一个 alpha 角(从下一个平行线开始)。

回答by Carles Araguz

Although the accepted answer solves the question, there's a little misconception at the end. Dodecahedronsare (or could be) regular polyhedron where all faces have the same area. That seems to be the case of the Epcot (which, by the way, is not a dodecahedronat all). Since the solution proposed by @Kevin does not provide this characteristic I thought I could add an approach that does.

尽管接受的答案解决了问题,但最后还是有一些误解。十二面体是(或可能是)正多面体,其中所有面都具有相同的面积。未来世界似乎就是这种情况(顺便说一下,它根本不是十二面体)。由于@Kevin 提出的解决方案没有提供这个特性,我想我可以添加一种方法。

A good way to generate an N-faced polyhedron where all vertices lay in the same sphere andall its faces have similar area/surface is starting with an icosahedron and the iteratively sub-dividing and normalizing its triangular faces (as suggested in the accepted answer). Dodecahedrons, for instance, are actually truncatedicosahedrons.

生成 N 面多面体的一种好方法,其中所有顶点都位于同一个球体中,并且所有面都具有相似的面积/表面,从二十面体开始,然后对其三角形面进行迭代细分和归一化(如已接受的答案中所建议) )。例如,十二面体实际上是截断的二十面体

Regular icosahedronshave 20 faces (12 vertices) and can easily be constructed from 3 golden rectangles; it's just a matter of having this as a starting point instead of an octahedron. You may find an example here.

正二十面体有 20 个面(12 个顶点),可以很容易地由 3 个黄金矩形构成;这只是将其作为起点而不是八面体的问题。你可以在这里找到一个例子。

I know this is a bit off-topic but I believe it may help if someone gets here looking for this specific case.

我知道这有点离题,但我相信如果有人来这里寻找这个特定案例可能会有所帮助。

回答by ismail ak?n ?elik

void draw_sphere()
{

    //              z
    //              |
    //               __
    //             /|          
    //              |           
    //              |           
    //              |    *      \
    //              | _ _| _ _ _ |    _y
    //             / \c  |n     /                    p3 --- p2
    //            /   \o |i                           |     |
    //           /     \s|s      z=sin(v)            p0 --- p1
    //         |/__              y=cos(v) *sin(u)
    //                           x=cos(v) *cos(u) 
    //       /
    //      x
    //


    double pi = 3.141592;
    double di =0.02;
    double dj =0.04;
    double du =di*2*pi;
    double dv =dj*pi;


    for (double i = 0; i < 1.0; i+=di)  //horizonal
    for (double j = 0; j < 1.0; j+=dj)  //vertical
    {       
        double u = i*2*pi;      //0     to  2pi
        double v = (j-0.5)*pi;  //-pi/2 to pi/2

        double  p[][3] = { 
            cos(v)     * cos(u)      ,cos(v)     * sin(u)       ,sin(v),
            cos(v)     * cos(u + du) ,cos(v)     * sin(u + du)  ,sin(v),
            cos(v + dv)* cos(u + du) ,cos(v + dv)* sin(u + du)  ,sin(v + dv),
            cos(v + dv)* cos(u)      ,cos(v + dv)* sin(u)       ,sin(v + dv)};

        //normal
        glNormal3d(cos(v+dv/2)*cos(u+du/2),cos(v+dv/2)*sin(u+du/2),sin(v+dv/2));

        glBegin(GL_POLYGON);
            glTexCoord2d(i,   j);    glVertex3dv(p[0]);
            glTexCoord2d(i+di,j);    glVertex3dv(p[1]);
            glTexCoord2d(i+di,j+dj); glVertex3dv(p[2]);
            glTexCoord2d(i,   j+dj); glVertex3dv(p[3]);
        glEnd();
    }
}

回答by PepeElMago33

Python adaptation of @Constantinius answer:

@Constantinius 答案的 Python 改编:

lats = 10
longs = 10
r = 10

for i in range(lats):
    lat0 = pi * (-0.5 + i / lats)
    z0 = sin(lat0)
    zr0 = cos(lat0)

    lat1 = pi * (-0.5 + (i+1) / lats)
    z1 = sin(lat1)
    zr1 = cos(lat1)

    glBegin(GL_QUAD_STRIP)
    for j in range(longs+1):
        lng = 2 * pi * (j+1) / longs
        x = cos(lng)
        y = sin(lng)

        glNormal(x * zr0, y * zr0, z0)
        glVertex(r * x * zr0, r * y * zr0, r * z0)
        glNormal(x * zr1, y * zr1, z1)
        glVertex(r * x * zr1, r * y * zr1, r * z1)

    glEnd()

回答by Steven2163712

One way is to make a quad that faces the camera and write a vertex and fragment shader that renders something that looks like a sphere. You could use equations for a circle/sphere that you can find on the internet.

一种方法是制作一个面向相机的四边形并编写一个顶点和片段着色器来渲染看起来像一个球体的东西。您可以使用可以在互联网上找到的圆/球体的方程式。

One nice thing is that the silhouette of a sphere looks the same from any angle. However, if the sphere is not in the center of a perspective view, then it would appear perhaps more like an ellipse. You could work out the equations for this and put them in the fragment shading. Then the light shading needs to changed as the player moves, if you do indeed have a player moving in 3D space around the sphere.

一件好事是球体的轮廓从任何角度看起来都一样。然而,如果球体不在透视图的中心,那么它可能看起来更像一个椭圆。你可以计算出这个方程并将它们放在片段着色中。然后,如果确实有玩家在球体周围的 3D 空间中移动,则需要在玩家移动时更改光照阴影。

Can anyone comment on if they have tried this or if it would be too expensive to be practical?

任何人都可以评论他们是否尝试过这个或者它是否太昂贵而不实用?