From b52b1b0d4221da7ba742832abc7deb616003432c Mon Sep 17 00:00:00 2001 From: annieversary Date: Fri, 12 Aug 2022 22:22:37 +0100 Subject: [PATCH] fixed 0.8, added pixel postprocessing shader --- .gitignore | 1 + Cargo.lock | 22 +- assets/shaders/post-processing.wgsl | 25 ++ assets/textures/BayerMatrix.png | Bin 0 -> 2114 bytes assets/textures/noise.png | Bin 0 -> 241 bytes build-wasm.sh | 24 ++ deploy-wasm.sh | 7 + index.html | 45 ++++ src/camera.rs | 124 ++++++++- src/light_balls.rs | 1 + src/main.rs | 18 +- src/player.rs | 4 +- src/rendering/pbr.frag | 392 ---------------------------- src/rendering/pbr.vert | 36 --- src/rendering/pipeline.rs | 58 ---- 15 files changed, 247 insertions(+), 510 deletions(-) create mode 100644 assets/shaders/post-processing.wgsl create mode 100644 assets/textures/BayerMatrix.png create mode 100644 assets/textures/noise.png create mode 100755 build-wasm.sh create mode 100755 deploy-wasm.sh create mode 100644 index.html delete mode 100644 src/rendering/pbr.frag delete mode 100644 src/rendering/pbr.vert delete mode 100644 src/rendering/pipeline.rs diff --git a/.gitignore b/.gitignore index 0592392..4cd37f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target .DS_Store +/wasm_out/ diff --git a/Cargo.lock b/Cargo.lock index 9ac5cb7..3801e1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2998,9 +2998,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -3008,13 +3008,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -3035,9 +3035,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3045,9 +3045,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -3058,9 +3058,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "web-sys" diff --git a/assets/shaders/post-processing.wgsl b/assets/shaders/post-processing.wgsl new file mode 100644 index 0000000..2d616b4 --- /dev/null +++ b/assets/shaders/post-processing.wgsl @@ -0,0 +1,25 @@ +#import bevy_pbr::mesh_view_bindings + +@group(1) @binding(0) +var texture: texture_2d; +@group(1) @binding(1) +var our_sampler: sampler; + +@group(1) @binding(2) +var noise: texture_2d; +@group(1) @binding(3) +var noise_sampler: sampler; + +@fragment +fn fragment( + @builtin(position) position: vec4, + #import bevy_sprite::mesh2d_vertex_output +) -> @location(0) vec4 { + // Get screen position with coordinates from 0 to 1 + let uv = position.xy / vec2(view.width, view.height); + let uv = floor(uv * 400.0) / 400.0; + + let col = textureSample(texture, our_sampler, uv); + + return round(col * 50.0) / 50.0; +} diff --git a/assets/textures/BayerMatrix.png b/assets/textures/BayerMatrix.png new file mode 100644 index 0000000000000000000000000000000000000000..86ca8d1e43c7beff70275f9862db7456707256fd GIT binary patch literal 2114 zcmbVN4NMbf815JXf*`U@nG<)OEFxKZ*P|`;v@lA+;wVFGwCLEl-W~L`?bYj*76v22 z7@~2`L- z_kExD{obq2%3Kl}JUdt*5QL_yRoUPj?!ANj!1Lp+1{ruwwWxEf0zt?N-upSh_D>@P zf+>ECR>$i!892$BLagxJAkZWlk($fWuSpnbfH~>QorGQsTHK@jt zLK_%$F-I>i&eW2{Ye_i;CnZ9Q9XKE`(L4b;Oh&U6cPQX7UL1_Q(+CWWLHM-_IN56u z(rL1w6qchQnFtk^nu*OmOc(y8S z!WQ^azy_YT;0R*3+eP*`5z7@IV!2$7pcsN-LVyrji_ARX5Sp#?CmB?S*wBl2WsE)Ukm_gH5#9dr}|gv3BdiCpOS3f~OTQ$eLJ)VnS4^70Yq4 z43{RX8b@hZipei}DoTz^#giydC=|gH|AwPTJfG!E1c;C^5d}12F&Ds)Pd7Y;HL@Hq z2i%E!ay~sJC5vP886#M*W-n1f>8U9)v0Nq-f}qBn)M)T@vy~^zB%Q8Oz(5TV!%(=C zAmT_YffDLTlon#NBtfXB@+l#uCuxaXCXFNX#f5bXjcJ$t$8PywN;A!sL;Z&Wh?vuX7 zhR!>Z?KQHx&=o^x$W=XD=IOn+>YO+FJHj6Zj6D8*;ITl^*4CziXQVEh)_G=)v|6PM zSGiC9!s9{Z+m>Fxdeyyj=kCTvRk;8D{rfXAGmq$72L=Y-fYA8(T6J{T^y#IE_1f6c zM~^ODyciNt5fTz|l*-=ITfTn%`dzyg1XQfE+v728kZZ>mEfUV`xpL+60dqmCF3r8p zXgoe#2zub<%a<#(v9{vk*L1*eK?JNE6n|L~z|nPf-i5dZDS$VhK*Z%JaA%~pB! z{*faAuJ(4P(xr}$z<0cpn;V)OzNNc@r~_7GUUX)hJ%3)kGkadp;N1u4v z74iJFkw11dGz{L^x2h?uQrXkfbKt;%?(XiEmX^4tFHg#$v5Dw zLH^3JvZR`&O)V`5g3Jo8l*|sjKl;8iW5uo9K6mdLpsot8?$XEB#70j0@x+M*5mPs( zJ?~ij*?^g47_l=$={h-F=(%9r(|hbXS6^T6%xE}em=hIcYkxa8H+SZyDHP`Pcszpx z4@xfOp;r>VZ*R}xbU8UkuXDAvwFw`&W0vN%>L{%Jd`EM0b7Nx$6n+pro`?QGeWi`v zdHSYQDt+zM@UW`uTv2QdE|*XB_XBiYmt0u}hWYXus!&92=>|DEbolTGK&GLg_Z*JM zxpNEg9Z^wHksns8;4R&qKU<8lSL#pL6XuvqrhL!K+lp`Bx|3?d6_jwxN1($c8S lX`8}|ze|j$Zft3J9OyP&+4gJb?OEQxe0pl8s#%#={wJMcBHsW2 literal 0 HcmV?d00001 diff --git a/assets/textures/noise.png b/assets/textures/noise.png new file mode 100644 index 0000000000000000000000000000000000000000..bcc1ef088845a220ac35a1e90572957403255443 GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^93TuL7#^lP3utk3dK zXTKnj?Q_cLV_?&s#(NBmM+%O8&;2I2eYqx(ov`1%aNEzcmnVVj59@#Vow+_qdm#fO a8-wHrxh<6oHS~a1GI+ZBxvX + + + + moria + + + + + + + +

moria

+

+ controls: WASD - movement, Mouse click - move lights +

+ + + diff --git a/src/camera.rs b/src/camera.rs index 0e0a791..1ca55d5 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,4 +1,17 @@ -use bevy::prelude::*; +use bevy::{ + prelude::*, + reflect::TypeUuid, + render::{ + camera::{Camera as BevyCamera, RenderTarget}, + render_resource::{ + AsBindGroup, Extent3d, ShaderRef, TextureDescriptor, TextureDimension, TextureFormat, + TextureUsages, + }, + texture::BevyDefault, + view::RenderLayers, + }, + sprite::{Material2d, MaterialMesh2dBundle}, +}; use bevy_mod_raycast::{RayCastMethod, RayCastSource}; use crate::{pillar::UnlitPillar, player::*}; @@ -7,11 +20,113 @@ pub struct MyRaycastSet; #[derive(Component)] pub struct Camera; -pub fn spawn_camera(commands: &mut Commands) { + +pub fn spawn_camera( + mut commands: Commands, + asset_server: Res, + mut windows: ResMut, + mut meshes: ResMut>, + mut post_processing_materials: ResMut>, + mut images: ResMut>, +) { + let window = windows.get_primary_mut().unwrap(); + let size = Extent3d { + width: window.physical_width(), + height: window.physical_height(), + ..default() + }; + + // This is the texture that will be rendered to. + let mut image = Image { + texture_descriptor: TextureDescriptor { + label: None, + size, + dimension: TextureDimension::D2, + format: TextureFormat::bevy_default(), + mip_level_count: 1, + sample_count: 1, + usage: TextureUsages::TEXTURE_BINDING + | TextureUsages::COPY_DST + | TextureUsages::RENDER_ATTACHMENT, + }, + ..default() + }; + + // fill image.data with zeroes + image.resize(size); + + let image_handle = images.add(image); + + // Main camera, first to render commands - .spawn_bundle(Camera3dBundle::default()) + .spawn_bundle(Camera3dBundle { + camera: BevyCamera { + target: RenderTarget::Image(image_handle.clone()), + ..default() + }, + ..default() + }) .insert(Camera) .insert(RayCastSource::::new()); + + // This specifies the layer used for the post processing camera, which will be attached to the post processing camera and 2d quad. + let post_processing_pass_layer = RenderLayers::layer((RenderLayers::TOTAL_LAYERS - 1) as u8); + + let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new( + size.width as f32, + size.height as f32, + )))); + + let noise = asset_server.load("textures/noise.png"); + + // This material has the texture that has been rendered. + let material_handle = post_processing_materials.add(PostProcessingMaterial { + source_image: image_handle, + noise, + }); + + // Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader. + commands + .spawn_bundle(MaterialMesh2dBundle { + mesh: quad_handle.into(), + material: material_handle, + transform: Transform { + translation: Vec3::new(0.0, 0.0, 1.5), + ..default() + }, + ..default() + }) + .insert(post_processing_pass_layer); + + // The post-processing pass camera. + commands + .spawn_bundle(Camera2dBundle { + camera: BevyCamera { + // renders after the first main camera which has default value: 0. + priority: 1, + ..default() + }, + ..Camera2dBundle::default() + }) + .insert(post_processing_pass_layer); +} + +#[derive(AsBindGroup, TypeUuid, Clone)] +#[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"] +pub struct PostProcessingMaterial { + /// In this example, this image will be the result of the main camera. + #[texture(0)] + #[sampler(1)] + source_image: Handle, + #[texture(2)] + #[sampler(3)] + noise: Handle, +} + +impl Material2d for PostProcessingMaterial { + fn fragment_shader() -> ShaderRef { + "shaders/post-processing.wgsl".into() + } } pub fn camera_follow_player( @@ -38,7 +153,8 @@ pub fn update_raycast_with_cursor( for mut pick_source in query.iter_mut() { // Grab the most recent cursor event if it exists: if let Some(cursor_latest) = cursor.iter().last() { - pick_source.cast_method = RayCastMethod::Screenspace(cursor_latest.position); + // the 2.0 is bc of post-processing, which messes up the coordinates + pick_source.cast_method = RayCastMethod::Screenspace(cursor_latest.position * 2.0); } } } diff --git a/src/light_balls.rs b/src/light_balls.rs index 10e75a9..df0ad82 100644 --- a/src/light_balls.rs +++ b/src/light_balls.rs @@ -6,6 +6,7 @@ const RANGE: f32 = 25.0; #[derive(Component)] pub struct LightBall; + pub fn light_up_ball_when_close_to_player( mut commands: Commands, player: Query<(&Transform, &Player), Without>, diff --git a/src/main.rs b/src/main.rs index e0a0c5f..9209524 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use bevy::{pbr::AmbientLight, prelude::*, window::close_on_esc}; +use bevy::{pbr::AmbientLight, prelude::*, sprite::Material2dPlugin, window::close_on_esc}; use bevy_mod_raycast::{DefaultRaycastingPlugin, RayCastMesh, RaycastSystem}; // use bevy_prototype_debug_lines::*; @@ -25,10 +25,11 @@ pub enum AppState { fn main() { App::new() - .insert_resource(Msaa { samples: 4 }) + .insert_resource(Msaa { samples: 1 }) .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) // .add_plugin(DebugLinesPlugin::with_depth_test(true)) + .add_plugin(Material2dPlugin::::default()) .init_resource::() .init_resource::() .init_resource::() @@ -47,7 +48,11 @@ fn main() { // loading .add_system_set(SystemSet::on_update(AppState::Loading).with_system(loading::loading)) // game - .add_system_set(SystemSet::on_enter(AppState::Game).with_system(setup)) + .add_system_set( + SystemSet::on_enter(AppState::Game) + .with_system(setup) + .with_system(spawn_camera), + ) .add_system_set( SystemSet::on_update(AppState::Game) .with_system(update_raw_mouse_coords) @@ -71,6 +76,7 @@ fn main() { .run(); } +#[allow(clippy::too_many_arguments)] fn setup( mut commands: Commands, assets: Res, @@ -79,7 +85,10 @@ fn setup( mut ambient: ResMut, light_ball_materials: Res, pillar_materials: Res, + asset_server: Res, ) { + asset_server.watch_for_changes().unwrap(); + // set ambient light to very low ambient.color = Color::rgb(0.3, 0.3, 0.3); ambient.brightness = 0.01; @@ -144,7 +153,4 @@ fn setup( Vec3::new(i as f32 * 30.0, 2.0, -10.0), ); } - - // camera - spawn_camera(&mut commands); } diff --git a/src/player.rs b/src/player.rs index a8d633d..6f1a222 100644 --- a/src/player.rs +++ b/src/player.rs @@ -28,10 +28,8 @@ pub fn spawn_player( // light commands - .spawn() + .spawn_bundle(SpatialBundle::default()) .insert(LightFriends) - .insert(Transform::default()) - .insert(GlobalTransform::default()) .with_children(|children| { let count = 6; for i in 0..count { diff --git a/src/rendering/pbr.frag b/src/rendering/pbr.frag deleted file mode 100644 index 6a77023..0000000 --- a/src/rendering/pbr.frag +++ /dev/null @@ -1,392 +0,0 @@ -// From the Filament design doc -// https://google.github.io/filament/Filament.html#table_symbols -// Symbol Definition -// v View unit vector -// l Incident light unit vector -// n Surface normal unit vector -// h Half unit vector between l and v -// f BRDF -// f_d Diffuse component of a BRDF -// f_r Specular component of a BRDF -// α Roughness, remapped from using input perceptualRoughness -// σ Diffuse reflectance -// Ω Spherical domain -// f0 Reflectance at normal incidence -// f90 Reflectance at grazing angle -// χ+(a) Heaviside function (1 if a>0 and 0 otherwise) -// nior Index of refraction (IOR) of an interface -// ⟨n⋅l⟩ Dot product clamped to [0..1] -// ⟨a⟩ Saturated value (clamped to [0..1]) - -// The Bidirectional Reflectance Distribution Function (BRDF) describes the surface response of a standard material -// and consists of two components, the diffuse component (f_d) and the specular component (f_r): -// f(v,l) = f_d(v,l) + f_r(v,l) -// -// The form of the microfacet model is the same for diffuse and specular -// f_r(v,l) = f_d(v,l) = 1 / { |n⋅v||n⋅l| } ∫_Ω D(m,α) G(v,l,m) f_m(v,l,m) (v⋅m) (l⋅m) dm -// -// In which: -// D, also called the Normal Distribution Function (NDF) models the distribution of the microfacets -// G models the visibility (or occlusion or shadow-masking) of the microfacets -// f_m is the microfacet BRDF and differs between specular and diffuse components -// -// The above integration needs to be approximated. - -#version 450 - -const int MAX_LIGHTS = 30; - -struct Light { - mat4 proj; - vec4 pos; - vec4 color; -}; - -layout(location = 0) in vec3 v_WorldPosition; -layout(location = 1) in vec3 v_WorldNormal; -layout(location = 2) in vec2 v_Uv; - -#ifdef STANDARDMATERIAL_NORMAL_MAP -layout(location = 3) in vec4 v_WorldTangent; -#endif - -layout(location = 0) out vec4 o_Target; - -layout(set = 0, binding = 0) uniform CameraViewProj { - mat4 ViewProj; -}; -layout(std140, set = 0, binding = 1) uniform CameraPosition { - vec4 CameraPos; -}; - -layout(std140, set = 1, binding = 0) uniform Lights { - vec4 AmbientColor; - uvec4 NumLights; - Light SceneLights[MAX_LIGHTS]; -}; - -layout(set = 3, binding = 0) uniform StandardMaterial_base_color { - vec4 base_color; -}; - -#ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE -layout(set = 3, binding = 1) uniform texture2D StandardMaterial_base_color_texture; -layout(set = 3, - binding = 2) uniform sampler StandardMaterial_base_color_texture_sampler; -#endif - -#ifndef STANDARDMATERIAL_UNLIT - -layout(set = 3, binding = 3) uniform StandardMaterial_roughness { - float perceptual_roughness; -}; - -layout(set = 3, binding = 4) uniform StandardMaterial_metallic { - float metallic; -}; - -# ifdef STANDARDMATERIAL_METALLIC_ROUGHNESS_TEXTURE -layout(set = 3, binding = 5) uniform texture2D StandardMaterial_metallic_roughness_texture; -layout(set = 3, - binding = 6) uniform sampler StandardMaterial_metallic_roughness_texture_sampler; -# endif - -layout(set = 3, binding = 7) uniform StandardMaterial_reflectance { - float reflectance; -}; - -# ifdef STANDARDMATERIAL_NORMAL_MAP -layout(set = 3, binding = 8) uniform texture2D StandardMaterial_normal_map; -layout(set = 3, - binding = 9) uniform sampler StandardMaterial_normal_map_sampler; -# endif - -# if defined(STANDARDMATERIAL_OCCLUSION_TEXTURE) -layout(set = 3, binding = 10) uniform texture2D StandardMaterial_occlusion_texture; -layout(set = 3, - binding = 11) uniform sampler StandardMaterial_occlusion_texture_sampler; -# endif - -layout(set = 3, binding = 12) uniform StandardMaterial_emissive { - vec4 emissive; -}; - -# if defined(STANDARDMATERIAL_EMISSIVE_TEXTURE) -layout(set = 3, binding = 13) uniform texture2D StandardMaterial_emissive_texture; -layout(set = 3, - binding = 14) uniform sampler StandardMaterial_emissive_texture_sampler; -# endif - -# define saturate(x) clamp(x, 0.0, 1.0) -const float PI = 3.141592653589793; - -float pow5(float x) { - float x2 = x * x; - return x2 * x2 * x; -} - -// distanceAttenuation is simply the square falloff of light intensity -// combined with a smooth attenuation at the edge of the light radius -// -// light radius is a non-physical construct for efficiency purposes, -// because otherwise every light affects every fragment in the scene -float getDistanceAttenuation(const vec3 posToLight, float inverseRadiusSquared) { - float distanceSquare = dot(posToLight, posToLight); - float factor = distanceSquare * inverseRadiusSquared; - float smoothFactor = saturate(1.0 - factor * factor); - float attenuation = smoothFactor * smoothFactor; - return attenuation * 1.0 / max(distanceSquare, 1e-4); -} - -// Normal distribution function (specular D) -// Based on https://google.github.io/filament/Filament.html#citation-walter07 - -// D_GGX(h,α) = α^2 / { π ((n⋅h)^2 (α2−1) + 1)^2 } - -// Simple implementation, has precision problems when using fp16 instead of fp32 -// see https://google.github.io/filament/Filament.html#listing_speculardfp16 -float D_GGX(float roughness, float NoH, const vec3 h) { - float oneMinusNoHSquared = 1.0 - NoH * NoH; - float a = NoH * roughness; - float k = roughness / (oneMinusNoHSquared + a * a); - float d = k * k * (1.0 / PI); - return d; -} - -// Visibility function (Specular G) -// V(v,l,a) = G(v,l,α) / { 4 (n⋅v) (n⋅l) } -// such that f_r becomes -// f_r(v,l) = D(h,α) V(v,l,α) F(v,h,f0) -// where -// V(v,l,α) = 0.5 / { n⋅l sqrt((n⋅v)^2 (1−α2) + α2) + n⋅v sqrt((n⋅l)^2 (1−α2) + α2) } -// Note the two sqrt's, that may be slow on mobile, see https://google.github.io/filament/Filament.html#listing_approximatedspecularv -float V_SmithGGXCorrelated(float roughness, float NoV, float NoL) { - float a2 = roughness * roughness; - float lambdaV = NoL * sqrt((NoV - a2 * NoV) * NoV + a2); - float lambdaL = NoV * sqrt((NoL - a2 * NoL) * NoL + a2); - float v = 0.5 / (lambdaV + lambdaL); - return v; -} - -// Fresnel function -// see https://google.github.io/filament/Filament.html#citation-schlick94 -// F_Schlick(v,h,f_0,f_90) = f_0 + (f_90 − f_0) (1 − v⋅h)^5 -vec3 F_Schlick(const vec3 f0, float f90, float VoH) { - // not using mix to keep the vec3 and float versions identical - return f0 + (f90 - f0) * pow5(1.0 - VoH); -} - -float F_Schlick(float f0, float f90, float VoH) { - // not using mix to keep the vec3 and float versions identical - return f0 + (f90 - f0) * pow5(1.0 - VoH); -} - -vec3 fresnel(vec3 f0, float LoH) { - // f_90 suitable for ambient occlusion - // see https://google.github.io/filament/Filament.html#lighting/occlusion - float f90 = saturate(dot(f0, vec3(50.0 * 0.33))); - return F_Schlick(f0, f90, LoH); -} - -// Specular BRDF -// https://google.github.io/filament/Filament.html#materialsystem/specularbrdf - -// Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m -// f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) } -vec3 specular(vec3 f0, float roughness, const vec3 h, float NoV, float NoL, - float NoH, float LoH) { - float D = D_GGX(roughness, NoH, h); - float V = V_SmithGGXCorrelated(roughness, NoV, NoL); - vec3 F = fresnel(f0, LoH); - - return (D * V) * F; -} - -// Diffuse BRDF -// https://google.github.io/filament/Filament.html#materialsystem/diffusebrdf -// fd(v,l) = σ/π * 1 / { |n⋅v||n⋅l| } ∫Ω D(m,α) G(v,l,m) (v⋅m) (l⋅m) dm - -// simplest approximation -// float Fd_Lambert() { -// return 1.0 / PI; -// } -// -// vec3 Fd = diffuseColor * Fd_Lambert(); - -// Disney approximation -// See https://google.github.io/filament/Filament.html#citation-burley12 -// minimal quality difference -float Fd_Burley(float roughness, float NoV, float NoL, float LoH) { - float f90 = 0.5 + 2.0 * roughness * LoH * LoH; - float lightScatter = F_Schlick(1.0, f90, NoL); - float viewScatter = F_Schlick(1.0, f90, NoV); - return lightScatter * viewScatter * (1.0 / PI); -} - -// From https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile -vec3 EnvBRDFApprox(vec3 f0, float perceptual_roughness, float NoV) { - const vec4 c0 = { -1, -0.0275, -0.572, 0.022 }; - const vec4 c1 = { 1, 0.0425, 1.04, -0.04 }; - vec4 r = perceptual_roughness * c0 + c1; - float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y; - vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw; - return f0 * AB.x + AB.y; -} - -float perceptualRoughnessToRoughness(float perceptualRoughness) { - // clamp perceptual roughness to prevent precision problems - // According to Filament design 0.089 is recommended for mobile - // Filament uses 0.045 for non-mobile - float clampedPerceptualRoughness = clamp(perceptualRoughness, 0.089, 1.0); - return clampedPerceptualRoughness * clampedPerceptualRoughness; -} - -// from https://64.github.io/tonemapping/ -// reinhard on RGB oversaturates colors -vec3 reinhard(vec3 color) { - return color / (1.0 + color); -} - -vec3 reinhard_extended(vec3 color, float max_white) { - vec3 numerator = color * (1.0f + (color / vec3(max_white * max_white))); - return numerator / (1.0 + color); -} - -// luminance coefficients from Rec. 709. -// https://en.wikipedia.org/wiki/Rec._709 -float luminance(vec3 v) { - return dot(v, vec3(0.2126, 0.7152, 0.0722)); -} - -vec3 change_luminance(vec3 c_in, float l_out) { - float l_in = luminance(c_in); - return c_in * (l_out / l_in); -} - -vec3 reinhard_luminance(vec3 color) { - float l_old = luminance(color); - float l_new = l_old / (1.0f + l_old); - return change_luminance(color, l_new); -} - -vec3 reinhard_extended_luminance(vec3 color, float max_white_l) { - float l_old = luminance(color); - float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l))); - float l_new = numerator / (1.0f + l_old); - return change_luminance(color, l_new); -} - -#endif - -void main() { - vec4 output_color = base_color; -#ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE - output_color *= texture(sampler2D(StandardMaterial_base_color_texture, - StandardMaterial_base_color_texture_sampler), - v_Uv); -#endif - -#ifndef STANDARDMATERIAL_UNLIT - // calculate non-linear roughness from linear perceptualRoughness -# ifdef STANDARDMATERIAL_METALLIC_ROUGHNESS_TEXTURE - vec4 metallic_roughness = texture(sampler2D(StandardMaterial_metallic_roughness_texture, StandardMaterial_metallic_roughness_texture_sampler), v_Uv); - // Sampling from GLTF standard channels for now - float metallic = metallic * metallic_roughness.b; - float perceptual_roughness = perceptual_roughness * metallic_roughness.g; -# endif - - float roughness = perceptualRoughnessToRoughness(perceptual_roughness); - - vec3 N = normalize(v_WorldNormal); - -# ifdef STANDARDMATERIAL_NORMAL_MAP - vec3 T = normalize(v_WorldTangent.xyz); - vec3 B = cross(N, T) * v_WorldTangent.w; -# endif - -# ifdef STANDARDMATERIAL_DOUBLE_SIDED - N = gl_FrontFacing ? N : -N; -# ifdef STANDARDMATERIAL_NORMAL_MAP - T = gl_FrontFacing ? T : -T; - B = gl_FrontFacing ? B : -B; -# endif -# endif - -# ifdef STANDARDMATERIAL_NORMAL_MAP - mat3 TBN = mat3(T, B, N); - N = TBN * normalize(texture(sampler2D(StandardMaterial_normal_map, StandardMaterial_normal_map_sampler), v_Uv).rgb * 2.0 - 1.0); -# endif - -# ifdef STANDARDMATERIAL_OCCLUSION_TEXTURE - float occlusion = texture(sampler2D(StandardMaterial_occlusion_texture, StandardMaterial_occlusion_texture_sampler), v_Uv).r; -# else - float occlusion = 1.0; -# endif - -# ifdef STANDARDMATERIAL_EMISSIVE_TEXTURE - vec4 emissive = emissive; - // TODO use .a for exposure compensation in HDR - emissive.rgb *= texture(sampler2D(StandardMaterial_emissive_texture, StandardMaterial_emissive_texture_sampler), v_Uv).rgb; -# endif - - vec3 V = normalize(CameraPos.xyz - v_WorldPosition.xyz); - // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" - float NdotV = max(dot(N, V), 1e-4); - - // Remapping [0,1] reflectance to F0 - // See https://google.github.io/filament/Filament.html#materialsystem/parameterization/remapping - vec3 F0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + output_color.rgb * metallic; - - // Diffuse strength inversely related to metallicity - vec3 diffuseColor = output_color.rgb * (1.0 - metallic); - - // accumulate color - vec3 light_accum = vec3(0.0); - for (int i = 0; i < int(NumLights.x) && i < MAX_LIGHTS; ++i) { - Light light = SceneLights[i]; - - vec3 lightDir = light.pos.xyz - v_WorldPosition.xyz; - vec3 L = normalize(lightDir); - - float rangeAttenuation = - getDistanceAttenuation(lightDir, light.pos.w); - - vec3 H = normalize(L + V); - float NoL = saturate(dot(N, L)); - float NoH = saturate(dot(N, H)); - float LoH = saturate(dot(L, H)); - - vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH); - vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); - - // Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩ - // where - // f(v,l) = (f_d(v,l) + f_r(v,l)) * light_color - // Φ is light intensity - - // our rangeAttentuation = 1 / d^2 multiplied with an attenuation factor for smoothing at the edge of the non-physical maximum light radius - // It's not 100% clear where the 1/4π goes in the derivation, but we follow the filament shader and leave it out - - // See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminanceEquation - // TODO compensate for energy loss https://google.github.io/filament/Filament.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance - // light.color.rgb is premultiplied with light.intensity on the CPU - light_accum += - ((diffuse + specular) * light.color.rgb) * (rangeAttenuation * NoL); - } - - vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV); - vec3 specular_ambient = EnvBRDFApprox(F0, perceptual_roughness, NdotV); - - output_color.rgb = light_accum; - output_color.rgb += (diffuse_ambient + specular_ambient) * AmbientColor.xyz * occlusion; - output_color.rgb += emissive.rgb * output_color.a; - - // tone_mapping - output_color.rgb = reinhard_luminance(output_color.rgb); - // Gamma correction. - // Not needed with sRGB buffer - // output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2)); -#endif - - o_Target = output_color; -} diff --git a/src/rendering/pbr.vert b/src/rendering/pbr.vert deleted file mode 100644 index 533a163..0000000 --- a/src/rendering/pbr.vert +++ /dev/null @@ -1,36 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 Vertex_Position; -layout(location = 1) in vec3 Vertex_Normal; -layout(location = 2) in vec2 Vertex_Uv; - -#ifdef STANDARDMATERIAL_NORMAL_MAP -layout(location = 3) in vec4 Vertex_Tangent; -#endif - -layout(location = 0) out vec3 v_WorldPosition; -layout(location = 1) out vec3 v_WorldNormal; -layout(location = 2) out vec2 v_Uv; - -layout(set = 0, binding = 0) uniform CameraViewProj { - mat4 ViewProj; -}; - -#ifdef STANDARDMATERIAL_NORMAL_MAP -layout(location = 3) out vec4 v_WorldTangent; -#endif - -layout(set = 2, binding = 0) uniform Transform { - mat4 Model; -}; - -void main() { - vec4 world_position = Model * vec4(Vertex_Position, 1.0); - v_WorldPosition = world_position.xyz; - v_WorldNormal = mat3(Model) * Vertex_Normal; - v_Uv = Vertex_Uv; -#ifdef STANDARDMATERIAL_NORMAL_MAP - v_WorldTangent = vec4(mat3(Model) * Vertex_Tangent.xyz, Vertex_Tangent.w); -#endif - gl_Position = ViewProj * world_position; -} diff --git a/src/rendering/pipeline.rs b/src/rendering/pipeline.rs deleted file mode 100644 index c275efe..0000000 --- a/src/rendering/pipeline.rs +++ /dev/null @@ -1,58 +0,0 @@ -use bevy::asset::Assets; -use bevy::render::{ - pipeline::{ - BlendFactor, BlendOperation, BlendState, ColorTargetState, ColorWrite, CompareFunction, - DepthBiasState, DepthStencilState, PipelineDescriptor, StencilFaceState, StencilState, - }, - shader::{Shader, ShaderStage, ShaderStages}, - texture::TextureFormat, -}; - -// pub const PBR_PIPELINE_HANDLE: HandleUntyped = -// HandleUntyped::weak_from_u64(PipelineDescriptor::TYPE_UUID, 13148362314012771389); - -pub(crate) fn build_pbr_pipeline(shaders: &mut Assets) -> PipelineDescriptor { - PipelineDescriptor { - depth_stencil: Some(DepthStencilState { - format: TextureFormat::Depth32Float, - depth_write_enabled: true, - depth_compare: CompareFunction::Less, - stencil: StencilState { - front: StencilFaceState::IGNORE, - back: StencilFaceState::IGNORE, - read_mask: 0, - write_mask: 0, - }, - bias: DepthBiasState { - constant: 0, - slope_scale: 0.0, - clamp: 0.0, - }, - clamp_depth: false, - }), - color_target_states: vec![ColorTargetState { - format: TextureFormat::default(), - color_blend: BlendState { - src_factor: BlendFactor::SrcAlpha, - dst_factor: BlendFactor::OneMinusSrcAlpha, - operation: BlendOperation::Add, - }, - alpha_blend: BlendState { - src_factor: BlendFactor::One, - dst_factor: BlendFactor::One, - operation: BlendOperation::Add, - }, - write_mask: ColorWrite::ALL, - }], - ..PipelineDescriptor::new(ShaderStages { - vertex: shaders.add(Shader::from_glsl( - ShaderStage::Vertex, - include_str!("pbr.vert"), - )), - fragment: Some(shaders.add(Shader::from_glsl( - ShaderStage::Fragment, - include_str!("pbr.frag"), - ))), - }) - } -}