Skip to content

Commit

Permalink
Merge pull request #24 from meyerzinn/pbr
Browse files Browse the repository at this point in the history
change rendering engine to use PBR with standard pipelines
  • Loading branch information
Game4all committed May 4, 2023
2 parents 6b1da7d + f77d552 commit d6e7846
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 457 deletions.
41 changes: 20 additions & 21 deletions assets/shaders/terrain_pipeline.wgsl
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
#import bevy_pbr::mesh_types
#import bevy_pbr::mesh_view_bindings
#import bevy_pbr::fog
#import bevy_pbr::pbr_ambient

#import "shaders/voxel_data.wgsl"
#import "shaders/terrain_uniforms.wgsl"
#import "shaders/noise.wgsl"
#import "shaders/fog.wgsl"

@group(1) @binding(0)
var<uniform> mesh: Mesh;

#import bevy_pbr::pbr_bindings
#import bevy_pbr::mesh_bindings
#import bevy_pbr::mesh_functions

#import bevy_pbr::pbr_types
#import bevy_pbr::utils
#import bevy_pbr::clustered_forward
#import bevy_pbr::lighting
#import bevy_pbr::pbr_ambient
#import bevy_pbr::shadows
#import bevy_pbr::fog
#import bevy_pbr::pbr_functions

#import "shaders/voxel_data.wgsl"
#import "shaders/terrain_uniforms.wgsl"
#import "shaders/noise.wgsl"
#import "shaders/fog.wgsl"

struct Vertex {
@location(0) position: vec3<f32>,
@location(1) voxel_data: u32,
Expand Down Expand Up @@ -50,14 +46,13 @@ struct Fragment {
@builtin(front_facing) front_facing: bool,
/// The normalized normal of the voxel.
@location(0) voxel_normal: vec3<f32>,
/// The extracted color data from voxel data.
@location(1) voxel_color: u32,
/// The voxel data.
@location(1) voxel_data: u32,
/// The world position of the voxel vertex.
@location(2) world_position: vec3<f32>,
};

fn prepare_pbr_input_from_voxel_mat(voxel_mat: VoxelMat, frag: Fragment) -> PbrInput {

var base_color: vec4<f32> = voxel_mat.base_color;
base_color = base_color + hash(vec4<f32>(floor(frag.world_position - frag.voxel_normal * 0.5), 1.0)) * 0.0226;

Expand All @@ -66,26 +61,30 @@ fn prepare_pbr_input_from_voxel_mat(voxel_mat: VoxelMat, frag: Fragment) -> PbrI
pbr_input.material.perceptual_roughness = voxel_mat.perceptual_roughness;
pbr_input.material.emissive = voxel_mat.emissive;
pbr_input.material.reflectance = voxel_mat.reflectance;

pbr_input.material.base_color = base_color;

pbr_input.frag_coord = frag.frag_coord;
pbr_input.world_position = vec4<f32>(frag.world_position, 1.0);
pbr_input.world_normal = (f32(frag.front_facing) * 2.0 - 1.0) * mesh_normal_local_to_world(frag.voxel_normal);

pbr_input.is_orthographic = view.projection[3].w == 1.0;
pbr_input.N = normalize(mesh_normal_local_to_world(frag.voxel_normal));
pbr_input.V = calculate_view(vec4<f32>(frag.world_position, 1.0), pbr_input.is_orthographic);
pbr_input.flags = mesh.flags;
return pbr_input;
}

@fragment
fn fragment(frag: Fragment) -> @location(0) vec4<f32> {
let material = VOXEL_MATERIALS.materials[voxel_data_extract_material_index(frag.voxel_color)];
let material = voxel_materials[voxel_data_extract_material_index(frag.voxel_data)];

/// PBR lighting input data preparation
var pbr_input = prepare_pbr_input_from_voxel_mat(material, frag);
let pbr_colour = pbr(pbr_input);
let pbr_colour = tone_mapping(pbr(pbr_input));

// @todo: switch to bevy_pbr::fog

//fragment distance from camera, used to determine amount of fog to apply.
let fog_distance = distance(frag.world_position, view.world_position);
return ffog_apply_fog(fog_distance, f32(terrain_settings.render_distance) * f32(TERRAIN_CHUNK_LENGTH), f32(TERRAIN_CHUNK_LENGTH), pbr_colour);
}
return ffog_apply_fog(fog_distance, f32(render_distance) * f32(TERRAIN_CHUNK_LENGTH), f32(TERRAIN_CHUNK_LENGTH), pbr_colour);
}
29 changes: 5 additions & 24 deletions assets/shaders/terrain_uniforms.wgsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

const VOXEL_MAT_FLAG_LIQUID: u32 = 2u; // 1 << 1
const TERRAIN_CHUNK_LENGTH: u32 = 32u;

Expand All @@ -11,27 +10,9 @@ struct VoxelMat {
reflectance: f32,
};

// A GPU-suited representation of voxel materials.
struct VoxelMaterials {
materials: array<VoxelMat>
};

struct TerrainRenderSettings {
render_distance: u32,
};
@group(1) @binding(0)
var<uniform> render_distance: u32;

@group(2) @binding(0)
var<storage> VOXEL_MATERIALS: VoxelMaterials;

@group(2) @binding(1)
var<storage> terrain_settings: TerrainRenderSettings;

// Returns computed fragment color from the current ambient light + diffuse per face lighting
fn calc_voxel_lighting(col: vec3<f32>, n: vec3<f32>) -> vec3<f32> {
let per_face_light = vec3<f32>(0.8, 1.0, 0.6);
let normal = abs(dot(n, vec3<f32>(1., 0., 0.)) * per_face_light.x)
+ abs(dot(n, vec3<f32>(0., 1., 0.)) * per_face_light.y)
+ abs(dot(n, vec3<f32>(0., 0., 1.)) * per_face_light.z);

return normal * col + lights.ambient_color.xyz * 0.21;
}
// A GPU-suited representation of voxel materials.
@group(1) @binding(1)
var<uniform> voxel_materials: array<VoxelMat, 256>;
10 changes: 5 additions & 5 deletions src/voxel/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
///! Storage primitives for storing voxel data
/// Storage primitives for storing voxel data
pub mod storage;

///! Utils for managing a voxel world.
/// Utils for managing a voxel world.
mod world;
pub use world::*;

///! Terrain generator.
/// Terrain generator.
pub mod terraingen;

///! Systems and utilities for rendering voxels.
/// Systems and utilities for rendering voxels.
pub mod render;

///! Systems for defining voxel materials with physical properties.
/// Systems for defining voxel materials with physical properties.
pub mod material;

/// rust ports of signed distance field functions for use in world generation.
Expand Down
139 changes: 139 additions & 0 deletions src/voxel/render/chunk_material.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use crate::voxel::material::VoxelMaterialRegistry;
use bevy::{
prelude::*,
reflect::TypeUuid,
render::{
extract_component::ExtractComponent,
mesh::MeshVertexAttribute,
render_resource::{AsBindGroup, ShaderType, VertexFormat},
},
};

#[derive(Component, Clone, Default, ExtractComponent)]
/// A marker component for voxel meshes.
pub struct VoxelTerrainMesh;

impl VoxelTerrainMesh {
pub const ATTRIBUTE_DATA: MeshVertexAttribute =
MeshVertexAttribute::new("Vertex_Data", 0x696969, VertexFormat::Uint32);
}

#[derive(ShaderType, Clone, Copy, Default)]
pub struct GpuVoxelMaterial {
base_color: Color,
flags: u32,
emissive: Color,
perceptual_roughness: f32,
metallic: f32,
reflectance: f32,
}

#[derive(AsBindGroup, ShaderType, Clone, TypeUuid)]
#[uuid = "1e31e29e-73d8-419c-8293-876ae81d2636"]
pub struct GpuTerrainUniforms {
#[uniform(0)]
pub render_distance: u32,
#[uniform(1)]
pub materials: [GpuVoxelMaterial; 256],
}

impl Default for GpuTerrainUniforms {
fn default() -> Self {
Self {
render_distance: 16,
materials: [default(); 256],
}
}
}

impl Material for GpuTerrainUniforms {
fn vertex_shader() -> bevy::render::render_resource::ShaderRef {
"shaders/terrain_pipeline.wgsl".into()
}

fn fragment_shader() -> bevy::render::render_resource::ShaderRef {
"shaders/terrain_pipeline.wgsl".into()
}

fn specialize(
_pipeline: &bevy::pbr::MaterialPipeline<Self>,
descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
layout: &bevy::render::mesh::MeshVertexBufferLayout,
_key: bevy::pbr::MaterialPipelineKey<Self>,
) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> {
let vertex_layout = layout.get_layout(&[
Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
VoxelTerrainMesh::ATTRIBUTE_DATA.at_shader_location(1),
])?;
descriptor.vertex.buffers = vec![vertex_layout];
Ok(())
}
}

fn update_chunk_material_singleton(
mut commands: Commands,
mut materials: ResMut<Assets<GpuTerrainUniforms>>,
chunk_material: ResMut<ChunkMaterialSingleton>,
voxel_materials: Res<VoxelMaterialRegistry>,
mut chunk_entities: Query<(Entity, &mut Handle<GpuTerrainUniforms>)>,
) {
if chunk_material.is_changed() {
let mut gpu_mats = GpuTerrainUniforms {
materials: [GpuVoxelMaterial {
base_color: Color::WHITE,
flags: 0,
..Default::default()
}; 256],
render_distance: 32,
};

voxel_materials
.iter_mats()
.enumerate()
.for_each(|(index, material)| {
gpu_mats.materials[index].base_color = material.base_color;
gpu_mats.materials[index].flags = material.flags.bits();
gpu_mats.materials[index].emissive = material.emissive;
gpu_mats.materials[index].perceptual_roughness = material.perceptual_roughness;
gpu_mats.materials[index].metallic = material.metallic;
gpu_mats.materials[index].reflectance = material.reflectance;
});

let chunk_material = materials.add(gpu_mats);
commands.insert_resource(ChunkMaterialSingleton(chunk_material.clone()));

for (_, mut mat) in &mut chunk_entities {
*mat = chunk_material.clone();
}
}
}

#[derive(Resource, Deref, DerefMut)]
pub struct ChunkMaterialSingleton(Handle<GpuTerrainUniforms>);

impl FromWorld for ChunkMaterialSingleton {
fn from_world(world: &mut World) -> Self {
let mut materials = world.resource_mut::<Assets<GpuTerrainUniforms>>();
Self(materials.add(GpuTerrainUniforms::default()))
}
}

#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, SystemSet)]
/// Systems that prepare the global [ChunkMaterialSingleton] value.
pub struct ChunkMaterialSet;

pub struct ChunkMaterialPlugin;

impl Plugin for ChunkMaterialPlugin {
fn build(&self, app: &mut App) {
// @todo: figure out race conditions w/ other systems
app.add_plugin(MaterialPlugin::<GpuTerrainUniforms>::default())
.init_resource::<ChunkMaterialSingleton>()
.add_system(
update_chunk_material_singleton
.run_if(resource_changed::<VoxelMaterialRegistry>())
.in_set(ChunkMaterialSet)
.in_base_set(CoreSet::Update),
);
}
}
7 changes: 2 additions & 5 deletions src/voxel/render/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
mod mesh;
pub use mesh::*;

mod pipeline;
pub use pipeline::*;

mod terrain_uniforms;
pub use terrain_uniforms::*;
mod chunk_material;
pub use chunk_material::*;
Loading

0 comments on commit d6e7846

Please sign in to comment.