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