It looks like you're new here. If you want to get involved, click one of these buttons!
Here's a modified version of the ripple shader. It adds support for texture tiling, and you can set where the centre of the ripples is, so you can make the shader more interactive. Tap and drag your finger across the screen to make ripples in the image. As you can see, the ripples flow across the boundaries between the tile iterations.
If you use this with a repeating water texture it looks great. Edit: the ripples now decrease in size the further they are away from the centre
--# Main
-- SuperRipple
-- Ripple shader + texture tiling + set the centre of the ripples
displayMode(STANDARD)
function setup()
--create image
local img = readImage("Cargo Bot:Codea Icon")
setContext(img)
textWrapWidth(img.width)
textMode(CORNER)
textAlign(CENTER)
font("IowanOldStyle-Roman")
fill(0, 114, 255, 255)
fontSize(40)
text("Tap or drag your finger on the screen to make ripples\n")
setContext()
--pos and dimesnions of rectangle
local pos = vec2(WIDTH, HEIGHT)/2
local w,h = WIDTH*0.8, HEIGHT*0.8
local radius = vec2(w,h)/2
local aa = pos - radius --bottom left corner of rect
local bb = pos + radius
texScale = img.width * 0.75 --what scale the texture will be displayed at
--create mesh
m = mesh()
m.texture = img
--set up rect
local rIdx = m:addRect(pos.x, pos.y, w,h)
m:setRectTex(rIdx, aa.x/texScale, aa.y/texScale, bb.x/texScale, bb.y/texScale) --texCoords according to tiler formula. nb textures will be rendered square in order to keep ripples circular.
--set up shader
m.shader = shader(SuperRipple.vs, SuperRipple.fs)
m.shader.centre = vec2(1.5,1.5) --eg centre of second tile from the bottom-left
parameter.watch("m.shader.centre")
anim={Freq = 8} --value to animate with tween
rippleTween = tween(anim.Freq*0.3, anim, {Freq=0.3}, tween.easing.backOut)
end
function draw()
background(40, 40, 50)
m.shader.time = ElapsedTime
m.shader.freq = anim.Freq
m:draw()
end
function touched(t)
m.shader.centre = vec2(t.x, t.y)/texScale --convert touch into tiled-texture space
if t.state==BEGAN then
if rippleTween then
tween.stop(rippleTween)
rippleTween = nil
end
anim.Freq = 2
elseif t.state==ENDED then
anim.Freq = math.max(5, anim.Freq)
rippleTween = tween(anim.Freq*0.3, anim, {Freq=0.3}, tween.easing.backOut)
else
anim.Freq = 1 + 5 * smoothstep(vec2(t.deltaX + t.deltaY):len()*0.5,0,6)
end
end
function smoothstep(t,a,b)
local a,b = a or 0,b or 1
local t = math.min(1,math.max(0,(t-a)/(b-a)))
return t * t * (3 - 2 * t)
end
--# SuperRipple
SuperRipple = { --set the centre point of the ripple
vs=[[ // Super Ripple vertex shader
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
gl_Position = modelViewProjection * position;
vColor = color;
vTexCoord = texCoord;
}]],
fs = [[// Ripple fragment shader
uniform lowp sampler2D texture;
uniform highp float time;
uniform highp float freq;
uniform lowp vec2 centre;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
highp vec2 tc = vTexCoord.xy;
highp vec2 p = 1.5 * (tc-centre);
highp float len = length(p);
highp vec2 uv = fract(tc + (p/len)*freq*max(0.3, 2.-len)*cos(len*24.0-time*4.0)*0.03);
gl_FragColor = texture2D(texture,uv) * vColor;
}
]]
}
Comments
Ok, here's a version that lets you set up to 5 ripple points, allowing multitouch. This creates a much more realistic watery effect.
Edit : fixed a bug in the shader for loop, it should be
for (int i=0; i<5; ++i)
. So much simpler to writefor i=0, 4 do
!You are having fun, aren't you? Nice work. B-)
Is this something that could be used in a game - like the naval combat game I'm building at the moment? I'm not just thinking of translating it to a 3D plane representing sea, but also about performance. Special effects are nice as long as they don't eat precious FPS.
It's a hypothetical question in my case, because I don't really need ripples, but I have seen a number of wonderful visual effects, such as grass rippling, that never got used for anything. It would be a shame if your effects suffered the same fate.
This isn't really ready for prime time, but in fact, the reason why I needed to write this is for a 3D project I'm working on. Not only does it look good for water surfaces, it looks amazing for 3D bodies of water too. I can't share code yet, but it's derived from the above. Check it out:
The single-splash version is fine performance wise. I've tested it with meshes covering the whole screen.
What I've done above is use the same ripple data to modify the surface normal, creating a very 3D looking-effect.
If it's for a massive sea plain then I would implement it on a separate "splash patch" rather than on the main mesh.
I'm probably not going to bother implementing the multi-splash version, as that is too much gratuitous eye-candy! But one of the first big hit iOS apps, back in the early days, was simply a coin-toss wishing fountain, based on the exact same GLES ripple shader code. You touched the screen to toss coins into the water, and could choose different backgrounds (including from the camera). I think he open sourced it. I probably should have looked at that before putting this together...
very nice!
...although, playing with it some more, maybe in a busy game with lots going on 5 splash-points is overkill, but 2 looks way better than 1. I reckon you only need two ripple sources for the patterns to disturb one another, creating that much more organic-looking effect. And one extra ripple shouldn't be too much of a performance hit. I'll experiment...
I updated the above code to make multitouch-tracking a bit simpler, and sped the animation up a bit to try to make the liquid seem more watery and less gloopy.
In the 3D version, I'm currently testing 3 splash-points. It seems to be optimal in terms of creating a realistic effect without too much overhead.
Very impressive!
Thanks, it was fun to make. I updated the above code again, to make it easier to change the number of splash-points in the shader. The most complex thing about the code is that it doesn't seem to be possible to have tables of variable length (dynamically resized) in GLES, so fitting and tracking a varying amount of touches into a fixed 5-space array takes a bit of work. The game version of the code is simpler in that I just use the last 3 collision events to position the splashes in the water, and I don't have to worry about multi-touch.
@yojimbo2000 That is really impressive, great job dude.
Very nicer work.
I note the splash seems to happen a little below and to the left of my touch, but that's a very minor thing!
fancy ripple special effect and 3d special effect
@Ignatz yeah, I noticed that. The offset gets bigger as you get further from the origin. Weird thing is, it doesn't seem to happen in the 3d version. I'll look into it.
Ok, I've fixed it. The issue was that I had forgotten that the last two coordinates of the texCoords command are width and height, not absolute coordinates, so it should be
bb-aa
not justbb
. So as you moved away from the origin, the touch gradually began to be offset by whateveraa
was. Updated the code above to fix this. @Ignatz thanks for testing and for the bug report.Any time, making bugs is my specialty, fixing them not so easy :P