Thursday, March 30, 2017

libgdx - How to animate abstract 2d top down water texture?



I currently implement a game with a top down view of the ocean. I use the following, a little abstract texture: enter image description here


The actual texture is transparent, I added the green-like color for clarity.


The problem I now have is, that I don't know how to animate this texture so the water looks nice. I tried to move the texture with a sin wave: texture.y += sin(angle). Of course now the whole texture is moving which looks kind of unrealistic. The next thing I tried is to add another layer and implement a parallax effect. So that reflections under the water surface would also move, but much slower. It looks a little better but still not... nice enough.


I think the best looking animation would be, if the individual cells would expand and contract, kind of like a web or piece of cloth. Imagine if someone would slightly pull at one vertex of these cells and the neighbored cell would expand and the cell I pull towards to (or push to) would contract. Kind of like a web of springs(?). But I have no clue how to implement something like this:




  • Whats the math-model for this? Something with springs, where forces push/ pull?

  • And if so, how do I map this model to the given texture? Keeping all the curves and what not...


(I'm also open to different ideas/answers on how to animate the given texture. Realism is not the point here, just some nice looking water like movements...)



I posted a libgdx example in this post: 2d water animation is jagged and not smooth (see answer about texture filtering)



Answer



A common way this is done is using an indirect texture lookup in the shader to distort the display texture:


Animated gif showing water animation


Here I'm using a texture with some low-frequency colour noise (tiling smooth blobs of random colours), and scrolling it across the display geometry over time.



enter image description here


Instead of drawing the colours from this texture, I instead take the red & green channels and subtract 0.5f to turn them into a pseudorandom 2D vector that changes smoothly over time & space.


I can then add a small multiple of this vector to my UV coordinates, before sampling from the main water texture. This shifts the part of the texture we're reading & displaying, warping it around.


By averaging two samples from this noise, scrolling in opposite directions, we can hide the direction of movement so it just looks like aimless sloshing.


In Unity the shader would look like this - it should be simple enough to translate to the shader language of your choice:


fixed4 frag (v2f i) : SV_Target
{
float2 waveUV = i.uv * _NoiseScale;
float2 travel = _NoiseScrollVelocity * _Time.x;


float2 uv = i.uv;
uv += _Distortion * (tex2D(_Noise, waveUV + travel).rg - 0.5f);
waveUV += 0.2f; // Force an offset between the two samples.
uv += _Distortion * (tex2D(_Noise, waveUV - travel).rg - 0.5f);

// Sample the main texture from the distorted UV coordinates.
fixed4 col = tex2D(_MainTex, uv);

return col;
}

No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...