Durante le ultime lezioni avrete sicuramente notato, girovagando per il paesaggio, che se si va sott'acqua e si guarda verso l'alto si
ottiene una veduta non molto bella:
Il cielo nero
Per risolvere questo inghippo bisogna apportare alcune piccole modifiche sia al codice sorgente XNA sia a quello HLSL. Per prima cosa,
inseriamo una nuova variabile globale in testa alla classe Game, IsUnderwater, che determinerà quando siamo sott'acqua e quando non lo
siamo più. Essa viene impostata nel metodo UpdatePosition:
PrivateSub 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
Dim Y AsSingleIf (Math.Abs(CameraPosition.X) < Me.TerrainWidth / 2) And _
(Math.Abs(CameraPosition.Z) < Me.TerrainLength / 2) ThenDim X1, X2, Z1, Z2 As Int32
With CameraPosition
X1 = CInt(Math.Floor(.X))
X2 = CInt(Math.Ceiling(.X))
Z1 = CInt(Math.Floor(.Z))
Z2 = CInt(Math.Ceiling(.Z))
EndWithIf X1 <> X2 And Z1 <> Z2 ThenDim Q11, Q12, Q21, Q22 AsSingle
Q11 = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
Q12 = HeightData(X1 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
Q21 = HeightData(X2 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
Q22 = HeightData(X2 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
Dim X, Z AsSingle
X = CameraPosition.X
Z = CameraPosition.Z
Y = (Q11 * (X2 - X) * (Z2 - Z) + _
Q21 * (X - X1) * (Z2 - Z) + _
Q12 * (X2 - X) * (Z - Z1) + _
Q22 * (X - X1) * (Z - Z1)) / ((X2 - X1) * (Z2 - Z1))
Else
Y = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
EndIfEndIfIf (IsJumping) And (CameraPosition.Y + JumpingYVelocity) > Y + 0.5 Then
CameraPosition.Y += JumpingYVelocity
JumpingYVelocity = JumpingYInitial - Gravity * JumpTime
Else
CameraPosition.Y = Y + 0.5
IsJumping = False
EndIf
'L'espressione (CameraPosition.Y < WaterHeight) restituisce
'True se la telecamera è sotto la superficie dell'acqua,
'altrimenti False
IsUnderwater = (CameraPosition.Y < WaterHeight)
UpdateView()
EndSub
Altra importante procedura da modificare è DrawRefractionMap, poiché essa taglia abitualmente tutto quello che si trova sopra
la superficie dell'acqua. Ma se noi ci troviamo sotto di essa, dovremo vedere proprio ciò che viene eliminato: in sostanza, dobbiamo
ribaltare il piano di sezione quando IsUnderwater = True:
PrivateSub DrawRefractionMap()
'Tutto quello che si è detto prima si ottiene semplicemente
'ponendo IsUnderwater come quarto parametro della funzione
'CreatePlane. Infatti quando quel parametro è True il piano
'viene ribaltato. La modifica che avevamo fatto tempo addietro si
'rivela ora molto utileDim RefractionPlane As Plane = _
CreatePlane(WaterHeight, ViewMatrix, New Vector3(0, -1, 0), _
IsUnderwater)
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)
'Altra cosa importantissima: quando siamo sul terreno, la refraction
'map ci permette solo di vedere sotto la superficie dell'acqua e
'quindi non c'è bisogno di disegnare il cielo. Ma se si
'guarda verso l'alto, allora è necessario reintrodurloIf IsUnderwater Then
DrawSky(ViewMatrix)
EndIf
DrawTerrain(ViewMatrix)
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
RefractionMap = RefractionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
EndSub
Per far funzionare le modifiche apportate, bisogna però modificare anche il codice HLSL. Per prima cosa introduciamo una nuova
variabile globale Underwater di tipo boolean, sotto la prima lista di parametri:
float4x4 View;
float4x4 Projection;
float4x4 World;
float3 LightDirection;
float AmbientFactor;
bool LightEnabled;
//Determina se ci si trova sott'acqua
bool Underwater;
Spostiamoci nel Pixel Shader della tecnica Water:
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);
//Se ci si trova sott'acqua, imposta ReflectionColor = RefractionColor.
//Questo avviene perchè la reflection map quando ci si
//trova sott'acqua non esiste: primo, perchè non c'è
//riflessione; secondo, perchè non abbiamo modificato
//il codice XNA opportunamente. Questo significa che si vede solo
//ciò che si trova sopra la superficie, come ci dice la
//refraction map
if(Underwater)
ReflectionColor = RefractionColor;
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.35f, 0.35f, 0.55f, 0.9f);
Output.Color = lerp(Result, WaterColor, 0.15f);
Normal = (BumpColor.rbg - 0.5f) * 2.0f;
float3 ReflectionVector = -reflect(LightDirection, Normal);
float Specular = dot(normalize(ReflectionVector), normalize(Vector));
Specular = pow(Specular, 256);
Output.Color.rgb += Specular;
return Output;
}
nel metodo DrawWater.
A questo punto, otterrete un risultato simile:
Il cielo visto da sott'acqua
Per raffinare ulteriormente il fondale, potremmo introdurre un effetto nebbia artigianale, che faccia sfumare verso un azzurrino le parti
più lontane. Per far questo spostate il parametro CameraPosition all'inizio del file, sotto Underwater, poi modificate come segue:
struct MultiVertexToPixel
{
float4 Position : POSITION;
float4 Color : COLOR0;
float3 Normal : TEXCOORD0;
float2 TextureCoords : TEXCOORD1;
float4 LightDirection : TEXCOORD2;
float4 TextureWeights : TEXCOORD3;
float Depth : TEXCOORD4;
//Contiene la posizione 3D del punto
float4 StartPos : TEXCOORD5;
};
//...
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);
//Calcola la posizione 3D del punto
Output.StartPos = mul(inPos, World);
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;
if(Underwater)
{
//Crea il colore azzurrino/bluastro/grigio che caratterizza
//il fondale
float4 WaterColor = float4(0.592f, 0.714f, 0.835f, 0.9f);
//Calcola la distanza tra il punto e la telecamera, usando
//la funzione predefinita distance
float Distance = distance(inPixel.StartPos, CameraPosition);
//Esegue un'interpolazione lineare tra il colore ottenuto prima
//e quello del fondale, secondo un fattore Distance / 30.0f, che
//viene ristretto dalla funzione clamp tra 0.2f e 0.7f. Questa
//restrizione di campo implica due cose:
//1. Anche il terreno vicino all'osservatore è sempre un
//po' sfumato
//2. Le parti più lontane non diventano azzurre, ma mentengono
//un elevato coefficiente di sfumature che permette comunque
//di riconoscere la texture della sabbia
Output.Color = lerp(Output.Color, WaterColor, clamp(Distance / 30.0f, 0.2f, 0.7f));
}
return Output;
}
Il risultato è questo:
Il cielo visto da sott'acqua
Niente male per ora!
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
Private WDeclaration As VertexDeclaration
'MATRICI -----------------------------------------Private ViewMatrix As Matrix
Private ProjectionMatrix As Matrix
Private WorldMatrix As Matrix
Private ReflectionViewMatrix 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 = 0.08
'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
Private ReflectionTarget As RenderTarget2D
Private ReflectionMap As Texture2D
Private WaterVertices() As VertexPositionTexture
Private WaterVBuffer As VertexBuffer
Private BumpMap As Texture2D
'SALTO ------------------------------------------Private IsJumping AsBoolean = False
Private JumpingYVelocity AsSinglePrivate JumpStart As TimeSpan
Private JumpTime AsSinglePrivate JumpingYInitial AsSingle = 0.17
Private Gravity AsSingle = 0.9
'ALTRO -------------------------------------------Private IsUnderwater AsBoolean = False
#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(ByVal View As Matrix)
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(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()
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 View As Matrix, _
ByVal Normal As Vector3, ByVal ClipAbove AsBoolean)
Normal.Normalize()
Dim PlaneCoef AsNew Vector4(Normal, Height)
Dim HSMatrix As Matrix = _
Matrix.Transpose( _
Matrix.Invert(View * ProjectionMatrix))
If ClipAbove Then
PlaneCoef *= -1
EndIf
PlaneCoef = Vector4.Transform(PlaneCoef, HSMatrix)
ReturnNew Plane(PlaneCoef)
EndFunctionPrivateSub DrawRefractionMap()
Dim RefractionPlane As Plane = _
CreatePlane(WaterHeight + 0.0F, ViewMatrix, New Vector3(0, -1, 0), IsUnderwater)
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)
If IsUnderwater Then
DrawSky(ViewMatrix)
EndIf
DrawTerrain(ViewMatrix)
Me.GraphicsDevice.ClipPlanes(0).IsEnabled = False
Me.GraphicsDevice.SetRenderTarget(0, Nothing)
RefractionMap = RefractionTarget.GetTexture()
Me.GraphicsDevice.Clear(Color.CornflowerBlue)
EndSubPrivateSub DrawReflectionMap()
Dim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim Forward AsNew Vector3(0, 0, -1)
Dim Right AsNew 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 - 0.5F, 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)
EndSubPrivateSub 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)
EndSubPrivateSub DrawWater(ByVal Time AsSingle)
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)
Shader.Parameters("Time").SetValue(Time)
Shader.Parameters("WindDirection").SetValue(New Vector3(0, 0, 1))
Shader.Parameters("WindForce").SetValue(0.0002F)
Shader.Parameters("Underwater").SetValue(IsUnderwater)
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()
ForEach Pass As EffectPass In Shader.CurrentTechnique.Passes
Pass.Begin()
WithMe.GraphicsDevice
.VertexDeclaration = WDeclaration
Me.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, WaterVertices.Length / 3)
EndWith
Pass.End()
Next
Shader.End()
Me.GraphicsDevice.RenderState.CullMode = Prev
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
Dim Y AsSingleIf (Math.Abs(CameraPosition.X) < Me.TerrainWidth / 2) And _
(Math.Abs(CameraPosition.Z) < Me.TerrainLength / 2) ThenDim X1, X2, Z1, Z2 As Int32
With CameraPosition
X1 = CInt(Math.Floor(.X))
X2 = CInt(Math.Ceiling(.X))
Z1 = CInt(Math.Floor(.Z))
Z2 = CInt(Math.Ceiling(.Z))
EndWithIf X1 <> X2 And Z1 <> Z2 ThenDim Q11, Q12, Q21, Q22 AsSingle
Q11 = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
Q12 = HeightData(X1 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
Q21 = HeightData(X2 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
Q22 = HeightData(X2 + Me.TerrainWidth / 2, -Z2 + Me.TerrainLength / 2)
Dim X, Z AsSingle
X = CameraPosition.X
Z = CameraPosition.Z
Y = (Q11 * (X2 - X) * (Z2 - Z) + _
Q21 * (X - X1) * (Z2 - Z) + _
Q12 * (X2 - X) * (Z - Z1) + _
Q22 * (X - X1) * (Z - Z1)) / ((X2 - X1) * (Z2 - Z1))
Else
Y = HeightData(X1 + Me.TerrainWidth / 2, -Z1 + Me.TerrainLength / 2)
EndIfEndIfIf (IsJumping) And (CameraPosition.Y + JumpingYVelocity) > Y + 0.5 Then
CameraPosition.Y += JumpingYVelocity
JumpingYVelocity = JumpingYInitial - Gravity * JumpTime
Else
CameraPosition.Y = Y + 0.5
IsJumping = False
EndIfDim Temp AsBoolean = IsUnderwater
IsUnderwater = (CameraPosition.Y < WaterHeight)
If (Temp = False) And (IsUnderwater = True) Then
MoveSpeed /= 2.0F
Gravity = 0.6
ElseIf (Temp = True) And (IsUnderwater = False) Then
MoveSpeed *= 2.0F
Gravity = 0.9
EndIf
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")
BumpMap = GetTexture(AppPath & "\BumpMap.jpg")
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)
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, _
0.1, 1000)
CameraPosition = New Vector3(14, 16, 51)
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(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 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)
Me.GraphicsDevice.Indices = IBuffer
Me.GraphicsDevice.Vertices(0).SetSource( _
VBuffer, 0, VertexPositionNormalMultitexture.SizeInBytes)
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()
Dim k As KeyboardState = Keyboard.GetState
If k.IsKeyDown(Keys.M) ThenEndIfIf k.IsKeyDown(Keys.Space) And (IsJumping = False) Then
JumpStart = GameTime.TotalGameTime
JumpingYVelocity = JumpingYInitial
IsJumping = True
ElseIf IsJumping Then
JumpTime = (GameTime.TotalGameTime - JumpStart).TotalSeconds
EndIfMyBase.Update(GameTime)
EndSubProtectedOverridesSub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawReflectionMap()
DrawSky(ViewMatrix)
DrawTerrain(ViewMatrix)
DrawWater(gameTime.TotalGameTime.TotalMilliseconds / 100.0F)
MyBase.Draw(gameTime)
EndSub#End RegionEndClass