Ora che abbiamo finito di creare le maps necessario, si può iniziare a visualizzare l'acqua.
Alcune modifiche al vecchio codice
Prima di continuare, è necessario apportare alcune modifiche al vecchio codice. Per prima cosa, bisogna parametrizzare le procedure di
disegno. Mi spiego meglio. Fino ad ora si è sempre dichiarato il metodo di disegno (DrawQualcosa) senza parametri, e si sono
usate le variabili globali dichiarate in testa al sorgente. Ora non è più possibile, perchè nell'usare la nuova tecnica
dell'acqua avremo bisogno di entrambe le matrici View e ReflectionView, che nel codice precedente si scambiavano l'un l'altra. Modificate
il codice in modo da far accettare ai metodi Draw una matrice View che indica quale matrice di vista usare, o ricopiate il codice provvisto
in fondo alla pagina alla fine del capitolo. Inoltre, sarà necessario avere ReflectionView come variabile globale.
In seconda istanza, dovremo impostare i buffer di vertici e di indici per il terreno nella procedura DrawTerrain, poiché useremo
altri vertici per disegnare l'acqua. Modificate come segue:
Per disegnare la superficie dell'acqua, che è un quadrato, occorrono 2 triangoli: in questo caso, sarebbe inutile definire degli indici
data l'infima quantità di dati con cui dobbiamo lavorare. Useremo, perciò solamente 6 vertici, tre per ogni triangolo. Prima
dichiariamo le nuove variabili:
Private WaterVertices() As VertexPositionTexture
Private WaterVBuffer As VertexBuffer
Private WDeclaration As VertexDeclaration
Poi il nuovo metodo:
PrivateSub SetWaterVertices()
ReDim WaterVertices(5)
'Rappresentà metà del lato del quadrato
'che delimita la superficie dell'acqua. Avremo
'quindi un quadrato di 800 unità per lato,
'con il centro nell'origine. Lo so, forse è
'un po' eccessivo, ma non si sa mai...Dim HalfSize As Int16 = 400
'Definisce i vertici dei due triangoli
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)
'E le loro coordinate texture
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)
'Carica il buffer
WaterVBuffer = New VertexBuffer(Me.GraphicsDevice, _
WaterVertices.Length * VertexPositionTexture.SizeInBytes, _
BufferUsage.WriteOnly)
WaterVBuffer.SetData(WaterVertices)
'Crea la nuova dichiarazione
WDeclaration = New VertexDeclaration(Me.GraphicsDevice, _
VertexPositionTexture.VertexElements)
EndSub
La tecnica dell'acqua in HLSL
È venuto il momento di mettere le mani sul file shader. Fino ad ora ho fornito i sorgenti hlsl direttamente all'inizio del tutorial,
ma da ora in poi, dato che ho esposto in breve il linguaggio, riporterò il codice passo passo esattamente come ho sempre fatto con
XNA.
Aprite il file SimpleShader.fx nella cartella bin/Debug del progetto, con il blocco note o qualsiasi altro editor di testo, e portatevi in
fondo al file. Iniziamo una nuova tecnica:
Per prima cosa, bisogna dichiarare i parametri aggiuntivi che essa necessita per funzionare. Potete dichiararli qui alla fine o all'inizio,
fa lo stesso:
//La matrice di vista speculare. Servirà per i calcolifloat4x4 ReflectionView;
//E le due mapsTexture ReflectionMap;
Texture RefractionMap;
//I sampler associati alle texturesampler 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;
};
Quindi definiamo le strutture usate per passare dati dal vertex shader al pixel shader:
//I dati elaborati dal vertex shader conterranno solo due membri:
//la posizione del pixel sullo schermo e la sua relativa posizione
//sulla reflection map. Non servirà nient'altrostruct WaterVertexToPixel
{
float4 Position : POSITION;
float4 ReflectionPos : TEXCOORD1;
};
//Come sempre, il pixel shader restituisce in output una struttura
//contenente solo il colore finale del pixelstruct WaterPixelToFrame
{
float4 Color : COLOR0;
};
Quindi scriviamo gli shader veri e propri:
//Il tipo di vertici usato è VertexPositionTexture, perciò
//i parametri del vertex shader saranno solo una posizione
//e una coppia di coordinate texture
WaterVertexToPixel WaterVertexShader(float4 inPos : POSITION,
float2 inTex: TEXCOORD)
{
WaterVertexToPixel Output = (WaterVertexToPixel)0;
//Calcola la matrice risultante dal prodotto di World, View
//e Projection. Questa, moltiplicata per inPos fornirà
//la posizione del pixel sullo schermo, in accordo con la
//matrice di vista originariafloat4x4 ViewProjection = mul(View, Projection);
float4x4 WorldViewProjection = mul(World, ViewProjection);
//Calcola la matrice risultante dal prodotto di World,
//ReflectionView e Projection. Questa, moltiplicata per inPos
//fornirà la posizione del pixel sulla reflection map,
//in accordo con la vista offerta dalla telecamera ribaltatafloat4x4 ReflectionViewProjection = mul(ReflectionView, Projection);
float4x4 WorldReflectionViewProjection =
mul(World, ReflectionViewProjection);
//Mette in Output la posizione del pixel sullo schermo e
//sulla reflection map
Output.Position = mul(inPos, WorldViewProjection);
Output.ReflectionPos = mul(inPos, WorldReflectionViewProjection);
return Output;
}
WaterPixelToFrame WaterPixelShader(WaterVertexToPixel inPixel)
{
WaterPixelToFrame Output = (WaterPixelToFrame)0;
//Calcola le coordinate texture giuste. Ricordate che il prodotto
//di una matrice combinata con un Vector4 restituisce un
//Vector4 in cui le singole coordinate sono espresse in
//coordinate normalizzate per il device grafico, e perciò
//sia X che Y possono assumere valori tra -1 e 1. Tuttavia,
//per indicare un pixel in una texture servono coordinate comprese
//tra 0 e 1 (altrimeti si andrebbe fuori da bordi). L'operazione
//matematica che "trasforma" un numero tra -1 e 1 in uno
//proporzionalmente coordispondente tra 0 e 1 è per
//l'appunto:
//b = (a + 1) / 2 = a / 2 + 0.5float2 TexCoords;
TexCoords.x = (inPixel.ReflectionPos.x / inPixel.ReflectionPos.w) / 2.0f + 0.5f;
//Qui c'è un meno perchè la coordinata Y cresce scendendo
//verso il basso, mentre il risultato del prodotto matriciale prevede
//una Y che decresce verso il basso
TexCoords.y = -(inPixel.ReflectionPos.y / inPixel.ReflectionPos.w) / 2.0f + 0.5f;
//Preleva il colore dalla reflecion map a queste coordinate
Output.Color = tex2D(ReflectionSampler, TexCoords);
return Output;
}
//Dichiara la tecnicatechnique Water
{
pass Pass0
{
VertexShader = compile vs_2_0 WaterVertexShader();
PixelShader = compile ps_2_0 WaterPixelShader();
}
}
e il metodo di disegno in XNA:
PrivateSub 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)
'Attiva la CullMode = None solo per questa azioneDim Prev As CullMode = Me.GraphicsDevice.RenderState.CullMode
Me.GraphicsDevice.RenderState.CullMode = CullMode.None
'Imposta i bufferMe.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
Dopo averlo inserito in Draw, come utlima istruzione, potrete avere come risultato questo:
Vedete l'acqua? No, è solo uno specchio
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 = 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
Private ReflectionTarget As RenderTarget2D
Private ReflectionMap As Texture2D
Private WaterVertices() As VertexPositionTexture
Private WaterVBuffer As VertexBuffer
#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 + 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)
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, 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)
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
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)
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()
EndSubProtectedOverridesSub UnloadContent()
MyBase.UnloadContent()
EndSub#End Region#Region "Aggiornamento mondo 3D"
Dim a AsSingle = -0.27
Dim b AsSingle = 0.33
Dim c AsSingle = -0.86
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
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()
EndIfMyBase.Update(GameTime)
EndSubProtectedOverridesSub Draw(ByVal gameTime As GameTime)
Me.Graphics.GraphicsDevice.Clear(Color.CornflowerBlue)
DrawRefractionMap()
DrawReflectionMap()
DrawSky(ViewMatrix)
DrawTerrain(ViewMatrix)
DrawWater(gameTime.TotalGameTime.Seconds * 10)
MyBase.Draw(gameTime)
EndSub#End RegionEndClass
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.