This is my first attempt at it, using an article I found as inspiration also. Really ugly grass texture and no lightning yet, but things move at least

http://http.developer.nvidia.com/GPUGems/gpugems_ch07.html

```
--# Main
-- Grass
function setup()
displayMode(FULLSCREEN)
R,H = 40,1
base = mesh()
base.vertices = {
vec3(-1,0,-1), vec3(1,0,-1),
vec3(1,0,1), vec3(1,0,1),
vec3(-1,0,1), vec3(-1,0,-1)
}
base:setColors(color(46, 106, 61, 255))
g = Grass()
end
function draw()
background(0, 0, 0, 255)
camera(0,H,5,0,0,0)
perspective(45)
rotate(R, 0,1,0)
g:draw()
scale(4)
base:draw()
end
function touched(touch)
if touch.state == MOVING then
R = R + touch.deltaX*.5
H = math.max(H - touch.deltaY*.02, .5)
end
end
--# Grass
Grass = class()
function mult(m, v)
return vec3(
m[1]*v.x+m[2]*v.y+m[3]*v.z,
m[5]*v.x+m[6]*v.y+m[7]*v.z,
m[9]*v.x+m[10]*v.y+m[11]*v.z
)
end
function Grass:init(x)
self.m = mesh()
http.request("https://dl.dropbox.com/s/2kboo35minb8opx/Photo%202013-03-19%2010%2011%2028.jpg?token_hash=AAFsT-JmLRpPP5F0pet5tZHo2c0-aaOu7LpwzHCWXVPS_g&dl=1",function(data)
self.m.texture = data
end)
-- self.m.texture = readImage("Documents:Grasses")
self.m.shader = GrassShader
local sq = {
vec3(0,0,0), vec3(1,0,0),
vec3(1,1,0), vec3(1,1,0),
vec3(0,1,0), vec3(0,0,0)
}
local vs, uvs = {}, {}
for x=-5,5 do for z=-5,5 do
local r = vec3(math.random(-.5,.5), 0, math.random(-.5,.5))
for j = 0,2 do
local m = matrix():rotate(60*j,0,1,0)
for i,v in ipairs(sq) do
local p = v - vec3(.5,0,0)
p = mult(m,p) + vec3(x,0,z)*.7 + r
table.insert(vs, p)
table.insert(uvs, v)
end
end
end end
self.m.vertices = vs
self.m.texCoords = uvs
self.m:setColors(255,255,255,255)
end
function Grass:draw()
self.m.shader.time = ElapsedTime
self.m:draw()
end
--# GrassShader
GrassShader = shader("Effects:Ripple")
GrassShader.vertexProgram = [[
uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
uniform highp float time;
// Noise
float hash( float n ) {
return fract(sin(n)*43758.5453);
}
float noise( in vec2 x ) {
vec2 p = floor(x);
vec2 f = fract(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0;
float res = mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x), mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y);
return res;
}
float fbm( vec2 p ) {
float f = 0.0;
f += 0.50000*noise( p ); p = p*2.02;
// f += 0.25000*noise( p ); p = p*2.03;
// f += 0.12500*noise( p ); p = p*2.01;
// f += 0.06250*noise( p ); p = p*2.04;
f += 0.03125*noise( p );
return f/0.984375;
}
void main()
{
vColor = color;
vTexCoord = vec2(texCoord.x, texCoord.y);
vec4 p = modelViewProjection * position;
if(vTexCoord.y > .9) {
float n = fbm(p.xy*time*.2)*4. - 2.;
p = p + vec4(n,0.,0.,0.);
}
lowp vec4 nor = normalize(p);
lowp float vShade = (dot(nor.xyz,vec3(-1.,0.,0.))+0.2)/1.2;
if (vShade >1.0) {vShade=1.0;}
if (vShade <0.2) {vShade=0.2;}
vColor.rgb = vColor.rgb * vShade;
gl_Position = p;
}
]]
GrassShader.fragmentProgram = [[
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
lowp vec4 col = texture2D( texture, vTexCoord );
if(col.r > 0.5) discard;
gl_FragColor = col;
}
]]
```