B19. L'effetto Fresnel
Ora è venuto il momento di conferire trasparenza all'acqua, e renderla più realistica. Per far questo, aggiungeremo alla
superficie perfettamente riflettente che abbiamo appena creato gli effetti derivanti dalla rifrazione, usando, quindi, anche la Refraction
Map. Tuttavia, per miscelare i due elementi, bisogna prima sapere come viene matematicamente attuato il calcolo del coefficiente di Fresnel,
che determina quanta riflessione e quanta rifrazione ci siano in ogni punto della superficie.
L'unico fattore che influisce su tale coefficiente è l'angolazione con cui la telecamera osserva l'acqua: più ci si avvicina
alla normale (sguardo a 90° sulla superficie) e maggiore sarà il contributo della rifrazione, fino a diventare il 100% in
corrispondenza del vettore; più la direzione dello sguardo si avvicina ad essere parallela alla superficie,
e maggiore sarà il contributo della riflessione, fino a diventare il 100% quando si guarda dritto verso l'orizzonte. In conclusione,
la composizione dei due fenomeni può essere schematicamente riassunta così:
Nello schema, la freccia blu indica la direzione con la quale si sta osservando, e il raggio verticale superiore simbolizza la normale della
superficie. Dato che la normale è sempre un vettore di lunghezza unitaria, la circonferenza tracciata ha anch'essa raggio unitario, ed
è perciò una circonferenza goniometrica, dove le componente della rifrazione coincide perfettamente con il seno dell'angolo
θ segnalato in figura. Con una semplice formula di trigonometria, però, possiamo provare che sinθ = cos(θ - π/2).
In questa figura più geometricizzata, abbiamo che OB è il vettore normale di modulo 1, e AO è un vettore anch'esso di
modulo 1, il cui verso coincide con quello del vettore telecamera-origine. Quindi, il coseno di θ - π/2 coincide con il coseno dell'angolo
AOB, che è l'angolo compreso tra i due vettori. Qual è quella operazione che, nel caso di vettori di lunghezza unitaria,
restituisce il coseno dell'angolo fra essi compreso? Il prodotto scalare, ancora una volta: la funzione dot. Anche in questo caso, come
nel calcolo della luce, useremo dot() per calcolare le componenti dell'effetto fresnel.
Dato che abbiamo bisogno di un nuovo parametro (la posizione della telecamera) e due nuovi membri della struttura WaterVertexToPixel (ossia
la posizione del punto nello spazio e le coordinate texture della refraction map), modifichiamo il codice di conseguenza:
Non dimenticatevi di passare il nuovo parametro posizione telecamera nel metodo DrawWater:
Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Input
Imports Microsoft.Xna.Framework.Graphics
Imports System.Runtime.InteropServices
Public Class Game
Inherits Microsoft.Xna.Framework.Game
#Region "Strutture aggiuntive"
Private Shared DoubleSize As Byte = Marshal.SizeOf(GetType(Double))
Private Structure VertexPositionNormalColor
Public Position As Vector3
Public Normal As Vector3
Public Color As Color
Public Shared SizeInBytes As Int16 = 7 * 4
Public Shared 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) _
}
End Structure
Private Structure VertexPositionNormalMultitexture
Public Position As Vector3
Public Normal As Vector3
Public TextureCoordinate As Vector4
Public TextureWeight As Vector4
Public Shared SizeInBytes As Int16 = (3 + 3 + 4 + 4) * 4
Public Shared 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) _
}
End Structure
Private Structure SkyCubeFace
Public Vertices() As VertexPositionTexture
Public Indices() As Int32
Public Texture As Texture2D
End Structure
#End Region
#Region "Variabili globali"
Private AppPath As String = _
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
Private WDeclaration As VertexDeclaration
'MATRICI -----------------------------------------
Private ViewMatrix As Matrix
Private ProjectionMatrix As Matrix
Private WorldMatrix As Matrix
Private ReflectionViewMatrix As Matrix
'HEIGHTDATA --------------------------------------
Private HeightData(,) As Single
Private Const MinHeight As Single = 0
Private Const MaxHeight As Single = 30
Private TerrainWidth, TerrainLength As Int32
'TELECAMERA -------------------------------------
Private PrevMouseState As MouseState
Private LeftRightRotation As Single = MathHelper.PiOver2
Private UpDownRotation As Single = -MathHelper.Pi / 10.0F
Private CameraPosition As Vector3
Private RotationSpeed As Single = 0.3
Private MoveSpeed As Single = 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 As Single = 5.0
Private RefractionTarget As RenderTarget2D
Private RefractionMap As Texture2D
Private ReflectionTarget As RenderTarget2D
Private ReflectionMap As Texture2D
Private WaterVertices() As VertexPositionTexture
Private WaterVBuffer As VertexBuffer
Private BumpMap As Texture2D
#End Region
#Region "Funzioni utili"
Private Function GetEffect(ByVal FileName As String) As Effect
Dim CompEffect As CompiledEffect = _
Effect.CompileEffectFromFile(FileName, _
Nothing, Nothing, _
CompilerOptions.None, _
TargetPlatform.Windows)
Return New Effect(Me.GraphicsDevice, _
CompEffect.GetEffectCode, _
CompilerOptions.None, Nothing)
End Function
Private Function GetTexture(ByVal FileName As String)
Return Texture2D.FromFile(Me.GraphicsDevice, FileName)
End Function
#End Region
#Region "Caricamento Heightmap"
Private Sub 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)
Next
For 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)
Next
Next
VDeclaration = New VertexDeclaration(Me.GraphicsDevice, _
VertexPositionNormalMultitexture.VertexElements)
End Sub
Private Sub SetVertices()
ReDim Vertices(TerrainWidth * TerrainLength - 1)
Dim Strip As Single = (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 As Single = 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 As Single = .TextureWeight.X
Total += .TextureWeight.Y
Total += .TextureWeight.Z
Total += .TextureWeight.W
.TextureWeight.X /= Total
.TextureWeight.Y /= Total
.TextureWeight.Z /= Total
.TextureWeight.W /= Total
End With
Next
Next
End Sub
Private Sub 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
Next
Next
End Sub
Private Sub SetNormals()
For I As Int32 = 0 To Vertices.Length - 1
Vertices(I).Normal = New Vector3(0, 0, 0)
Next
For 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
Next
For Each V As VertexPositionNormalMultitexture In Vertices
V.Normal.Normalize()
Next
End Sub
Private Sub 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)
End Sub
#End Region
#Region "Cielo"
Private Function 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))
Next
Return Result
End Function
Private Sub 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)
End With
For I As Byte = 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)
End With
Next
SDeclaration = New VertexDeclaration(Me.GraphicsDevice, VertexPositionTexture.VertexElements)
End Sub
Private Sub DrawSky(ByVal View As Matrix)
Dim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
Me.GraphicsDevice.RenderState.CullMode = CullMode.None
For I As Byte = 0 To 4
Shader.CurrentTechnique = Shader.Techniques("Textured")
Shader.Parameters("View").SetValue(View)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue(Matrix.Identity)
Shader.Parameters("LightEnabled").SetValue(False)
Shader.Parameters("ATexture").SetValue(SkyFaces(I))
Shader.Begin()
For Each Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
With Me.GraphicsDevice
.VertexDeclaration = SDeclaration
.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, SkyCube(I).Vertices, 0, SkyCube(I).Vertices.Length, SkyCube(I).Indices, 0, SkyCube(I).Indices.Length / 3)
End With
Pass.End()
Next
Shader.End()
Next
Me.GraphicsDevice.RenderState.CullMode = Prev
End Sub
#End Region
#Region "Acqua"
Private Function CreatePlane(ByVal Height As Single, ByVal View As Matrix, _
ByVal Normal As Vector3, ByVal ClipAbove As Boolean)
Normal.Normalize()
Dim PlaneCoef As New Vector4(Normal, Height)
Dim HSMatrix As Matrix = _
Matrix.Transpose( _
Matrix.Invert(View * ProjectionMatrix))
If ClipAbove Then
PlaneCoef *= -1
End If
PlaneCoef = Vector4.Transform(PlaneCoef, HSMatrix)
Return New Plane(PlaneCoef)
End Function
Private Sub DrawRefractionMap()
Dim RefractionPlane As Plane = _
CreatePlane(WaterHeight + 1.5, ViewMatrix, 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(ViewMatrix)
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
RefractionMap = RefractionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
End Sub
Private Sub DrawReflectionMap()
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim Forward As New Vector3(0, 0, -1)
Dim Right As New Vector3(1, 0, 0)
Dim TargetPos As Vector3 = _
CameraPosition + Vector3.Transform(Forward, Rotation)
Dim ReflectionPos As Vector3 = CameraPosition
Dim ReflectionTargetPos As Vector3 = TargetPos
ReflectionPos.Y = 2 * WaterHeight - ReflectionPos.Y
ReflectionTargetPos.Y = 2 * WaterHeight - ReflectionTargetPos.Y
Dim ReflectionUp As Vector3 = Vector3.Cross( _
Vector3.Transform(Right, Rotation), ReflectionTargetPos - ReflectionPos)
ReflectionViewMatrix = Matrix.CreateLookAt( _
ReflectionPos, ReflectionTargetPos, ReflectionUp)
Dim ReflectionPlane As Plane = _
CreatePlane(WaterHeight, ReflectionViewMatrix, New Vector3(0, -1, 0), True)
Me.GraphicsDevice.ClipPlanes(0).Plane = ReflectionPlane
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = True
Me.GraphicsDevice.SetRenderTarget(0, ReflectionTarget)
Me.GraphicsDevice.Clear(ClearOptions.Target Or _
ClearOptions.DepthBuffer, Color.Black, 1.0F, 0)
DrawTerrain(ReflectionViewMatrix)
DrawSky(ReflectionViewMatrix)
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
ReflectionMap = ReflectionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
End Sub
Private Sub SetWaterVertices()
ReDim WaterVertices(5)
Dim HalfSize As Int16 = 400
WaterVertices(0).Position = New Vector3(-HalfSize, WaterHeight, HalfSize)
WaterVertices(1).Position = New Vector3(-HalfSize, WaterHeight, -HalfSize)
WaterVertices(2).Position = New Vector3(HalfSize, WaterHeight, HalfSize)
WaterVertices(3).Position = New Vector3(HalfSize, WaterHeight, HalfSize)
WaterVertices(4).Position = New Vector3(-HalfSize, WaterHeight, -HalfSize)
WaterVertices(5).Position = New Vector3(HalfSize, WaterHeight, -HalfSize)
WaterVertices(0).TextureCoordinate = New Vector2(0, 1)
WaterVertices(1).TextureCoordinate = New Vector2(0, 0)
WaterVertices(2).TextureCoordinate = New Vector2(1, 1)
WaterVertices(3).TextureCoordinate = New Vector2(1, 1)
WaterVertices(4).TextureCoordinate = New Vector2(0, 0)
WaterVertices(5).TextureCoordinate = New Vector2(1, 0)
WaterVBuffer = New VertexBuffer(Me.GraphicsDevice, _
WaterVertices.Length * VertexPositionTexture.SizeInBytes, _
BufferUsage.WriteOnly)
WaterVBuffer.SetData(WaterVertices)
WDeclaration = New VertexDeclaration(Me.GraphicsDevice, VertexPositionTexture.VertexElements)
End Sub
Private Sub DrawWater(ByVal Time As Single)
Shader.CurrentTechnique = Shader.Techniques("Water")
Shader.Parameters("View").SetValue(ViewMatrix)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue(Matrix.Identity)
Shader.Parameters("ReflectionView").SetValue(ReflectionViewMatrix)
Shader.Parameters("RefractionMap").SetValue(RefractionMap)
Shader.Parameters("ReflectionMap").SetValue(ReflectionMap)
Shader.Parameters("BumpMap").SetValue(BumpMap)
Shader.Parameters("WaveLength").SetValue(0.01F)
Shader.Parameters("WaveHeight").SetValue(0.3F)
Shader.Parameters("CameraPosition").SetValue(CameraPosition)
Dim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
Me.GraphicsDevice.RenderState.CullMode = CullMode.None
Me.GraphicsDevice.Vertices(0).SetSource( _
WaterVBuffer, 0, VertexPositionTexture.SizeInBytes)
Shader.Begin()
For Each Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
With Me.GraphicsDevice
.VertexDeclaration = WDeclaration
Me.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, WaterVertices.Length / 3)
End With
Pass.End()
Next
Shader.End()
Me.GraphicsDevice.RenderState.CullMode = Prev
End Sub
#End Region
#Region "Gestione telecamera"
Private Sub ProcessMouseInput(ByVal ElapsedTime As Single)
Dim CurrentMouseState As MouseState = Mouse.GetState
If CurrentMouseState <> PrevMouseState Then
Dim DX As Single = _
CurrentMouseState.X - PrevMouseState.X
Dim DY As Single = _
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()
End If
End Sub
Private Sub ProcessKeyboardInput()
Dim Motion As New Vector3(0, 0, 0)
Dim KeyState As KeyboardState = Keyboard.GetState
If KeyState.IsKeyDown(Keys.W) Then
Motion += New Vector3(0, 0, -1)
End If
If KeyState.IsKeyDown(Keys.S) Then
Motion += New Vector3(0, 0, 1)
End If
If KeyState.IsKeyDown(Keys.D) Then
Motion += New Vector3(1, 0, 0)
End If
If KeyState.IsKeyDown(Keys.A) Then
Motion += New Vector3(-1, 0, 0)
End If
If KeyState.IsKeyDown(Keys.Up) Then
Motion += New Vector3(0, 1, 0)
End If
If KeyState.IsKeyDown(Keys.Down) Then
Motion += New Vector3(0, -1, 0)
End If
UpdatePosition(Motion)
End Sub
Private Sub UpdateView()
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim Forward As New Vector3(0, 0, -1)
Dim Up As New 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)
End Sub
Private Sub 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()
End Sub
#End Region
#Region "Gestione risorse"
Sub New()
Me.Graphics = New GraphicsDeviceManager(Me)
Me.Content.RootDirectory = "content"
End Sub
Protected Overrides Sub Initialize()
MyBase.Initialize()
End Sub
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")
BumpMap = GetTexture(AppPath & "\BumpMap.jpg")
For I As Byte = 0 To 4
SkyFaces(I) = GetTexture(AppPath & "\skyface" & (I + 1) & ".png")
Next
Dim Params As PresentationParameters = _
Me.GraphicsDevice.PresentationParameters
RefractionTarget = New RenderTarget2D(Me.GraphicsDevice, _
Params.BackBufferWidth, Params.BackBufferHeight, 1, _
Me.GraphicsDevice.DisplayMode.Format)
ReflectionTarget = New RenderTarget2D(Me.GraphicsDevice, _
Params.BackBufferWidth, Params.BackBufferHeight, 1, _
Me.GraphicsDevice.DisplayMode.Format)
LoadHeightMap(GetTexture(AppPath & "\HeightMap.bmp"))
LoadSkyDome()
SetVertices()
SetIndices()
SetNormals()
SetWaterVertices()
CopyToBuffer()
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _
MathHelper.PiOver4, _
Me.GraphicsDevice.Viewport.AspectRatio, _
1, 1000)
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
Protected Overrides Sub UnloadContent()
MyBase.UnloadContent()
End Sub
#End Region
#Region "Aggiornamento mondo 3D"
Dim a As Single = -0.27
Dim b As Single = 0.33
Dim c As Single = -0.86
Private Sub DrawTerrain(ByVal View As Matrix)
Shader.CurrentTechnique = Shader.Techniques("MultiTextured")
Shader.Parameters("View").SetValue(View)
Shader.Parameters("Projection").SetValue(ProjectionMatrix)
Shader.Parameters("World").SetValue( _
Matrix.CreateTranslation( _
New Vector3(-TerrainWidth / 2, 0, TerrainLength / 2)))
Dim LightDirection As New 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)
Me.GraphicsDevice.Indices = IBuffer
Me.GraphicsDevice.Vertices(0).SetSource( _
VBuffer, 0, VertexPositionNormalMultitexture.SizeInBytes)
Shader.Begin()
For Each Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
With Me.GraphicsDevice
.VertexDeclaration = VDeclaration
Me.GraphicsDevice.DrawIndexedPrimitives( _
PrimitiveType.TriangleList, 0, 0, _
Vertices.Length, 0, Indices.Length / 3)
End With
Pass.End()
Next
Shader.End()
End Sub
Protected Overrides Sub Update(ByVal GameTime As GameTime)
ProcessMouseInput(GameTime.ElapsedGameTime.TotalSeconds)
ProcessKeyboardInput()
Dim k As KeyboardState = Keyboard.GetState
a += IIf(k.IsKeyDown(Keys.T), 1, 0)
a -= IIf(k.IsKeyDown(Keys.G), 1, 0)
b += IIf(k.IsKeyDown(Keys.Y), 1, 0)
b -= IIf(k.IsKeyDown(Keys.H), 1, 0)
c += IIf(k.IsKeyDown(Keys.U), 1, 0)
c -= IIf(k.IsKeyDown(Keys.J), 1, 0)
If k.IsKeyDown(Keys.M) Then
ReflectionMap.Save("prova.jpg", ImageFileFormat.Jpg)
Me.Exit()
End If
MyBase.Update(GameTime)
End Sub
Protected Overrides Sub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawReflectionMap()
DrawSky(ViewMatrix)
DrawTerrain(ViewMatrix)
DrawWater(gameTime.TotalGameTime.Milliseconds / 1000)
MyBase.Draw(gameTime)
End Sub
#End Region
End Class
struct VertexToPixel
{
float4 Position : POSITION;
float4 Color : COLOR0;
float LightingFactor : TEXCOORD0;
float2 TextureCoords : TEXCOORD1;
};
struct PixelToFrame
{
float4 Color : COLOR0;
};
float4x4 View;
float4x4 Projection;
float4x4 World;
float3 LightDirection;
float AmbientFactor;
bool LightEnabled;
texture ATexture;
sampler TextureSampler = sampler_state
{
Texture = <ATexture>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Mirror;
AddressV = Mirror;
};
//--------------------------------------------
//Tecnica 1 : Colore
//--------------------------------------------
VertexToPixel ColoredVertexShader(float4 inPos : POSITION, float4 inColor: COLOR)
{
VertexToPixel Output = (VertexToPixel)0;
float4x4 ViewProjection = mul(View, Projection);
float4x4 WorldViewProjection = mul(World, ViewProjection);
Output.Position = mul(inPos, WorldViewProjection);
Output.Color = inColor;
return Output;
}
PixelToFrame ColoredPixelShader(VertexToPixel inPixel)
{
PixelToFrame Output = (PixelToFrame)0;
Output.Color = inPixel.Color;
return Output;
}
technique Colored
{
pass Pass0
{
VertexShader = compile vs_1_1 ColoredVertexShader();
PixelShader = compile ps_1_1 ColoredPixelShader();
}
}
//--------------------------------------------
//Tecnica 2 : Colore + Luce
//--------------------------------------------
VertexToPixel ColoredPlusVertexShader(float4 inPos : POSITION, float4 inColor: COLOR, float3 inNormal: NORMAL)
{
VertexToPixel Output = (VertexToPixel)0;
float4x4 ViewProjection = mul(View, Projection);
float4x4 WorldViewProjection = mul(World, ViewProjection);
Output.Position = mul(inPos, WorldViewProjection);
Output.Color = inColor;
Output.LightingFactor = 1;
if (LightEnabled)
{
float3 Normal = normalize(mul(normalize(inNormal), World));
Output.LightingFactor = saturate(dot(Normal, -LightDirection));
}
return Output;
}
PixelToFrame ColoredPlusPixelShader(VertexToPixel inPixel)
{
PixelToFrame Output = (PixelToFrame)0;
Output.Color = inPixel.Color;
Output.Color.rgb *= saturate(inPixel.LightingFactor + AmbientFactor);
return Output;
}
technique ColoredPlus
{
pass Pass0
{
VertexShader = compile vs_1_1 ColoredPlusVertexShader();
PixelShader = compile ps_1_1 ColoredPlusPixelShader();
}
}
//--------------------------------------------
//Tecnica 3 : Textured
//--------------------------------------------
VertexToPixel TexturedVertexShader(float4 inPos : POSITION, float3 inNormal: NORMAL, float2 inTexCoords: TEXCOORD0)
{
VertexToPixel Output = (VertexToPixel)0;
float4x4 ViewProjection = mul (View, Projection);
float4x4 WorldViewProjection = mul (World, ViewProjection);
Output.Position = mul(inPos, WorldViewProjection);
Output.TextureCoords = inTexCoords;
Output.LightingFactor = 1;
if (LightEnabled)
{
float3 Normal = normalize(mul(normalize(inNormal), World));
Output.LightingFactor = saturate(dot(Normal, -LightDirection));
}
return Output;
}
PixelToFrame TexturedPixelShader(VertexToPixel inPixel)
{
PixelToFrame Output = (PixelToFrame)0;
Output.Color = tex2D(TextureSampler, inPixel.TextureCoords);
Output.Color.rgb *= saturate(inPixel.LightingFactor + AmbientFactor);
return Output;
}
technique Textured
{
pass Pass0
{
VertexShader = compile vs_1_1 TexturedVertexShader();
PixelShader = compile ps_1_1 TexturedPixelShader();
}
}
//--------------------------------------------
//Tecnica 4 : MultiTextured
//--------------------------------------------
struct MultiVertexToPixel
{
float4 Position : POSITION;
float4 Color : COLOR0;
float3 Normal : TEXCOORD0;
float2 TextureCoords : TEXCOORD1;
float4 LightDirection : TEXCOORD2;
float4 TextureWeights : TEXCOORD3;
float Depth : TEXCOORD4;
};
struct MultiPixelToFrame
{
float4 Color : COLOR0;
};
Texture Texture0;
Texture Texture1;
Texture Texture2;
Texture Texture3;
sampler TextureSampler0 = sampler_state
{
Texture = <Texture0>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Wrap;
AddressV = Wrap;
};
sampler TextureSampler1 = sampler_state
{
Texture = <Texture1>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Wrap;
AddressV = Wrap;
};
sampler TextureSampler2 = sampler_state
{
Texture = <Texture2>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Mirror;
AddressV = Mirror;
};
sampler TextureSampler3 = sampler_state
{
Texture = <Texture3>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Mirror;
AddressV = Mirror;
};
MultiVertexToPixel MultiTexturedVertexShader(float4 inPos : POSITION, float3 inNormal: NORMAL, float4 inTexCoords: TEXCOORD0, float4 inTexWeights: TEXCOORD1)
{
MultiVertexToPixel Output = (MultiVertexToPixel)0;
float4x4 ViewProjection = mul(View, Projection);
float4x4 WorldViewProjection = mul(World, ViewProjection);
Output.Position = mul(inPos, WorldViewProjection);
Output.Normal = mul(normalize(inNormal), World);
Output.TextureCoords = inTexCoords;
Output.LightDirection.xyz = -LightDirection;
Output.LightDirection.w = 1;
Output.TextureWeights = inTexWeights;
Output.Depth = Output.Position.z / Output.Position.w;
return Output;
}
MultiPixelToFrame MultiTexturedPixelShader(MultiVertexToPixel inPixel)
{
MultiPixelToFrame Output = (MultiPixelToFrame)0;
float lightingFactor = 1;
if (LightEnabled)
lightingFactor = saturate(saturate(dot(inPixel.Normal, inPixel.LightDirection)) + AmbientFactor);
float blendDistance = 0.99f;
float blendWidth = 0.005f;
float blendFactor = clamp((inPixel.Depth - blendDistance) / blendWidth, 0, 1);
float4 farColor;
farColor = tex2D(TextureSampler0, inPixel.TextureCoords) * inPixel.TextureWeights.x;
farColor += tex2D(TextureSampler1, inPixel.TextureCoords) * inPixel.TextureWeights.y;
farColor += tex2D(TextureSampler2, inPixel.TextureCoords) * inPixel.TextureWeights.z;
farColor += tex2D(TextureSampler3, inPixel.TextureCoords) * inPixel.TextureWeights.w;
float4 nearColor;
float2 nearTextureCoords = inPixel.TextureCoords * 3;
nearColor = tex2D(TextureSampler0, nearTextureCoords) * inPixel.TextureWeights.x;
nearColor += tex2D(TextureSampler1, nearTextureCoords) * inPixel.TextureWeights.y;
nearColor += tex2D(TextureSampler2, nearTextureCoords) * inPixel.TextureWeights.z;
nearColor += tex2D(TextureSampler3, nearTextureCoords) * inPixel.TextureWeights.w;
Output.Color = lerp(nearColor, farColor, blendFactor);
Output.Color *= lightingFactor;
return Output;
}
technique MultiTextured
{
pass Pass0
{
VertexShader = compile vs_2_0 MultiTexturedVertexShader();
PixelShader = compile ps_2_0 MultiTexturedPixelShader();
}
}
//--------------------------------------------
//Tecnica 5 : Acqua
//--------------------------------------------
float4x4 ReflectionView;
Texture ReflectionMap;
Texture RefractionMap;
Texture BumpMap;
float WaveLength;
float WaveHeight;
float3 CameraPosition;
sampler ReflectionSampler = sampler_state
{
texture = <ReflectionMap>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Mirror;
AddressV = Mirror;
};
sampler RefractionSampler = sampler_state
{
texture = <RefractionMap>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Mirror;
AddressV = Mirror;
};
sampler BumpSampler = sampler_state
{
texture = <BumpMap>;
Magfilter = Linear;
Minfilter = Linear;
Mipfilter = Linear;
AddressU = Mirror;
AddressV = Mirror;
};
struct WaterVertexToPixel
{
float4 Position : POSITION;
float4 ReflectionPos : TEXCOORD1;
float2 BumpPos : TEXCOORD2;
float4 RefractionPos : TEXCOORD3;
float4 PointPosition : TEXCOORD4;
};
struct WaterPixelToFrame
{
float4 Color : COLOR0;
};
WaterVertexToPixel WaterVertexShader(float4 inPos : POSITION, float2 inTex: TEXCOORD)
{
WaterVertexToPixel Output = (WaterVertexToPixel)0;
float4x4 ViewProjection = mul(View, Projection);
float4x4 WorldViewProjection = mul(World, ViewProjection);
float4x4 ReflectionViewProjection = mul(ReflectionView, Projection);
float4x4 WorldReflectionViewProjection = mul(World, ReflectionViewProjection);
Output.Position = mul(inPos, WorldViewProjection);
Output.ReflectionPos = mul(inPos, WorldReflectionViewProjection);
Output.RefractionPos = mul(inPos, WorldViewProjection);
Output.BumpPos = inTex / WaveLength;
Output.PointPosition = mul(inPos, World);
return Output;
}
WaterPixelToFrame WaterPixelShader(WaterVertexToPixel inPixel)
{
WaterPixelToFrame Output = (WaterPixelToFrame)0;
float2 TexCoords;
TexCoords.x = (inPixel.ReflectionPos.x / inPixel.ReflectionPos.w) / 2.0f + 0.5f;
TexCoords.y = -(inPixel.ReflectionPos.y / inPixel.ReflectionPos.w) / 2.0f + 0.5f;
float4 BumpColor = tex2D(BumpSampler, inPixel.BumpPos);
float2 Perturbation = WaveHeight * (BumpColor.rg - 0.5f) * 2.0f;
float2 NewTexCoords = TexCoords + Perturbation;
float4 ReflectionColor = tex2D(ReflectionSampler, NewTexCoords);
float2 RefractionTexCoords;
RefractionTexCoords.x = (inPixel.RefractionPos.x / inPixel.RefractionPos.w) / 2.0f + 0.5f;
RefractionTexCoords.y = -(inPixel.RefractionPos.y / inPixel.RefractionPos.w) / 2.0f + 0.5f;
float2 RefractionNewTexCoords = RefractionTexCoords + Perturbation;
float4 RefractionColor = tex2D(RefractionSampler, RefractionNewTexCoords);
float3 Vector = normalize(CameraPosition - inPixel.PointPosition);
float3 Normal = float3(0, 1, 0);
float FresnelFactor = dot(Vector, Normal);
float4 Result = lerp(ReflectionColor, RefractionColor, FresnelFactor);
float4 WaterColor = float4(0.3f, 0.3f, 0.5f, 1.0f);
Output.Color = lerp(Result, WaterColor, 0.2f);
return Output;
}
technique Water
{
pass Pass0
{
VertexShader = compile vs_2_0 WaterVertexShader();
PixelShader = compile ps_2_0 WaterPixelShader();
}
}