Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic PBR implementation #261

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/bevy_pbr/src/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ impl Default for PbrComponents {
bind_group: 3,
binding: 0,
},
// StandardMaterial_pbr
DynamicBinding {
bind_group: 3,
binding: 3,
},
],
..Default::default()
},
Expand Down
6 changes: 4 additions & 2 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ pub struct Light {
pub color: Color,
pub fov: f32,
pub depth: Range<f32>,
pub attenuation: f32,
}

impl Default for Light {
fn default() -> Self {
Light {
color: Color::rgb(1.0, 1.0, 1.0),
color: Color::rgba(1.0, 1.0, 1.0, 100.0),
depth: 0.1..50.0,
fov: f32::to_radians(60.0),
attenuation: 100.0,
}
}
}
Expand Down Expand Up @@ -49,7 +51,7 @@ impl LightRaw {
let (x, y, z) = translation.0.into();
LightRaw {
proj: proj.to_cols_array_2d(),
pos: [x, y, z, 1.0],
pos: [x, y, z, light.attenuation], // pos.w is the attenuation.
color: light.color.into(),
}
}
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use bevy_asset::{self, Handle};
use bevy_render::{color::Color, renderer::RenderResources, shader::ShaderDefs, texture::Texture};
use bevy_render::{color::Color, renderer::{RenderResources}, shader::ShaderDefs, texture::Texture};
use bevy_math::Vec2;

/// A material with "standard" properties used in PBR lighting
#[derive(RenderResources, ShaderDefs)]
pub struct StandardMaterial {
pub albedo: Color,
/// Represented as roughness/metallic.
pub pbr: Vec2,
#[shader_def]
pub albedo_texture: Option<Handle<Texture>>,
#[render_resources(ignore)]
Expand All @@ -16,6 +19,7 @@ impl Default for StandardMaterial {
fn default() -> Self {
StandardMaterial {
albedo: Color::rgb(1.0, 1.0, 1.0),
pbr: Vec2::new(0.01, 0.08),
albedo_texture: None,
shaded: true,
}
Expand Down
136 changes: 125 additions & 11 deletions crates/bevy_pbr/src/render_graph/forward_pipeline/forward.frag
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@ const int MAX_LIGHTS = 10;

struct Light {
mat4 proj;
vec4 pos;
vec3 pos;
float attenuation;
vec4 color;
};

layout(location = 0) in vec3 v_Position;
layout(location = 1) in vec3 v_Normal;
layout(location = 2) in vec2 v_Uv;
layout(location = 3) in vec3 w_Position;


layout(location = 0) out vec4 o_Target;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};

layout(set = 1, binding = 0) uniform Lights {
Expand All @@ -27,33 +31,143 @@ layout(set = 3, binding = 0) uniform StandardMaterial_albedo {
vec4 Albedo;
};

layout(set = 3, binding = 3) uniform StandardMaterial_pbr {
vec2 pbr;
};

# ifdef STANDARDMATERIAL_ALBEDO_TEXTURE
layout(set = 3, binding = 1) uniform texture2D StandardMaterial_albedo_texture;
layout(set = 3, binding = 2) uniform sampler StandardMaterial_albedo_texture_sampler;
# endif

# ifdef STANDARDMATERIAL_SHADED

#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;
}

float getSquareFalloffAttenuation(float distanceSquare, float falloff) {
float factor = distanceSquare * falloff;
float smoothFactor = saturate(1.0 - factor * factor);
return smoothFactor * smoothFactor;
}

float getDistanceAttenuation(const highp vec3 posToLight, float falloff) {
float distanceSquare = dot(posToLight, posToLight);
float attenuation = getSquareFalloffAttenuation(distanceSquare, falloff);
return attenuation * 1.0 / max(distanceSquare, 1e-4);
}

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;
}

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;
}

vec3 F_Schlick(const vec3 f0, float f90, float VoH) {
return f0 + (f90 - f0) * pow5(1.0 - VoH);
}

float F_Schlick(float f0, float f90, float VoH) {
return f0 + (f90 - f0) * pow5(1.0 - VoH);
}

vec3 fresnel(vec3 f0, float LoH) {
float f90 = saturate(dot(f0, vec3(50.0 * 0.33)));
return F_Schlick(f0, f90, LoH);
}

vec3 isotropicLobe(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;
}

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);
}

vec3 computeDiffuseColor(const vec3 baseColor, float metallic) {
return baseColor * (1.0 - metallic);
}

#define MIN_N_DOT_V 1e-4

float clampNoV(float NoV) {
// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
return max(NoV, MIN_N_DOT_V);
}

# endif

void main() {
vec4 output_color = Albedo;
vec4 output_color = vec4(1.0, 0.0, 0.0, 1.0); //Albedo;
# ifdef STANDARDMATERIAL_ALBEDO_TEXTURE
output_color *= texture(
sampler2D(StandardMaterial_albedo_texture, StandardMaterial_albedo_texture_sampler),
v_Uv);
# endif

# ifdef STANDARDMATERIAL_SHADED
vec3 normal = normalize(v_Normal);
vec3 ambient = vec3(0.05, 0.05, 0.05);
float roughness = pbr.x;
float metallic = pbr.y;
vec3 N = normalize(v_Normal);
vec3 V = normalize(CameraPos.xyz - v_Position.xyz);

vec3 F0 = vec3(0.04);
F0 = mix(F0, output_color.rgb, metallic);

vec3 diffuseColor = computeDiffuseColor(output_color.rgb, metallic);

// accumulate color
vec3 color = ambient;
vec3 light_accum = vec3(0.0);
for (int i=0; i<int(NumLights.x) && i<MAX_LIGHTS; ++i) {
Light light = SceneLights[i];
// compute Lambertian diffuse term
vec3 light_dir = normalize(light.pos.xyz - v_Position);
float diffuse = max(0.0, dot(normal, light_dir));
// add light contribution
color += diffuse * light.color.xyz;

vec3 lightDir = light.pos.xyz - v_Position.xyz;
vec3 L = normalize(lightDir);

float rangeAttenuation = getDistanceAttenuation(lightDir, light.attenuation);

vec3 H = normalize(L + V);

float NdotL = clampNoV(dot(N, L));
float NdotV = clampNoV(dot(N, V));
float NoL = saturate(NdotL);
float NoH = saturate(dot(N, H));
float LoH = saturate(dot(L, H));

vec3 specular = isotropicLobe(F0, roughness, H, NdotV, NoL, NoH, LoH);
vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH);

light_accum += ((diffuse + specular) * light.color.xyz) * (light.color.w * NdotL);
}
output_color.xyz *= color;

output_color.xyz = light_accum;

// Gamma correction.
output_color.xyz = output_color.xyz / (output_color.xyz + vec3(1.0));
Copy link
Contributor

@fu5ha fu5ha Sep 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this is not gamma correction (i.e. correcting for the output color space transfer function) but is actually an artistic tone mapping operator (Reinhard)

output_color.xyz = pow(output_color.xyz, vec3(1.0/2.2));
# endif

// multiply the light by material color
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ layout(location = 2) in vec2 Vertex_Uv;
layout(location = 0) out vec3 v_Position;
layout(location = 1) out vec3 v_Normal;
layout(location = 2) out vec2 v_Uv;
layout(location = 3) out vec3 w_Position;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};

layout(set = 2, binding = 0) uniform Transform {
Expand All @@ -19,7 +21,8 @@ layout(set = 2, binding = 0) uniform Transform {
void main() {
v_Normal = (Model * vec4(Vertex_Normal, 1.0)).xyz;
v_Normal = mat3(Model) * Vertex_Normal;
v_Position = (Model * vec4(Vertex_Position, 1.0)).xyz;
v_Position = Vertex_Position;
w_Position = (Model * vec4(Vertex_Position, 1.0)).xyz;
v_Uv = Vertex_Uv;
gl_Position = ViewProj * vec4(v_Position, 1.0);
gl_Position = ViewProj * Model * vec4(v_Position, 1.0);
}
24 changes: 15 additions & 9 deletions crates/bevy_render/src/render_graph/nodes/camera_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,19 @@ pub fn camera_node_system(
// PERF: this write on RenderResourceAssignments will prevent this system from running in parallel
// with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>,
query: Query<(&Camera, &Transform)>,
query: Query<(&Camera, &Transform, &Translation)>,
) {
let render_resource_context = &**render_resource_context;

let (camera, transform) = if let Some(camera_entity) = active_cameras.get(&state.camera_name) {
let (camera, transform, translation) = if let Some(camera_entity) = active_cameras.get(&state.camera_name) {
let camera = query.get::<Camera>(camera_entity).unwrap();
let transform = query.get::<Transform>(camera_entity).unwrap();
let translation = query.get::<Translation>(camera_entity).unwrap();

(
query.get::<Camera>(camera_entity).unwrap(),
query.get::<Transform>(camera_entity).unwrap(),
camera,
transform,
translation,
)
} else {
return;
Expand All @@ -90,7 +95,7 @@ pub fn camera_node_system(
render_resource_context.map_buffer(staging_buffer);
staging_buffer
} else {
let size = std::mem::size_of::<[[f32; 4]; 4]>();
let size = std::mem::size_of::<[[f32; 4]; 5]>();
let buffer = render_resource_context.create_buffer(BufferInfo {
size,
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
Expand All @@ -116,15 +121,16 @@ pub fn camera_node_system(
staging_buffer
};

let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
let camera_matrix: [f32; 16] =
(camera.projection_matrix * transform.value.inverse()).to_cols_array();
let matrix_size = std::mem::size_of::<[[f32; 4]; 5]>();
let mut camera_matrix_array: Vec<f32> = (camera.projection_matrix * transform.value.inverse()).to_cols_array().to_vec();
camera_matrix_array.extend_from_slice(&[translation.x(), translation.y(), translation.z(), 1.0]);
let camera_gpu_data = camera_matrix_array.as_slice();

render_resource_context.write_mapped_buffer(
staging_buffer,
0..matrix_size as u64,
&mut |data, _renderer| {
data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes());
data[0..matrix_size].copy_from_slice(camera_gpu_data.as_bytes());
},
);
render_resource_context.unmap_buffer(staging_buffer);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_graph/nodes/pass_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl<Q: HecsQuery> PassNode<Q> {
index: 0,
bind_type: BindType::Uniform {
dynamic: false,
properties: vec![UniformProperty::Struct(vec![UniformProperty::Mat4])],
properties: vec![UniformProperty::Struct(vec![UniformProperty::Mat4, UniformProperty::Vec4])],
},
shader_stage: BindingShaderStage::VERTEX | BindingShaderStage::FRAGMENT,
}],
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_render/src/shader/shader_reflect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ mod tests {
layout(location = 0) out vec4 v_Position;
layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};
layout(set = 1, binding = 0) uniform texture2D Texture;

Expand Down Expand Up @@ -451,6 +452,7 @@ mod tests {
layout(location = 0) out vec4 v_Position;
layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};
layout(set = 1, binding = 0) uniform texture2D Texture;

Expand Down
1 change: 1 addition & 0 deletions crates/bevy_sprite/src/render/sprite.vert
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ layout(location = 0) out vec2 v_Uv;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};

layout(set = 2, binding = 0) uniform Transform {
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_sprite/src/render/sprite_sheet.vert
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ layout(location = 1) out vec4 v_Color;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};

// TODO: merge dimensions into "sprites" buffer when that is supported in the Uniforms derive abstraction
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_ui/src/render/ui.vert
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ layout(location = 0) out vec2 v_Uv;

layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};

layout(set = 1, binding = 0) uniform Transform {
Expand Down
4 changes: 2 additions & 2 deletions examples/3d/msaa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ fn setup(
commands
// cube
.spawn(PbrComponents {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
mesh: meshes.add(Mesh::from(shape::Icosphere { radius: 1.0, subdivisions: 32, })),
material: materials.add(Color::rgb(0.5, 0.4, 0.3).into()),
..Default::default()
})
// light
.spawn(LightComponents {
translation: Translation::new(4.0, 8.0, 4.0),
translation: Translation::new(40.0, 80.0, 40.0),
..Default::default()
})
// camera
Expand Down
1 change: 1 addition & 0 deletions examples/shader/shader_custom_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const VERTEX_SHADER: &str = r#"
layout(location = 0) in vec3 Vertex_Position;
layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};
layout(set = 1, binding = 0) uniform Transform {
mat4 Model;
Expand Down
1 change: 1 addition & 0 deletions examples/shader/shader_defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const VERTEX_SHADER: &str = r#"
layout(location = 0) in vec3 Vertex_Position;
layout(set = 0, binding = 0) uniform Camera {
mat4 ViewProj;
vec4 CameraPos;
};
layout(set = 1, binding = 0) uniform Transform {
mat4 Model;
Expand Down