nightshade 0.8.2

A cross-platform data-oriented game engine.
Documentation
struct ShadowUniforms {
    light_view_projection: mat4x4<f32>,
    cascade_index: u32,
    time: f32,
    wind_strength: f32,
    wind_frequency: f32,
    wind_direction: vec2<f32>,
    _padding: vec2<f32>,
}

struct GrassInstance {
    position: vec3<f32>,
    rotation: f32,
    height: f32,
    width: f32,
    species_index: u32,
    lod: u32,
    base_color: vec4<f32>,
    tip_color: vec4<f32>,
    bend: vec2<f32>,
}

struct VertexInput {
    @builtin(vertex_index) vertex_index: u32,
    @builtin(instance_index) instance_index: u32,
}

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
}

@group(0) @binding(0) var<uniform> shadow_uniforms: ShadowUniforms;
@group(0) @binding(1) var<storage, read> instances: array<GrassInstance>;
@group(0) @binding(2) var<storage, read> visible_indices: array<u32>;

fn blade_vertex_position(vertex_index: u32, height: f32, width: f32) -> vec3<f32> {
    let segment = vertex_index / 2u;
    let side = f32(vertex_index % 2u) * 2.0 - 1.0;

    let t = f32(segment) / 3.0;

    let curvature = 0.3;
    let curve_offset = curvature * t * t;

    let segment_width = width * (1.0 - t * 0.8);

    var position: vec3<f32>;
    position.x = side * segment_width * 0.5;
    position.y = t * height;
    position.z = curve_offset;

    if vertex_index == 6u {
        position.x = 0.0;
        position.y = height;
        position.z = curvature;
    }

    return position;
}

fn rotation_matrix_y(angle: f32) -> mat3x3<f32> {
    let c = cos(angle);
    let s = sin(angle);
    return mat3x3<f32>(
        vec3<f32>(c, 0.0, s),
        vec3<f32>(0.0, 1.0, 0.0),
        vec3<f32>(-s, 0.0, c)
    );
}

fn wind_displacement(world_pos: vec3<f32>, height_factor: f32) -> vec3<f32> {
    let base_freq = shadow_uniforms.wind_frequency * 0.5;
    let gust_freq = shadow_uniforms.wind_frequency * 2.0;
    let time = shadow_uniforms.time;
    let strength = shadow_uniforms.wind_strength;
    let direction = shadow_uniforms.wind_direction;

    let wave1 = sin(world_pos.x * base_freq + time * 1.5) * 0.5 + 0.5;
    let wave2 = sin(world_pos.z * base_freq * 0.7 + time * 1.2) * 0.5 + 0.5;
    let wave3 = sin((world_pos.x + world_pos.z) * gust_freq + time * 3.0) * 0.3;

    let combined_wave = (wave1 * wave2 + wave3) * strength * height_factor * height_factor;

    var displacement: vec3<f32>;
    displacement.x = direction.x * combined_wave;
    displacement.y = -combined_wave * 0.1;
    displacement.z = direction.y * combined_wave;

    return displacement;
}

@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
    var output: VertexOutput;

    let visible_index = visible_indices[input.instance_index];
    let instance = instances[visible_index];

    var local_pos = blade_vertex_position(input.vertex_index, instance.height, instance.width);

    let height_factor = local_pos.y / instance.height;

    let rot = rotation_matrix_y(instance.rotation);
    local_pos = rot * local_pos;

    let wind_disp = wind_displacement(instance.position, height_factor);

    let world_pos = instance.position + local_pos + wind_disp;

    output.position = shadow_uniforms.light_view_projection * vec4<f32>(world_pos, 1.0);

    return output;
}

@fragment
fn fs_main() {
}