mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
more built-in shader variables for lighting
- pass light info in forward rendering shader - removed 2d renderer
This commit is contained in:
+2
-8
@@ -11,15 +11,9 @@ use fyrox::{
|
||||
pool::Handle,
|
||||
sstorage::ImmutableString,
|
||||
},
|
||||
engine::{executor::Executor, GraphicsContext, GraphicsContextParams},
|
||||
engine::{executor::Executor, GraphicsContextParams},
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
gui::{
|
||||
message::MessageDirection,
|
||||
text::{TextBuilder, TextMessage},
|
||||
widget::WidgetBuilder,
|
||||
UiNode,
|
||||
},
|
||||
material::{Material, PropertyValue, SharedMaterial},
|
||||
plugin::{Plugin, PluginConstructor, PluginContext},
|
||||
resource::texture::Texture,
|
||||
@@ -59,7 +53,7 @@ impl SceneLoader {
|
||||
.set_property(
|
||||
&ImmutableString::new("diffuseTexture"),
|
||||
PropertyValue::Sampler {
|
||||
value: Some(resource_manager.request::<Texture, _>("Crate.png")),
|
||||
value: Some(resource_manager.request::<Texture, _>("examples/Crate.png")),
|
||||
fallback: Default::default(),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
r#"
|
||||
layout(location = 0) in vec3 vertexPosition;
|
||||
layout(location = 1) in vec2 vertexTexCoord;
|
||||
layout(location = 2) in vec4 vertexColor;
|
||||
|
||||
uniform mat4 fyrox_worldViewProjection;
|
||||
uniform mat4 fyrox_worldMatrix;
|
||||
@@ -59,7 +58,7 @@
|
||||
texCoord = vertexTexCoord;
|
||||
fragmentPosition = (fyrox_worldMatrix * vec4(vertexPosition, 1.0)).xyz;
|
||||
gl_Position = fyrox_worldViewProjection * vec4(vertexPosition, 1.0);
|
||||
color = vertexColor;
|
||||
color = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
"#,
|
||||
|
||||
@@ -67,12 +66,12 @@
|
||||
r#"
|
||||
uniform sampler2D diffuseTexture;
|
||||
|
||||
uniform int lightCount;
|
||||
uniform vec4 lightColorRadius[16]; // xyz - color, w = radius
|
||||
uniform vec3 lightPosition[16];
|
||||
uniform vec3 lightDirection[16];
|
||||
uniform vec2 lightParameters[16]; // x = hotspot angle, y - full cone angle delta
|
||||
uniform vec3 ambientLightColor;
|
||||
uniform int fyrox_lightCount;
|
||||
uniform vec4 fyrox_lightsColorRadius[16]; // xyz - color, w = radius
|
||||
uniform vec3 fyrox_lightsPosition[16];
|
||||
uniform vec3 fyrox_lightsDirection[16];
|
||||
uniform vec2 fyrox_lightsParameters[16]; // x = hotspot angle, y - full cone angle delta
|
||||
uniform vec3 fyrox_ambientLightColor;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
@@ -82,15 +81,15 @@
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 lighting = ambientLightColor;
|
||||
for(int i = 0; i < lightCount; ++i) {
|
||||
vec3 lighting = fyrox_ambientLightColor;
|
||||
for(int i = 0; i < fyrox_lightCount; ++i) {
|
||||
// "Unpack" light parameters.
|
||||
float halfHotspotAngleCos = lightParameters[i].x;
|
||||
float halfConeAngleCos = lightParameters[i].y;
|
||||
vec3 lightColor = lightColorRadius[i].xyz;
|
||||
float radius = lightColorRadius[i].w;
|
||||
vec3 lightPosition = lightPosition[i];
|
||||
vec3 direction = lightDirection[i];
|
||||
float halfHotspotAngleCos = fyrox_lightsParameters[i].x;
|
||||
float halfConeAngleCos = fyrox_lightsParameters[i].y;
|
||||
vec3 lightColor = fyrox_lightsColorRadius[i].xyz;
|
||||
float radius = fyrox_lightsColorRadius[i].w;
|
||||
vec3 lightPosition = fyrox_lightsPosition[i];
|
||||
vec3 direction = fyrox_lightsDirection[i];
|
||||
|
||||
// Calculate lighting.
|
||||
vec3 toFragment = fragmentPosition - lightPosition;
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
//! path).
|
||||
|
||||
use crate::{
|
||||
core::{math::Rect, scope_profile, sstorage::ImmutableString},
|
||||
core::{
|
||||
algebra::{Vector2, Vector4},
|
||||
color::Color,
|
||||
math::{frustum::Frustum, Rect},
|
||||
scope_profile,
|
||||
sstorage::ImmutableString,
|
||||
},
|
||||
renderer::{
|
||||
apply_material,
|
||||
batch::RenderDataBatchStorage,
|
||||
@@ -17,9 +23,14 @@ use crate::{
|
||||
state::PipelineState,
|
||||
},
|
||||
storage::MatrixStorageCache,
|
||||
GeometryCache, MaterialContext, QualitySettings, RenderPassStatistics,
|
||||
GeometryCache, LightData, MaterialContext, QualitySettings, RenderPassStatistics,
|
||||
},
|
||||
scene::{
|
||||
camera::Camera,
|
||||
graph::Graph,
|
||||
light::{directional::DirectionalLight, point::PointLight, spot::SpotLight},
|
||||
mesh::RenderPath,
|
||||
},
|
||||
scene::{camera::Camera, mesh::RenderPath},
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
@@ -29,6 +40,7 @@ pub(crate) struct ForwardRenderer {
|
||||
|
||||
pub(crate) struct ForwardRenderContext<'a, 'b> {
|
||||
pub state: &'a mut PipelineState,
|
||||
pub graph: &'b Graph,
|
||||
pub camera: &'b Camera,
|
||||
pub geom_cache: &'a mut GeometryCache,
|
||||
pub texture_cache: &'a mut TextureCache,
|
||||
@@ -42,6 +54,7 @@ pub(crate) struct ForwardRenderContext<'a, 'b> {
|
||||
pub black_dummy: Rc<RefCell<GpuTexture>>,
|
||||
pub volume_dummy: Rc<RefCell<GpuTexture>>,
|
||||
pub matrix_storage: &'a mut MatrixStorageCache,
|
||||
pub ambient_light: Color,
|
||||
}
|
||||
|
||||
impl ForwardRenderer {
|
||||
@@ -61,6 +74,7 @@ impl ForwardRenderer {
|
||||
|
||||
let ForwardRenderContext {
|
||||
state,
|
||||
graph,
|
||||
camera,
|
||||
geom_cache,
|
||||
texture_cache,
|
||||
@@ -74,10 +88,60 @@ impl ForwardRenderer {
|
||||
black_dummy,
|
||||
volume_dummy,
|
||||
matrix_storage,
|
||||
ambient_light,
|
||||
} = args;
|
||||
|
||||
let initial_view_projection = camera.view_projection_matrix();
|
||||
|
||||
let frustum = Frustum::from_view_projection_matrix(camera.view_projection_matrix())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut light_data = LightData::default();
|
||||
for light in graph.linear_iter() {
|
||||
if !light.global_visibility() || light_data.count == light_data.parameters.len() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (radius, half_cone_angle_cos, half_hotspot_angle_cos, color) =
|
||||
if let Some(point) = light.cast::<PointLight>() {
|
||||
(
|
||||
point.radius(),
|
||||
std::f32::consts::PI.cos(),
|
||||
std::f32::consts::PI.cos(),
|
||||
point.base_light_ref().color().as_frgb(),
|
||||
)
|
||||
} else if let Some(spot) = light.cast::<SpotLight>() {
|
||||
(
|
||||
spot.distance(),
|
||||
(spot.hotspot_cone_angle() * 0.5).cos(),
|
||||
(spot.full_cone_angle() * 0.5).cos(),
|
||||
spot.base_light_ref().color().as_frgb(),
|
||||
)
|
||||
} else if let Some(directional) = light.cast::<DirectionalLight>() {
|
||||
(
|
||||
f32::INFINITY,
|
||||
std::f32::consts::PI.cos(),
|
||||
std::f32::consts::PI.cos(),
|
||||
directional.base_light_ref().color().as_frgb(),
|
||||
)
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if frustum.is_intersects_aabb(&light.world_bounding_box()) {
|
||||
let light_num = light_data.count;
|
||||
|
||||
light_data.position[light_num] = light.global_position();
|
||||
light_data.direction[light_num] = light.up_vector();
|
||||
light_data.color_radius[light_num] =
|
||||
Vector4::new(color.x, color.y, color.z, radius);
|
||||
light_data.parameters[light_num] =
|
||||
Vector2::new(half_cone_angle_cos, half_hotspot_angle_cos);
|
||||
|
||||
light_data.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for batch in batch_storage
|
||||
.batches
|
||||
.iter()
|
||||
@@ -132,6 +196,8 @@ impl ForwardRenderer {
|
||||
volume_dummy: volume_dummy.clone(),
|
||||
matrix_storage,
|
||||
persistent_identifier: instance.persistent_identifier,
|
||||
light_data: Some(&light_data),
|
||||
ambient_light,
|
||||
});
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -34,6 +34,12 @@ pub enum BuiltInUniform {
|
||||
BlendShapesStorage,
|
||||
BlendShapesWeights,
|
||||
BlendShapesCount,
|
||||
LightCount,
|
||||
LightsColorRadius,
|
||||
LightsPosition,
|
||||
LightsDirection,
|
||||
LightsParameters,
|
||||
AmbientLight,
|
||||
// Must be last.
|
||||
Count,
|
||||
}
|
||||
@@ -438,7 +444,18 @@ fn fetch_built_in_uniform_locations(
|
||||
fetch_uniform_location(state, program, "fyrox_blendShapesWeights");
|
||||
locations[BuiltInUniform::BlendShapesCount as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_blendShapesCount");
|
||||
|
||||
locations[BuiltInUniform::LightCount as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_lightCount");
|
||||
locations[BuiltInUniform::LightsColorRadius as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_lightsColorRadius");
|
||||
locations[BuiltInUniform::LightsPosition as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_lightsPosition");
|
||||
locations[BuiltInUniform::LightsDirection as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_lightsDirection");
|
||||
locations[BuiltInUniform::LightsParameters as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_lightsParameters");
|
||||
locations[BuiltInUniform::AmbientLight as usize] =
|
||||
fetch_uniform_location(state, program, "fyrox_ambientLightColor");
|
||||
locations
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +344,8 @@ impl GBuffer {
|
||||
black_dummy: black_dummy.clone(),
|
||||
volume_dummy: volume_dummy.clone(),
|
||||
persistent_identifier: instance.persistent_identifier,
|
||||
light_data: None,
|
||||
ambient_light: Color::WHITE, // TODO
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+57
-18
@@ -16,7 +16,6 @@ pub mod framework;
|
||||
pub mod batch;
|
||||
pub mod cache;
|
||||
pub mod debug_renderer;
|
||||
pub mod renderer2d;
|
||||
pub mod storage;
|
||||
pub mod ui_renderer;
|
||||
|
||||
@@ -77,7 +76,6 @@ use crate::{
|
||||
hdr::HighDynamicRangeRenderer,
|
||||
light::{DeferredLightRenderer, DeferredRendererContext, LightingStatistics},
|
||||
particle_system_renderer::{ParticleSystemRenderContext, ParticleSystemRenderer},
|
||||
renderer2d::Renderer2d,
|
||||
sprite_renderer::{SpriteRenderContext, SpriteRenderer},
|
||||
storage::MatrixStorageCache,
|
||||
ui_renderer::{UiRenderContext, UiRenderer},
|
||||
@@ -86,6 +84,7 @@ use crate::{
|
||||
scene::{camera::Camera, mesh::surface::SurfaceData, Scene, SceneContainer},
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use fyrox_core::algebra::Vector4;
|
||||
use glow::HasContext;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use glutin::{
|
||||
@@ -738,7 +737,6 @@ pub struct Renderer {
|
||||
geometry_cache: GeometryCache,
|
||||
forward_renderer: ForwardRenderer,
|
||||
fxaa_renderer: FxaaRenderer,
|
||||
renderer2d: Renderer2d,
|
||||
texture_event_receiver: Receiver<ResourceEvent>,
|
||||
shader_event_receiver: Receiver<ResourceEvent>,
|
||||
matrix_storage: MatrixStorageCache,
|
||||
@@ -946,6 +944,26 @@ fn blit_pixels(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) struct LightData<const N: usize = 16> {
|
||||
count: usize,
|
||||
color_radius: [Vector4<f32>; N],
|
||||
position: [Vector3<f32>; N],
|
||||
direction: [Vector3<f32>; N],
|
||||
parameters: [Vector2<f32>; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for LightData<N> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
count: 0,
|
||||
color_radius: [Default::default(); N],
|
||||
position: [Default::default(); N],
|
||||
direction: [Default::default(); N],
|
||||
parameters: [Default::default(); N],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MaterialContext<'a, 'b, 'c> {
|
||||
pub material: &'a Material,
|
||||
pub program_binding: &'a mut GpuProgramBinding<'b, 'c>,
|
||||
@@ -963,6 +981,8 @@ pub(crate) struct MaterialContext<'a, 'b, 'c> {
|
||||
pub light_position: &'a Vector3<f32>,
|
||||
pub blend_shapes_storage: Option<&'a TextureResource>,
|
||||
pub blend_shapes_weights: &'a [f32],
|
||||
pub light_data: Option<&'a LightData>,
|
||||
pub ambient_light: Color,
|
||||
|
||||
// Fallback samplers.
|
||||
pub normal_dummy: Rc<RefCell<GpuTexture>>,
|
||||
@@ -1012,6 +1032,38 @@ pub(crate) fn apply_material(ctx: MaterialContext) {
|
||||
.set_vector3(location, ctx.light_position);
|
||||
}
|
||||
|
||||
if let Some(light_data) = ctx.light_data {
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::LightCount as usize] {
|
||||
ctx.program_binding
|
||||
.set_i32(location, light_data.count as i32);
|
||||
}
|
||||
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsColorRadius as usize] {
|
||||
ctx.program_binding
|
||||
.set_vector4_slice(location, &light_data.color_radius);
|
||||
}
|
||||
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsPosition as usize] {
|
||||
ctx.program_binding
|
||||
.set_vector3_slice(location, &light_data.position);
|
||||
}
|
||||
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsDirection as usize] {
|
||||
ctx.program_binding
|
||||
.set_vector3_slice(location, &light_data.direction);
|
||||
}
|
||||
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::LightsParameters as usize] {
|
||||
ctx.program_binding
|
||||
.set_vector2_slice(location, &light_data.parameters);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::AmbientLight as usize] {
|
||||
ctx.program_binding
|
||||
.set_srgb_color(location, &ctx.ambient_light);
|
||||
}
|
||||
|
||||
if let Some(location) = &built_in_uniforms[BuiltInUniform::BlendShapesStorage as usize] {
|
||||
if let Some(texture) = ctx
|
||||
.blend_shapes_storage
|
||||
@@ -1258,7 +1310,6 @@ impl Renderer {
|
||||
ui_frame_buffers: Default::default(),
|
||||
fxaa_renderer: FxaaRenderer::new(&mut state)?,
|
||||
statistics: Statistics::default(),
|
||||
renderer2d: Renderer2d::new(&mut state)?,
|
||||
shader_event_receiver,
|
||||
texture_event_receiver,
|
||||
shader_cache,
|
||||
@@ -1365,7 +1416,6 @@ impl Renderer {
|
||||
pub fn flush(&mut self) {
|
||||
self.texture_cache.clear();
|
||||
self.geometry_cache.clear();
|
||||
self.renderer2d.flush();
|
||||
}
|
||||
|
||||
/// Renders given UI into specified render target. This method is especially useful if you need
|
||||
@@ -1491,7 +1541,6 @@ impl Renderer {
|
||||
self.update_texture_cache(dt);
|
||||
self.update_shader_cache(dt);
|
||||
self.geometry_cache.update(dt);
|
||||
self.renderer2d.update_caches(dt);
|
||||
}
|
||||
|
||||
fn render_frame(
|
||||
@@ -1708,21 +1757,10 @@ impl Renderer {
|
||||
viewport,
|
||||
textures: &mut self.texture_cache,
|
||||
})?;
|
||||
/*
|
||||
|
||||
self.statistics += self.renderer2d.render(
|
||||
state,
|
||||
camera,
|
||||
&mut scene_associated_data.hdr_scene_framebuffer,
|
||||
viewport,
|
||||
graph,
|
||||
&mut self.texture_cache,
|
||||
self.white_dummy.clone(),
|
||||
scene.rendering_options.ambient_lighting_color,
|
||||
)?;
|
||||
*/
|
||||
self.statistics += self.forward_renderer.render(ForwardRenderContext {
|
||||
state,
|
||||
graph,
|
||||
camera,
|
||||
geom_cache: &mut self.geometry_cache,
|
||||
texture_cache: &mut self.texture_cache,
|
||||
@@ -1736,6 +1774,7 @@ impl Renderer {
|
||||
black_dummy: self.black_dummy.clone(),
|
||||
volume_dummy: self.volume_dummy.clone(),
|
||||
matrix_storage: &mut self.matrix_storage,
|
||||
ambient_light: scene.rendering_options.ambient_lighting_color,
|
||||
})?;
|
||||
|
||||
for render_pass in self.scene_render_passes.iter() {
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
use crate::{
|
||||
core::{
|
||||
algebra::{Matrix4, Vector2},
|
||||
color::Color,
|
||||
math::TriangleDefinition,
|
||||
scope_profile,
|
||||
},
|
||||
renderer::framework::{
|
||||
geometry_buffer::{
|
||||
AttributeDefinition, AttributeKind, BufferBuilder, ElementKind, GeometryBuffer,
|
||||
GeometryBufferBuilder, GeometryBufferKind,
|
||||
},
|
||||
state::PipelineState,
|
||||
},
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use fyrox_core::algebra::{Vector3, Vector4};
|
||||
use fyrox_resource::entry::TimedEntry;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Vertex {
|
||||
position: Vector3<f32>,
|
||||
tex_coord: Vector2<f32>,
|
||||
}
|
||||
|
||||
pub struct Mesh {
|
||||
vertices: Vec<Vertex>,
|
||||
triangles: Vec<TriangleDefinition>,
|
||||
}
|
||||
|
||||
impl Mesh {
|
||||
pub fn new_unit_quad() -> Self {
|
||||
let vertices = vec![
|
||||
Vertex {
|
||||
position: Vector3::new(-0.5, 0.5, 0.0),
|
||||
tex_coord: Vector2::new(1.0, 0.0),
|
||||
},
|
||||
Vertex {
|
||||
position: Vector3::new(0.5, 0.5, 0.0),
|
||||
tex_coord: Vector2::new(0.0, 0.0),
|
||||
},
|
||||
Vertex {
|
||||
position: Vector3::new(0.5, -0.5, 0.0),
|
||||
tex_coord: Vector2::new(0.0, 1.0),
|
||||
},
|
||||
Vertex {
|
||||
position: Vector3::new(-0.5, -0.5, 0.0),
|
||||
tex_coord: Vector2::new(1.0, 1.0),
|
||||
},
|
||||
];
|
||||
|
||||
let triangles = vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])];
|
||||
|
||||
Self {
|
||||
vertices,
|
||||
triangles,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct GeometryCache {
|
||||
map: FxHashMap<usize, TimedEntry<GeometryBuffer>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct InstanceData {
|
||||
pub color: Color,
|
||||
/// UV Transform of the instance, where:
|
||||
/// - `x` - UV rect X offset
|
||||
/// - `y` - UV rect Y offset
|
||||
/// - `z` - UV rect X scale
|
||||
/// - `w` - UV rect Y scale
|
||||
pub uv_transform: Vector4<f32>,
|
||||
pub world_matrix: Matrix4<f32>,
|
||||
}
|
||||
|
||||
impl GeometryCache {
|
||||
pub fn get(&mut self, state: &mut PipelineState, data: &Mesh) -> &mut GeometryBuffer {
|
||||
scope_profile!();
|
||||
|
||||
let key = (data as *const _) as usize;
|
||||
|
||||
let geometry_buffer = self.map.entry(key).or_insert_with(|| {
|
||||
let geometry_buffer = GeometryBufferBuilder::new(ElementKind::Triangle)
|
||||
.with_buffer_builder(
|
||||
BufferBuilder::new(
|
||||
GeometryBufferKind::StaticDraw,
|
||||
Some(data.vertices.as_slice()),
|
||||
)
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 0,
|
||||
divisor: 0,
|
||||
kind: AttributeKind::Float3,
|
||||
normalized: false,
|
||||
})
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 1,
|
||||
divisor: 0,
|
||||
kind: AttributeKind::Float2,
|
||||
normalized: false,
|
||||
}),
|
||||
)
|
||||
// Buffer for instance data.
|
||||
.with_buffer_builder(
|
||||
BufferBuilder::new::<InstanceData>(GeometryBufferKind::DynamicDraw, None)
|
||||
// Color
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 2,
|
||||
kind: AttributeKind::UnsignedByte4,
|
||||
normalized: true,
|
||||
divisor: 1,
|
||||
})
|
||||
// Transform
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 3,
|
||||
kind: AttributeKind::Float4,
|
||||
normalized: false,
|
||||
divisor: 1,
|
||||
})
|
||||
// World Matrix
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 4,
|
||||
kind: AttributeKind::Float4,
|
||||
normalized: false,
|
||||
divisor: 1,
|
||||
})
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 5,
|
||||
kind: AttributeKind::Float4,
|
||||
normalized: false,
|
||||
divisor: 1,
|
||||
})
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 6,
|
||||
kind: AttributeKind::Float4,
|
||||
normalized: false,
|
||||
divisor: 1,
|
||||
})
|
||||
.with_attribute(AttributeDefinition {
|
||||
location: 7,
|
||||
kind: AttributeKind::Float4,
|
||||
normalized: false,
|
||||
divisor: 1,
|
||||
}),
|
||||
)
|
||||
.build(state)
|
||||
.unwrap();
|
||||
|
||||
geometry_buffer.bind(state).set_triangles(&data.triangles);
|
||||
|
||||
TimedEntry {
|
||||
value: geometry_buffer,
|
||||
time_to_live: 20.0,
|
||||
}
|
||||
});
|
||||
|
||||
geometry_buffer.time_to_live = 20.0;
|
||||
geometry_buffer
|
||||
}
|
||||
|
||||
pub fn update(&mut self, dt: f32) {
|
||||
scope_profile!();
|
||||
|
||||
for entry in self.map.values_mut() {
|
||||
entry.time_to_live -= dt;
|
||||
}
|
||||
self.map.retain(|_, v| v.time_to_live > 0.0);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
//! A renderer responsible for drawing 2D scenes.
|
||||
|
||||
use crate::renderer::framework::framebuffer::BlendParameters;
|
||||
use crate::{
|
||||
core::{
|
||||
algebra::{Vector2, Vector3, Vector4},
|
||||
color::Color,
|
||||
math::{aabb::AxisAlignedBoundingBox, frustum::Frustum, Rect},
|
||||
sstorage::ImmutableString,
|
||||
},
|
||||
renderer::{
|
||||
framework::{
|
||||
error::FrameworkError,
|
||||
framebuffer::{DrawParameters, FrameBuffer},
|
||||
gpu_program::{GpuProgram, UniformLocation},
|
||||
gpu_texture::GpuTexture,
|
||||
state::{BlendFactor, BlendFunc, PipelineState},
|
||||
},
|
||||
renderer2d::cache::{GeometryCache, InstanceData, Mesh},
|
||||
RenderPassStatistics, TextureCache,
|
||||
},
|
||||
scene::{
|
||||
camera::Camera,
|
||||
dim2::rectangle::Rectangle,
|
||||
graph::Graph,
|
||||
light::{directional::DirectionalLight, point::PointLight, spot::SpotLight},
|
||||
},
|
||||
utils::value_as_u8_slice,
|
||||
};
|
||||
use fxhash::{FxHashMap, FxHasher};
|
||||
use std::{cell::RefCell, cmp::Ordering, hash::Hasher, rc::Rc};
|
||||
|
||||
mod cache;
|
||||
|
||||
struct SpriteShader {
|
||||
program: GpuProgram,
|
||||
wvp_matrix: UniformLocation,
|
||||
diffuse_texture: UniformLocation,
|
||||
light_count: UniformLocation,
|
||||
light_color_radius: UniformLocation,
|
||||
light_position: UniformLocation,
|
||||
light_direction: UniformLocation,
|
||||
light_parameters: UniformLocation,
|
||||
ambient_light_color: UniformLocation,
|
||||
}
|
||||
|
||||
impl SpriteShader {
|
||||
pub fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
|
||||
let fragment_source = include_str!("shaders/sprite_fs.glsl");
|
||||
let vertex_source = include_str!("shaders/sprite_vs.glsl");
|
||||
|
||||
let program =
|
||||
GpuProgram::from_source(state, "RectangleShader", vertex_source, fragment_source)?;
|
||||
Ok(Self {
|
||||
wvp_matrix: program.uniform_location(state, &ImmutableString::new("viewProjection"))?,
|
||||
diffuse_texture: program
|
||||
.uniform_location(state, &ImmutableString::new("diffuseTexture"))?,
|
||||
light_count: program.uniform_location(state, &ImmutableString::new("lightCount"))?,
|
||||
light_color_radius: program
|
||||
.uniform_location(state, &ImmutableString::new("lightColorRadius"))?,
|
||||
light_direction: program
|
||||
.uniform_location(state, &ImmutableString::new("lightDirection"))?,
|
||||
light_position: program
|
||||
.uniform_location(state, &ImmutableString::new("lightPosition"))?,
|
||||
light_parameters: program
|
||||
.uniform_location(state, &ImmutableString::new("lightParameters"))?,
|
||||
ambient_light_color: program
|
||||
.uniform_location(state, &ImmutableString::new("ambientLightColor"))?,
|
||||
program,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Renderer2d {
|
||||
sprite_shader: SpriteShader,
|
||||
quad: Mesh,
|
||||
geometry_cache: GeometryCache,
|
||||
batch_storage: SpriteBatchStorage,
|
||||
instance_data_set: Vec<InstanceData>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SpriteBatchStorage {
|
||||
batches: Vec<Batch>,
|
||||
index_map: FxHashMap<u64, usize>,
|
||||
}
|
||||
|
||||
impl SpriteBatchStorage {
|
||||
fn generate_batches(
|
||||
&mut self,
|
||||
state: &mut PipelineState,
|
||||
graph: &Graph,
|
||||
texture_cache: &mut TextureCache,
|
||||
white_dummy: Rc<RefCell<GpuTexture>>,
|
||||
) {
|
||||
self.index_map.clear();
|
||||
for batch in self.batches.iter_mut() {
|
||||
batch.instances.clear();
|
||||
}
|
||||
|
||||
let mut batch_index = 0;
|
||||
for node in graph.linear_iter() {
|
||||
if let Some(rectangle) = node.cast::<Rectangle>() {
|
||||
if !rectangle.global_visibility() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let texture = rectangle.texture().map_or_else(
|
||||
|| white_dummy.clone(),
|
||||
|t| {
|
||||
texture_cache
|
||||
.get(state, t)
|
||||
.unwrap_or_else(|| white_dummy.clone())
|
||||
},
|
||||
);
|
||||
|
||||
let z = rectangle.global_position().z;
|
||||
|
||||
let mut hasher = FxHasher::default();
|
||||
// Objects with different Z coordinate will go into separate batches.
|
||||
hasher.write(value_as_u8_slice(&z));
|
||||
// Objects with different textures will go into separate batches.
|
||||
hasher.write_u64(&*texture.borrow() as *const _ as u64);
|
||||
let batch_id = hasher.finish();
|
||||
|
||||
let index = *self.index_map.entry(batch_id).or_insert_with(|| {
|
||||
let index = batch_index;
|
||||
batch_index += 1;
|
||||
index as usize
|
||||
});
|
||||
|
||||
// Reuse old batches to prevent redundant memory allocations
|
||||
let batch = if let Some(batch) = self.batches.get_mut(index) {
|
||||
batch.texture = texture.clone();
|
||||
batch.z = z;
|
||||
batch
|
||||
} else {
|
||||
self.batches.push(Batch {
|
||||
instances: Default::default(),
|
||||
texture: texture.clone(),
|
||||
z,
|
||||
});
|
||||
self.batches.last_mut().unwrap()
|
||||
};
|
||||
|
||||
let uv_rect = rectangle.uv_rect();
|
||||
let uv_transform = Vector4::new(uv_rect.x(), uv_rect.y(), uv_rect.w(), uv_rect.h());
|
||||
|
||||
batch.instances.push(Instance {
|
||||
gpu_data: InstanceData {
|
||||
color: rectangle.color().srgb_to_linear(),
|
||||
uv_transform,
|
||||
world_matrix: rectangle.global_transform(),
|
||||
},
|
||||
aabb: rectangle.world_bounding_box(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort back-to-front for correct blending.
|
||||
self.batches.sort_by(|a, b| {
|
||||
if a.z < b.z {
|
||||
Ordering::Greater
|
||||
} else if a.z > b.z {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Instance {
|
||||
gpu_data: InstanceData,
|
||||
aabb: AxisAlignedBoundingBox,
|
||||
}
|
||||
|
||||
struct Batch {
|
||||
instances: Vec<Instance>,
|
||||
texture: Rc<RefCell<GpuTexture>>,
|
||||
z: f32,
|
||||
}
|
||||
|
||||
impl Renderer2d {
|
||||
pub(crate) fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
|
||||
Ok(Self {
|
||||
sprite_shader: SpriteShader::new(state)?,
|
||||
quad: Mesh::new_unit_quad(),
|
||||
geometry_cache: Default::default(),
|
||||
batch_storage: Default::default(),
|
||||
instance_data_set: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn update_caches(&mut self, dt: f32) {
|
||||
self.geometry_cache.update(dt);
|
||||
}
|
||||
|
||||
pub(crate) fn render(
|
||||
&mut self,
|
||||
state: &mut PipelineState,
|
||||
camera: &Camera,
|
||||
frame_buffer: &mut FrameBuffer,
|
||||
viewport: Rect<i32>,
|
||||
graph: &Graph,
|
||||
texture_cache: &mut TextureCache,
|
||||
white_dummy: Rc<RefCell<GpuTexture>>,
|
||||
ambient_color: Color,
|
||||
) -> Result<RenderPassStatistics, FrameworkError> {
|
||||
let mut stats = RenderPassStatistics::default();
|
||||
let quad = self.geometry_cache.get(state, &self.quad);
|
||||
|
||||
self.batch_storage
|
||||
.generate_batches(state, graph, texture_cache, white_dummy);
|
||||
|
||||
let view_projection = camera.view_projection_matrix();
|
||||
|
||||
let frustum = Frustum::from_view_projection_matrix(camera.view_projection_matrix())
|
||||
.unwrap_or_default();
|
||||
|
||||
const MAX_LIGHTS: usize = 16;
|
||||
let mut light_count = 0;
|
||||
let mut light_color_radius = [Vector4::default(); MAX_LIGHTS];
|
||||
let mut light_position = [Vector3::default(); MAX_LIGHTS];
|
||||
let mut light_direction = [Vector3::default(); MAX_LIGHTS];
|
||||
let mut light_parameters = [Vector2::default(); MAX_LIGHTS];
|
||||
|
||||
for light in graph.linear_iter() {
|
||||
if !light.global_visibility() || light_count == MAX_LIGHTS {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (radius, half_cone_angle_cos, half_hotspot_angle_cos, color) =
|
||||
if let Some(point) = light.cast::<PointLight>() {
|
||||
(
|
||||
point.radius(),
|
||||
std::f32::consts::PI.cos(),
|
||||
std::f32::consts::PI.cos(),
|
||||
point.base_light_ref().color().as_frgb(),
|
||||
)
|
||||
} else if let Some(spot) = light.cast::<SpotLight>() {
|
||||
(
|
||||
spot.distance(),
|
||||
(spot.hotspot_cone_angle() * 0.5).cos(),
|
||||
(spot.full_cone_angle() * 0.5).cos(),
|
||||
spot.base_light_ref().color().as_frgb(),
|
||||
)
|
||||
} else if let Some(directional) = light.cast::<DirectionalLight>() {
|
||||
(
|
||||
f32::INFINITY,
|
||||
std::f32::consts::PI.cos(),
|
||||
std::f32::consts::PI.cos(),
|
||||
directional.base_light_ref().color().as_frgb(),
|
||||
)
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if frustum.is_intersects_aabb(&light.world_bounding_box()) {
|
||||
let light_num = light_count;
|
||||
|
||||
light_position[light_num] = light.global_position();
|
||||
light_direction[light_num] = light.up_vector();
|
||||
light_color_radius[light_num] = Vector4::new(color.x, color.y, color.z, radius);
|
||||
light_parameters[light_num] =
|
||||
Vector2::new(half_cone_angle_cos, half_hotspot_angle_cos);
|
||||
|
||||
light_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for batch in self.batch_storage.batches.iter() {
|
||||
self.instance_data_set.clear();
|
||||
for instance in batch.instances.iter() {
|
||||
if frustum.is_intersects_aabb(&instance.aabb) {
|
||||
self.instance_data_set.push(instance.gpu_data.clone());
|
||||
}
|
||||
}
|
||||
|
||||
quad.set_buffer_data(state, 1, &self.instance_data_set);
|
||||
|
||||
if !self.instance_data_set.is_empty() {
|
||||
let shader = &self.sprite_shader;
|
||||
stats += frame_buffer.draw_instances(
|
||||
self.instance_data_set.len(),
|
||||
quad,
|
||||
state,
|
||||
viewport,
|
||||
&shader.program,
|
||||
&DrawParameters {
|
||||
cull_face: None,
|
||||
color_write: Default::default(),
|
||||
depth_write: true,
|
||||
stencil_test: None,
|
||||
depth_test: true,
|
||||
blend: Some(BlendParameters {
|
||||
func: BlendFunc::new(
|
||||
BlendFactor::SrcAlpha,
|
||||
BlendFactor::OneMinusSrcAlpha,
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
stencil_op: Default::default(),
|
||||
},
|
||||
|mut program_binding| {
|
||||
program_binding
|
||||
.set_matrix4(&shader.wvp_matrix, &view_projection)
|
||||
.set_texture(&shader.diffuse_texture, &batch.texture)
|
||||
.set_i32(&shader.light_count, light_count as i32)
|
||||
.set_vector4_slice(&shader.light_color_radius, &light_color_radius)
|
||||
.set_vector3_slice(&shader.light_direction, &light_direction)
|
||||
.set_vector3_slice(&shader.light_position, &light_position)
|
||||
.set_vector2_slice(&shader.light_parameters, &light_parameters)
|
||||
.set_vector3(&shader.ambient_light_color, &ambient_color.as_frgb());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) {
|
||||
self.geometry_cache.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
uniform sampler2D diffuseTexture;
|
||||
|
||||
uniform int lightCount;
|
||||
uniform vec4 lightColorRadius[16]; // xyz - color, w = radius
|
||||
uniform vec3 lightPosition[16];
|
||||
uniform vec3 lightDirection[16];
|
||||
uniform vec2 lightParameters[16]; // x = hotspot angle, y - full cone angle delta
|
||||
uniform vec3 ambientLightColor;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec2 texCoord;
|
||||
in vec4 color;
|
||||
in vec3 fragmentPosition;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 lighting = ambientLightColor;
|
||||
for(int i = 0; i < lightCount; ++i) {
|
||||
// "Unpack" light parameters.
|
||||
float halfHotspotAngleCos = lightParameters[i].x;
|
||||
float halfConeAngleCos = lightParameters[i].y;
|
||||
vec3 lightColor = lightColorRadius[i].xyz;
|
||||
float radius = lightColorRadius[i].w;
|
||||
vec3 lightPosition = lightPosition[i];
|
||||
vec3 direction = lightDirection[i];
|
||||
|
||||
// Calculate lighting.
|
||||
vec3 toFragment = fragmentPosition - lightPosition;
|
||||
float distance = length(toFragment);
|
||||
vec3 toFragmentNormalized = toFragment / distance;
|
||||
float distanceAttenuation = S_LightDistanceAttenuation(distance, radius);
|
||||
float spotAngleCos = dot(toFragmentNormalized, direction);
|
||||
float directionalAttenuation = smoothstep(halfConeAngleCos, halfHotspotAngleCos, spotAngleCos);
|
||||
lighting += lightColor * (distanceAttenuation * directionalAttenuation);
|
||||
}
|
||||
|
||||
FragColor = vec4(lighting, 1.0) * color * S_SRGBToLinear(texture(diffuseTexture, texCoord));
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
layout(location = 0) in vec3 vertexPosition;
|
||||
layout(location = 1) in vec2 vertexTexCoord;
|
||||
layout(location = 2) in vec4 vertexColor;
|
||||
layout(location = 3) in vec4 uvTransform;
|
||||
layout(location = 4) in mat4 worldMatrix;
|
||||
|
||||
uniform mat4 viewProjection;
|
||||
|
||||
out vec2 texCoord;
|
||||
out vec4 color;
|
||||
out vec3 fragmentPosition;
|
||||
|
||||
void main()
|
||||
{
|
||||
texCoord = vertexTexCoord * uvTransform.zw + uvTransform.xy;
|
||||
vec4 worldPosition = worldMatrix * vec4(vertexPosition, 1.0);
|
||||
fragmentPosition = worldPosition.xyz;
|
||||
gl_Position = viewProjection * worldPosition;
|
||||
color = vertexColor;
|
||||
}
|
||||
@@ -25,6 +25,7 @@ use crate::{
|
||||
light::directional::{DirectionalLight, FrustumSplitOptions, CSM_NUM_CASCADES},
|
||||
},
|
||||
};
|
||||
use fyrox_core::color::Color;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
pub struct Cascade {
|
||||
@@ -307,6 +308,8 @@ impl CsmRenderer {
|
||||
black_dummy: black_dummy.clone(),
|
||||
volume_dummy: volume_dummy.clone(),
|
||||
persistent_identifier: instance.persistent_identifier,
|
||||
light_data: None, // TODO
|
||||
ambient_light: Color::WHITE, // TODO
|
||||
});
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -291,6 +291,8 @@ impl PointShadowMapRenderer {
|
||||
black_dummy: black_dummy.clone(),
|
||||
volume_dummy: volume_dummy.clone(),
|
||||
persistent_identifier: instance.persistent_identifier,
|
||||
light_data: None, // TODO
|
||||
ambient_light: Color::WHITE, // TODO
|
||||
});
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -208,6 +208,8 @@ impl SpotShadowMapRenderer {
|
||||
black_dummy: black_dummy.clone(),
|
||||
volume_dummy: volume_dummy.clone(),
|
||||
persistent_identifier: instance.persistent_identifier,
|
||||
light_data: None, // TODO
|
||||
ambient_light: Color::WHITE, // TODO
|
||||
});
|
||||
},
|
||||
)?;
|
||||
|
||||
Reference in New Issue
Block a user