2011-10-07 49 views
68

有没有教程可以解释我如何在OpenGL中绘制球体而不必使用gluSphere()在OpenGL中绘制球体而不使用gluSphere()?

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

+2

查找数学解释的球坐标(特别是从球坐标到笛卡尔坐标的转换)。 –

回答

222

您可以做到的一种方法是以带有三角形侧面的柏拉图式固体开始 - 例如octahedron。然后,取每个三角形和递归地把它分解成更小的三角形,像这样:

recursively drawn triangles

一旦你有了点足量,你规范自己的载体,使他们从中心所有的距离不变的固体。这会导致两侧凸出成类似于球体的形状,并随着增加点数而增加平滑度。

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

enter image description here

A和B是隔开6个单位。但假设我们想找到AB线点是从A 12个单位处

enter image description here

我们可以说,C是B上相对于A的归一化的形式,距离12我们可以得到下用这样的代码:

#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 line segment

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

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

normalized polygons

level 1 bulging octahedron level 3 bulging octahedron

如果你看一下在Epcot球体,你可以排序的看到工作中这种技术。这是一个十二面体,凸出的脸让它看起来更圆。

+1

我宁愿删除链接到epcot球体。它可能会让初学者感到困惑,因为每个三角形又被细分为三个等腰三角形(类似于sqrt(3)细分的第一部分)。我相信你会找到一个更好的例子。 –

+0

我在家用机器上有很好的实现。下班后我会很高兴在一些截图中进行编辑。 – Kevin

+0

感谢您的想法。但我不明白关于如何通过使矢量正态化的部分,我可以将两侧凸出成类似于球体的形状?我如何凸出双方? – Carven

2

快速解释样本中的代码。你应该看看功能void drawSphere(double r, int lats, int longs)。参数lat定义您的球体中有多少条水平线以及多少条垂直线。 r是你的球体的半径。

现在有一个迭代超过lat/lon和顶点坐标计算,使用简单的三角。

计算出的顶点现在使用glVertex...()作为GL_QUAD_STRIP发送到您的GPU,这意味着您要发送与先前发送的两个相同的四个顶点。

现在所有你必须了解的是三角函数是如何工作的,但我想你可以很容易地找到它。

1

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

+3

虽然我理解了“半英寸”的含义,在这种情况下,我认为你可能想为另外95%的不熟练的[胡言乱语俚语](http://en.wikipedia.org/wiki/Rhyming_slang)的读者编辑它! – Flexo

+1

啊哈!采取点:-)我的意思是'捏'在'学习';-) – blockchaindev

12

我会进一步解释使用经度和纬度生成一个球体的一种流行方式(另 方式,icospheres,已经在最流行的答案在写这篇文章的时候解释。)

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

˚Füv)= [cos(u)的* SIN(v)* R,COS(v)* R,罪(U )* sin(v)* r]

其中:

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

生成球体包括以固定间隔评估参数函数。

例如,要生成16行经度的,会有沿着û轴线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) 
} 
} 
+0

投票表决似乎有点苛刻。这是通过球体参数方程提出离散构造的唯一答案之一。根据一个球体可能被认为是一堆圆圈在它们靠近两极时缩小的情况,这也可能更容易理解。 –

+1

你好,我只想指出,p0,p1,p2,p3的每个值的第二个应该是v或vn,而不是u或un。 – nicole

+0

@nicole:感谢您的纠正。 –

0

我的示例如何使用“三角形带”来绘制“极性”球体的),它由在图个对:

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角度(从下一个平行线)。

0

的一种方法是使面向相机四,写一个顶点和片段着色器呈现的东西,看起来像一个球体。你可以使用公式可以在互联网上找到一个圆圈/球体。

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

任何人都可以发表评论,如果他们曾经尝试这样做,或者如果它过于昂贵而不实用?

+0

这只在平行投影下才是真实的。如果使用透视投影,则渲染输出中的球体轮廓不是**,通常是一个圆。 –

0

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

的一个好方法,以产生一个N面的多面体,其中所有顶点在相同的球体所有其表面具有相同的面积开始与二十面体和迭代子分割和规范化其三角形面铺设(如在接受的答案中提出)。例如十二面体实际上是truncated icosahedrons

Regular icosahedrons有20个面(12个顶点),可以很容易地从3个黄金矩形构造;这只是一个以此为起点而不是八面体的问题。你可以找到一个例子here

我知道这是一个有点偏离主题,但我相信它会帮助,如果有人送过来找这个特定的情况下。