Ruotare una sfera usando OpenGL: differenze tra le versioni

Da Gambas-it.org - Wikipedia.
Vai alla navigazione Vai alla ricerca
Nessun oggetto della modifica
Nessun oggetto della modifica
 
(2 versioni intermedie di uno stesso utente non sono mostrate)
Riga 4: Riga 4:
* gb.opengl
* gb.opengl
* gb.opengl.glu
* gb.opengl.glu
  <FONT Color=gray>' ''Variabile per tenere traccia dell'ngolo di rotazione per far ruotare la sfera''</font>
Sarà possibile aumentare o diminuire la dimensione del disegno della Sfera, cliccando rispettivamente sui tasti "+" e "-" della tastiera.
  Private angle As Float  
  <FONT Color=gray>' ''Variabile per tenere traccia dell'angolo di rotazione per far ruotare la sfera''</font>
  Private angle As Float
Private zoom As Short = 50
   
   
   
   
Riga 65: Riga 67:
   GL.LoadIdentity()
   GL.LoadIdentity()
  <FONT Color=gray>' ''FOV = 45°, rapporto larghezza/altezza, clip vicino/lontano:''</font>
  <FONT Color=gray>' ''FOV = 45°, rapporto larghezza/altezza, clip vicino/lontano:''</font>
   Glu.Perspective(45.0, GLArea1.Width / GLArea1.Height, 0.1, 100.0)
   Glu.Perspective(zoom, GLArea1.Width / GLArea1.Height, 0.1, 100.0)
   
   
  <FONT Color=gray>' ''=== Trasformazioni vista ===''</font>
  <FONT Color=gray>' ''=== Trasformazioni vista ===''</font>
Riga 131: Riga 133:
  <FONT Color=gray>' ''Richiede il ridisegno della scena:''</font>
  <FONT Color=gray>' ''Richiede il ridisegno della scena:''</font>
   GLArea1.Refresh()
   GLArea1.Refresh()
End
Public Sub Form_KeyPress()  <FONT Color=gray>' ''Gestione della tastiera per controllare lo zoom''</font>
  Select Case Key.Code
    Case Key["+"]
      zoom -= 1
    Case Key["-"]
      zoom += 1
  End Select
  zoom = Clamp(zoom, 1, 100)
  GLArea1.Refresh
   
   
  End
  End
Riga 143: Riga 161:
* Avvolgerla sulla sfera usando coordinate sferiche (UV)
* Avvolgerla sulla sfera usando coordinate sferiche (UV)
* Farla ruotare, così da simulare il globo.
* Farla ruotare, così da simulare il globo.
Bisognerà utilizzare un file immagine in formato PNG del globo terracqueo.
   
   
Mostriamo un possibile codice, che presuppone che l'immagine della terra sia presente nella cartella "/tmp".
Mostriamo un possibile codice, nel quale sarà mostrato anche un punto rosso sulla posizione geografica della città di Roma.
  Private texID As Integer       <FONT Color=gray>' ''ID della texture OpenGL''</font>
<BR>Sarà possibile altresì aumentare o diminuire la dimensione del disegno della Sfera, cliccando rispettivamente sui tasti "+" e "-" della tastiera.
  Private angle As Float          <FONT Color=gray>' ''Angolo di rotazione della sfera''</font>
Private angle As Float  <FONT Color=gray>' ''Angolo di rotazione della sfera (animazione)''</font>
  Private texID As Integer   <FONT Color=gray>' ''ID della texture caricata da OpenGL''</font>
  Private textureLoaded As Boolean  <FONT Color=gray>' ''Flag che indica se la texture è stata caricata''</font>
Private zoom As Short = 50
   
   
   
   
Riga 158: Riga 178:
   End With
   End With
   
   
   Timer1.Delay = 16  <FONT Color=gray>' ''~60 FPS''</font>
   Timer1.Delay = 32
   Timer1.Start()
   Timer1.Start()
   
   
Riga 164: Riga 184:
   
   
   
   
  Public Sub GLArea1_Ready()
  Public Sub GLArea1_Draw()
<FONT Color=gray>' ''Carica l'immagine PNG da usare come texture terrestre:''</font>
  Dim img As Image = Image.Load("<FONT Color=darkgreen>''/tmp/immagine_globo_terrestre.PNG''</font>")
   
   
   texID = LoadTextureFromImage(img)
   If Not textureLoaded Then
  If texID = 0 Then
    Dim img As Image = Image.Load("<FONT Color=darkgreen>''/tmp/immagine_globo_terrestre.PNG''</font>")
     Message.Error("Errore durante il caricamento della texture.")
    texID = LoadTexture(img)
    textureLoaded = True
     Print "Texture caricata in GLArea1_Draw()"
   Endif
   Endif
End
Public Sub GLArea1_Draw()
   
   
  <FONT Color=gray>' ''Blocca il disegno se la texture non è ancora stata caricata:''</font>
  <FONT Color=gray>' ''Blocca il disegno se la texture non è ancora stata caricata:''</font>
Riga 188: Riga 202:
  <FONT Color=gray>' ''Attiva l'uso delle texture:''</font>
  <FONT Color=gray>' ''Attiva l'uso delle texture:''</font>
   GL.Enable(GL.TEXTURE_2D)
   GL.Enable(GL.TEXTURE_2D)
   GL.Color3f(1.0, 1.0, 1.0)         ' Colore pieno: evita che la texture venga scurita
<FONT Color=gray>' ''Nessuna luce per test chiaro:''</font>
  GL.Disable(GL.LIGHTING)
<FONT Color=gray>' ''Colore pieno: evita che la texture venga scurita:''</font>
   GL.Color3f(1.0, 1.0, 1.0)
   GL.BindTexture(GL.TEXTURE_2D, texID)
   GL.BindTexture(GL.TEXTURE_2D, texID)
   
   
Riga 194: Riga 211:
   GL.MatrixMode(GL.PROJECTION)
   GL.MatrixMode(GL.PROJECTION)
   GL.LoadIdentity()
   GL.LoadIdentity()
   Glu.Perspective(45.0, GLArea1.Width / GLArea1.Height, 0.1, 100.0)
   Glu.Perspective(zoom, GLArea1.Width / GLArea1.Height, 0.1, 100.0)
   
   
  <FONT Color=gray>' ''Posiziona e ruota la "camera" (ossia l'osservatore):''</font>
  <FONT Color=gray>' ''Posiziona e ruota la "camera" (ossia l'osservatore):''</font>
Riga 206: Riga 223:
   
   
  <FONT Color=gray>' ''Aggiunge un punto geografico visibile (es. Roma):''</font>
  <FONT Color=gray>' ''Aggiunge un punto geografico visibile (es. Roma):''</font>
   DrawGeoPoint(41.9, 12.55, 1.00)    ' latitudine, longitudine, raggio (se il valore del raggio distanzia il punto dalla sfera)
   DrawGeoPoint(41.53, 12.32, 1.0)    <FONT Color=gray>' ''latitudine, longitudine, raggio (se il valore del raggio distanzia il punto dalla sfera)''</font>
   
   
   GL.Flush()
   GL.Flush()
Riga 225: Riga 242:
   
   
   
   
  Private Function LoadTextureFromImage(img As Image) As Integer    <FONT Color=gray>' ''Carica un oggetto Image come texture OpenGL''</font>
  Private Function LoadTexture(img As Image) As Integer    <FONT Color=gray>' ''Carica la texture da un oggetto Image''</font>
   
   
   Dim tex As Integer
   Dim tex As Integer
  Dim w As Integer = img.Width
  Dim h As Integer = img.Height
   
   
  If img = Null Then
   tex = GL.GenTextures(1)[0]        <FONT Color=gray>' ''Genera un nuovo ID texture''</font>
    Print "Errore: immagine nulla!"
    Return 0
  Endif
   tex = GL.GenTextures(1)[0]        ' Genera un nuovo ID texture
   GL.BindTexture(GL.TEXTURE_2D, tex)
   GL.BindTexture(GL.TEXTURE_2D, tex)
   
   
Riga 308: Riga 322:
   z = radius * Sin(theta) * Sin(phi)
   z = radius * Sin(theta) * Sin(phi)
   
   
   GL.PointSize(8.0)                    <FONT Color=gray>' ''Rende il punto visibile''</font>
   GL.PointSize(3.0)                    <FONT Color=gray>' ''Rende il punto visibile''</font>
   GL.Disable(GL.LIGHTING)
   GL.Disable(GL.LIGHTING)
   GL.Disable(GL.TEXTURE_2D)
   GL.Disable(GL.TEXTURE_2D)
Riga 319: Riga 333:
   GL.Enable(GL.TEXTURE_2D)
   GL.Enable(GL.TEXTURE_2D)
   GL.Enable(GL.LIGHTING)
   GL.Enable(GL.LIGHTING)
End
Public Sub Form_KeyPress()  <FONT Color=gray>' ''Gestione della tastiera per controllare lo zoom''</font>
  Select Case Key.Code
    Case Key["+"]
      zoom -= 1
    Case Key["-"]
      zoom += 1
  End Select
  zoom = Clamp(zoom, 1, 100)
  GLArea1.Refresh
   
   
  End
  End

Versione attuale delle 14:42, 21 lug 2025

Mostreremo un possibile codice, usando OpenGL, con il quale verrà disegnata una sfera blu che ruota in 3D in senso antiorario.

E' necessario attivare una GLArea, un Timer, il Componente grafico per la compatibilità con OpenGL (ad esempio: gb.gui.qt.opengl), nonché i Componenti:

  • gb.opengl
  • gb.opengl.glu

Sarà possibile aumentare o diminuire la dimensione del disegno della Sfera, cliccando rispettivamente sui tasti "+" e "-" della tastiera.

' Variabile per tenere traccia dell'angolo di rotazione per far ruotare la sfera
Private angle As Float
Private zoom As Short = 50


Public Sub Form_Open()

 With Me
   .Title = "Sfera che ruota in senso antiorario"
   .Move(0, 0, Screen.AvailableWidth, Screen.AvailableHeight)
   .Arrangement = Arrange.Fill
 End With

 Timer1.Delay = 16   ' ~60 FPS
 Timer1.Start()

End


Public Sub GLArea1_Draw()

' Pulisce il buffer del colore e della profondità (Z-buffer):
 GL.Clear(GL.COLOR_BUFFER_BIT Or GL.DEPTH_BUFFER_BIT)
' Abilita il test di profondità (necessario per disegnare correttamente gli oggetti 3D sovrapposti):
 GL.Enable(GL.DEPTH_TEST)

' Abilita il sistema di illuminazione OpenGL:
 GL.Enable(GL.LIGHTING)
' Abilita la luce numero 0:
 GL.Enable(GL.LIGHT0)

' Imposta la luce come direzionale (ultimo valore = 0.0):
 Dim lightPos As Float[] = [1.0, 1.0, 1.0, 0.0]

' Componenti della luce: ambientale, diffusa e speculare:
 Dim ambient  As Float[] = [0.2, 0.2, 0.2, 1.0]    ' luce "globale"
 Dim diffuse  As Float[] = [0.8, 0.8, 0.8, 1.0]    ' luce diretta
 Dim specular As Float[] = [1.0, 1.0, 1.0, 1.0]    ' riflessi brillanti

' Applica luce:
 GL.Lightfv(GL.LIGHT0, GL.POSITION, lightPos)
 GL.Lightfv(GL.LIGHT0, GL.AMBIENT, ambient)
 GL.Lightfv(GL.LIGHT0, GL.DIFFUSE, diffuse)
 GL.Lightfv(GL.LIGHT0, GL.SPECULAR, specular)

' === Definizione materiale della sfera ===
' Colore e riflessi del materiale della sfera:
 Dim matAmbient  As Float[] = [0.1, 0.1, 0.3, 1.0]    ' colore in ombra
 Dim matDiffuse  As Float[] = [0.2, 0.4, 1.0, 1.0]    ' colore principale
 Dim matSpecular As Float[] = [1.0, 1.0, 1.0, 1.0]    ' riflesso tipo "metallo"
 Dim shininess As Float = 64.0                        ' intensità del riflesso

' Applica il materiale alla superficie FRONT (anteriore):
 GL.Materialfv(GL.FRONT, GL.AMBIENT, matAmbient)
 GL.Materialfv(GL.FRONT, GL.DIFFUSE, matDiffuse)
 GL.Materialfv(GL.FRONT, GL.SPECULAR, matSpecular)
 GL.Materialf(GL.FRONT, GL.SHININESS, shininess)

' === Imposta la proiezione (visione prospettica) ===
 GL.MatrixMode(GL.PROJECTION)
 GL.LoadIdentity()
' FOV = 45°, rapporto larghezza/altezza, clip vicino/lontano:
 Glu.Perspective(zoom, GLArea1.Width / GLArea1.Height, 0.1, 100.0)

' === Trasformazioni vista ===
 GL.MatrixMode(GL.MODELVIEW)
 GL.LoadIdentity()
 GL.Translatef(0.0, 0.0, -4.0)         ' Sposta la scena indietro (zoom out)
 GL.Rotatef(angle, 0.0, 1.0, 0.0)      ' Ruota attorno all'asse Y (in senso antiorario)

' === Disegna la sfera ===
' raggio = 1.0, 30 suddivisioni in altezza e larghezza:
 DrawSphere(1.0, 30, 30)

' Forza l'esecuzione immediata dei comandi:
 GL.Flush()

End


Private Procedure DrawSphere(radius As Float, slices As Integer, stacks As Integer)

' Disegna manualmente una sfera usando coordinate sferiche
' slices = numero di "colonne" (longitudine), stacks = "righe" (latitudine)

 Dim i, j As Integer
 Dim theta1, theta2, phi As Float
 Dim x, y, z As Float

 For i = 0 To stacks - 1
   theta1 = Pi * i / stacks           ' latitudine corrente
   theta2 = Pi * (i + 1) / stacks     ' latitudine successiva

   GL.Begin(GL.QUAD_STRIP)            ' Usa strisce di quadrilateri

   For j = 0 To slices
     phi = 2 * Pi * j / slices        ' longitudine corrente

' Primo punto sul "parallelo" corrente:
     x = Cos(phi) * Sin(theta1)
     y = Cos(theta1)
     z = Sin(phi) * Sin(theta1)

     GL.Normal3f(x, y, z)             ' Normale per illuminazione
     GL.Vertex3f(radius * x, radius * y, radius * z)

' Secondo punto sul "parallelo" successivo:
     x = Cos(phi) * Sin(theta2)
     y = Cos(theta2)
     z = Sin(phi) * Sin(theta2)
     GL.Normal3f(x, y, z)
     GL.Vertex3f(radius * x, radius * y, radius * z)
   Next

   GL.End()
 Next

End


Public Sub Timer1_Timer()

' Ruota di 1 grado a ogni fotogramma (~60 FPS):
 angle += 1.0
' Riavvolge dopo un giro completo:
 If angle >= 360 Then angle = 0
' Richiede il ridisegno della scena:
 GLArea1.Refresh()

End


Public Sub Form_KeyPress()   ' Gestione della tastiera per controllare lo zoom

 Select Case Key.Code
   Case Key["+"]
     zoom -= 1
   Case Key["-"]
     zoom += 1
 End Select

 zoom = Clamp(zoom, 1, 100)

 GLArea1.Refresh

End


Sfera 3D texturizzata con mappa terrestre e coordinate geografiche

Ora mostreremo un codice per ottenere una sfera 3D texturizzata con mappa terrestre, rotante in senso antiorario e con posizionamento preciso di punti geografici.

Per ottenere la rappresentazione della mappa terrestre al di sopra di una sfera 3D disegnata manualmente in Gambas con OpenGL, è necessario applicare una texture.
Per pplicare una texture mappata che rappresenti il globo terrestre, si deve:

  • Usare un'immagine 2D rettangolare della Terra (es. la mappa in proiezione equirettangolare, ossia: orizzontale = longitudine, verticale = latitudine);
  • Avvolgerla sulla sfera usando coordinate sferiche (UV)
  • Farla ruotare, così da simulare il globo.

Mostriamo un possibile codice, nel quale sarà mostrato anche un punto rosso sulla posizione geografica della città di Roma.
Sarà possibile altresì aumentare o diminuire la dimensione del disegno della Sfera, cliccando rispettivamente sui tasti "+" e "-" della tastiera.

Private angle As Float   ' Angolo di rotazione della sfera (animazione)
Private texID As Integer   ' ID della texture caricata da OpenGL
Private textureLoaded As Boolean   ' Flag che indica se la texture è stata caricata
Private zoom As Short = 50


Public Sub Form_Open()

 With Me
   .Title = "Sfera che ruota in senso antiorario"
   .Move(0, 0, Screen.AvailableWidth, Screen.AvailableHeight)
   .Arrangement = Arrange.Fill
 End With

 Timer1.Delay = 32
 Timer1.Start()

End


Public Sub GLArea1_Draw()

 If Not textureLoaded Then
   Dim img As Image = Image.Load("/tmp/immagine_globo_terrestre.PNG") 
   texID = LoadTexture(img)
   textureLoaded = True
   Print "Texture caricata in GLArea1_Draw()"
 Endif

' Blocca il disegno se la texture non è ancora stata caricata:
 If texID = 0 Then Return

' Pulisce schermo e buffer di profondità:
 GL.Clear(GL.COLOR_BUFFER_BIT Or GL.DEPTH_BUFFER_BIT)
 GL.Enable(GL.DEPTH_TEST)

' Attiva l'uso delle texture:
 GL.Enable(GL.TEXTURE_2D)
' Nessuna luce per test chiaro:
 GL.Disable(GL.LIGHTING)
' Colore pieno: evita che la texture venga scurita:
 GL.Color3f(1.0, 1.0, 1.0)
 GL.BindTexture(GL.TEXTURE_2D, texID)

' Imposta proiezione prospettica:
 GL.MatrixMode(GL.PROJECTION)
 GL.LoadIdentity()
 Glu.Perspective(zoom, GLArea1.Width / GLArea1.Height, 0.1, 100.0)

' Posiziona e ruota la "camera" (ossia l'osservatore):
 GL.MatrixMode(GL.MODELVIEW)
 GL.LoadIdentity()
 GL.Translatef(0.0, 0.0, -4.0)      ' Allontana la sfera dalla camera
 GL.Rotatef(angle, 0.0, 1.0, 0.0)   ' Ruota la sfera attorno all'asse Y

' Disegna la sfera con la texture mappata:
 DrawTexturedSphere(1.0, 40, 40)

' Aggiunge un punto geografico visibile (es. Roma):
 DrawGeoPoint(41.53, 12.32, 1.0)    ' latitudine, longitudine, raggio (se il valore del raggio distanzia il punto dalla sfera)

 GL.Flush()

End


Public Sub Timer1_Timer()

' Incrementa l'angolo per far ruotare la sfera:
 angle += 1.0
 If angle >= 360 Then angle = 0

' Richiede il ridisegno della scena:
 GLArea1.Refresh()

End


Private Function LoadTexture(img As Image) As Integer    ' Carica la texture da un oggetto Image

 Dim tex As Integer
 Dim w As Integer = img.Width
 Dim h As Integer = img.Height

 tex = GL.GenTextures(1)[0]        ' Genera un nuovo ID texture
 GL.BindTexture(GL.TEXTURE_2D, tex)

' Imposta parametri di qualità e comportamento della texture:
 GL.TexParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR)
 GL.TexParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.LINEAR)
 GL.TexParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.REPEAT)
 GL.TexParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.REPEAT)

' Carica i pixel RGBA dell'immagine come texture:
 GL.TexImage2D(img)

 Return tex

End


Private Procedure DrawTexturedSphere(radius As Float, slices As Integer, stacks As Integer)    ' Disegna una sfera con texture mappata usando coordinate UV

 Dim i, j As Integer
 Dim theta1, theta2, phi As Float
 Dim x, y, z, u, v As Float

 For i = 0 To stacks - 1
   theta1 = Pi * i / stacks
   theta2 = Pi * (i + 1) / stacks

   GL.Begin(GL.QUAD_STRIP)
   For j = 0 To slices
     phi = 2 * Pi * j / slices

' Primo vertice:
     x = Cos(phi) * Sin(theta1)
     y = Cos(theta1)
     z = Sin(phi) * Sin(theta1)

     u = 1.0 - j / slices            ' Inversione asse U per correzione "specchio"
     v = i / stacks

     GL.Normal3f(x, y, z)
     GL.TexCoord2f(u, v)
     GL.Vertex3f(radius * x, radius * y, radius * z)

' Secondo vertice:
     x = Cos(phi) * Sin(theta2)
     y = Cos(theta2)
     z = Sin(phi) * Sin(theta2)

     u = 1.0 - j / slices            ' Inversione asse U per correzione "specchio"
     v = (i + 1) / stacks

     GL.Normal3f(x, y, z)
     GL.TexCoord2f(u, v)
     GL.Vertex3f(radius * x, radius * y, radius * z)
   Next
   GL.End()
 Next

End


Private Procedure DrawGeoPoint(lat As Float, lon As Float, radius As Float)    ' Disegna un punto rosso su una latitudine/longitudine geografica

 Dim theta, phi As Float
 Dim x, y, z As Float

' Conversione coordinate geografiche → coordinate sferiche 3D:
 theta = Rad(90.0 - lat)
 phi   = Rad(-(lon + 180.0))          ' Allineato alla texture centrata sul Pacifico: longitudine reale (est positivo)

 x = radius * Sin(theta) * Cos(phi)
 y = radius * Cos(theta)
 z = radius * Sin(theta) * Sin(phi)

 GL.PointSize(3.0)                    ' Rende il punto visibile
 GL.Disable(GL.LIGHTING)
 GL.Disable(GL.TEXTURE_2D)
 GL.Color3f(1.0, 0.0, 0.0)            ' Colore rosso

 GL.Begin(GL.POINTS)
   GL.Vertex3f(x, y, z)
 GL.End()

 GL.Enable(GL.TEXTURE_2D)
 GL.Enable(GL.LIGHTING)

End


Public Sub Form_KeyPress()   ' Gestione della tastiera per controllare lo zoom

 Select Case Key.Code
   Case Key["+"]
     zoom -= 1
   Case Key["-"]
     zoom += 1
 End Select

 zoom = Clamp(zoom, 1, 100)

 GLArea1.Refresh

End