Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
209 lines (171 sloc) 7.17 KB
<!DOCTYPE html>
<html>
<head>
<title>Grass</title>
<script src="myr.js"></script>
<script src="convTex.js"></script>
<script src="cubicNoise.js"></script>
</head>
<body>
<canvas id="renderer" width=1200 height=800></canvas>
<script>
const gridSize = 32;
const brushRadius = 150;
const grassHeight = 100;
const grassClearance = 5;
const grassSpacing = 24;
const grassLayers = [];
const bladeWidth = 20;
const bladeSpacing = 4;
const bladeColor = new Myr.Color(0.36, 0.68, 0.33);
const bladeBaseColor = new Myr.Color(0.36 * 0.4, 0.68 * 0.4, 0.33 * 0.4);
const canvas = document.getElementById("renderer");
const rect = canvas.getBoundingClientRect();
const mousePrevious = new Myr.Vector(0, 0);
const mouseCurrent = new Myr.Vector(0, 0);
const myr = new Myr(canvas);
const application = new myr.Surface(myr.getWidth(), myr.getHeight());
const applyPush = new myr.Surface(brushRadius * 2, brushRadius * 2);
const texture = new ConvTex(
myr,
new myr.Shader(
"void main() {" +
"lowp vec4 pixel = texture(source, uv);" +
"color = clamp(vec4(" +
"pixel.rg + (pixel.ba - vec2(0.5)) * 0.1," +
"vec2(0.5) + (pixel.ba - vec2(0.5) - (pixel.rg - vec2(0.5)) * 0.1) * 0.93), 0.0, 1.0);" +
"}",
["source"],
[]
),
myr.getWidth(),
myr.getHeight(),
1);
const applicationShader = new myr.Shader(
"void main() {" +
"lowp vec4 sourcePixel = texture(source, uv) * colorFilter;" +
"lowp vec4 targetPixel = texture(target, uv);" +
"color = vec4(targetPixel.rg, targetPixel.ba + (sourcePixel.rg - vec2(0.5)) * sourcePixel.a);" +
"}",
["target", "source"],
[]
);
const grassShader = new myr.Shader(
"void main() {" +
"lowp vec2 delta = texture(displacement, vec2(uv.x, base)).xy;" +
"lowp vec2 uvOffset = pow(1.0 - uv.y, 2.0) * (delta - vec2(0.5));" +
"lowp float lighting = 1.0 - uvOffset.y * 2.0;" +
"uvOffset.y += length(uvOffset) * 3.0 * (1.0 - uv.y);" +
"color = texture(source, uv - uvOffset * pixelSize * 180.0) * lighting;" +
"}",
["source", "displacement"],
["base"]
);
const noiseConfig = cubicNoiseConfig(Math.round(Math.random() * (1 << 32)), 64);
const GrassLayer = function(height, base) {
const _surface = new myr.Surface(myr.getWidth(), height + grassClearance);
const _uvy = base / myr.getHeight() + Math.random() * 0.05;
const fill = () => {
_surface.bind();
for (let x = -Math.floor(Math.random() * bladeWidth); x < _surface.getWidth(); x += bladeWidth + Math.floor(Math.random() * bladeSpacing)) {
const h = 0.4 + cubicNoiseSample(noiseConfig, x, base) * 0.6;
myr.primitives.drawTriangleGradient(
bladeBaseColor,
x, height + grassClearance,
bladeBaseColor,
x + bladeWidth, height + grassClearance,
bladeColor,
x + bladeWidth * 0.5, grassClearance + height * (1 - h)
);
}
};
this.draw = () => {
grassShader.setVariable("base", _uvy);
grassShader.setSurface("source", _surface);
grassShader.draw(0, base - height - grassClearance);
};
this.getBase = () => base;
fill();
};
let brushed = false;
applicationShader.setSurface("source", application);
// Initialize convtex
texture.setClearColor(new Myr.Color(0.5, 0.5, 0.5, 0.5));
texture.getFront().bind();
texture.getFront().clear();
// Initialize push shape
applyPush.bind();
for (let y = 0; y < applyPush.getHeight(); ++y) for (let x = 0; x < applyPush.getWidth(); ++x) {
const delta = new Myr.Vector(x - brushRadius, y - brushRadius);
const length = delta.length();
const direction = delta.angle();
if (length > brushRadius)
continue;
myr.blendDisable();
myr.primitives.drawPoint(new Myr.Color(
0.5 + 0.5 * Math.cos(direction),
0.5 + 0.5 * Math.sin(direction),
0,
Math.min(1 - length / brushRadius, length / brushRadius)
), x, y);
myr.blendEnable();
}
// Initialize grass
for (let y = grassSpacing; y < myr.getHeight() + grassHeight; y += grassSpacing) {
const layer = new GrassLayer(grassHeight, y);
grassLayers.push(layer);
}
myr.setClearColor(bladeBaseColor);
myr.utils.loop(function(timeStep) {
if (brushed) {
texture.apply(applicationShader);
application.bind();
application.clear();
}
brushed = false;
texture.update();
myr.bind();
myr.clear();
grassShader.setSurface("displacement", texture.getFront());
for (const layer of grassLayers) {
if (mouseCurrent.y < layer.getBase() && mouseCurrent.y >= layer.getBase() - grassSpacing)
myr.primitives.drawTriangle(
Myr.Color.BLUE,
mouseCurrent.x - 30,
mouseCurrent.y,
mouseCurrent.x + 30,
mouseCurrent.y,
mouseCurrent.x,
mouseCurrent.y - 200
);
layer.draw();
}
texture.getFront().drawScaled(0, 0, 0.2, 0.2);
myr.flush();
return true;
});
canvas.addEventListener("mousemove", event => {
mousePrevious.x = mouseCurrent.x;
mousePrevious.y = mouseCurrent.y;
mouseCurrent.x = event.clientX - rect.left;
mouseCurrent.y = event.clientY - rect.top;
if (!mousePrevious.equals(mouseCurrent)) {
const dx = mouseCurrent.x - mousePrevious.x;
const dy = mouseCurrent.y - mousePrevious.y;
const dl = Math.sqrt(dx * dx + dy * dy);
application.bind();
application.clear();
myr.blendDisable();
myr.setAlpha(Math.min(1, dl / 80));
applyPush.draw(
mousePrevious.x - brushRadius,
mousePrevious.y - brushRadius,
);
myr.setAlpha(1);
myr.blendEnable();
brushed = true;
}
});
</script>
</body>
</html>