Implement PBR

This commit is contained in:
reo 2025-09-26 16:49:36 +03:00
parent f34a9b01a0
commit 3503fc15d2
14 changed files with 659 additions and 10 deletions

View file

@ -14,7 +14,7 @@ use raidillon_core::engine::EngineTrait;
use raidillon_core::time;
use raidillon_core::time::Time;
use crate::render::debug_ui::ImguiBridge;
use crate::render::BasicMeshRenderingSystem;
use crate::render::PbrMeshRenderingSystem;
use crate::GliumAssetManager;
pub struct GliumPlatform<E: EngineTrait<PlatformCtx = PlatformContext>> {
@ -45,7 +45,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
let time = time::Time::new(time_cfg);
// Install rendering systems
rendering_system_manager.add::<BasicMeshRenderingSystem>(&display, &window);
rendering_system_manager.add::<PbrMeshRenderingSystem>(&display, &window);
rendering_system_manager.add::<ImguiBridge>(&display, &window);
Self {

View file

@ -1,4 +1,4 @@
mod basic;
mod pbr;
pub mod debug_ui;
pub use basic::BasicMeshRenderingSystem;
pub use pbr::PbrMeshRenderingSystem;

View file

@ -0,0 +1,192 @@
// AI Generated
use glium::{uniform, Display, Program, Surface};
use glium::glutin::surface::WindowSurface;
use glium::texture::{RawImage2d, SrgbTexture2d, Texture2d};
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction};
use glam::{Vec3, Vec4, Mat4};
use crate::RenderingSystem;
use crate::system::RenderingContext;
use raidillon_assets::include_shader;
pub use raidillon_platform::Camera;
use raidillon_ecs::components::{ModelHandle, PointLight};
use raidillon_ecs::Transform;
use crate::model::Model;
pub struct PbrMeshRenderingSystem {
program: Program,
white_srgb: SrgbTexture2d,
black_srgb: SrgbTexture2d,
white_linear: Texture2d,
params: glium::DrawParameters<'static>,
}
impl RenderingSystem for PbrMeshRenderingSystem {
fn initialize(display: &Display<WindowSurface>, _window: &glium::winit::window::Window) -> Self {
const VERT_SRC: &str = include_shader!("pbr.vert");
const FRAG_SRC: &str = include_shader!("pbr.frag");
let program = Program::from_source(display, VERT_SRC, FRAG_SRC, None).unwrap();
let white_srgb = {
let data = vec![255u8, 255u8, 255u8, 255u8];
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
SrgbTexture2d::new(display, raw).unwrap()
};
let black_srgb = {
let data = vec![0u8, 0u8, 0u8, 255u8];
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
SrgbTexture2d::new(display, raw).unwrap()
};
let white_linear = {
let data = vec![255u8, 255u8, 255u8, 255u8];
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
Texture2d::new(display, raw).unwrap()
};
let params = glium::DrawParameters {
depth: glium::Depth {
test: glium::draw_parameters::DepthTest::IfLess,
write: true,
.. Default::default()
},
.. Default::default()
};
Self {
program,
white_srgb,
black_srgb,
white_linear,
params,
}
}
fn render(&mut self, ctx: &mut RenderingContext) {
// Acquire camera
let cam = match ctx.scene.world.query::<&Camera>().iter().next() {
Some((_, cam)) => *cam,
None => {
eprintln!("[renderer] No camera component found. Skipping frame");
return;
}
};
let view: Mat4 = cam.view();
let projection: Mat4 = cam.projection();
// Directional light setup
let dir_light_dir_world: Vec3 = Vec3::new(-0.5, 3.0, -0.25).normalize();
let dir_light_dir_view: Vec3 = (view * Vec4::new(dir_light_dir_world.x, dir_light_dir_world.y, dir_light_dir_world.z, 0.0)).truncate().normalize();
let dir_light_color: Vec3 = Vec3::splat(1.0);
let dir_light_intensity: f32 = 3.5;
// Collect point lights
const MAX_POINT_LIGHTS: usize = 3;
let mut pl_pos = [[0.0f32; 3]; MAX_POINT_LIGHTS];
let mut pl_col = [[0.0f32; 3]; MAX_POINT_LIGHTS];
let mut pl_int = [0.0f32; MAX_POINT_LIGHTS];
let mut pl_range = [0.0f32; MAX_POINT_LIGHTS];
let mut pl_count: i32 = 0;
for (_, (tr, pl)) in ctx.scene.world.query::<(&Transform, &PointLight)>().iter() {
if (pl_count as usize) >= MAX_POINT_LIGHTS { break; }
let pos_view = (view * Vec4::new(tr.translation.x, tr.translation.y, tr.translation.z, 1.0)).truncate();
pl_pos[pl_count as usize] = [pos_view.x, pos_view.y, pos_view.z];
pl_col[pl_count as usize] = [pl.color.x, pl.color.y, pl.color.z];
pl_int[pl_count as usize] = pl.intensity;
pl_range[pl_count as usize] = pl.range;
pl_count += 1;
}
let asset_manager = ctx.asset_manager.borrow();
for (_, (tr, mh)) in ctx.scene.world.query::<(&Transform, &ModelHandle)>().iter() {
let model_any = match asset_manager.get_model(&mh.0) { Some(m) => m, _ => continue };
let model = match model_any.downcast_ref::<Model>() { Some(m) => m, None => continue };
let mesh = &model.mesh;
let mat = &model.material;
// Base color texture (sRGB)
let base_color_tex: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_srgb);
let mut base_sampler = base_color_tex.sampled();
base_sampler = base_sampler.wrap_function(SamplerWrapFunction::Repeat);
base_sampler = base_sampler.minify_filter(MinifySamplerFilter::Linear);
base_sampler = base_sampler.magnify_filter(MagnifySamplerFilter::Linear);
let has_base_color_map: i32 = if mat.base_color.is_some() { 1 } else { 0 };
// MR map (linear)
let mr_tex: &Texture2d = mat.metallic_roughness.as_ref().unwrap_or(&self.white_linear);
let mut mr_sampler = mr_tex.sampled();
mr_sampler = mr_sampler.wrap_function(SamplerWrapFunction::Repeat);
mr_sampler = mr_sampler.minify_filter(MinifySamplerFilter::Linear);
mr_sampler = mr_sampler.magnify_filter(MagnifySamplerFilter::Linear);
let has_mr_map: i32 = if mat.metallic_roughness.is_some() { 1 } else { 0 };
// Occlusion map (linear, R)
let occlusion_tex: &Texture2d = mat.occlusion.as_ref().unwrap_or(&self.white_linear);
let mut occl_sampler = occlusion_tex.sampled();
occl_sampler = occl_sampler.wrap_function(SamplerWrapFunction::Repeat);
occl_sampler = occl_sampler.minify_filter(MinifySamplerFilter::Linear);
occl_sampler = occl_sampler.magnify_filter(MagnifySamplerFilter::Linear);
let has_occlusion_map: i32 = if mat.occlusion.is_some() { 1 } else { 0 };
// Emissive map (sRGB)
let emissive_tex: &SrgbTexture2d = mat.emissive.as_ref().unwrap_or(&self.black_srgb);
let mut emissive_sampler = emissive_tex.sampled();
emissive_sampler = emissive_sampler.wrap_function(SamplerWrapFunction::Repeat);
emissive_sampler = emissive_sampler.minify_filter(MinifySamplerFilter::Linear);
emissive_sampler = emissive_sampler.magnify_filter(MagnifySamplerFilter::Linear);
let has_emissive_map: i32 = if mat.emissive.is_some() { 1 } else { 0 };
let bc = mat.base_color_factor;
let ef = mat.emissive_factor;
let uniforms = uniform! {
model: tr.matrix().to_cols_array_2d(),
view: view.to_cols_array_2d(),
projection: projection.to_cols_array_2d(),
uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
uv_scale: [mat.uv_scale.x, mat.uv_scale.y],
// Material
u_base_color_map: base_sampler,
u_has_base_color_map: has_base_color_map,
u_base_color_factor: [bc[0], bc[1], bc[2], bc[3]],
u_metallic_roughness_map: mr_sampler,
u_has_metallic_roughness_map: has_mr_map,
u_metallic_factor: mat.metal_factor,
u_roughness_factor: mat.roughness_factor,
u_occlusion_map: occl_sampler,
u_has_occlusion_map: has_occlusion_map,
u_emissive_map: emissive_sampler,
u_has_emissive_map: has_emissive_map,
u_emissive_factor: [ef[0], ef[1], ef[2]],
// Directional light (view-space)
u_dir_light_dir: [dir_light_dir_view.x, dir_light_dir_view.y, dir_light_dir_view.z],
u_dir_light_color: [dir_light_color.x, dir_light_color.y, dir_light_color.z],
u_dir_light_intensity: dir_light_intensity,
// Point lights (view-space)
u_point_light_count: pl_count,
u_point_light_pos: pl_pos,
u_point_light_color: pl_col,
u_point_light_intensity: pl_int,
u_point_light_range: pl_range,
};
ctx.target.draw(
&mesh.vbuf,
&mesh.ibuf,
&self.program,
&uniforms,
&self.params,
).unwrap();
}
}
}