A fairly common way to do fur, foliage, and other stuff based on densities is to simply alpha-blend several shells together. (A nice overview is found here.) This has been nagging at me for some time, so I gave it a shot a few days ago.
My current results ( N.B. uses the memory bitmap plugin for the density mask):
local function RoundUpMask (x) local xx = x + 13 -- 3 black pixels per side, for mask, plus 7 to round -- all but multiples of 8 past the next such multiple return xx - xx % 8 -- Remove the overshot to land on a multiple end local Dim = 128 local MaskDim = RoundUpMask(Dim) local Diff = (MaskDim - Dim) / 2 local memoryBitmap = require("plugin.memoryBitmap") local mask = memoryBitmap.newTexture{ width = MaskDim, height = MaskDim, format = "mask" } local iota = {} for i = 1, Dim^2 do iota[i] = i end local n = #iota for i = 1, math.ceil(.125 \* #iota) do local j = math.random(n) local index = iota[j] iota[j], iota[n], n = iota[n], iota[j], n - 1 local x = (index - 1) % Dim + 1 local y = (index - x) / Dim + 1 mask:setPixel(x + Diff, y + Diff, 1, 1, 1) end local omask = graphics.newMask(mask.filename, mask.baseDir) local function DataAndVertex (kernel) kernel.vertexData = { { name = "cx", index = 0 }, { name = "cy", index = 1 }, { name = "inclination", index = 2, min = 0, max = 90, default = 12.5 }, { name = "taper\_to", index = 3, min = 0, max = 1, default = 1 } } kernel.vertex = [[P\_POSITION vec2 VertexKernel (P\_POSITION vec2 pos) { P\_UV vec2 diff = pos - CoronaVertexUserData.xy; diff.x \*= mix(CoronaVertexUserData.w, 1., CoronaTexCoord.y); // diff.y \*= sin(radians(CoronaVertexUserData.z)); return CoronaVertexUserData.xy + diff; }]] end -- Generator -- Filter -- local k = { category = "filter", name = "incline" } DataAndVertex(k) graphics.defineEffect(k) for layer = 1, 3 do local objects = display.newGroup() local N = 50 for i = 1, N do local x = display.contentCenterX local object = display.newRect(objects, x, display.contentCenterY - i \* 1.75, display.contentWidth, 70) object.m\_y = object.y object:setMask(omask) object.maskScaleX, object.maskScaleY = object.width / Dim, object.height / Dim -- can adapt this, e.g. for fur local g = 1 - (i - 1) / N \* .57 local r, b = 0, 0 object:setFillColor(r, g, b, 1 - (i - 1) / N) object.fill.effect = "filter.custom.incline" object.fill.effect.cx = object.x object.fill.effect.cy = object.y object.fill.effect.inclination = 45 object.fill.effect.taper\_to = .37 end local prev Runtime:addEventListener("enterFrame", function(event) local now = event.time local diff = (prev and now - prev or 0) / 1000 local xprev = objects[1].x local wind = math.random(30, 45) for i = 2, N do local d = 13.5 \* (i / N) \* diff local x = xprev + d \* (2 \* math.sin(event.time / 200 + i / N + layer \* 2.7) - 1) x = math.max(xprev - 3.5, math.min(xprev + 3.5, x)) local obj = objects[i] obj.x = x obj.y = obj.m\_y + wind \* (i / N)^3 xprev = x end prev = now end) end
It’s a bit of a mess at the moment, with cut-and-paste artifacts, lots of magic numbers, and such, but demonstrates something like windy grass. I have vague ideas how I might contort this to work with a normal map, but it’s a long way off… that’s ultimately what I’m going for, though, e.g. to cover animal sprites with fur.
Anyhow, just throwing it out there.