I looking for either an existing shader, or a little help writing one… I would like to make a 2D flag wave effect. I’ve never written a shader before but after a few hours reading online I was able to come up with this test that looks like a flag waving in the wind if you use lots and lot of imagination.
Is this something that can be applied to a 2D sprite? I tend to stay mostly in the 2D realm so I’m not well versed in vertex shaders. Truth be told, I’m not well versed in pixel shaders either, but I dabble
I’m wondering if it might be possible to use some refraction techniques to achieve the desired result in purely 2D. A while back I ended up writing a lens shader for an XNA project I was working on. It might be useful here… I’ll just post the shader, feel free to monkey with it as desired
sampler2D inputSampler : register(s0);
// Adapted from http://steve.hollasch.net/cgindex/render/refraction.txt
float3 TransmissionDirection(float nSource, float nTarget, float3 incoming, float3 normal)
float eta, c1, cs2;
eta = nSource / nTarget;
c1 = dot(incoming, normal);
cs2 = 1 - eta * eta * (1 - c1 * c1);
float3 result = float3(0, 0, 0);
if (cs2 >= 0)
result = eta * incoming + (c1 - sqrt(cs2)) * normal;
float4 LensRefraction(float2 tcPos: TEXCOORD0) : COLOR0
float4 color = tex2D(inputSampler, tcPos);
// Get the screen position for our point, then get the position vector.
float2 screenPos = tcPos * xTextureSize;
float2 positionVector = screenPos - xLensCenter;
// If the lens height is greater than zero, calculate the radius of the new lens.
float rPrime = xLensRadius;
if (xLensHeight > 0)
rPrime = sqrt(xLensRadius * xLensRadius - xLensHeight * xLensHeight);
if (length(positionVector) <= rPrime)
// Only bother to do all this calculation if we are inside the lens radius.
// Calculate our normal, which is the direction from the center of the lens to where our
// view ray contacts the lens.
float3 lensCenter = float3(xLensCenter.x, xLensCenter.y, 0);
float3 rayContact = float3(screenPos.x, screenPos.y, sqrt(xLensRadius * xLensRadius - positionVector.x * positionVector.x - positionVector.y * positionVector.y));
float3 normalVector = rayContact - lensCenter;
float3 normalDir = normalize(normalVector);
// Calculate the transmission directrion
float3 transDir = TransmissionDirection(xNSource, xNTarget, float3(0, 0, -1), normalDir);
// Get where the transmission vector intersects with the height plane.
float3 pCutPlane = lensCenter + float3(0, 0, xLensHeight);
float3 nCutPlane = float3(0, 0, 1);
float t = dot(nCutPlane, pCutPlane - rayContact) / dot(nCutPlane, transDir);
float3 intersectPoint = rayContact + t * transDir;
// Get the new pixel from our sampler.
float2 tcIntersectPoint = float2(clamp(intersectPoint.x / xTextureSize.x, 0, 1), clamp(intersectPoint.y / xTextureSize.y, 0, 1));
color = tex2D(inputSampler, tcIntersectPoint);
// Apply the tint...
// Calculate the ratio of how much further the ray traveled due to refraction as opposed
// to if it just traveled straight through.
float3 transmissionLength = length(intersectPoint - rayContact);
float3 materialPercent = (rayContact.z - (xLensRadius - xLensHeight)) / transmissionLength;
// Now use that ratio to apply a tint to the colour we obtained.
color.r += (xLensTint.r * xLensTint.a * (1 - materialPercent));
color.g += (xLensTint.g * xLensTint.a * (1 - materialPercent));
color.b += (xLensTint.b * xLensTint.a * (1 - materialPercent));
PixelShader = compile ps_2_0 LensRefraction();
Well, it’s not for a 2D flag. It’s for a lens effect. It’s just a thing I wrote one time that I thought might be adaptable to this problem.
I had originally thought about something like what you posted, where it skews the image in the X and Y (UV), but the 3D example you posted gives a depth effect. I was thinking, how might we achieve that in 2D and remembered that lens shader I had kicking around.
The end result is not for me haha, I’m not the OP. It’s just my own personal take on it
Anyway, no what I meant by depth was like, the Z axis to the user’s view. You see this result in the 3D example you posted in your archived blog post because you’re actually manipulating vertices in 3D. This is why I suggested the lens shader, because it will warp things with the illusion of depth in the Z axis.
This would also have the added benefit of keeping the boundaries of the rectangle more or less stable, as it would be on a real flag. Of course, perhaps the OP isn’t looking for realism… these are just the thoughts I have, lol. The nice thing about coding is that there’s never any real solution, just the thing you come up with that works!
For the lens way though, I don’t really know how to string it together to get the alternating pockets. It’s a really simplified one that I wrote when I made a demoscene style video as the photo album for my buddy’s wedding back in 2014. Haha we were both into Future Crew when we were younger so I modeled it after 2nd Reality. Anyway, it just checks ray interaction with a virtual lens that is convex, circular, and bounded.
I’m wondering if it could be applied in a cylindrical fashion in bands of convex and concave refraction areas to give a similar effect to your archived blog post.
Anyhoo, yes it’s complicated and I don’t really know how to do it right now, but there’s fun in speculation
(I think your shader looks fine though, just not what I was envisioning in my own take.)