在不使用gluSphere()的情况下在OpenGL中绘制球体?

Car*_*ven 77 c++ opengl geometry

是否有任何教程解释我如何在OpenGL中绘制球体而不必使用gluSphere()

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

Kev*_*vin 259

你能做到的一种方法是从具有三角形边的柏拉图式实体开始 - 例如八面体.然后,取每个三角形并递归地将其分解为更小的三角形,如下所示:

递归绘制的三角形

一旦你有足够的点数,你就可以对它们的矢量进行标准化,使它们距离实体的中心都是一个恒定的距离.这会使侧面凸出成类似于球体的形状,随着您增加点数而增加平滑度.

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

在此输入图像描述

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
Run Code Online (Sandbox Code Playgroud)

如果我们在很多点上进行这种归一化处理,所有这些都是相同的点A并且具有相同的距离R,那么归一化的点将全部位于具有中心A和半径R的圆弧上.

鼓胀线段

在这里,黑点从一条线开始并"凸出"成弧形.

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

归一化多边形

1级鼓胀八面体 3级鼓胀八面体

如果你看一下Epcot的球体,你可以在工作中看到这种技术.它是一个带有凸出面孔的十二面体,使它看起来更圆.


Pet*_* O. 22

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

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

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

哪里:

  • r是半径;
  • 是经度,从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)
 }
}
Run Code Online (Sandbox Code Playgroud)

  • 您好,我只是想指出p0,p1,p2,p3的每个值的第二个应该是v或vn,而不是u或un. (2认同)

Con*_*ius 5

快速解释了示例中的代码。您应该研究一下功能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();
    }
}
Run Code Online (Sandbox Code Playgroud)

参数lat定义了您的球体中要有多少条水平线以及lon几条垂直线。r是球体的半径。

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

现在计算出的顶点利用发送到您的GPU glVertex...()GL_QUAD_STRIP,这意味着你要发送形成与先前2派四每两个顶点。

现在,您只需要了解三角函数的工作方式即可,但是我想您可以轻松地弄清楚。

  • ** drawSPhere()**的源代码在哪里? (2认同)
  • 半径丢失。 (2认同)