mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
prefilter environment map to be physically correct
This commit is contained in:
@@ -192,7 +192,7 @@ impl GpuFrameBufferTrait for GlFrameBuffer {
|
||||
self.depth_attachment.as_ref()
|
||||
}
|
||||
|
||||
fn set_cubemap_face(&self, attachment_index: usize, face: CubeMapFace) {
|
||||
fn set_cubemap_face(&self, attachment_index: usize, face: CubeMapFace, level: usize) {
|
||||
let server = self.state.upgrade().unwrap();
|
||||
|
||||
unsafe {
|
||||
@@ -209,7 +209,7 @@ impl GpuFrameBufferTrait for GlFrameBuffer {
|
||||
glow::COLOR_ATTACHMENT0 + attachment_index as u32,
|
||||
face.into_gl(),
|
||||
Some(texture.id()),
|
||||
0,
|
||||
level as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,8 +213,9 @@ pub trait GpuFrameBufferTrait: GpuFrameBufferAsAny {
|
||||
/// Returns an optional depth/stencil attachment.
|
||||
fn depth_attachment(&self) -> Option<&Attachment>;
|
||||
|
||||
/// Sets an active face of a cube map (only for frame buffers that using cube maps for rendering).
|
||||
fn set_cubemap_face(&self, attachment_index: usize, face: CubeMapFace);
|
||||
/// Sets an active face of a cube map of the given mip level. Only for frame buffers that using
|
||||
/// cube maps for rendering.
|
||||
fn set_cubemap_face(&self, attachment_index: usize, face: CubeMapFace, level: usize);
|
||||
|
||||
/// Performs data transfer from one frame buffer to another with scaling. It copies a region
|
||||
/// defined by `src_x0`, `src_y0`, `src_x1`, `src_y1` coordinates from the frame buffer and
|
||||
|
||||
+7
-8
@@ -26,6 +26,7 @@ use crate::{
|
||||
},
|
||||
resource::texture::{Texture, TextureResource},
|
||||
};
|
||||
use fyrox_core::err_once;
|
||||
use fyrox_graphics::gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind};
|
||||
use fyrox_graphics::sampler::{
|
||||
GpuSampler, GpuSamplerDescriptor, MagnificationFilter, MinificationFilter, WrapMode,
|
||||
@@ -247,13 +248,11 @@ impl TextureCache {
|
||||
}
|
||||
Err(e) => {
|
||||
drop(texture_data_guard);
|
||||
Log::writeln(
|
||||
MessageKind::Error,
|
||||
format!(
|
||||
"Failed to create GPU texture from {} texture. Reason: {:?}",
|
||||
texture_resource.kind(),
|
||||
e
|
||||
),
|
||||
err_once!(
|
||||
texture_resource.key() as usize,
|
||||
"Failed to create GPU texture from {} texture. Reason: {:?}",
|
||||
texture_resource.kind(),
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -269,7 +268,7 @@ impl TextureCache {
|
||||
self.cache.clear();
|
||||
}
|
||||
|
||||
pub fn unload(&mut self, texture: TextureResource) {
|
||||
pub fn unload(&mut self, texture: &TextureResource) {
|
||||
if let Some(texture) = texture.state().data() {
|
||||
self.cache.remove(&texture.cache_index);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
//! Contains various algorithms for image convolution, primarily for lighting.
|
||||
|
||||
use crate::{
|
||||
core::{
|
||||
algebra::{Matrix4, Point3},
|
||||
color::Color,
|
||||
math::Rect,
|
||||
ImmutableString,
|
||||
},
|
||||
renderer::{
|
||||
cache::{
|
||||
shader::{binding, property, PropertyGroup, RenderMaterial},
|
||||
uniform::UniformBufferCache,
|
||||
},
|
||||
resources::RendererResources,
|
||||
utils::CubeMapFaceDescriptor,
|
||||
},
|
||||
};
|
||||
use fyrox_core::some_or_break;
|
||||
use fyrox_graphics::{
|
||||
error::FrameworkError,
|
||||
framebuffer::{Attachment, GpuFrameBuffer},
|
||||
gpu_texture::{GpuTexture, GpuTextureDescriptor, GpuTextureKind, PixelKind},
|
||||
server::GraphicsServer,
|
||||
stats::RenderPassStatistics,
|
||||
ElementRange,
|
||||
};
|
||||
|
||||
pub struct EnvironmentMapConvolution {
|
||||
framebuffer: GpuFrameBuffer,
|
||||
mip_count: usize,
|
||||
pub(crate) size: usize,
|
||||
}
|
||||
|
||||
impl EnvironmentMapConvolution {
|
||||
pub fn new(server: &dyn GraphicsServer, size: usize) -> Result<Self, FrameworkError> {
|
||||
let mip_count = ((size as f32).log2().floor() + 1.0) as usize;
|
||||
let cube_map = server.create_texture(GpuTextureDescriptor {
|
||||
name: "EnvironmentMapConvolution",
|
||||
kind: GpuTextureKind::Cube { size },
|
||||
pixel_kind: PixelKind::RGB8,
|
||||
mip_count,
|
||||
data: None,
|
||||
base_level: 0,
|
||||
max_level: mip_count,
|
||||
})?;
|
||||
Ok(Self {
|
||||
framebuffer: server.create_frame_buffer(None, vec![Attachment::color(cube_map)])?,
|
||||
mip_count,
|
||||
size,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cube_map(&self) -> &GpuTexture {
|
||||
&self.framebuffer.color_attachments()[0].texture
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
environment_map: &GpuTexture,
|
||||
uniform_buffer_cache: &mut UniformBufferCache,
|
||||
renderer_resources: &RendererResources,
|
||||
) -> Result<RenderPassStatistics, FrameworkError> {
|
||||
let mut stats = RenderPassStatistics::default();
|
||||
|
||||
let projection_matrix =
|
||||
Matrix4::new_perspective(1.0, std::f32::consts::FRAC_PI_2, 0.0125, 32.0);
|
||||
|
||||
for mip in 0..self.mip_count {
|
||||
let roughness = (mip as f32) / (self.mip_count - 1) as f32;
|
||||
let size = some_or_break!(self.size.checked_shr(mip as u32));
|
||||
let viewport = Rect::new(0, 0, size as i32, size as i32);
|
||||
for face in CubeMapFaceDescriptor::cube_faces() {
|
||||
self.framebuffer.set_cubemap_face(0, face.face, mip);
|
||||
self.framebuffer
|
||||
.clear(viewport, Some(Color::WHITE), None, None);
|
||||
|
||||
let view_matrix =
|
||||
Matrix4::look_at_rh(&Default::default(), &Point3::from(face.look), &face.up);
|
||||
|
||||
let wvp = projection_matrix * view_matrix;
|
||||
let properties = PropertyGroup::from([
|
||||
property("worldViewProjection", &wvp),
|
||||
property("roughness", &roughness),
|
||||
]);
|
||||
let material = RenderMaterial::from([
|
||||
binding(
|
||||
"environmentMap",
|
||||
(environment_map, &renderer_resources.linear_clamp_sampler),
|
||||
),
|
||||
binding("properties", &properties),
|
||||
]);
|
||||
|
||||
stats += renderer_resources
|
||||
.shaders
|
||||
.environment_map_convolution
|
||||
.run_pass(
|
||||
1,
|
||||
&ImmutableString::new("Primary"),
|
||||
&self.framebuffer,
|
||||
&renderer_resources.cube,
|
||||
viewport,
|
||||
&material,
|
||||
uniform_buffer_cache,
|
||||
ElementRange::Full,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ pub mod utils;
|
||||
pub mod visibility;
|
||||
|
||||
mod bloom;
|
||||
mod convolution;
|
||||
mod forward_renderer;
|
||||
mod fxaa;
|
||||
mod gbuffer;
|
||||
@@ -49,6 +50,7 @@ mod settings;
|
||||
mod shadow;
|
||||
mod ssao;
|
||||
|
||||
use crate::renderer::convolution::EnvironmentMapConvolution;
|
||||
use crate::renderer::ssao::ScreenSpaceAmbientOcclusionRenderer;
|
||||
use crate::{
|
||||
asset::{event::ResourceEvent, manager::ResourceManager},
|
||||
@@ -183,6 +185,9 @@ fn recreate_render_data_if_needed<T: Any>(
|
||||
|
||||
/// A set of frame buffers and renderers that can be used to render to.
|
||||
pub struct RenderDataContainer {
|
||||
/// Prefiltered environment map.
|
||||
pub environment_map_convolution: Rc<RefCell<Option<EnvironmentMapConvolution>>>,
|
||||
|
||||
/// Screen space ambient occlusion renderer.
|
||||
pub ssao_renderer: ScreenSpaceAmbientOcclusionRenderer,
|
||||
|
||||
@@ -290,6 +295,7 @@ impl RenderDataContainer {
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
environment_map_convolution: Default::default(),
|
||||
ssao_renderer: ScreenSpaceAmbientOcclusionRenderer::new(server, width, height)?,
|
||||
gbuffer: GBuffer::new(server, width, height)?,
|
||||
hdr_renderer: HighDynamicRangeRenderer::new(server)?,
|
||||
@@ -694,7 +700,7 @@ impl Renderer {
|
||||
}
|
||||
|
||||
/// Unloads texture from GPU memory.
|
||||
pub fn unload_texture(&mut self, texture: TextureResource) {
|
||||
pub fn unload_texture(&mut self, texture: &TextureResource) {
|
||||
self.texture_cache.unload(texture)
|
||||
}
|
||||
|
||||
@@ -941,7 +947,7 @@ impl Renderer {
|
||||
if let Some(face) = observer.cube_map_face {
|
||||
observer_render_data
|
||||
.ldr_scene_framebuffer
|
||||
.set_cubemap_face(0, face);
|
||||
.set_cubemap_face(0, face, 0);
|
||||
}
|
||||
|
||||
self.texture_cache.try_register(
|
||||
@@ -1257,6 +1263,7 @@ impl Renderer {
|
||||
// At first, render the reflection probes to off-screen render target and generate mipmaps
|
||||
// for the cube maps.
|
||||
for observer in observers.reflection_probes.iter() {
|
||||
let server = self.server.clone();
|
||||
let render_data = self.render_scene_observer(
|
||||
observer,
|
||||
scene_handle,
|
||||
@@ -1266,7 +1273,26 @@ impl Renderer {
|
||||
resource_manager,
|
||||
)?;
|
||||
let probe_cube_map = render_data.ldr_scene_frame_texture().clone();
|
||||
self.server.generate_mipmap(&probe_cube_map);
|
||||
let size = if let GpuTextureKind::Cube { size } = probe_cube_map.kind() {
|
||||
size
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let convolution = render_data.environment_map_convolution.clone();
|
||||
let mut convolution = convolution.borrow_mut();
|
||||
if convolution.is_none() || convolution.as_ref().is_some_and(|conv| conv.size != size) {
|
||||
*convolution = Some(EnvironmentMapConvolution::new(&*server, size)?);
|
||||
}
|
||||
let convolution = convolution.as_ref().unwrap();
|
||||
self.statistics += convolution.render(
|
||||
&probe_cube_map,
|
||||
&mut self.uniform_buffer_cache,
|
||||
&self.renderer_resources,
|
||||
)?;
|
||||
let rt = observer.render_target.as_ref().unwrap();
|
||||
self.texture_cache.unload(rt);
|
||||
self.texture_cache
|
||||
.try_register(&*self.server, rt, convolution.cube_map().clone())?;
|
||||
}
|
||||
|
||||
// Then render everything else.
|
||||
|
||||
@@ -92,6 +92,8 @@ pub struct ShadersContainer {
|
||||
pub box_blur: RenderPassContainer,
|
||||
/// User interface shader.
|
||||
pub ui: RenderPassContainer,
|
||||
/// Environment map convolution shader.
|
||||
pub environment_map_convolution: RenderPassContainer,
|
||||
}
|
||||
|
||||
impl ShadersContainer {
|
||||
@@ -171,6 +173,10 @@ impl ShadersContainer {
|
||||
server,
|
||||
include_str!("../../../fyrox-material/src/shader/standard/widget.shader"),
|
||||
)?,
|
||||
environment_map_convolution: RenderPassContainer::from_str(
|
||||
server,
|
||||
include_str!("shaders/prefilter.shader"),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
r#"
|
||||
layout (location = 0) in vec3 vertexPosition;
|
||||
|
||||
out vec4 color;
|
||||
out vec3 localPos;
|
||||
|
||||
void main()
|
||||
@@ -96,7 +95,6 @@
|
||||
return normalize(sampleVec);
|
||||
}
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
void main()
|
||||
{
|
||||
@@ -104,7 +102,7 @@
|
||||
vec3 R = N;
|
||||
vec3 V = R;
|
||||
|
||||
const uint SAMPLE_COUNT = 1024u;
|
||||
const uint SAMPLE_COUNT = 256u;
|
||||
float totalWeight = 0.0;
|
||||
vec3 prefilteredColor = vec3(0.0);
|
||||
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
|
||||
|
||||
@@ -163,7 +163,7 @@ impl PointShadowMapRenderer {
|
||||
Matrix4::new_perspective(1.0, std::f32::consts::FRAC_PI_2, z_near, z_far);
|
||||
|
||||
for face in self.faces.iter() {
|
||||
framebuffer.set_cubemap_face(0, face.face);
|
||||
framebuffer.set_cubemap_face(0, face.face, 0);
|
||||
framebuffer.clear(viewport, Some(Color::WHITE), Some(1.0), None);
|
||||
|
||||
let light_look_at = light_pos + face.look;
|
||||
|
||||
Reference in New Issue
Block a user