diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl new file mode 100644 index 0000000..0f33b8a --- /dev/null +++ b/assets/shaders/custom_material.wgsl @@ -0,0 +1,23 @@ +#import bevy_pbr::mesh_vertex_output MeshVertexOutput +#import bevy_pbr::mesh_view_bindings globals + +struct CustomMaterial { + color: vec4, +}; + +@group(1) @binding(0) +var material: CustomMaterial; +@group(1) @binding(1) +var base_color_texture: texture_2d; +@group(1) @binding(2) +var base_color_sampler: sampler; + +@fragment +fn fragment( + mesh: MeshVertexOutput, +) -> @location(0) vec4 { + let speed = 2.0; + let repeated_uv = fract(mesh.uv); + let scrolled_uv = repeated_uv+ vec2(cos(globals.time*speed), cos(globals.time*speed)); + return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv); +} diff --git a/assets/shaders/repeated.wgsl b/assets/shaders/repeated.wgsl new file mode 100644 index 0000000..34f775d --- /dev/null +++ b/assets/shaders/repeated.wgsl @@ -0,0 +1,135 @@ +// Same imports as +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::pbr_bindings +#import bevy_pbr::mesh_bindings + +#import bevy_pbr::utils +#import bevy_pbr::clustered_forward +#import bevy_pbr::lighting +#import bevy_pbr::shadows +#import bevy_pbr::fog +#import bevy_pbr::pbr_functions + +struct Repeats { + horizontal: u32, + vertical: u32, + _wasm_padding1: u32, + _wasm_padding2: u32, +} + +@group(1) @binding(0) +var texture: texture_2d; +@group(1) @binding(1) +var texture_sampler: sampler; +@group(1) @binding(2) +var repeats: Repeats; + +struct FragmentInput { + @builtin(front_facing) is_front: bool, + @builtin(position) frag_coord: vec4, + #import bevy_pbr::mesh_vertex_output +} + + +fn get_texture_sample(coords: vec2) -> vec4 { + let repeated_coords = vec2( + (coords.x % (1. / f32(repeats.horizontal))) * f32(repeats.horizontal), + (coords.y % (1. / f32(repeats.vertical))) * f32(repeats.vertical) + ); + return textureSample(texture, texture_sampler, repeated_coords); +} + +/// Adapted from +fn get_pbr_output(in: FragmentInput) -> vec4 { + var material = standard_material_new(); + material.perceptual_roughness = 1.0; + + var output_color: vec4 = material.base_color; + + + // NOTE: Unlit bit not set means == 0 is true, so the true case is if lit + if ((material.flags & STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u) { + // Prepare a 'processed' StandardMaterial by sampling all textures to resolve + // the material members + var pbr_input = pbr_input_new(); + pbr_input.frag_coord = in.frag_coord; + pbr_input.world_position = in.world_position; + pbr_input.world_normal = in.world_normal; + pbr_input.material = material; + + // TODO use .a for exposure compensation in HDR + var emissive: vec4 = material.emissive; + + pbr_input.material.emissive = emissive; + + var metallic: f32 = material.metallic; + var perceptual_roughness: f32 = material.perceptual_roughness; + + pbr_input.material.metallic = metallic; + pbr_input.material.perceptual_roughness = perceptual_roughness; + + var occlusion: f32 = 1.0; + + pbr_input.frag_coord = in.frag_coord; + pbr_input.world_position = in.world_position; + pbr_input.world_normal = prepare_world_normal( + in.world_normal, + (material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u, + in.is_front, + ); + + pbr_input.is_orthographic = view.projection[3].w == 1.0; + + pbr_input.N = apply_normal_mapping( + material.flags, + pbr_input.world_normal, +#ifdef VERTEX_TANGENTS +#ifdef STANDARDMATERIAL_NORMAL_MAP + in.world_tangent, +#endif +#endif +#ifdef VERTEX_UVS + in.uv, +#endif + ); + pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); + pbr_input.occlusion = occlusion; + + pbr_input.flags = mesh.flags; + + output_color = pbr(pbr_input); + } else { + output_color = alpha_discard(material, output_color); + } + + // fog + if (fog.mode != FOG_MODE_OFF && (material.flags & STANDARD_MATERIAL_FLAGS_FOG_ENABLED_BIT) != 0u) { + output_color = apply_fog(output_color, in.world_position.xyz, view.world_position.xyz); + } + +#ifdef TONEMAP_IN_SHADER + output_color = tone_mapping(output_color); +#endif +#ifdef DEBAND_DITHER + var output_rgb = output_color.rgb; + output_rgb = powsafe(output_rgb, 1.0 / 2.2); + output_rgb = output_rgb + screen_space_dither(in.frag_coord.xy); + // This conversion back to linear space is required because our output texture format is + // SRGB; the GPU will assume our output is linear and will apply an SRGB conversion. + output_rgb = powsafe(output_rgb, 2.2); + output_color = vec4(output_rgb, output_color.a); +#endif +#ifdef PREMULTIPLY_ALPHA + output_color = premultiply_alpha(material.flags, output_color); +#endif + return output_color; +} + +@fragment +fn fragment(in: FragmentInput) -> @location(0) vec4 { + let texture = get_texture_sample(in.uv); + let pbr_output = get_pbr_output(in); + + + return texture * pbr_output; +} \ No newline at end of file diff --git a/assets/sprites/pendejo-1.png b/assets/sprites/pendejo-1.png new file mode 100644 index 0000000..583b047 Binary files /dev/null and b/assets/sprites/pendejo-1.png differ diff --git a/assets/sprites/pendejo-2.png b/assets/sprites/pendejo-2.png new file mode 100644 index 0000000..4cad1b5 Binary files /dev/null and b/assets/sprites/pendejo-2.png differ diff --git a/assets/textures/cabron.jpg b/assets/textures/cabron.jpg new file mode 100644 index 0000000..40cfa32 Binary files /dev/null and b/assets/textures/cabron.jpg differ diff --git a/examples/particles.rs b/examples/particles.rs new file mode 100644 index 0000000..49c6db8 --- /dev/null +++ b/examples/particles.rs @@ -0,0 +1,47 @@ +//! Renders an animated sprite by loading all animation frames from a single image (a sprite sheet) +//! into a texture atlas, and changing the displayed image periodically. + +use bevy::prelude::*; +use bevy_particle_systems::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) + .add_plugins(ParticleSystemPlugin) // prevents blurry sprites + .add_systems(Startup, spawn_particles) + .run(); +} + +fn spawn_particles(mut commands: Commands, asset_server: Res) { + commands.spawn(Camera2dBundle::default()); + + commands + .spawn(ParticleSystemBundle { + particle_system: ParticleSystem { + max_particles: 50_000, + texture: ParticleTexture::Sprite(asset_server.load("px.png")), + spawn_rate_per_second: 1000.0.into(), + initial_speed: JitteredValue::jittered(200.0, -50.0..50.0), + lifetime: JitteredValue::jittered(8.0, -2.0..2.0), + color: ColorOverTime::Gradient(Curve::new(vec![ + CurvePoint::new(Color::PURPLE, 0.0), + CurvePoint::new(Color::RED, 0.5), + CurvePoint::new(Color::rgba(0.0, 0.0, 1.0, 0.0), 1.0), + ])), + looping: true, + system_duration_seconds: 10.0, + max_distance: Some(300.0), + scale: 2.0.into(), + bursts: vec![ + ParticleBurst::new(0.0, 1000), + ParticleBurst::new(2.0, 1000), + ParticleBurst::new(4.0, 1000), + ParticleBurst::new(6.0, 1000), + ParticleBurst::new(8.0, 1000), + ], + ..ParticleSystem::default() + }, + ..ParticleSystemBundle::default() + }) + .insert(Playing); +} diff --git a/examples/shaders.rs b/examples/shaders.rs new file mode 100644 index 0000000..bdc16db --- /dev/null +++ b/examples/shaders.rs @@ -0,0 +1,64 @@ +//! A shader and a material that uses it. + +use bevy::{ + prelude::*, + reflect::{TypePath, TypeUuid}, + render::render_resource::{AsBindGroup, ShaderRef}, +}; + +fn main() { + App::new() + .add_plugins((DefaultPlugins, MaterialPlugin::::default())) + .add_systems(Startup, setup) + .run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + asset_server: Res, +) { + // cube + commands.spawn(MaterialMeshBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 3.0 })), + transform: Transform::from_xyz(0.0, 0.5, 0.0), + material: materials.add(CustomMaterial { + color: Color::WHITE, + color_texture: Some(asset_server.load("textures/cabron.jpg")), + alpha_mode: AlphaMode::Blend, + }), + ..default() + }); + + // camera + commands.spawn(Camera3dBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }); +} + +/// The Material trait is very configurable, but comes with sensible defaults for all methods. +/// You only need to implement functions for features that need non-default behavior. See the Material api docs for details! +impl Material for CustomMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/custom_material.wgsl".into() + } + + fn alpha_mode(&self) -> AlphaMode { + self.alpha_mode + } +} + +// This is the struct that will be passed to your shader +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] +#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] +pub struct CustomMaterial { + #[uniform(0)] + color: Color, + #[texture(1)] + #[sampler(2)] + color_texture: Option>, + alpha_mode: AlphaMode, +}