2011-10-07 95 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

我知道這是一個有點偏離主題,但我相信它會幫助,如果有人送過來找這個特定的情況下。