Per creare una mappa di rifrazione, dobbiamo immaginare di tagliare tutta la scenza all'altezza dell'acqua: ogni cosa che si trova sotto il
piano di sezione, sarà ciò che effettivamente viene visto attraverso la rifrazione. Per far questo, prima di tutto,
bisogna dichiarare delle nuove variabili:
Private WaterHeight AsSingle = 5.0
Private RefractionTarget As RenderTarget2D
Private RefractionMap As Texture2D
WaterHeight rappresenta l'altezza dell'acqua, ossia la coordinata Y alla quale verrà posta la sua superficie. RefractionMap, invece,
rappresenta un'immagine, che conterrà, ovviamente, la refraction map da noi calcolata in precedenza. RefractionTarget è una
variabile di tipo nuovo: RenderTarget2D. Il render target ("bersaglio del rendering") costituisce il flusso sul quale viene disegnata la
scena vera e propria. Abbiamo sempre usato un render target, senza accorgercene: lo schermo. Lo stream ad esso associato, che inviava
informazioni direttamente al monitor per la visualizzazione, era il flusso di dati che usavamo. Ora che dobbiamo disegnare una speciale map,
ed è necessario vedere una parte della scena, basta ridirigere il rendering da un'altra parte, ossia su un render target alternativo,
che in questo caso si chiama RefractionTarget: ciò che vi verrà disegnato sopra sarà poi trasferito su una semplice
immagine, la refraction map, appunto. Per inizializzare il render target, serve questo codice:
Protected Overrides Sub LoadContent()
Shader = GetEffect(AppPath & "\SimpleShader.fx")
Grass = GetTexture(AppPath & "\grass.dds")
Sand = GetTexture(AppPath & "\sand.dds")
Rock = GetTexture(AppPath & "\rock.dds")
Snow = GetTexture(AppPath & "\snow.dds")
For I As Byte = 0 To 4
SkyFaces(I) = GetTexture(AppPath & "\skyface" & (I + 1) & ".png")
NextDim Params As PresentationParameters = _
Me.GraphicsDevice.PresentationParameters
'Inizializza il render target. Il primo parametro è
'il device grafico da usare; il secondo e il terzo sono
'le dimensioni della finestra; il quarto indica i livelli
'di mip map da creare (uno solo in questo caso); il
'quinto indica il formato di superficie da usare: lo
'otteniamo direttamente dal device
RefractionTarget = New RenderTarget2D(Me.GraphicsDevice, _
Params.BackBufferWidth, Params.BackBufferHeight, 1, _
Me.GraphicsDevice.DisplayMode.Format)
LoadHeightMap(GetTexture(AppPath & "\HeightMap.bmp"))
LoadSkyDome()
SetVertices()
SetIndices()
SetNormals()
CopyToBuffer()
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _
MathHelper.PiOver4, _
Me.GraphicsDevice.Viewport.AspectRatio, _
1, 500)
CameraPosition = New Vector3(0, 40, TerrainLength / 2)
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
PrevMouseState = Mouse.GetState
UpdateView()
MyBase.LoadContent()
End Sub
Sezionare il mondo
Per prima cosa, dobbiamo tagliare in due il mondo 3D e far sì che sia visibile solo la parte di terreno che la telecamera può
fisicamente vedere attraverso l'acqua. Questa è la funzione usata per creare un piano (Plane):
'Crea un piano a partire dall'altezza e dalla sua normalePrivateFunction CreatePlane(ByVal Height AsSingle, _
ByVal Normal As Vector3, ByVal ClipAbove AsBoolean)
Normal.Normalize()
Dim PlaneCoef AsNew Vector4(Normal, Height)
'HSMatrix sta per Homogeneous Space MatrixDim HSMatrix As Matrix = _
Matrix.Transpose( _
Matrix.Invert(ViewMatrix * ProjectionMatrix))
If ClipAbove Then
PlaneCoef *= -1
EndIf
PlaneCoef = Vector4.Transform(PlaneCoef, HSMatrix)
ReturnNew Plane(PlaneCoef)
EndFunction
Non ho commentato il sorgente perchè ci sarebbero troppe cose da dire. Prima di tutto, bisogna sapere cosa occorre per creare un
piano: è necessario una normale, che ne indichi l'inclinazione, e un'altezza, che specifichi la distanza del piano dal centro.
Si può dimostrare geometricamente che una distanza e una normale indicano uno e un solo piano dello spazio. Ora abbiamo questi
due dati racchiusi in un Vector4. Una volta che avremo creato il piano, esso eliminerà automaticamente dalla scena tutto ciò
che vi si trova al di sotto. Aggiungendo un'altra variabile booleana, possiamo anche introdurre una variazione sul tema e decidere se
eliminare tutto ciò che si trova sotto o sopra: in questo modo potremo riusare la funzione per creare la reflection map. Per
invertire la direzione di clipping (let. "tagliar via"), basta invertire il Vector4, moltiplicandolo per -1. Tuttavia, avrete notato che c'è
dell'altro codice. Infatti, il processo di clipping avviene dopo che tutti i vertici sono passati attraverso il vertex shader e le
cui coordinate, quindi, sono state trasformate in coordinate normalizzate per il device grafico. Questo significa che ogni vertice visibile
possiede coordinate X e Y comprese tra -1 e 1, e Z compresa tra 0 e 1; ma noi abbiamo passato la normale e l'altezza in riferimento allo
spazio 3d standard! Per questo, bisogna trasformare il vettore a quattro dimensioni PlaneCoef con la trasformazione inversa rispetto
a quella operata dal vertex shader. Dato che quest'ultima moltiplica le posizioni per una matrice composta da View e Projection, noi
dovremmo eseguire l'operazione inversa. Siccome non si può "dividere per una matrice", bisogna moltiplicare il vettore per la
trasposta dell'invertita della matrice originaria. Non pretendo che capiate il funzionamento matematico, ma è necessario capire perchè
lo si fa.
Disegnare la Refraction Map
Abbiamo completato la funzione che crea piani di sezione. Prima di usarla, dobbiamo apportare un'importante modifica al sorgente. Dato che
avremo bisogno di disegnare solo il terreno per ottenere una mappa corretta, bisogna racchiudere tutto il codice che esegue questo
compito in una nuova procedura. Modificate Draw come segue:
#Region "Aggiornamento mondo 3D"
PrivateSub DrawTerrain()
Shader.CurrentTechnique = Shader.Techniques("MultiTextured")
Shader.Parameters("View").SetValue(ViewMatrix)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue( _
Matrix.CreateTranslation( _
New Vector3(-TerrainWidth / 2, 0, TerrainLength / 2)))
Dim LightDirection AsNew Vector3(27.73, -17.67, 6.14)
LightDirection.Normalize()
Shader.Parameters("LightEnabled").SetValue(True)
Shader.Parameters("LightDirection").SetValue(LightDirection)
Shader.Parameters("AmbientFactor").SetValue(0.3F)
Shader.Parameters("Texture0").SetValue(Sand)
Shader.Parameters("Texture1").SetValue(Grass)
Shader.Parameters("Texture2").SetValue(Rock)
Shader.Parameters("Texture3").SetValue(Snow)
Shader.Begin()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = VDeclaration
Me.GraphicsDevice.DrawIndexedPrimitives( _
PrimitiveType.TriangleList, 0, 0, _
Vertices.Length, 0, Indices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
EndSub
'...ProtectedOverridesSub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawSky()
Me.CopyToBuffer()
DrawTerrain()
MyBase.Draw(gameTime)
EndSub#End Region
Ora possiamo passare alla creazione vera e propria della refraction map:
'Crea una refraction map della scena correntePrivateSub DrawRefractionMap()
'Crea un nuovo piano di sezione. La normale punta nella
'direzione negativa delle Y e taglia via tutto ciò
'che si trova sotto di lui. Il "sotto" è relativo
'alla sua normale, poichè, dato che viene rivolto
'verso il basso, in realtà elimina la parte superiore
'del paesaggioDim RefractionPlane As Plane = _
CreatePlane(WaterHeight + 1.5, New Vector3(0, -1, 0), False)
'Imposta il piano di sezioneMe.GraphicsDevice.ClipPlanes(0).Plane = RefractionPlane
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = True
'Cambia il render target del deviceMe.GraphicsDevice.SetRenderTarget(0, RefractionTarget)
'Pulisce la scena, colorando tutto di neroMe.GraphicsDevice.Clear(ClearOptions.Target Or _
ClearOptions.DepthBuffer, Color.Black, 1.0F, 0)
'Disegna il terreno: tutto ciò che ne consegue
'viene posto sul render target e NON sullo schermo
DrawTerrain()
'Disabilita il piano di sezioneMe.GraphicsDevice.ClipPlanes(0).IsEnabled = False
'Reimposta il render target sullo schermo. Notare
'che impostarlo a Nothing equivale a impostare il
'target predefinito, ossia lo schermo.Me.GraphicsDevice.SetRenderTarget(0, Nothing)
'Ottiene la refraction map in forma di immagine
RefractionMap = RefractionTarget.GetTexture()
'Ripulisce lo schermo per il disegno vero e proprioMe.GraphicsDevice.Clear(Color.CornflowerBlue)
EndSub
'...ProtectedOverridesSub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawSky()
Me.CopyToBuffer()
DrawTerrain()
MyBase.Draw(gameTime)
EndSub
Facendo correre il programma, non vedrete niente, ed è normale, perchè la refraction map resta in memoria. Per vedere se
tutto funziona correttamente, potete inserire questo codice:
ProtectedOverridesSub Update(ByVal GameTime As GameTime)
ProcessMouseInput(GameTime.ElapsedGameTime.TotalSeconds)
ProcessKeyboardInput()
'Se si preme M, salva l'immagine sul file prova.jpg,
'che si troveà nella cartella bin/Debug del progettoIf k.IsKeyDown(Keys.M) Then
RefractionMap.Save("prova.jpg", ImageFileFormat.Jpg)
'Esce dal gioco - potete anche toglierloMe.Exit()
EndIfMyBase.Update(GameTime)
EndSub
E otterrete questo risultato:
Solo quello che si vede sotto l'acqua
Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Input
Imports Microsoft.Xna.Framework.Graphics
Imports System.Runtime.InteropServices
PublicClass Game
Inherits Microsoft.Xna.Framework.Game
#Region "Strutture aggiuntive"
PrivateShared DoubleSize AsByte = Marshal.SizeOf(GetType(Double))
PrivateStructure VertexPositionNormalColor
Public Position As Vector3
Public Normal As Vector3
Public Color As Color
PublicShared SizeInBytes As Int16 = 7 * 4
PublicShared VertexElements() As VertexElement = New VertexElement() _
{ _
New VertexElement(0, 0, VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Position, 0), _
New VertexElement(0, DoubleSize * 3, _
VertexElementFormat.Color, _
VertexElementMethod.Default, _
VertexElementUsage.Color, 0), _
New VertexElement(0, DoubleSize * 4, _
VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Normal, 0) _
}
EndStructurePrivateStructure VertexPositionNormalMultitexture
Public Position As Vector3
Public Normal As Vector3
Public TextureCoordinate As Vector4
Public TextureWeight As Vector4
PublicShared SizeInBytes As Int16 = (3 + 3 + 4 + 4) * 4
PublicShared VertexElements As VertexElement() = New VertexElement() _
{ _
New VertexElement(0, 0, VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Position, 0), _
New VertexElement(0, 4 * 3, _
VertexElementFormat.Vector3, _
VertexElementMethod.Default, _
VertexElementUsage.Normal, 0), _
New VertexElement(0, 4 * 6, _
VertexElementFormat.Vector4, _
VertexElementMethod.Default, _
VertexElementUsage.TextureCoordinate, 0), _
New VertexElement(0, 4 * 10, _
VertexElementFormat.Vector4, _
VertexElementMethod.Default, _
VertexElementUsage.TextureCoordinate, 1) _
}
EndStructurePrivateStructure SkyCubeFace
Public Vertices() As VertexPositionTexture
Public Indices() As Int32
Public Texture As Texture2D
EndStructure#End Region#Region "Variabili globali"
Private AppPath AsString = _
My.Application.Info.DirectoryPath
Private Graphics As GraphicsDeviceManager
'VERTICI, INDICI E BUFFER ------------------------Private Shader As Effect
Private Vertices() As VertexPositionNormalMultitexture
Private Indices() As Int32
Private VBuffer As VertexBuffer
Private IBuffer As IndexBuffer
Private VDeclaration As VertexDeclaration
Private SDeclaration As VertexDeclaration
'MATRICI -----------------------------------------Private ViewMatrix As Matrix
Private ProjectionMatrix As Matrix
Private WorldMatrix As Matrix
'HEIGHTDATA --------------------------------------Private HeightData(,) AsSinglePrivateConst MinHeight AsSingle = 0
PrivateConst MaxHeight AsSingle = 30
Private TerrainWidth, TerrainLength As Int32
'TELECAMERA -------------------------------------Private PrevMouseState As MouseState
Private LeftRightRotation AsSingle = MathHelper.PiOver2
Private UpDownRotation AsSingle = -MathHelper.Pi / 10.0F
Private CameraPosition As Vector3
Private RotationSpeed AsSingle = 0.3
Private MoveSpeed AsSingle = 1
'TEXTURE ----------------------------------------Private Grass As Texture2D
Private Sand As Texture2D
Private Rock As Texture2D
Private Snow As Texture2D
'CIELO ------------------------------------------Private SkyFaces(4) As Texture2D
Private SkyCube(4) As SkyCubeFace
'ACQUA ------------------------------------------Private WaterHeight AsSingle = 5.0
Private RefractionTarget As RenderTarget2D
Private RefractionMap As Texture2D
#End Region#Region "Funzioni utili"
PrivateFunction GetEffect(ByVal FileName AsString) As Effect
Dim CompEffect As CompiledEffect = _
Effect.CompileEffectFromFile(FileName, _
Nothing, Nothing, _
CompilerOptions.None, _
TargetPlatform.Windows)
ReturnNew Effect(Me.GraphicsDevice, _
CompEffect.GetEffectCode, _
CompilerOptions.None, Nothing)
EndFunctionPrivateFunction GetTexture(ByVal FileName AsString)
Return Texture2D.FromFile(Me.GraphicsDevice, FileName)
EndFunction#End Region#Region "Caricamento Heightmap"
PrivateSub LoadHeightMap(ByVal HeightMap As Texture2D)
TerrainWidth = HeightMap.Width
TerrainLength = HeightMap.Height
Dim Colors(TerrainWidth * TerrainLength - 1) As Color
HeightMap.GetData(Colors)
For I As Int32 = 0 To TerrainWidth - 1
ReDim HeightData(I, TerrainLength - 1)
NextFor X As Int32 = 0 To TerrainWidth - 1
For Z As Int32 = 0 To TerrainLength - 1
HeightData(X, Z) = MinHeight + _
(Colors(X + Z * TerrainWidth).R / 255) * _
(MaxHeight - MinHeight)
NextNext
VDeclaration = New VertexDeclaration(Me.GraphicsDevice, _
VertexPositionNormalMultitexture.VertexElements)
EndSubPrivateSub SetVertices()
ReDim Vertices(TerrainWidth * TerrainLength - 1)
Dim Strip AsSingle = (MaxHeight - MinHeight) / 4
For X As Int32 = 0 To TerrainWidth - 1
For Z As Int32 = 0 To TerrainLength - 1
With Vertices(X + Z * TerrainWidth)
Dim Y AsSingle = HeightData(X, Z)
.Position = New Vector3(X, Y, -Z)
.TextureCoordinate.X = X / 32
.TextureCoordinate.Y = Z / 32
.TextureWeight.X = MathHelper.Clamp(1.0F - Math.Abs(Y - 0) / 8.0F, 0, 1)
.TextureWeight.Y = MathHelper.Clamp(1.0F - Math.Abs(Y - 12) / 6.0F, 0, 1)
.TextureWeight.Z = MathHelper.Clamp(1.0F - Math.Abs(Y - 20) / 6.0F, 0, 1)
.TextureWeight.W = MathHelper.Clamp(1.0F - Math.Abs(Y - 30) / 6.0F, 0, 1)
Dim Total AsSingle = .TextureWeight.X
Total += .TextureWeight.Y
Total += .TextureWeight.Z
Total += .TextureWeight.W
.TextureWeight.X /= Total
.TextureWeight.Y /= Total
.TextureWeight.Z /= Total
.TextureWeight.W /= Total
EndWithNextNextEndSubPrivateSub SetIndices()
ReDim Indices((TerrainWidth - 1) * (TerrainLength - 1) * 6 - 1)
Dim Counter As Int32 = 0
For X As Int32 = 0 To TerrainWidth - 2
For Z As Int32 = 0 To TerrainLength - 2
Dim LowerLeft As Int16 = X + Z * TerrainWidth
Dim LowerRight As Int16 = (X + 1) + Z * TerrainWidth
Dim TopLeft As Int16 = X + (Z + 1) * TerrainWidth
Dim TopRight As Int16 = (X + 1) + (Z + 1) * TerrainWidth
Indices(Counter) = TopLeft
Indices(Counter + 1) = LowerRight
Indices(Counter + 2) = LowerLeft
Counter += 3
Indices(Counter) = TopLeft
Indices(Counter + 1) = TopRight
Indices(Counter + 2) = LowerRight
Counter += 3
NextNextEndSubPrivateSub SetNormals()
For I As Int32 = 0 To Vertices.Length - 1
Vertices(I).Normal = New Vector3(0, 0, 0)
NextFor I As Int16 = 0 To (Indices.Length / 3) - 1
Dim Index1 As Int16 = Indices(I * 3)
Dim Index2 As Int16 = Indices(I * 3 + 1)
Dim Index3 As Int16 = Indices(I * 3 + 2)
Dim Side1 As Vector3 = _
Vertices(Index1).Position - Vertices(Index3).Position
Dim Side2 As Vector3 = _
Vertices(Index1).Position - Vertices(Index2).Position
Dim Normal As Vector3 = Vector3.Cross(Side1, Side2)
Vertices(Index1).Normal += Normal
Vertices(Index2).Normal += Normal
Vertices(Index3).Normal += Normal
NextForEach V As VertexPositionNormalMultitexture In Vertices
V.Normal.Normalize()
NextEndSubPrivateSub CopyToBuffer()
VBuffer = New VertexBuffer(Me.GraphicsDevice, _
Vertices.Length * VertexPositionNormalMultitexture.SizeInBytes, _
BufferUsage.WriteOnly)
VBuffer.SetData(Vertices)
IBuffer = New IndexBuffer(Me.GraphicsDevice, _
GetType(Int32), Indices.Length, BufferUsage.WriteOnly)
IBuffer.SetData(Indices)
Me.GraphicsDevice.Indices = IBuffer
Me.GraphicsDevice.Vertices(0).SetSource( _
VBuffer, 0, VertexPositionNormalMultitexture.SizeInBytes)
EndSub#End Region#Region "Cielo"
PrivateFunction GetElements(Of T)(ByVal Sources() As T, ByVal ParamArray Indices() As Int32) As T()
Dim Result(Indices.Length - 1) As T
For I As Int16 = 0 To Indices.Length - 1
Result(I) = Sources(Indices(I))
NextReturn Result
EndFunctionPrivateSub LoadSkyDome()
Dim SkyVertices(7) As VertexPositionTexture
Dim HalfSize As Int16 = 200
Dim HalfHeight As Int16 = 150
SkyVertices(0).Position = New Vector3(-HalfSize, 200, HalfSize)
SkyVertices(1).Position = New Vector3(-HalfSize, 200, -HalfSize)
SkyVertices(2).Position = New Vector3(HalfSize, 200, HalfSize)
SkyVertices(3).Position = New Vector3(HalfSize, 200, -HalfSize)
SkyVertices(4).Position = New Vector3(-HalfSize, 0, HalfSize)
SkyVertices(5).Position = New Vector3(-HalfSize, 0, -HalfSize)
SkyVertices(6).Position = New Vector3(HalfSize, 0, HalfSize)
SkyVertices(7).Position = New Vector3(HalfSize, 0, -HalfSize)
SkyCube(0).Vertices = GetElements(SkyVertices, 0, 1, 2, 3)
SkyCube(1).Vertices = GetElements(SkyVertices, 5, 1, 4, 0)
SkyCube(2).Vertices = GetElements(SkyVertices, 4, 0, 6, 2)
SkyCube(3).Vertices = GetElements(SkyVertices, 6, 2, 7, 3)
SkyCube(4).Vertices = GetElements(SkyVertices, 7, 3, 5, 1)
With SkyCube(0)
.Indices = New Int32() {0, 1, 2, 2, 1, 3}
.Texture = SkyFaces(0)
.Vertices(0).TextureCoordinate = New Vector2(0, 1)
.Vertices(1).TextureCoordinate = New Vector2(0, 0)
.Vertices(2).TextureCoordinate = New Vector2(1, 1)
.Vertices(3).TextureCoordinate = New Vector2(1, 0)
EndWithFor I AsByte = 1 To 4
With SkyCube(I)
.Indices = New Int32() {0, 1, 2, 2, 1, 3}
.Texture = SkyFaces(I)
.Vertices(0).TextureCoordinate = New Vector2(0, 0.5)
.Vertices(1).TextureCoordinate = New Vector2(0, 0)
.Vertices(2).TextureCoordinate = New Vector2(1, 0.5)
.Vertices(3).TextureCoordinate = New Vector2(1, 0)
EndWithNext
SDeclaration = New VertexDeclaration(Me.GraphicsDevice, VertexPositionTexture.VertexElements)
EndSubPrivateSub DrawSky()
Dim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
Me.GraphicsDevice.RenderState.CullMode = CullMode.None
For I AsByte = 0 To 4
Shader.CurrentTechnique = Shader.Techniques("Textured")
Shader.Parameters("View").SetValue(ViewMatrix)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue(Matrix.Identity)
Shader.Parameters("LightEnabled").SetValue(False)
Shader.Parameters("ATexture").SetValue(SkyFaces(I))
Shader.Begin()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = SDeclaration
.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, SkyCube(I).Vertices, 0, SkyCube(I).Vertices.Length, SkyCube(I).Indices, 0, SkyCube(I).Indices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
NextMe.GraphicsDevice.RenderState.CullMode = Prev
EndSub#End Region#Region "Acqua"
PrivateFunction CreatePlane(ByVal Height AsSingle, _
ByVal Normal As Vector3, ByVal ClipAbove AsBoolean)
Normal.Normalize()
Dim PlaneCoef AsNew Vector4(Normal, Height)
Dim HSMatrix As Matrix = _
Matrix.Transpose( _
Matrix.Invert(ViewMatrix * ProjectionMatrix))
If ClipAbove Then
PlaneCoef *= -1
EndIf
PlaneCoef = Vector4.Transform(PlaneCoef, HSMatrix)
ReturnNew Plane(PlaneCoef)
EndFunctionPrivateSub DrawRefractionMap()
Dim RefractionPlane As Plane = _
CreatePlane(WaterHeight + 1.5, New Vector3(0, -1, 0), False)
Me.GraphicsDevice.ClipPlanes(0).Plane = RefractionPlane
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = True
Me.GraphicsDevice.SetRenderTarget(0, RefractionTarget)
Me.GraphicsDevice.Clear(ClearOptions.Target Or ClearOptions.DepthBuffer, Color.Black, 1.0F, 0)
DrawTerrain()
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
RefractionMap = RefractionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
EndSub#End Region#Region "Gestione telecamera"
PrivateSub ProcessMouseInput(ByVal ElapsedTime AsSingle)
Dim CurrentMouseState As MouseState = Mouse.GetState
If CurrentMouseState <> PrevMouseState ThenDim DX AsSingle = _
CurrentMouseState.X - PrevMouseState.X
Dim DY AsSingle = _
CurrentMouseState.Y - PrevMouseState.Y
LeftRightRotation -= DX * RotationSpeed * ElapsedTime
UpDownRotation -= DY * RotationSpeed * ElapsedTime
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
UpdateView()
EndIfEndSubPrivateSub ProcessKeyboardInput()
Dim Motion AsNew Vector3(0, 0, 0)
Dim KeyState As KeyboardState = Keyboard.GetState
If KeyState.IsKeyDown(Keys.W) Then
Motion += New Vector3(0, 0, -1)
EndIfIf KeyState.IsKeyDown(Keys.S) Then
Motion += New Vector3(0, 0, 1)
EndIfIf KeyState.IsKeyDown(Keys.D) Then
Motion += New Vector3(1, 0, 0)
EndIfIf KeyState.IsKeyDown(Keys.A) Then
Motion += New Vector3(-1, 0, 0)
EndIfIf KeyState.IsKeyDown(Keys.Up) Then
Motion += New Vector3(0, 1, 0)
EndIfIf KeyState.IsKeyDown(Keys.Down) Then
Motion += New Vector3(0, -1, 0)
EndIf
UpdatePosition(Motion)
EndSubPrivateSub UpdateView()
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim Forward AsNew Vector3(0, 0, -1)
Dim Up AsNew Vector3(0, 1, 0)
Dim RotForward As Vector3 = _
Vector3.Transform(Forward, Rotation)
Dim RotUp As Vector3 = Vector3.Transform(Up, Rotation)
Dim Target As Vector3 = CameraPosition + RotForward
ViewMatrix = Matrix.CreateLookAt(CameraPosition, Target, RotUp)
EndSubPrivateSub UpdatePosition(ByVal Motion As Vector3)
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion * MoveSpeed
UpdateView()
EndSub#End Region#Region "Gestione risorse"
SubNew()
Me.Graphics = New GraphicsDeviceManager(Me)
Me.Content.RootDirectory = "content"
EndSubProtectedOverridesSub Initialize()
MyBase.Initialize()
EndSubProtectedOverridesSub LoadContent()
Shader = GetEffect(AppPath & "\SimpleShader.fx")
Grass = GetTexture(AppPath & "\grass.dds")
Sand = GetTexture(AppPath & "\sand.dds")
Rock = GetTexture(AppPath & "\rock.dds")
Snow = GetTexture(AppPath & "\snow.dds")
For I AsByte = 0 To 4
SkyFaces(I) = GetTexture(AppPath & "\skyface" & (I + 1) & ".png")
NextDim Params As PresentationParameters = _
Me.GraphicsDevice.PresentationParameters
RefractionTarget = New RenderTarget2D(Me.GraphicsDevice, _
Params.BackBufferWidth, Params.BackBufferHeight, 1, _
Me.GraphicsDevice.DisplayMode.Format)
LoadHeightMap(GetTexture(AppPath & "\HeightMap.bmp"))
LoadSkyDome()
SetVertices()
SetIndices()
SetNormals()
CopyToBuffer()
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _
MathHelper.PiOver4, _
Me.GraphicsDevice.Viewport.AspectRatio, _
1, 500)
CameraPosition = New Vector3(0, 40, TerrainLength / 2)
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
PrevMouseState = Mouse.GetState
UpdateView()
MyBase.LoadContent()
EndSubProtectedOverridesSub UnloadContent()
MyBase.UnloadContent()
EndSub#End Region#Region "Aggiornamento mondo 3D"
PrivateSub DrawTerrain()
Shader.CurrentTechnique = Shader.Techniques("MultiTextured")
Shader.Parameters("View").SetValue(ViewMatrix)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue( _
Matrix.CreateTranslation( _
New Vector3(-TerrainWidth / 2, 0, TerrainLength / 2)))
Dim LightDirection AsNew Vector3(27.73, -17.67, 6.14)
LightDirection.Normalize()
Shader.Parameters("LightEnabled").SetValue(True)
Shader.Parameters("LightDirection").SetValue(LightDirection)
Shader.Parameters("AmbientFactor").SetValue(0.3F)
Shader.Parameters("Texture0").SetValue(Sand)
Shader.Parameters("Texture1").SetValue(Grass)
Shader.Parameters("Texture2").SetValue(Rock)
Shader.Parameters("Texture3").SetValue(Snow)
Shader.Begin()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = VDeclaration
Me.GraphicsDevice.DrawIndexedPrimitives( _
PrimitiveType.TriangleList, 0, 0, _
Vertices.Length, 0, Indices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
EndSubProtectedOverridesSub Update(ByVal GameTime As GameTime)
ProcessMouseInput(GameTime.ElapsedGameTime.TotalSeconds)
ProcessKeyboardInput()
If k.IsKeyDown(Keys.M) Then
RefractionMap.Save("prova.jpg", ImageFileFormat.Jpg)
Me.Exit()
EndIfMyBase.Update(GameTime)
EndSubProtectedOverridesSub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawSky()
Me.CopyToBuffer()
DrawTerrain()
MyBase.Draw(gameTime)
EndSub#End RegionEndClass
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.