-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
Copy pathspecular_tint.rs
227 lines (204 loc) · 7.18 KB
/
specular_tint.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
//! Demonstrates specular tints and maps.
use std::f32::consts::PI;
use bevy::{color::palettes::css::WHITE, core_pipeline::Skybox, prelude::*};
/// The camera rotation speed in radians per frame.
const ROTATION_SPEED: f32 = 0.005;
/// The rate at which the specular tint hue changes in degrees per frame.
const HUE_SHIFT_SPEED: f32 = 0.2;
static SWITCH_TO_MAP_HELP_TEXT: &str = "Press Space to switch to a specular map";
static SWITCH_TO_SOLID_TINT_HELP_TEXT: &str = "Press Space to switch to a solid specular tint";
/// The current settings the user has chosen.
#[derive(Resource, Default)]
struct AppStatus {
/// The type of tint (solid or texture map).
tint_type: TintType,
/// The hue of the solid tint in radians.
hue: f32,
}
/// Assets needed by the demo.
#[derive(Resource)]
struct AppAssets {
/// A color tileable 3D noise texture.
noise_texture: Handle<Image>,
}
impl FromWorld for AppAssets {
fn from_world(world: &mut World) -> Self {
let asset_server = world.resource::<AssetServer>();
Self {
noise_texture: asset_server.load("textures/AlphaNoise.png"),
}
}
}
/// The type of specular tint that the user has selected.
#[derive(Clone, Copy, PartialEq, Default)]
enum TintType {
/// A solid color.
#[default]
Solid,
/// A Perlin noise texture.
Map,
}
/// The entry point.
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Bevy Specular Tint Example".into(),
..default()
}),
..default()
}))
.init_resource::<AppAssets>()
.init_resource::<AppStatus>()
.insert_resource(AmbientLight {
color: Color::BLACK,
brightness: 0.0,
..default()
})
.add_systems(Startup, setup)
.add_systems(Update, rotate_camera)
.add_systems(Update, (toggle_specular_map, update_text).chain())
.add_systems(Update, shift_hue.after(toggle_specular_map))
.run();
}
/// Creates the scene.
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
app_status: Res<AppStatus>,
mut meshes: ResMut<Assets<Mesh>>,
mut standard_materials: ResMut<Assets<StandardMaterial>>,
) {
// Spawns a camera.
commands.spawn((
Transform::from_xyz(-2.0, 0.0, 3.5).looking_at(Vec3::ZERO, Vec3::Y),
Camera {
hdr: true,
..default()
},
Camera3d::default(),
Skybox {
image: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
brightness: 3000.0,
..default()
},
EnvironmentMapLight {
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
// We want relatively high intensity here in order for the specular
// tint to show up well.
intensity: 25000.0,
..default()
},
));
// Spawn the sphere.
commands.spawn((
Transform::from_rotation(Quat::from_rotation_x(PI * 0.5)),
Mesh3d(meshes.add(Sphere::default().mesh().uv(32, 18))),
MeshMaterial3d(standard_materials.add(StandardMaterial {
// We want only reflected specular light here, so we set the base
// color as black.
base_color: Color::BLACK,
reflectance: 1.0,
specular_tint: Color::hsva(app_status.hue, 1.0, 1.0, 1.0),
// The object must not be metallic, or else the reflectance is
// ignored per the Filament spec:
//
// <https://google.github.io/filament/Filament.html#listing_fnormal>
metallic: 0.0,
perceptual_roughness: 0.0,
..default()
})),
));
// Spawn the help text.
commands.spawn((
Node {
position_type: PositionType::Absolute,
bottom: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
app_status.create_text(),
));
}
/// Rotates the camera a bit every frame.
fn rotate_camera(mut cameras: Query<&mut Transform, With<Camera3d>>) {
for mut camera_transform in cameras.iter_mut() {
camera_transform.translation =
Quat::from_rotation_y(ROTATION_SPEED) * camera_transform.translation;
camera_transform.look_at(Vec3::ZERO, Vec3::Y);
}
}
/// Alters the hue of the solid color a bit every frame.
fn shift_hue(
mut app_status: ResMut<AppStatus>,
objects_with_materials: Query<&MeshMaterial3d<StandardMaterial>>,
mut standard_materials: ResMut<Assets<StandardMaterial>>,
) {
if app_status.tint_type != TintType::Solid {
return;
}
app_status.hue += HUE_SHIFT_SPEED;
for material_handle in objects_with_materials.iter() {
let Some(material) = standard_materials.get_mut(material_handle) else {
continue;
};
material.specular_tint = Color::hsva(app_status.hue, 1.0, 1.0, 1.0);
}
}
impl AppStatus {
/// Returns appropriate help text that reflects the current app status.
fn create_text(&self) -> Text {
let tint_map_help_text = match self.tint_type {
TintType::Solid => SWITCH_TO_MAP_HELP_TEXT,
TintType::Map => SWITCH_TO_SOLID_TINT_HELP_TEXT,
};
Text::new(tint_map_help_text)
}
}
/// Changes the specular tint to a solid color or map when the user presses
/// Space.
fn toggle_specular_map(
keyboard: Res<ButtonInput<KeyCode>>,
mut app_status: ResMut<AppStatus>,
app_assets: Res<AppAssets>,
objects_with_materials: Query<&MeshMaterial3d<StandardMaterial>>,
mut standard_materials: ResMut<Assets<StandardMaterial>>,
) {
if !keyboard.just_pressed(KeyCode::Space) {
return;
}
// Swap tint type.
app_status.tint_type = match app_status.tint_type {
TintType::Solid => TintType::Map,
TintType::Map => TintType::Solid,
};
for material_handle in objects_with_materials.iter() {
let Some(material) = standard_materials.get_mut(material_handle) else {
continue;
};
// Adjust the tint type.
match app_status.tint_type {
TintType::Solid => {
material.reflectance = 1.0;
material.specular_tint_texture = None;
}
TintType::Map => {
// Set reflectance to 2.0 to spread out the map's reflectance
// range from the default [0.0, 0.5] to [0.0, 1.0].
material.reflectance = 2.0;
// As the tint map is multiplied by the tint color, we set the
// latter to white so that only the map has an effect.
material.specular_tint = WHITE.into();
material.specular_tint_texture = Some(app_assets.noise_texture.clone());
}
};
}
}
/// Updates the help text at the bottom of the screen to reflect the current app
/// status.
fn update_text(mut text_query: Query<&mut Text>, app_status: Res<AppStatus>) {
for mut text in text_query.iter_mut() {
*text = app_status.create_text();
}
}