Skip to content

Commit

Permalink
Custom sprite material
Browse files Browse the repository at this point in the history
  • Loading branch information
ayamaev-se committed Dec 6, 2023
1 parent 593fe22 commit 1375eeb
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 63 deletions.
44 changes: 44 additions & 0 deletions assets/shaders/sprite.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#import bevy_pbr::{
pbr_fragment::pbr_input_from_standard_material,
pbr_functions::alpha_discard,
}

#ifdef PREPASS_PIPELINE
#import bevy_pbr::{
prepass_io::{VertexOutput, FragmentOutput},
pbr_deferred_functions::deferred_output,
}
#else
#import bevy_pbr::{
forward_io::{VertexOutput, FragmentOutput},
pbr_functions::{apply_pbr_lighting, main_pass_post_lighting_processing},
}
#endif

@fragment
fn fragment(
in: VertexOutput,
@builtin(front_facing) is_front: bool,
) -> FragmentOutput {
// generate a PbrInput struct from the StandardMaterial bindings
var pbr_input = pbr_input_from_standard_material(in, is_front);

if (pbr_input.material.base_color.a < 0.5) {
discard;
}

#ifdef PREPASS_PIPELINE
// in deferred mode we can't modify anything after that, as lighting is run in a separate fullscreen shader.
let out = deferred_output(in, pbr_input);
#else
var out: FragmentOutput;
// apply lighting
out.color = apply_pbr_lighting(pbr_input);

// apply in-shader post processing (fog, alpha-premultiply, and also tonemapping, debanding if the camera is non-hdr)
// note this does not include fullscreen postprocessing effects like bloom.
out.color = main_pass_post_lighting_processing(pbr_input, out.color);
#endif

return out;
}
88 changes: 88 additions & 0 deletions assets/shaders/sprite_prepass.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#import bevy_pbr::{
prepass_bindings,
mesh_functions,
prepass_io::{Vertex, VertexOutput, FragmentOutput},
skinning,
morph,
mesh_view_bindings::{view, previous_view_proj},
rgb9e5::vec3_to_rgb9e5_,
pbr_prepass_functions::prepass_alpha_discard,
pbr_prepass_functions,
pbr_bindings::material,
pbr_deferred_functions,
pbr_types::{
STANDARD_MATERIAL_FLAGS_UNLIT_BIT,
STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT
},
pbr_functions,
prepass_io,
pbr_types
}
#import bevy_render::instance_index::get_instance_index
#import bevy_shader_utils::simplex_noise_3d::simplex_noise_3d

@group(1) @binding(101) var base_teture: texture_2d<f32>;
@group(1) @binding(102) var base_teture_sampler: sampler;



// basically all of this prepass shader is just copy/pasted from
// the bevy pbr prepass shader.
@fragment
fn fragment(
in: VertexOutput,
@builtin(front_facing) is_front: bool,
) -> FragmentOutput {
pbr_prepass_functions::prepass_alpha_discard(in);

var out: FragmentOutput;

#ifdef DEPTH_CLAMP_ORTHO
out.frag_depth = in.clip_position_unclamped.z;
#endif // DEPTH_CLAMP_ORTHO

#ifdef NORMAL_PREPASS
// 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 {
let double_sided = (material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u;

let world_normal = pbr_functions::prepare_world_normal(
in.world_normal,
double_sided,
is_front,
);

let normal = pbr_functions::apply_normal_mapping(
material.flags,
world_normal,
double_sided,
is_front,
#ifdef VERTEX_TANGENTS
#ifdef STANDARDMATERIAL_NORMAL_MAP
in.world_tangent,
#endif // STANDARDMATERIAL_NORMAL_MAP
#endif // VERTEX_TANGENTS
#ifdef VERTEX_UVS
in.uv,
#endif // VERTEX_UVS
view.mip_bias,
);

out.normal = vec4(normal * 0.5 + vec3(0.5), 1.0);
} else {
out.normal = vec4(in.world_normal * 0.5 + vec3(0.5), 1.0);
}



#endif // NORMAL_PREPASS

#ifdef MOTION_VECTOR_PREPASS
out.motion_vector = pbr_prepass_functions::calculate_motion_vector(in.world_position, in.previous_world_position);
#endif

if textureSample(base_teture, base_teture_sampler, in.uv).a < 0.5 { discard; };

return out;
}
14 changes: 7 additions & 7 deletions src/common_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use bevy::prelude::*;

#[derive(Resource)]
pub struct CommonStorage {
pub plane : Handle<Mesh>
pub plane: Handle<Mesh>,
}

pub struct CommonStoragePlugin;
Expand All @@ -13,13 +13,13 @@ impl Plugin for CommonStoragePlugin {
}
}

pub fn init_common_storage(
mut commands : Commands,
mut meshes : ResMut<Assets<Mesh>>,
) {
pub fn init_common_storage(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
let storage = CommonStorage {
plane : meshes.add(Mesh::from(shape::Plane { size: 1.0, ..default() }))
plane: meshes.add(Mesh::from(shape::Plane {
size: 1.0,
..default()
})),
};

commands.insert_resource(storage);
}
}
1 change: 0 additions & 1 deletion src/debug_diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ impl Plugin for DiagnosticPlugin {
(setup_diagnostic_panel, apply_deferred, setup_counter),
)
.add_systems(Update, (fps_counting,))

.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::default());
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![allow(clippy::type_complexity)]

pub mod common_storage;
pub mod debug_diagnostic;
pub mod physics;
pub mod player;
pub mod safe_area;
pub mod sheep;
pub mod sprite_material;
pub mod test_level;
pub mod safe_area;
pub mod torch;
pub mod common_storage;

use std::f32::consts::PI;

Expand Down Expand Up @@ -44,10 +45,12 @@ impl Plugin for GamePlugin {
}

app.add_plugins((
player::PlayerPlugin,
physics::PhysicsPlugin,
common_storage::CommonStoragePlugin,
player::PlayerPlugin,
physics::PhysicsPlugin,
common_storage::CommonStoragePlugin,
torch::TorchPlugin,
safe_area::SafeAreaPlugin,
sprite_material::SpriteMaterialPlugin,
));

app.add_systems(Startup, (test_level::setup, sheep::setup));
Expand All @@ -65,4 +68,3 @@ impl Plugin for GamePlugin {
pub fn get_sprite_rotation() -> Quat {
Quat::from_euler(EulerRot::XYZ, PI / 2.0 - PI / 4.0, 0.0, 0.0)
}

24 changes: 17 additions & 7 deletions src/player.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use bevy::{input::mouse::MouseWheel, prelude::*, window::PrimaryWindow};
use bevy::{input::mouse::MouseWheel, pbr::ExtendedMaterial, prelude::*, window::PrimaryWindow};

use crate::{get_sprite_rotation, physics::Velocity};
use crate::{
get_sprite_rotation,
physics::Velocity,
sprite_material::{SpriteExtension, SpriteMaterial},
};

const DOG_PATH: &str = "test/dog.png";

Expand Down Expand Up @@ -72,22 +76,28 @@ fn spawn_player_by_event(
asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut sprite_material: ResMut<Assets<SpriteMaterial>>,
) {
for event in event_reader.read() {
let plane = meshes.add(Mesh::from(shape::Plane {
size: 1.0,
..default()
}));
let material = materials.add(StandardMaterial {
base_color_texture: Some(asset_server.load(DOG_PATH)),
alpha_mode: AlphaMode::Blend,
..default()
let material = sprite_material.add(SpriteMaterial {
base: StandardMaterial {
base_color_texture: Some(asset_server.load(DOG_PATH)),
alpha_mode: AlphaMode::Opaque,
..default()
},
extension: SpriteExtension {
base_teture: Some(asset_server.load(DOG_PATH)),
},
});

info!("Spawn player at {:?}", event.position);

commands.spawn((
PbrBundle {
MaterialMeshBundle {
mesh: plane.clone(),
material: material.clone(),
transform: Transform::from_translation(event.position + Vec3::new(0.0, 2.5, 0.0))
Expand Down
36 changes: 35 additions & 1 deletion src/safe_area.rs
Original file line number Diff line number Diff line change
@@ -1 +1,35 @@
//safe area description and logic
//safe area description and logic

use std::f32::consts::PI;

use bevy::prelude::*;

pub struct SafeAreaPlugin;

impl Plugin for SafeAreaPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, draw_safe_area);
}
}

#[derive(Component)]
pub enum SafeArea {
Rect { pos: Vec2, size: Vec2 },
Ellipse { pos1: Vec2, pos2: Vec2, radius: f32 },
}

fn draw_safe_area(mut gizmos: Gizmos, query: Query<&SafeArea>) {
for safe_area in query.iter() {
match safe_area {
SafeArea::Rect { pos, size } => {
gizmos.rect(
Vec3::new(pos.x, 0.001, pos.y),
Quat::from_euler(EulerRot::XYZ, PI / 2.0, 0.0, 0.0),
*size,
Color::GREEN,
);
}
SafeArea::Ellipse { pos1, pos2, radius } => {}
}
}
}
22 changes: 17 additions & 5 deletions src/sheep.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::f32::consts::PI;

use bevy::prelude::*;
use bevy::{pbr::ExtendedMaterial, prelude::*};
use rand::Rng;

use crate::{physics::Velocity, player::Bark};
use crate::{physics::Velocity, player::Bark, sprite_material::SpriteExtension};

const SHEEP_PATH: &str = "test/sheep.png";

Expand Down Expand Up @@ -92,6 +92,7 @@ pub fn setup(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
asset_server: Res<AssetServer>,
mut sprite_material: ResMut<Assets<ExtendedMaterial<StandardMaterial, SpriteExtension>>>,
) {
let square = meshes.add(
shape::Plane {
Expand All @@ -103,11 +104,22 @@ pub fn setup(
let sheep_texture: Handle<Image> = asset_server.load(SHEEP_PATH);

let sheep_material = materials.add(StandardMaterial {
base_color_texture: Some(sheep_texture),
base_color_texture: Some(sheep_texture.clone()),
alpha_mode: AlphaMode::Blend,
..default()
});

let sheep_sprite_material = sprite_material.add(ExtendedMaterial {
base: StandardMaterial {
base_color_texture: Some(sheep_texture.clone()),
alpha_mode: AlphaMode::Opaque,
..default()
},
extension: SpriteExtension {
base_teture: Some(sheep_texture.clone()),
},
});

//spawn sheeps
let r = 50.0;
let mut rng = rand::thread_rng();
Expand All @@ -124,9 +136,9 @@ pub fn setup(
}

commands.spawn((
PbrBundle {
MaterialMeshBundle {
mesh: square.clone(),
material: sheep_material.clone(),
material: sheep_sprite_material.clone(),
transform: Transform::from_xyz(pos.x, pos.y + 3.0, pos.z)
.with_rotation(Quat::from_euler(
EulerRot::XYZ,
Expand Down
40 changes: 40 additions & 0 deletions src/sprite_material.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//just for testing. Gizmo works very bad with AlphaMode::Blend. Its alternative shader with discard on depth step

use bevy::{
pbr::{ExtendedMaterial, MaterialExtension},
prelude::*,
render::render_resource::{AsBindGroup, ShaderRef},
};

pub type SpriteMaterial = ExtendedMaterial<StandardMaterial, SpriteExtension>;

pub struct SpriteMaterialPlugin;

impl Plugin for SpriteMaterialPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(MaterialPlugin::<
ExtendedMaterial<StandardMaterial, SpriteExtension>,
>::default());
}
}

#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
pub struct SpriteExtension {
#[texture(101)]
#[sampler(102)]
pub base_teture: Option<Handle<Image>>,
}

impl MaterialExtension for SpriteExtension {
fn fragment_shader() -> ShaderRef {
"shaders/sprite.wgsl".into()
}

fn deferred_fragment_shader() -> ShaderRef {
"shaders/sprite.wgsl".into()
}

fn prepass_fragment_shader() -> ShaderRef {
"shaders/sprite_prepass.wgsl".into()
}
}
Loading

0 comments on commit 1375eeb

Please sign in to comment.