Skip to content

Instantly share code, notes, and snippets.

@sp4cemonkey sp4cemonkey/Grass
Last active Dec 15, 2015

Embed
What would you like to do?
-- Grass
-- Use this function to perform your initial setup
function setup()
field = mesh()
--field.shader = shader("Documents:Grass")
field.shader = shader(GrassShader.vertexShader, GrassShader.fragmentShader)
bladeWidth = 0.15
bladeHeight = 1.5
midWeight = 0.4
size = vec2(30,30)
touchScale = 10
numBlades = 10000
field.shader.bladeHeight = bladeHeight
createField(field, size,numBlades)
generateTouchMesh(size)
--pressureZones are x,y then radius and finally type (1 = round pushout)
field.shader.pressureZone = vec4(-1000,-1000,0,0)
campos = vec2(0,30)
imageTouchSurface = image(WIDTH/touchScale,HEIGHT/touchScale)
currentPressurePoint = 1
pressurePoints = {}
pressurePoints[1] = vec4(-1000,-1000,0,0)
end
function generateTouchMesh(size)
--make a square the same footprint as our field to read touch against
touchMesh = mesh()
touchMesh:resize(6)
touchMesh.vertices = {
vec3(-size.x/2, 0, -size.y/2),
vec3(-size.x/2, 0, size.y/2),
vec3(size.x/2, 0, -size.y/2),
vec3(-size.x/2, 0, size.y/2),
vec3(size.x/2, 0, size.y/2),
vec3(size.x/2, 0, -size.y/2)
}
touchMesh.texCoords = {
vec2(0,0),
vec2(0,1),
vec2(1,0),
vec2(0,1),
vec2(1,1),
vec2(1,0)
}
touchMesh:setColors(color(255,255,255,255))
--call out for out texture
touchMesh.texture = generateCoordTexture(vec2(100,100))
end
function generateCoordTexture(resolution)
tex = image(resolution.x, resolution.y)
for x=1,resolution.x do
for y=1, resolution.y do
tex:set(x,y,x/resolution.x*255,y/resolution.y*255,0)
end
end
return tex
end
function createField(field, size, blades)
--data is a vec4 that encode x as weight and y as height from base z and w represent x,y on the field
dataBuffer = field:buffer("data")
for i=1,blades do
pos = vec3(math.random() * size.x - (size.x/2), 0, math.random() * size.y - (size.y/2))
createBlade(field, pos, pos + vec3(0,bladeHeight,0))
end
end
function createBlade(field, baseLocation, topLocation)
local firstVertex = field.size + 1
field:resize(firstVertex+11)
local bladeRotation = math.random(360)
--setup our vertex locations
--2d vertex positions
v2d = vec2(bladeWidth/2,0):rotate(math.rad(bladeRotation))
midLocation = (baseLocation + topLocation)/2
--calculate the vertices
--base corners
v1 = vec3(v2d.x+baseLocation.x,baseLocation.y, v2d.y+baseLocation.z)
v2 = vec3(-v2d.x+baseLocation.x, baseLocation.y, -v2d.y+baseLocation.z)
--mid corners
v3 = vec3((v2d.x)+midLocation.x, midLocation.y, (v2d.x)+midLocation.z)
v4 = vec3(-(v2d.x)+midLocation.x, midLocation.y, -(v2d.x)+midLocation.z)
--point
v5 = vec3(v2d.x/2+topLocation.x,topLocation.y, v2d.y/2+topLocation.z)
v6 = vec3(-v2d.x/2+topLocation.x, topLocation.y, -v2d.y/2+topLocation.z)
basecol = color(86, 153, 96, 255)
midcol = color(63, 163, 78, 255)
topcol = color(12, 229, 8, 255)
field:vertex(firstVertex, v1)
field:vertex(firstVertex+1, v2)
field:vertex(firstVertex+2, v3)
normal = (v2-v1):normalize():cross((v3-v1):normalize()):normalize()
field:normal(firstVertex, normal)
field:normal(firstVertex+1, normal)
field:normal(firstVertex+2, normal)
field:normal(firstVertex+3, normal)
field:normal(firstVertex+4, normal)
field:normal(firstVertex+5, normal)
field:normal(firstVertex+6, normal)
field:normal(firstVertex+7, normal)
field:normal(firstVertex+8, normal)
field:normal(firstVertex+9, normal)
field:normal(firstVertex+10, normal)
field:normal(firstVertex+11, normal)
field:vertex(firstVertex+3, v3)
field:vertex(firstVertex+4, v4)
field:vertex(firstVertex+5, v2)
field:vertex(firstVertex+6, v3)
field:vertex(firstVertex+7, v4)
field:vertex(firstVertex+8, v5)
field:vertex(firstVertex+9, v4)
field:vertex(firstVertex+10, v5)
field:vertex(firstVertex+11, v6)
field:color(firstVertex, basecol)
field:color(firstVertex+1, basecol)
field:color(firstVertex+2, midcol)
field:color(firstVertex+3, midcol)
field:color(firstVertex+4, midcol)
field:color(firstVertex+5, basecol)
field:color(firstVertex+6, midcol)
field:color(firstVertex+7, midcol)
field:color(firstVertex+8, topcol)
field:color(firstVertex+9, midcol)
field:color(firstVertex+10, topcol)
field:color(firstVertex+11, topcol)
dataBuffer[firstVertex] = vec4(0, 0, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+1] = vec4(0, 0, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+2] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+3] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+4] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+5] = vec4(0, 0, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+6] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+7] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+8] = vec4(1, topLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+9] = vec4(midWeight, midLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+10] = vec4(1, topLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
dataBuffer[firstVertex+11] = vec4(1, topLocation.y - baseLocation.y, baseLocation.x, baseLocation.z)
end
function touched(touch)
if touch.state == BEGAN or touch.state == MOVING then
r,g,b,a = imageTouchSurface:get(touch.x/touchScale, touch.y/touchScale)
r = r / 255 * size.x - size.x/2
g = g / 255 * size.y - size.y/2
if math.abs(r-pressurePoints[currentPressurePoint].x) + math.abs(g-pressurePoints[currentPressurePoint].y) > 1 then
currentPressurePoint = (currentPressurePoint + 1)%20
pressurePoints[currentPressurePoint] = vec4(r,g,3,ElapsedTime)
if currentPressurePoint == 1 then
field.shader.pressureZone1 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 2 then
field.shader.pressureZone2 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 3 then
field.shader.pressureZone3 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 4 then
field.shader.pressureZone4 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 5 then
field.shader.pressureZone5 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 6 then
field.shader.pressureZone6 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 7 then
field.shader.pressureZone7 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 8 then
field.shader.pressureZone8 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 9 then
field.shader.pressureZone9 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 10 then
field.shader.pressureZone10 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 11 then
field.shader.pressureZone11 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 12 then
field.shader.pressureZone12 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 13 then
field.shader.pressureZone13 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 14 then
field.shader.pressureZone14 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 15 then
field.shader.pressureZone15 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 16 then
field.shader.pressureZone16 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 17 then
field.shader.pressureZone17 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 18 then
field.shader.pressureZone18 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 19 then
field.shader.pressureZone19 = pressurePoints[currentPressurePoint]
elseif currentPressurePoint == 20 then
field.shader.pressureZone20 = pressurePoints[currentPressurePoint]
end
end
end
end
-- This function gets called once every frame
function draw()
campos = campos:rotate(math.rad(0.2))
output.clear()
print(1/DeltaTime)
field.shader.time = ElapsedTime
setContext(imageTouchSurface)
background(40, 40, 50)
camera(campos.x,20,campos.y, 0,0,0)
perspective()
touchMesh:draw()
setContext()
background(40,40,50)
camera(campos.x,20,campos.y, 0,0,0)
perspective()
field.shader.vEyePosition = vec3(campos.x, 20, campos.y)
field.shader.vLightPosition = vec3(10,80,20)
field.shader.mInvModel = modelMatrix():inverse():transpose()
field:draw()
end
GrassShader = {
vertexShader = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform vec4 pressureZone1;
uniform vec4 pressureZone2;
uniform vec4 pressureZone3;
uniform vec4 pressureZone4;
uniform vec4 pressureZone5;
uniform vec4 pressureZone6;
uniform vec4 pressureZone7;
uniform vec4 pressureZone8;
uniform vec4 pressureZone9;
uniform vec4 pressureZone10;
uniform vec4 pressureZone11;
uniform vec4 pressureZone12;
uniform vec4 pressureZone13;
uniform vec4 pressureZone14;
uniform vec4 pressureZone15;
uniform vec4 pressureZone16;
uniform vec4 pressureZone17;
uniform vec4 pressureZone18;
uniform vec4 pressureZone19;
uniform vec4 pressureZone20;
uniform float time;
uniform highp mat4 mInvModel;
uniform mediump vec3 vEyePosition;
uniform mediump vec3 vLightPosition;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec3 normal;
attribute vec4 data;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec3 lightDirection;
varying mediump vec3 eyeDirection;
varying mediump vec3 vNormal;
/// 2D Noise by Ian McEwan, Ashima Arts.
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+1.0)*x);
}
float snoise (vec2 v) {
const vec4 C = vec4(
0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz; x12.xy -= i1;
// Permutations
i = mod289(i);
// Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 60.0 * dot(m, g);
}
vec2 applyPressure(vec4 pressureZone)
{
float radius = length(pressureZone.xy - data.zw);
if (radius < pressureZone.z) {
float factor;
if (time < pressureZone.w + 2.5) {
if (time < pressureZone.w + 0.5) {
factor = 1.0 - (pressureZone.w + 0.5 - time)/0.5;
}
else {
factor = 1.0 - (time - pressureZone.w - 0.5)/2.0;
}
return (normalize(data.zw - pressureZone.xy) *
(1.0 - radius/pressureZone.z)) * factor * 2.0;
}
}
return vec2(0,0);
}
void main()
{
//convert the positions of the eye and light from world space to object space and then make a vector from position for ADS lighting
lightDirection = ((vec4(vLightPosition,1) * mInvModel) - position).xyz;
eyeDirection = ((vec4(vEyePosition,1) * mInvModel) - position).xyz;
//Pass the mesh color to the fragment shader
vColor = color;
//apply force by weight
vec2 deformation = vec2(snoise(vec2(data.z/15.0+time/2.0,data.w/15.0+time/2.0)),
snoise(vec2(data.z/15.0+time/2.0+5000.0,data.w/15.0+time/2.0+5000.0)));
//apply pressureZone
deformation = deformation + applyPressure(pressureZone1);
deformation = deformation + applyPressure(pressureZone2);
deformation = deformation + applyPressure(pressureZone3);
deformation = deformation + applyPressure(pressureZone4);
deformation = deformation + applyPressure(pressureZone5);
deformation = deformation + applyPressure(pressureZone6);
deformation = deformation + applyPressure(pressureZone7);
deformation = deformation + applyPressure(pressureZone8);
deformation = deformation + applyPressure(pressureZone9);
deformation = deformation + applyPressure(pressureZone10);
deformation = deformation + applyPressure(pressureZone11);
deformation = deformation + applyPressure(pressureZone12);
deformation = deformation + applyPressure(pressureZone13);
deformation = deformation + applyPressure(pressureZone14);
deformation = deformation + applyPressure(pressureZone15);
deformation = deformation + applyPressure(pressureZone16);
deformation = deformation + applyPressure(pressureZone17);
deformation = deformation + applyPressure(pressureZone18);
deformation = deformation + applyPressure(pressureZone19);
deformation = deformation + applyPressure(pressureZone20);
deformation = deformation * data.x;
//limit the force length max to length from root
if (length(deformation) > (data.y*0.99)) {
deformation = normalize(deformation)*(data.y*0.99);
}
//calculate the vertical adjustment to retain length with deformation
float heightReduction = -(data.y-sqrt(pow(data.y,2.0) - pow(length(deformation),2.0)));
vec4 positionModification = vec4(deformation.x,
heightReduction, deformation.y,0);
//adjust the normal by the deformation for lighting
float defHorLength = length(deformation);
vec2 defHorDirection = normalize(deformation);
//normal and color to fragment
//normals should be adjusted for pressure...
vNormal = normal;
if (data.y > 0.1) {
vNormal = normalize(normal + vec3(-defHorDirection.x*heightReduction/data.y,
defHorLength/data.y,
-defHorDirection.y*heightReduction/data.y));
}
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * (position + positionModification);
}
]],
fragmentShader = [[
precision lowp float;
uniform lowp sampler2D texture;
//uniform lowp sampler2D bumpMap;
varying lowp vec4 vColor;
varying mediump vec3 lightDirection;
varying mediump vec3 eyeDirection;
varying mediump vec3 vNormal;
const float c_zero = 0.0;
const float c_one = 1.0;
const float c_two = 2.0;
void main()
{
//if (!gl_FrontFacing) discard;
vec3 curNormal = normalize(vNormal);
lowp vec4 curCol = vColor;
vec3 vLightDirection = normalize(normalize(lightDirection));
vec3 vCameraDirection = normalize(eyeDirection);
lowp vec4 vAmbientColor = curCol * 0.2;
// Calculate Diffuse intensity
float fDiffuseIntensity = max(max( c_zero, dot( curNormal, vLightDirection )), dot(-curNormal, vLightDirection));
lowp vec4 vDiffuseColor = curCol * fDiffuseIntensity;
vAmbientColor.a = c_one;
vDiffuseColor.a = c_one;
//Set the output color to the texture color
gl_FragColor = vAmbientColor + vDiffuseColor;
//gl_FragColor = curCol;
}
]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.