Ruotare una sfera usando OpenGL: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 131: | Riga 131: | ||
<FONT Color=gray>' ''Richiede il ridisegno della scena:''</font> | <FONT Color=gray>' ''Richiede il ridisegno della scena:''</font> | ||
GLArea1.Refresh() | 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''. | |||
<BR>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. | |||
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". | |||
Private texID As Integer <FONT Color=gray>' ''ID della texture OpenGL''</font> | |||
Private angle As Float <FONT Color=gray>' ''Angolo di rotazione della sfera''</font> | |||
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 <FONT Color=gray>' ''~60 FPS''</font> | |||
Timer1.Start() | |||
End | |||
Public Sub GLArea1_Ready() | |||
<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 texID = 0 Then | |||
Message.Error("Errore durante il caricamento della texture.") | |||
Endif | |||
End | |||
Public Sub GLArea1_Draw() | |||
<FONT Color=gray>' ''Blocca il disegno se la texture non è ancora stata caricata:''</font> | |||
If texID = 0 Then Return | |||
<FONT Color=gray>' ''Pulisce schermo e buffer di profondità:''</font> | |||
GL.Clear(GL.COLOR_BUFFER_BIT Or GL.DEPTH_BUFFER_BIT) | |||
GL.Enable(GL.DEPTH_TEST) | |||
<FONT Color=gray>' ''Attiva l'uso delle texture:''</font> | |||
GL.Enable(GL.TEXTURE_2D) | |||
GL.Color3f(1.0, 1.0, 1.0) ' Colore pieno: evita che la texture venga scurita | |||
GL.BindTexture(GL.TEXTURE_2D, texID) | |||
<FONT Color=gray>' ''Imposta proiezione prospettica:''</font> | |||
GL.MatrixMode(GL.PROJECTION) | |||
GL.LoadIdentity() | |||
Glu.Perspective(45.0, GLArea1.Width / GLArea1.Height, 0.1, 100.0) | |||
<FONT Color=gray>' ''Posiziona e ruota la "camera" (ossia l'osservatore):''</font> | |||
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 | |||
<FONT Color=gray>' ''Disegna la sfera con la texture mappata:''</font> | |||
DrawTexturedSphere(1.0, 40, 40) | |||
<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) | |||
GL.Flush() | |||
End | |||
Public Sub Timer1_Timer() | |||
<FONT Color=gray>' ''Incrementa l'angolo per far ruotare la sfera:''</font> | |||
angle += 1.0 | |||
If angle >= 360 Then angle = 0 | |||
<FONT Color=gray>' ''Richiede il ridisegno della scena:''</font> | |||
GLArea1.Refresh() | |||
End | |||
Private Function LoadTextureFromImage(img As Image) As Integer <FONT Color=gray>' ''Carica un oggetto Image come texture OpenGL''</font> | |||
Dim tex As Integer | |||
If img = Null Then | |||
Print "Errore: immagine nulla!" | |||
Return 0 | |||
Endif | |||
tex = GL.GenTextures(1)[0] ' Genera un nuovo ID texture | |||
GL.BindTexture(GL.TEXTURE_2D, tex) | |||
<FONT Color=gray>' ''Imposta parametri di qualità e comportamento della texture:''</font> | |||
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) | |||
<FONT Color=gray>' ''Carica i pixel RGBA dell'immagine come texture:''</font> | |||
GL.TexImage2D(img) | |||
Return tex | |||
End | |||
Private Procedure DrawTexturedSphere(radius As Float, slices As Integer, stacks As Integer) <FONT Color=gray>' ''Disegna una sfera con texture mappata usando coordinate UV''</font> | |||
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 | |||
<FONT Color=gray>' ''Primo vertice:''</font> | |||
x = Cos(phi) * Sin(theta1) | |||
y = Cos(theta1) | |||
z = Sin(phi) * Sin(theta1) | |||
u = 1.0 - j / slices <FONT Color=gray>' ''Inversione asse U per correzione "specchio"''</font> | |||
v = i / stacks | |||
GL.Normal3f(x, y, z) | |||
GL.TexCoord2f(u, v) | |||
GL.Vertex3f(radius * x, radius * y, radius * z) | |||
<FONT Color=gray>' ''Secondo vertice:''</font> | |||
x = Cos(phi) * Sin(theta2) | |||
y = Cos(theta2) | |||
z = Sin(phi) * Sin(theta2) | |||
u = 1.0 - j / slices <FONT Color=gray>' ''Inversione asse U per correzione "specchio"''</font> | |||
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) <FONT Color=gray>' ''Disegna un punto rosso su una latitudine/longitudine geografica''</font> | |||
Dim theta, phi As Float | |||
Dim x, y, z As Float | |||
<FONT Color=gray>' ''Conversione coordinate geografiche → coordinate sferiche 3D:''</font> | |||
theta = Rad(90.0 - lat) | |||
phi = Rad(-(lon + 180.0)) <FONT Color=gray>' ''Allineato alla texture centrata sul Pacifico: longitudine reale (est positivo)''</font> | |||
x = radius * Sin(theta) * Cos(phi) | |||
y = radius * Cos(theta) | |||
z = radius * Sin(theta) * Sin(phi) | |||
GL.PointSize(8.0) <FONT Color=gray>' ''Rende il punto visibile''</font> | |||
GL.Disable(GL.LIGHTING) | |||
GL.Disable(GL.TEXTURE_2D) | |||
GL.Color3f(1.0, 0.0, 0.0) <FONT Color=gray>' ''Colore rosso''</font> | |||
GL.Begin(GL.POINTS) | |||
GL.Vertex3f(x, y, z) | |||
GL.End() | |||
GL.Enable(GL.TEXTURE_2D) | |||
GL.Enable(GL.LIGHTING) | |||
End | End | ||
Versione delle 11:08, 19 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
' Variabile per tenere traccia dell'ngolo di rotazione per far ruotare la sfera
Private angle As Float
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(45.0, 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
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.
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".
Private texID As Integer ' ID della texture OpenGL
Private angle As Float ' Angolo di rotazione della sfera
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_Ready()
' Carica l'immagine PNG da usare come texture terrestre:
Dim img As Image = Image.Load("/tmp/immagine_globo_terrestre.PNG")
texID = LoadTextureFromImage(img)
If texID = 0 Then
Message.Error("Errore durante il caricamento della texture.")
Endif
End
Public Sub GLArea1_Draw()
' 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)
GL.Color3f(1.0, 1.0, 1.0) ' Colore pieno: evita che la texture venga scurita
GL.BindTexture(GL.TEXTURE_2D, texID)
' Imposta proiezione prospettica:
GL.MatrixMode(GL.PROJECTION)
GL.LoadIdentity()
Glu.Perspective(45.0, 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.9, 12.55, 1.00) ' 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 LoadTextureFromImage(img As Image) As Integer ' Carica un oggetto Image come texture OpenGL
Dim tex As Integer
If img = Null Then
Print "Errore: immagine nulla!"
Return 0
Endif
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(8.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