I am writing a simple climate simulation which will be released soon as frontend for the Global Processing unit project.
One of the tasks is about plotting the Earth in OpenGL, for this I have bitmaps with continents, clouds, temperatures, etc, which I would like to project on the sphere.
I read through documentation on how to texture a sphere. After a while, I decide to go for this approach instead (in Freepascal, which is a phantastic Open source substitute for Delphi):
I create first a "sphere grid" in an array called sphere3d:
procedure init3DGrid(var w : TWorld; var clima : TClima); var i, j : Longint; p1 :T3DPoint; lat, lon, altitude : Extended; begin for j := 0 to 179 do for i := 0 to 359 do begin lat := YtoLat(j); //-90..+90 lon := XtoLon(i); //-180..+180 lat := lat/90 * Pi/2 + Pi/2; //0..180 lon := lon/180 * Pi + Pi; //0..360 // adding half a degree to longitude for triangles // in each second row if (j mod 2 = 0) then lon := lon + 1/720 * 2 * Pi; p1.x := - radius * sin(lat) * cos(lon); p1.z := radius * sin(lat) * sin(lon); p1.y := - radius * cos(lat); sphere3D[i][j] := p1; end; end;function XtoLon(x : Longint) : Double; begin if (x<0) or (x > 359) then raise Exception.create('x on array has to be between 0 and 359 but was '+IntToStr(x)); Result := x - 180; end;
function YtoLat(y : Longint) : Double; begin if (y<0) or (y > 180) then raise Exception.create('y on array has to be between 0 and 180 but was '+IntToStr(y)); Result := 90 - y; end;
And in the Paint method of my extended TOpenGLControl I do a call to plot3d with my color bitmap and the vertex array computed in the previous procedure Init3dgrid:
procedure plot3d(vertex : P3DGrid; colors : PGridColor); var i, j, target_i, target_j : Longint; p1,p2,p3,p4 : T3DPoint; r, g, b : Extended; begin for j := 0 to 179 do for i := 0 to 359 do begin target_i := i+1; target_j := j+1; if (target_i>359) then target_i := target_i-359; if (target_j>179) then target_j := target_j-179; p1 := vertex^[i] [j]; p2 := vertex^[target_i][j]; p3 := vertex^[i][target_j]; p4 := vertex^[target_i][target_j]; r := Red(colors^[i][j])/255; g := Green(colors^[i][j])/255; b := Blue(colors^[i][j])/255; glBegin(GL_TRIANGLES); glColor3f(r,g,b); glVertex3f( p1.x, p1.y, p1.z); glVertex3f( p2.x, p2.y, p2.z); glVertex3f( p3.x, p3.y, p3.z); glEnd(); glBegin(GL_TRIANGLES); glColor3f(r,g,b); glVertex3f( p2.x, p2.y, p2.z); glVertex3f( p3.x, p3.y, p3.z); glVertex3f( p4.x, p4.y, p4.z); glEnd(); end; end;
For my limited knowledge of OpenGL, this is easier to setup (and for me to understand) than loading a texture and using a cubic or cilindric projection on a sphere. The performance of this solution is also acceptable, at least for my Earth surface with 129600 (360x180*2) triangles.