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

发布于 2024-12-08 10:31:42 字数 370 浏览 0 评论 0原文

有没有教程解释如何在 OpenGL 中绘制球体而无需使用 gluSphere() ?

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

Are there any tutorials out there that explain how I can draw a sphere in OpenGL without having to use 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 site but 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

山人契 2024-12-15 10:31:42

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

recursivelydrawedtriangles

一旦你有足够的数量对于点,您可以标准化它们的向量,以便它们与实体中心的距离都是恒定的。这会导致侧面凸出成类似球体的形状,随着点数的增加,平滑度也会增加。

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

在此处输入图像描述

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

在此处输入图像描述

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

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

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

标准化多边形

级别 1凸八面体
level 3 凸出八面体

如果您在 Epcot,您可以看到这种技术的工作原理。它是一个十二面体,表面凸出,使其看起来更圆。

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.

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:

#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.

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.

normalized polygons

level 1 bulging octahedron
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.

我家小可爱 2024-12-15 10:31:42

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

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

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

其中:

  • r 是半径;
  • u是经度,范围是0到2π;
  • v 是纬度,范围从 0 到 π。

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

例如,要生成 16 条经度线,沿 u 轴将有 17 条网格线,步长为
π/8 (2π/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)
 }
}

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.)

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 ]

Where:

  • r is the radius;
  • u is the longitude, ranging from 0 to 2π; and
  • v is the latitude, ranging from 0 to π.

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 u axis, with a step of
π/8 (2π/16) (the 17th line wraps around).

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

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

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)
 }
}
千寻… 2024-12-15 10:31:42

示例中的代码很快得到了解释。您应该查看函数 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 定义您想要在球体中拥有多少条水平线,< code>lon 有多少条垂直线。 r 是球体的半径。

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

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

现在你需要了解的是三角函数是如何工作的,但我想你可以很容易地弄清楚。

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) {
    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 lat defines how many horizontal lines you want to have in your sphere and lon how many vertical lines. r is the radius of your sphere.

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

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.

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

自此以后,行同陌路 2024-12-15 10:31:42

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

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/).

海夕 2024-12-15 10:31:42

请参阅OpenGL红皮书: http://www.glprogramming.com/red/chapter02.html #名称8
它通过多边形细分来解决问题。

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

楠木可依 2024-12-15 10:31:42

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

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)如下参数方程,第二个点移动一个 alpha 角度(从下一条平行线开始)。

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).

梦与时光遇 2024-12-15 10:31:42

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

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

正二十面体有 20 个面(12 个顶点),可以轻松地由 3 个黄金矩形构建而成;问题只是以此为起点而不是八面体。您可以在此处找到示例。

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

Although the accepted answer solves the question, there's a little misconception at the end. Dodecahedrons are (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 dodecahedron at all). Since the solution proposed by @Kevin does not provide this characteristic I thought I could add an approach that does.

A good way to generate an N-faced polyhedron where all vertices lay in the same sphere and all 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 truncated icosahedrons.

Regular icosahedrons have 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.

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

煮茶煮酒煮时光 2024-12-15 10:31:42

@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()

Python adaptation of @Constantinius answer:

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()
揪着可爱 2024-12-15 10:31:42

输入图片此处描述

   void draw_sphere(float r)
    {
    
        float pi = 3.141592;
        float di = 0.02;
        float dj = 0.04;
        float db = di * 2 * pi;
        float da = dj * pi;
    
    
        for (float i = 0; i < 1.0; i += di) //horizonal
            for (float j = 0; j < 1.0; j += dj) //vertical
            {
                float b = i * 2 * pi;      //0     to  2pi
                float a = (j - 0.5) * pi;  //-pi/2 to pi/2
    
    
                //normal
                glNormal3f(
                    cos(a + da / 2) * cos(b + db / 2),
                    cos(a + da / 2) * sin(b + db / 2),
                    sin(a + da / 2));
    
                glBegin(GL_QUADS);
                //P1
                    glTexCoord2f(i, j);
                    glVertex3f(
                        r * cos(a) * cos(b),
                        r * cos(a) * sin(b),
                        r * sin(a));
                //P2
                    glTexCoord2f(i + di, j);//P2
                    glVertex3f(
                        r * cos(a) * cos(b + db),
                        r * cos(a) * sin(b + db),
                        r * sin(a));
                //P3
                    glTexCoord2f(i + di, j + dj);
                    glVertex3f(
                        r * cos(a + da) * cos(b + db),
                        r * cos(a + da) * sin(b + db),
                        r * sin(a + da));
                //P4
                    glTexCoord2f(i, j + dj);
                    glVertex3f(
                        r * cos(a + da) * cos(b),
                        r * cos(a + da) * sin(b),
                        r * sin(a + da));
                glEnd();
            }
    }

enter image description here

   void draw_sphere(float r)
    {
    
        float pi = 3.141592;
        float di = 0.02;
        float dj = 0.04;
        float db = di * 2 * pi;
        float da = dj * pi;
    
    
        for (float i = 0; i < 1.0; i += di) //horizonal
            for (float j = 0; j < 1.0; j += dj) //vertical
            {
                float b = i * 2 * pi;      //0     to  2pi
                float a = (j - 0.5) * pi;  //-pi/2 to pi/2
    
    
                //normal
                glNormal3f(
                    cos(a + da / 2) * cos(b + db / 2),
                    cos(a + da / 2) * sin(b + db / 2),
                    sin(a + da / 2));
    
                glBegin(GL_QUADS);
                //P1
                    glTexCoord2f(i, j);
                    glVertex3f(
                        r * cos(a) * cos(b),
                        r * cos(a) * sin(b),
                        r * sin(a));
                //P2
                    glTexCoord2f(i + di, j);//P2
                    glVertex3f(
                        r * cos(a) * cos(b + db),
                        r * cos(a) * sin(b + db),
                        r * sin(a));
                //P3
                    glTexCoord2f(i + di, j + dj);
                    glVertex3f(
                        r * cos(a + da) * cos(b + db),
                        r * cos(a + da) * sin(b + db),
                        r * sin(a + da));
                //P4
                    glTexCoord2f(i, j + dj);
                    glVertex3f(
                        r * cos(a + da) * cos(b),
                        r * cos(a + da) * sin(b),
                        r * sin(a + da));
                glEnd();
            }
    }
ゞ花落谁相伴 2024-12-15 10:31:42

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

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

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

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.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文