Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
1.1.0:water [2023/07/31 15:01] – created - external edit 127.0.0.1 | 1.1.0:water [2024/01/23 08:23] (current) – [Example] admin | ||
---|---|---|---|
Line 3: | Line 3: | ||
A water surface is not seldomly a popular game component. We do not provide directly a water surface component, but it is quite easy to setup one. | A water surface is not seldomly a popular game component. We do not provide directly a water surface component, but it is quite easy to setup one. | ||
- | {{ :water-example.jpg |}} | + | The water shader generates the surface by using different textures: DUDV, Normal, Specular, Displacement, |
+ | By those user specific textures you can control the look of your water. | ||
+ | |||
+ | Besides texturing you'll need additional render pass computation for rendering depth, refraction and reflection. | ||
+ | |||
+ | {{:1.1.0:g3d-water-new.png|}} | ||
Water rendering is based on: | Water rendering is based on: | ||
- | * a plane | + | * TGorillaPlane (best results) |
- | * refraction computation | + | * TGorillaWaterMaterialSource |
- | * reflection computation | + | * TGorillaRenderPassReflection (Reflections) |
- | * and the water shader itself | + | * TGorillaRenderPassRefraction (Depth + Refraction) |
===== Components ===== | ===== Components ===== | ||
Line 15: | Line 20: | ||
The plane is a simple and opaque instance of TGorillaPlane or TPlane. It will be used as basis mesh to render water onto. | The plane is a simple and opaque instance of TGorillaPlane or TPlane. It will be used as basis mesh to render water onto. | ||
+ | |||
+ | Using a none planar object is not recommended, | ||
==== Refraction ==== | ==== Refraction ==== | ||
Line 27: | Line 34: | ||
FRefraction.IgnoreControl(FWaterPlane); | FRefraction.IgnoreControl(FWaterPlane); | ||
</ | </ | ||
+ | |||
The water surface would be black, because it's image information will be computed in main render pass first. | The water surface would be black, because it's image information will be computed in main render pass first. | ||
+ | |||
+ | Because the refraction pass should render from a certain position, we have to adjust the surface position to the current water plane position everytime it changes: | ||
+ | <file pascal> | ||
+ | // set the current position of the water plane as surface plane | ||
+ | // this needs to be updated, if water plane moves | ||
+ | FRefraction.SurfacePosition.Point := TPoint3D(FWaterPlane.AbsolutePosition); | ||
+ | </ | ||
==== Reflection ==== | ==== Reflection ==== | ||
Line 47: | Line 62: | ||
// set the current position of the water plane as mirror plane | // set the current position of the water plane as mirror plane | ||
// this needs to be updated, if water plane moves | // this needs to be updated, if water plane moves | ||
- | FReflection.MirrorPosition := TPoint3D(FWaterPlane.AbsolutePosition); | + | FReflection.MirrorPosition.Point |
</ | </ | ||
In case plane size or position changes, you have to update those values in reflection pass. | In case plane size or position changes, you have to update those values in reflection pass. | ||
+ | ==== Displacement ==== | ||
+ | |||
+ | Our water shader supports displacement mapping to render waves. This manipulates vertex position in the vertex shader. | ||
+ | To use displacement mapping, apply a tetxure to the " | ||
+ | the " | ||
+ | |||
+ | When working with displacement mapping, vertices getting shifted. This leads to offsets in refraction and reflection, because | ||
+ | in those render passes we rendered with different information. | ||
+ | |||
+ | Therefor you have to adjust the reflection mirror position and refraction surface position by the " | ||
+ | |||
+ | <file pascal> | ||
+ | FReflection.MirrorPosition.Point := TPoint3D(FWaterPlane.AbsolutePosition) + | ||
+ | Point3D(0, -FWaterMaterial.DisplIntensity, | ||
+ | | ||
+ | FRefraction.SurfacePosition.Point := TPoint3D(FWaterPlane.AbsolutePosition) + | ||
+ | Point3D(0, -FWaterMaterial.DisplIntensity, | ||
+ | </ | ||
==== Water Material ==== | ==== Water Material ==== | ||
Line 62: | Line 95: | ||
* SpeculareTexture | * SpeculareTexture | ||
* FoamTexture | * FoamTexture | ||
+ | * ShoreTexture | ||
The different textures may look like these: | The different textures may look like these: | ||
Line 98: | Line 132: | ||
| ReflCorrection | By this value you can modify the color of reflection, default value: Vector3D(1.0, | | ReflCorrection | By this value you can modify the color of reflection, default value: Vector3D(1.0, | ||
| RefrCorrection | By this value you can modify the color of refraction on water surface, default value: Vector3D(1.1, | | RefrCorrection | By this value you can modify the color of refraction on water surface, default value: Vector3D(1.1, | ||
+ | |DepthIntensity|Control the depth map value intensity while computation. This influences the blending of edges and the mixture between refraction and reflection.| | ||
+ | |DisplIntensity|Control displacement mapping intensity| | ||
+ | |FoamIntensity|Control the color intensity of the applied foam texture.| | ||
+ | |ShoreIntensity|Control the color intensity of the applied shore foam texture.| | ||
+ | |ShoreWidth|Increase or decrease the size of the shore where the texture affects| | ||
==== Ripples ==== | ==== Ripples ==== | ||
Line 128: | Line 166: | ||
end; | end; | ||
</ | </ | ||
+ | |||
+ | Ripple computation in the vertex shader is embedded inside our water shader program. | ||
+ | In case you'll need additional handling in the fragment shader, you could a code snippet like that. | ||
+ | |||
+ | <file Pascal> | ||
+ | var LStr := TStringList.Create(); | ||
+ | try | ||
+ | LStr.Text := | ||
+ | ''' | ||
+ | void SurfaceShader(inout TLocals DATA){ | ||
+ | float l_TotalRippleEffect = 0.0; | ||
+ | float l_Time = abs(_TimeInfo.w); | ||
+ | vec3 l_TransfVertexPos = DATA.TransfVertPos.xyz; | ||
+ | |||
+ | for(int i = 0; i < int(_RippleCount); | ||
+ | float l_RippleDist = distance(l_TransfVertexPos.xz, | ||
+ | float l_RippleTime = abs((_TimeInfo.x + l_Time) - _Ripples[i].w); | ||
+ | float l_TimeLimit = clamp(1.0 / (l_RippleTime / (_RippleProximity * _RippleDecay)), | ||
+ | |||
+ | float l_RippleEffect = (-_RippleAmplification * exp(_RippleDecay * - l_RippleDist)); | ||
+ | l_RippleEffect *= cos(_RippleProximity * (l_RippleDist - l_RippleTime)); | ||
+ | l_RippleEffect *= l_TimeLimit; | ||
+ | |||
+ | l_TotalRippleEffect += exp(-l_RippleDist) * sin(0.5 * l_RippleDist) * l_RippleEffect; | ||
+ | } | ||
+ | |||
+ | if(l_TotalRippleEffect > 0.01){ | ||
+ | vec4 l_BoatTrail = tex2D(_WaterShore, | ||
+ | DATA.BaseColor.rgb += vec3(l_BoatTrail.rgb * l_TotalRippleEffect); | ||
+ | DATA.SumColor.rgb += vec3(l_BoatTrail.rgb * l_TotalRippleEffect); | ||
+ | } | ||
+ | } | ||
+ | '''; | ||
+ | |||
+ | FWaterMat.SurfaceShader := LStr; | ||
+ | finally | ||
+ | LStr.Free; | ||
+ | end; | ||
+ | </ | ||
+ | |||
+ | This snippet will render the water shore texture onto the ripples if they have a minimum size of 0.01. | ||
+ | ===== Tutorial ===== | ||
+ | |||
+ | {{youtube> | ||
+ | |||
===== Example ===== | ===== Example ===== | ||
Line 135: | Line 218: | ||
<file pascal> | <file pascal> | ||
- | procedure TForm1.CreateWater(const AAssetsPath | + | unit Unit1; |
- | var LTexPath | + | |
+ | interface | ||
+ | |||
+ | uses | ||
+ | System.SysUtils, | ||
+ | System.Types, | ||
+ | System.UITypes, | ||
+ | System.Classes, | ||
+ | System.Variants, | ||
+ | System.IOUtils, | ||
+ | System.Math.Vectors, | ||
+ | FMX.Types, | ||
+ | FMX.Controls, | ||
+ | FMX.Forms, | ||
+ | FMX.Graphics, | ||
+ | FMX.Dialogs, | ||
+ | FMX.Types3D, | ||
+ | FMX.Controls3D, | ||
+ | FMX.Objects3d, | ||
+ | FMX.MaterialSources, | ||
+ | FMX.StdCtrls, | ||
+ | FMX.Controls.Presentation, | ||
+ | Gorilla.DefTypes, | ||
+ | Gorilla.Viewport, | ||
+ | Gorilla.Skybox, | ||
+ | Gorilla.Cube, | ||
+ | Gorilla.Plane, | ||
+ | Gorilla.Terrain, | ||
+ | Gorilla.Terrain.Utils, | ||
+ | Gorilla.Terrain.Algorithm, | ||
+ | Gorilla.Controller.Passes.Reflection, | ||
+ | Gorilla.Controller.Passes.Refraction, | ||
+ | Gorilla.Material.Water, | ||
+ | Gorilla.Material.Lambert; | ||
+ | |||
+ | type | ||
+ | TForm1 = class(TForm) | ||
+ | procedure FormCreate(Sender: | ||
+ | private | ||
+ | FGorilla | ||
+ | FLight | ||
+ | FSkyBox | ||
+ | FTerrain | ||
+ | FTerrainMat : TGorillaLambertMaterialSource; | ||
+ | FWaterPlane : TGorillaPlane; | ||
+ | FWaterMat | ||
+ | FReflection : TGorillaRenderPassReflection; | ||
+ | FRefraction : TGorillaRenderPassRefraction; | ||
+ | FReflectBody : TGorillaCube; | ||
+ | FReflectBodyMat : TGorillaLambertMaterialSource; | ||
+ | public | ||
+ | { Public-Deklarationen } | ||
+ | end; | ||
+ | |||
+ | var | ||
+ | Form1: TForm1; | ||
+ | |||
+ | implementation | ||
+ | |||
+ | {$R *.fmx} | ||
+ | |||
+ | uses | ||
+ | Gorilla.Utils.Math, | ||
+ | Gorilla.Control, | ||
+ | Gorilla.Controller, | ||
+ | Gorilla.Context.Texturing; | ||
+ | |||
+ | procedure TForm1.FormCreate(Sender: TObject); | ||
+ | var LPath : String; | ||
begin | begin | ||
- | | + | |
+ | {$IFDEF MSWINDOWS} | ||
+ | LPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) | ||
+ | IncludeTrailingPathDelimiter('assets'); | ||
+ | {$ELSE} | ||
+ | LPath := IncludeTrailingPathDelimiter(TPath.GetHomePath()); | ||
+ | {$ENDIF} | ||
- | /// create | + | // Create |
- | | + | |
- | | + | |
- | | + | |
+ | FGorilla.OnMouseUp := DoOnViewportMouseUp; | ||
+ | FGorilla.FXAA := 0; | ||
+ | |||
+ | // Create a light source | ||
+ | FLight := TLight.Create(FGorilla); | ||
+ | FLight.Parent := FGorilla; | ||
+ | FLight.Name := ' | ||
+ | FLight.LightType := TLightType.Point; | ||
+ | FLight.AbsolutePosition := Vector3D(0, -25, -5); | ||
+ | |||
+ | // Skybox to have more reflections | ||
+ | FSkyBox := TGorillaSkyBox.Create(FGorilla); | ||
+ | FSkyBox.Parent := FGorilla; | ||
+ | |||
+ | // Terrain with random generation | ||
+ | FTerrain := TGorillaTerrain.Create(FGorilla); | ||
+ | FTerrain.Parent := FGorilla; | ||
+ | fTerrain.BeginUpdate(); | ||
try | try | ||
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | FWaterPlane.SubdivisionsWidth | + | |
- | | + | |
finally | finally | ||
- | | + | |
end; | end; | ||
- | /// create reflection | + | // Create a material for the terrain mesh |
- | | + | FTerrainMat := TGorillaLambertMaterialSource.Create(fTerrain); |
- | | + | FTerrainMat.Parent := FTerrain; |
- | FReflection.Camera | + | TGorillaLambertMaterialSource(FTerrainMat).Texture.LoadFromFile(LPath + ' |
+ | FTerrain.MaterialSource := FTerrainMat; | ||
+ | |||
+ | | ||
+ | FWaterPlane := TGorillaPlane.Create(FGorilla); | ||
+ | FWaterPlane.SetHitTestValue(false); | ||
+ | FWaterPlane.Parent := FGorilla; | ||
+ | FWaterPlane.RotationAngle.X := 90; | ||
+ | FWaterPlane.Width | ||
+ | FWaterPlane.Height := 10; | ||
+ | FWaterPlane.Position.Y := -0.5; | ||
+ | |||
+ | // High resolution for ripple testing - vertex shader modifies it | ||
+ | FWaterPlane.SubdivisionsWidth | ||
+ | FWaterPlane.SubdivisionsHeight := 256; | ||
+ | |||
+ | // Water shader | ||
+ | | ||
+ | | ||
+ | |||
+ | FWaterMat.DisplacementMap.LoadFromFile(LPath + ' | ||
+ | FWaterMat.DisplIntensity := 0.1; | ||
+ | FWaterMat.NormalMap.LoadFromFile(LPath + ' | ||
+ | FWaterMat.DUDVTexture.LoadFromFile(LPath + ' | ||
+ | FWaterMat.SpecularMap.LoadFromFile(LPath + ' | ||
+ | FWaterMat.FoamTexture.LoadFromFile(LPath + ' | ||
+ | FWaterMat.TextureTiling[WATER_TEX_FOAM] := PointF(4, 4); | ||
+ | |||
+ | FWaterMat.ShoreTexture.LoadFromFile(LPath + ' | ||
+ | FWaterMat.TextureTiling[WATER_TEX_SHORE] := PointF(2, 2); | ||
+ | |||
+ | // Link material to the water surface plane | ||
+ | FWaterPlane.MaterialSource := FWaterMat; | ||
+ | |||
+ | // Reflection render pass | ||
+ | FReflection | ||
+ | FReflection.Viewport | ||
+ | |||
+ | // Ignore the water plane, otherwise the rendering might be black | ||
FReflection.IgnoreControl(FWaterPlane); | FReflection.IgnoreControl(FWaterPlane); | ||
FReflection.MirrorSize := FWaterPlane.Width; | FReflection.MirrorSize := FWaterPlane.Width; | ||
| | ||
- | // set the current position of the water plane as mirror | + | // The mirror |
- | // this needs to be updated, if water plane moves | + | // Because displacement mapping is activated, we need to render reflections |
- | FReflection.MirrorPosition := TPoint3D(FWaterPlane.AbsolutePosition); | + | // with the displacement offset |
+ | FReflection.MirrorPosition.Point | ||
+ | Point3D(0, -FWaterMat.DisplIntensity, | ||
+ | |||
+ | // We do not need fullscreen rendering for reflections, | ||
+ | FReflection.ViewportScale := 0.5; | ||
FReflection.Enabled := true; | FReflection.Enabled := true; | ||
- | /// create refraction | + | // Refraction |
- | FRefraction := TGorillaRenderPassRefraction.Create(FViewport); | + | FRefraction := TGorillaRenderPassRefraction.Create(fGorilla); |
- | FRefraction.Viewport := FViewport; | + | FRefraction.Viewport := fGorilla; |
- | FRefraction.IgnoreControl(FWaterPlane); | + | |
+ | // Ignore the water plane, otherwise the rendering might be black | ||
+ | FRefraction.IgnoreControl(fWaterPlane); | ||
+ | |||
+ | // The mirror surface position is important to to render correctly | ||
+ | // Because displacement mapping is activated, we need to render reflections | ||
+ | // with the displacement offset | ||
+ | FRefraction.SurfacePosition.Point := TPoint3D(FWaterPlane.AbsolutePosition) + | ||
+ | Point3D(0, -FWaterMat.DisplIntensity, | ||
+ | |||
+ | // We do not need fullscreen rendering for refractions, | ||
+ | FRefraction.ViewportScale := 0.5; | ||
FRefraction.Enabled := true; | FRefraction.Enabled := true; | ||
- | /// create | + | // Link reflection and refraction render passes to the material |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | | + | |
- | FWaterMaterial.FoamTexture.LoadFromFile(LTexPath + ' | + | |
- | /// link reflection | + | // Create a reflective body and material |
- | | + | |
- | | + | |
- | | + | |
+ | FReflectBodyMat.Parent := FReflectBody; | ||
+ | FReflectBodyMat.UseTexture0 := false; | ||
+ | FReflectBody.MaterialSource := FReflectBodyMat; | ||
end; | end; | ||
+ | |||
+ | end. | ||
</ | </ | ||
Next step: [[bokeh|Bokeh]] | Next step: [[bokeh|Bokeh]] |