[SOLVED]Water effect 2.5D


I have make a 2.5D water, its works fine, and now I want to pass some effect.
Fisrt I want to make a caustic effect but I have a problem.
In my shader code wrap texture in a sampler state does’nt works. My texture is streched.

Texture2D CausticTexture;
Texture2D WaterTexture;

sampler CausticSampler = sampler_state
	Texture = <CausticTexture>;
	MinFilter = Linear;
	MagFilter = Linear;
	AddressU = Wrap;
	AddressV = Wrap;
sampler WaterSampler = sampler_state
	Texture = <WaterTexture>;
	MinFilter = Point;
	MagFilter = Point;
	AddressU = Wrap;
	AddressV = Wrap;

struct VertexIn
	float3 Position : SV_POSITION0;
	float2 TexCoord : TEXCOORD0;

struct VertexOut
	float4 Position : SV_POSITION0;
	float2 TexCoord : TEXCOORD0;

VertexOut VS(VertexIn vin)
	VertexOut vout;
	vout.Position = float4(vin.Position, 1);
	vout.TexCoord =  vin.TexCoord;
	return vout;

float4 PS(VertexOut input) : SV_TARGET
	float4 causticColor = tex2D(CausticSampler, input.TexCoord);
	float4 waterColor = tex2D(WaterSampler, input.TexCoord);
	float4 add= causticColor  + waterColor;

	return add;

technique SpriteDrawing
	pass Pass0
		VertexShader = compile vs_4_0_level_9_1 VS();
		PixelShader = compile ps_4_0 PS();

If you need more code tell me.
Thx in advance

1 Like

Ok, why we need to add “numberOfDuplicate”. My water plane bounds(4000,1600) and my caustic texture bounds(192,192).

I don’t understand :worried:

Edit : ok, I am a beginner xD, this has nothing to do with, so always stuck :frowning:


I think sleeping sometimes is good xD

It’s no perfect, but its pretty cool
CausticMap + NormalMap + NoiseMap

So , few hours to make this effect…
Now I know, physics engine is less difficcult than graphics engine :sweat_smile:

Now I want to try to make a reflection, If you have any suggestion for the begining :slightly_smiling_face:

Thx in advance

1 Like

That is Super cool!

@boot Thanks, but its just the begining :slight_smile:

If I invert the world matrix for the display of my background and I draw after, is it normal that it is exactly the same?
Could it be because of my camera?

I tried a lot of different things but my background always stays in the same position, in the same direction.

It’s due to my QuadRender?

public class QuadRender
//buffers for rendering the quad
public readonly VertexPositionNormalTexture[] _vertexBuffer;
private readonly short[] _indexBuffer;

    //Declaration des Vector3 position
    public Vector3 Origin;
    public Vector3 UpperLeft;
    public Vector3 LowerLeft;
    public Vector3 UpperRight;
    public Vector3 LowerRight;
    public Vector3 Normal;
    public Vector3 Up;
    public Vector3 Left;
    public Vector3 Position;

    //Déclaration du type de vertex à utiliser
    List<Vector2> textureCorner;

    public QuadRender(Vector3 origin, Vector3 normal, Vector3 up,float width, float height)
        textureCorner = new List<Vector2>();

        Position = Vector3.Zero;
        Origin = origin;
        Normal = normal;
        Up = up;

        // Calculate the quad corners
        Left = Vector3.Cross(normal, Up);
        Vector3 uppercenter = (Up * height / 2) + origin;
        UpperLeft = uppercenter + (Left * width / 2);
        UpperRight = uppercenter - (Left * width / 2);
        LowerLeft = UpperLeft - (Up * height);
        LowerRight = UpperRight - (Up * height);

         _vertexBuffer = new VertexPositionNormalTexture[4];

        Vector2 textureUpperLeft = new Vector2(0.0f, 0.0f);
        Vector2 textureUpperRight = new Vector2(1.0f, 0.0f);
        Vector2 textureLowerLeft = new Vector2(0.0f, 1.0f);
        Vector2 textureLowerRight = new Vector2(1.0f, 1.0f);

        _vertexBuffer[0].Position = LowerLeft;
        _vertexBuffer[0].TextureCoordinate = textureLowerLeft;
        _vertexBuffer[1].Position = UpperLeft;
        _vertexBuffer[1].TextureCoordinate = textureUpperLeft;
        _vertexBuffer[2].Position = LowerRight;
        _vertexBuffer[2].TextureCoordinate = textureLowerRight;
        _vertexBuffer[3].Position = UpperRight;
        _vertexBuffer[3].TextureCoordinate = textureUpperRight;

        _indexBuffer = new short[6];
        _indexBuffer[0] = 0;
        _indexBuffer[1] = 1;
        _indexBuffer[2] = 2;
        _indexBuffer[3] = 2;
        _indexBuffer[4] = 1;
        _indexBuffer[5] = 3;

    public void RenderQuad(GraphicsDevice graphicsDevice, Vector2 v1, Vector2 v2)
        _vertexBuffer[0].Position.X = v1.X;
        _vertexBuffer[0].Position.Y = -v2.Y;

        _vertexBuffer[2].Position.X = v2.X;
        _vertexBuffer[2].Position.Y = -v2.Y;

        _vertexBuffer[1].Position.X = v1.X;
        _vertexBuffer[1].Position.Y = -v1.Y;

        _vertexBuffer[3].Position.X = v2.X;
        _vertexBuffer[3].Position.Y = -v1.Y;

        graphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, _vertexBuffer, 0, 4, _indexBuffer, 0, 2);


Didn’t look at it other then glancing over it.
But address wrap is mostly used for pretty specific tiling cases you might want to use address mirror instead.
Also when you use wrap all it does is if the texture coordinates in the shader you pass go past 1 it uses the fractional value of the coordinates … soo…

if your passed texture sampled coordinates in your shader are 1.25 u say because you set that on a vertice. Then it will sample texture pixels at .25 instead wrapping around. The size of a texture however is 0 to 1 u and v from begining to end.

Mirror on the other hand for 1.25 u actually will subtract from 1.0 - .25 = .75 u

To draw reflections in 2d you could just render target the stuff you draw without the water then use the rendertarget to blend onto the water with the v coordinates fliped i.e. v = 1.0f - v; then drawn onto your water surface block or blended onto it.

searching for reflections should give quite a few results here is a couple.

for 3d the following requisite function isn’t in monogame and probably should be.

1 Like

Thx for your reply.
Actually I attempt to understand this tuto, and yes both subject are already open in the browser :wink:
but the first topic does not concern me by being in 2.5D.


I do it a little bit differently i make the water quad partially transparent and actually use a normal texture on the water plane for waves and then apply refraction based on the normals to the water quad.

Sorry for the late reply, I was working on Photoshop this week.
@ willmotil yeah, I will surely go in this direction after having successfully reflected on water.
So this weekend I have time to try again but the clip () function is already causing me problems.

struct VertexShaderOutput
	float4 Position : POSITION0;
	float3 TextureCoordinate : TEXCOORD0;
	float clip : SV_ClipDistance0;

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
	VertexShaderOutput output;
	input.Position.w = 1;
	float4 worldPosition = mul(input.Position, World);
	float4 viewPosition = mul(worldPosition, View);
	output.Position = mul(viewPosition, Projection);

	float4 VertexPosition = mul(input.Position, World);
	output.TextureCoordinate = VertexPosition - CameraPosition;
	//output.Position.w = 1;
	/*float4 cpw = mul(clipPlane, World);
	float4 cpv = mul(cpw, View);
	clipPlane = mul(cpv, Projection);*/
	output.clip = dot(worldPosition, clipPlane); //dot(output.Position, clipPlane); //dot(mul(input.Position, World), clipPlane);

	return output;

No matter what I give it as clipPlane vector, either it’s always cut in the middle of the screen or it follows the camera but never at the right distance.

I go back to understand this function :wink:

Clip in the vertex shader if i remember right will clip a entire triangle you don’t really want to clip there.

Clip is usually used to not draw a pixel typically when it has a low alpha value ect in the pixel shader.

So if ok if you have plane were your water lays at you can define that as a ray a ray has a position and normal ill denote that as Nw Pw the position lies in your world on the surface of the water the normal sticks up from it perpendicular to the surface. On the pixel shader. You can take each pixel of the triangles you draw in your scene.
Like input.Position ill just denote it as P and then subtract that by the point you passed into the shader Pw (that is a point on your waters surface), to get a direction.
so Dir= P - Pw;
normalize that so DirN = normalize(dir);
Now you have a normal from the surface of your water to the current pixel being drawn in your scene.
You also have a normal that you passed that is a surface normal for the waters plane.
Then by comparison (using a dot product of the two normals) result = dot(DirN, Nw);
You get a value that you can use to clip on one side of the water or the other.
To clip on the other side of the water just flip the sign of DirN aka result = dot(-DirN, Nw);
The resulting value’s sign depends on if the two normals point away from or are in parallel to each other aka above or below the water and so to will be the sign passed to the clip function.

So in the pixel shader you clip out pixels above or below the water plane.
clip(result); // clips pixels when the result value passed is negative.

I tried the clip function to see if it cut the triangles but without results in the vertex shader.
I use this
> float clip : SV_ClipDistance0;
with 5_0 shadermodel (doesn’t work on 4_0) and its not clip triangles.
And yes, my fisrt problem was the W distance, now I see my clip but I think I misunderstand something but I don’t no what :confused:

I will try your method and I tell you again.

Edit : Do I have to do my calculations in the vertex shader?
In the pixelshader I have an error
Shader model ps_4_0_level_9_1 doesn't allow reading from semantics position
:roll_eyes: No clip either with 5_0
I go back to work :wink:

So its ok now, I created a new variable to retrieve the position with TextCoord, I learned something cool again;)

But now another problem xD (I like that)

 float _distance =  2.0f * (camera3D_.Position_.Y - _heightWater);
 camera3D_.Position_.Y -= _distance;

This line invert my camera (up/down) in my renderTarget,
And whatever I do my camera shake :weary:

Go back to work :sweat_smile:

Hi :smiley:

To solve my camera problem I simply made a copy of the world matrix and recreate a view matrix.
I don’t know if it’s the best method but it works.
It’s not perfect but it’s finished, some details to settle at my clip, enlarge it a little maybe.
Anyway thank you for your support @willmotil.

Thanx guys :slight_smile:

I wanted to make a nice video with the final effects and the beautiful textures …
But if my texture is not at position.Y = 0 my reflection is no longer good at all because its always cut at position.Y =0.

I don’t understand why.

    float4 dir = input.Position3D - mul(pointW, 1);
    float4 dirN = normalize(dir);
    float result = dot(-dirN, mul(normalW, 1));
        uv.y = 1 - uv.y;
        // I need to do this cause of my quadrender (not sure)else I see nothing

Here its my camera parameters for my view :

    Matrix world = camera3D_.World_;
     Vector3 reflectionCameraPosition = world.Translation;
      reflectionCameraPosition.Y = world.Translation.Y - _heightWater * 2.0f;

      //Vector3 invUpVector = Vector3.Cross(new Vector3(1, 0, 0), reflectionCameraPosition);
       world = Matrix.CreateWorld(world.Translation, world.Forward, world.Up);
      Matrix reflectionMatrix = Matrix.CreateLookAt(reflectionCameraPosition, world.Forward +   reflectionCameraPosition, world.Down);

pointW = Vector4(0, -_heightWater, 0, 0); 
normalW = Vector4(0,1,0,0);

Edit : I think its my new view, it doesn’t change my camera angle,not sure after a sleepless night :weary:
reflectionCameraPosition.Y -= _heightWater * 2.0f;
_heightWater = it’s not a position of my water in the world space?
_heightWater = postion.Y water + postion.Y texture its works perfectly.
If someone know why, thank you so much

If you see something wrong thanx in advance .

Hi guys,

Sorry I think I have to express myself badly, I really have trouble with English and shaders …
I must miss something logical but I don’t really see what it is.

It may help you if you post your native language and English so we can use a translator.

Try to use English without using a translator though.

I will do it next time, it’s solved.

1 Like