mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-20 12:44:52 +02:00
228 lines
9.8 KiB
GLSL
228 lines
9.8 KiB
GLSL
// grass of the heightmap terrain (opengl46_grass): a blade made of the numbers of its vertex and its instance.
|
|
//
|
|
// the instances of a draw stand in a tile of a chunk. a blade takes its place in the tile from a sequence which spreads
|
|
// any number of its first members evenly (R2, shifted for each tile), so drawing fewer of them thins the grass out
|
|
// evenly; a blade picks its kind by the densities painted there, and gets its height, its turn and its lean from its own
|
|
// numbers, so it's the same in every frame. blades of no kind, in the holes, or past the share drawn at their distance are
|
|
// collapsed into a point outside the view. the vertices go up the blade in pairs, the last one is its tip.
|
|
// the chunk is drawn relative to its corner: x, z in metres from the corner, y the height as it is
|
|
|
|
#include <common>
|
|
|
|
// x, z: corner of the tile in the chunk, metres; y: side of the tile; w: blades of the tile where its densest kind is painted fully
|
|
uniform vec4 grass_tile;
|
|
// x: the share of the densest kind at the densest place of the tile (the blades of the tile are counted for it); y: segments
|
|
// of a blade; z, w: number of the tile in the world
|
|
uniform vec4 grass_part;
|
|
// x: side of the chunk, metres; y: cells of its grid along a side; z: grass samples along a side less one
|
|
uniform vec4 grass_chunk;
|
|
// the camera relative to the corner of the chunk (y as it is)
|
|
uniform vec4 grass_camera;
|
|
// x: distance the grass starts to thin out at, for speed; y: distance the fading starts at (gfx.grass.distance); z: where the
|
|
// grass ends (gfx.grass.fadedistance)
|
|
uniform vec4 grass_lod;
|
|
// x, y: direction of the wind (x, z of the world); z: its strength, 0..1; w: time, seconds
|
|
uniform vec4 grass_wind;
|
|
// the kinds of the chunk: colour at the root and at the tip (linear rgb), and height, its variation, width, lean
|
|
uniform vec4 grass_base[4];
|
|
uniform vec4 grass_tip[4];
|
|
uniform vec4 grass_shape[4];
|
|
// share of the blades of each kind against the densest one of the chunk
|
|
uniform vec4 grass_share;
|
|
// the colour of each kind dried out (linear rgb), and how much it follows the seasons
|
|
uniform vec4 grass_dry[4];
|
|
// the time of the year: x: how dry the grass is, y: how fresh (spring), z: how grown (share of the height)
|
|
uniform vec4 grass_season;
|
|
|
|
layout(binding = 0) uniform sampler2D grass_heights; // heights of the grid points, R32F
|
|
layout(binding = 1) uniform sampler2D grass_density; // densities of the kinds, RGBA8
|
|
layout(binding = 2) uniform sampler2D grass_holes; // holes cut in the ground, R8
|
|
|
|
out vec3 f_normal;
|
|
out vec4 f_pos;
|
|
out vec3 f_albedo;
|
|
out vec4 f_clip_pos;
|
|
out vec4 f_clip_future_pos;
|
|
out vec4 UserData;
|
|
|
|
uint hash(uint x)
|
|
{
|
|
x ^= x >> 16;
|
|
x *= 0x7feb352dU;
|
|
x ^= x >> 15;
|
|
x *= 0x846ca68bU;
|
|
x ^= x >> 16;
|
|
return x;
|
|
}
|
|
|
|
float random(inout uint state)
|
|
{
|
|
state = hash(state);
|
|
return float(state >> 8) * (1.0 / 16777216.0);
|
|
}
|
|
|
|
// the share of the blades drawn at specified distance for speed: fewer further away, each standing for more of them
|
|
float lod_thinning(float distance)
|
|
{
|
|
return distance <= grass_lod.x ? 1.0 : pow(grass_lod.x / distance, 1.5);
|
|
}
|
|
|
|
// the share of the blades drawn: the above, and fewer and fewer over the fading distance, where the grass really gets
|
|
// sparser; the same as lod_share() of opengl46grass.cpp
|
|
float lod_share(float distance)
|
|
{
|
|
return lod_thinning(distance) * (1.0 - smoothstep(grass_lod.y, grass_lod.z, distance));
|
|
}
|
|
|
|
// height of the ground at a point of the chunk and its normal, the way the full grid is triangulated
|
|
float ground(vec2 point, out vec3 normal)
|
|
{
|
|
float cells = grass_chunk.y;
|
|
float spacing = grass_chunk.x / cells;
|
|
vec2 place = clamp(point / spacing, vec2(0.0), vec2(cells));
|
|
ivec2 cell = min(ivec2(place), ivec2(int(cells) - 1));
|
|
vec2 t = place - vec2(cell);
|
|
float h00 = texelFetch(grass_heights, cell, 0).r;
|
|
float h10 = texelFetch(grass_heights, cell + ivec2(1, 0), 0).r;
|
|
float h01 = texelFetch(grass_heights, cell + ivec2(0, 1), 0).r;
|
|
float h11 = texelFetch(grass_heights, cell + ivec2(1, 1), 0).r;
|
|
// the quads are cut along the diagonal from (x0, z1) to (x1, z0), as the mesh is made
|
|
if (t.x + t.y <= 1.0)
|
|
{
|
|
normal = normalize(vec3(-(h10 - h00) / spacing, 1.0, -(h01 - h00) / spacing));
|
|
return h00 + t.x * (h10 - h00) + t.y * (h01 - h00);
|
|
}
|
|
normal = normalize(vec3(-(h11 - h01) / spacing, 1.0, -(h11 - h10) / spacing));
|
|
return h11 + (1.0 - t.x) * (h01 - h11) + (1.0 - t.y) * (h10 - h11);
|
|
}
|
|
|
|
void collapse()
|
|
{
|
|
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
f_normal = vec3(0.0, 1.0, 0.0);
|
|
f_pos = vec4(0.0);
|
|
f_albedo = vec3(0.0);
|
|
f_clip_pos = gl_Position;
|
|
f_clip_future_pos = gl_Position;
|
|
UserData = vec4(0.0);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
int blade = gl_InstanceID;
|
|
// the numbers of the blade, drawn in the same order whatever becomes of them
|
|
uint state = hash(uint(blade) * 0x9E3779B9U ^ hash(uint(int(grass_part.z)) * 0x85EBCA6BU ^ hash(uint(int(grass_part.w)) + 0x27D4EB2FU)));
|
|
uint tilestate = hash(uint(int(grass_part.z)) * 0x85EBCA6BU ^ hash(uint(int(grass_part.w)) + 0x165667B1U));
|
|
vec2 shift = vec2(random(tilestate), random(tilestate));
|
|
vec2 jitter = vec2(random(state), random(state)) - 0.5;
|
|
float pick = random(state);
|
|
float heightrandom = random(state) * 2.0 - 1.0;
|
|
float widthrandom = random(state);
|
|
float turnrandom = random(state) * 6.2831853;
|
|
float leanrandom = random(state) * 6.2831853;
|
|
float leanamount = random(state);
|
|
float shade = random(state);
|
|
float phase = random(state);
|
|
float early = random(state);
|
|
|
|
// place: R2 spreads the first blades evenly at any count, a little jitter breaks its lattice
|
|
vec2 spot = fract(shift + float(blade) * vec2(0.7548776662466927, 0.5698402909980532));
|
|
vec2 point = grass_tile.xy + fract(spot + jitter * (0.7 / sqrt(grass_tile.w))) * grass_tile.z;
|
|
if (any(lessThan(point, vec2(0.0))) || any(greaterThanEqual(point, vec2(grass_chunk.x))))
|
|
{
|
|
collapse();
|
|
return;
|
|
}
|
|
vec2 local = point / grass_chunk.x;
|
|
float samples = grass_chunk.z;
|
|
vec4 density = texture(grass_density, (local * samples + 0.5) / (samples + 1.0));
|
|
float holeside = float(textureSize(grass_holes, 0).x);
|
|
float hole = texture(grass_holes, (local * (holeside - 1.0) + 0.5) / holeside).r;
|
|
|
|
// the kind: the blades of the tile are shared among the kinds by their densities here
|
|
vec4 cover = density * grass_share / max(grass_part.x, 1e-3);
|
|
float covered = cover.x + cover.y + cover.z + cover.w;
|
|
if (covered > 1.0)
|
|
cover /= covered;
|
|
int kind = -1;
|
|
float sum = 0.0;
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
sum += cover[i];
|
|
if (pick < sum)
|
|
{
|
|
kind = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
vec3 groundnormal;
|
|
vec3 root = vec3(point.x, ground(point, groundnormal), point.y);
|
|
float distance = length(root - grass_camera.xyz);
|
|
float share = lod_share(distance);
|
|
float rank = (float(blade) + 0.5) / grass_tile.w;
|
|
if (kind < 0 || hole > 0.5 || rank >= share || distance > grass_lod.z)
|
|
{
|
|
collapse();
|
|
return;
|
|
}
|
|
|
|
vec4 shape = grass_shape[kind];
|
|
float height = shape.x * (1.0 + shape.y * heightrandom);
|
|
// the last of the blades drawn at the distance grow in, and the grass fades out at the end of its reach
|
|
height *= 1.0 - smoothstep(share * 0.8, share, rank);
|
|
// lower in winter, growing in spring
|
|
float seasonal = grass_dry[kind].a;
|
|
height *= mix(1.0, grass_season.z, seasonal);
|
|
// where the paint thins out the blades get shorter
|
|
height *= mix(0.5, 1.0, clamp(density[kind] * 1.5, 0.0, 1.0));
|
|
// the blades drawn further away stand for more of them, so they're wider
|
|
float width = shape.z * (0.8 + 0.4 * widthrandom) / sqrt(lod_thinning(distance));
|
|
|
|
// the face of the blade turns towards the camera further away, so the blades don't vanish edge on
|
|
vec2 facing = vec2(cos(turnrandom), sin(turnrandom));
|
|
vec2 toview = grass_camera.xz - root.xz;
|
|
toview = toview / max(length(toview), 1e-3);
|
|
facing = mix(facing, toview, smoothstep(grass_lod.x * 0.5, grass_lod.x * 2.0, distance));
|
|
facing = facing / max(length(facing), 1e-3);
|
|
vec2 across = vec2(-facing.y, facing.x);
|
|
|
|
// the lean of the blade and the wind: a steady push along it, with gusts running across the field, and a flutter
|
|
vec2 lean = vec2(cos(leanrandom), sin(leanrandom)) * (shape.w * (0.4 + 0.6 * leanamount));
|
|
float gust = 0.5 + 0.5 * sin(grass_wind.w * 0.9 - dot(root.xz, grass_wind.xy) * 0.12);
|
|
float flutter = sin(grass_wind.w * (2.0 + phase) + phase * 6.2831853);
|
|
vec2 sway = grass_wind.xy * grass_wind.z * (0.15 + 0.45 * gust + 0.08 * flutter);
|
|
vec2 bend = lean + sway;
|
|
// a long lean takes some of the height
|
|
float bent = min(length(bend), 0.9);
|
|
|
|
int segments = int(grass_part.y);
|
|
int level = min(gl_VertexID / 2, segments);
|
|
float side = (gl_VertexID >= segments * 2) ? 0.0 : ((gl_VertexID & 1) == 0 ? -0.5 : 0.5);
|
|
float t = float(level) / float(segments);
|
|
float halfwidth = width * (1.0 - pow(t, 1.5));
|
|
vec2 xz = root.xz + across * (side * halfwidth) + bend * (height * t * t);
|
|
float y = root.y - 0.02 + height * t * (1.0 - 0.35 * bent * t);
|
|
vec3 position = vec3(xz.x, y, xz.y);
|
|
|
|
// lit mostly as the ground under it, which keeps a field of blades from shimmering
|
|
vec3 normal = normalize(groundnormal * 0.8 + vec3(facing.x, 0.0, facing.y) * (side == 0.0 ? 0.0 : 0.25) + vec3(0.0, 0.3, 0.0));
|
|
// the seasons: some blades dry out (and green up) earlier than others, so a field turns gradually; a dry blade takes the
|
|
// dry colour, darker at its root. in spring the green is fresher
|
|
float dry = clamp(grass_season.x * seasonal * (0.6 + 0.8 * early), 0.0, 1.0);
|
|
vec3 bottom = mix(grass_base[kind].rgb, grass_dry[kind].rgb * 0.45, dry);
|
|
vec3 top = mix(grass_tip[kind].rgb, grass_dry[kind].rgb, dry);
|
|
top *= mix(vec3(1.0), vec3(1.08, 1.22, 0.85), grass_season.y * seasonal * (1.0 - dry));
|
|
vec3 albedo = mix(bottom, top, t) * (0.85 + 0.3 * shade);
|
|
// the lower part of the blades is shaded by the others
|
|
albedo *= mix(0.7, 1.0, t);
|
|
|
|
f_pos = modelview * vec4(position, 1.0);
|
|
f_normal = normalize(modelviewnormal * normal);
|
|
f_albedo = albedo;
|
|
f_clip_pos = projection * f_pos;
|
|
f_clip_future_pos = (projection * future) * f_pos;
|
|
gl_Position = f_clip_pos;
|
|
UserData = vec4(0.0);
|
|
}
|