implemented gamma correction

- every input texture considered as sRGB
- resolve #153
This commit is contained in:
Dmitry
2021-08-15 22:41:57 +03:00
parent 89d8c3577a
commit 9fa2d7d021
35 changed files with 468 additions and 82 deletions
+7 -1
View File
@@ -28,6 +28,7 @@ pub mod shared;
use crate::shared::{create_ui, fix_shadows_distance, Game, GameScene};
use rg3d::core::algebra::Vector2;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
event::{Event, VirtualKeyCode, WindowEvent},
event_loop::ControlFlow,
@@ -170,7 +171,12 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
game.engine.renderer.set_frame_size(size.into());
if let Err(e) = game.engine.renderer.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
+7 -1
View File
@@ -12,6 +12,7 @@ pub mod shared;
use crate::shared::create_camera;
use rg3d::core::futures;
use rg3d::engine::resource_manager::MaterialSearchOptions;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
core::{
algebra::{UnitQuaternion, Vector2, Vector3},
@@ -535,7 +536,12 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
engine.renderer.set_frame_size(size.into());
if let Err(e) = engine.renderer.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
+4 -1
View File
@@ -11,6 +11,7 @@ pub mod shared;
use crate::shared::create_camera;
use rg3d::core::algebra::{UnitQuaternion, Vector3};
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
core::{color::Color, pool::Handle},
engine::resource_manager::{MaterialSearchOptions, ResourceManager},
@@ -247,7 +248,9 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
engine.renderer.set_frame_size(size.into());
if let Err(e) = engine.renderer.set_frame_size(size.into()) {
Log::writeln(MessageKind::Error, format!("Unable to set frame size: {:?}", e));
}
}
WindowEvent::KeyboardInput { input, .. } => {
if let Some(key_code) = input.virtual_keycode {
+7 -1
View File
@@ -10,6 +10,7 @@ pub mod shared;
use crate::shared::create_camera;
use rg3d::engine::resource_manager::MaterialSearchOptions;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
core::{
algebra::{Matrix4, UnitQuaternion, Vector2, Vector3},
@@ -285,7 +286,12 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
engine.renderer.set_frame_size(size.into());
if let Err(e) = engine.renderer.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
+4 -1
View File
@@ -21,6 +21,7 @@ pub mod shared;
use crate::shared::{create_ui, fix_shadows_distance, Game, GameScene, LocomotionMachine, Player};
use rg3d::core::algebra::Vector2;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
core::{
visitor::VisitResult,
@@ -255,7 +256,9 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
game.engine.renderer.set_frame_size(size.into());
if let Err(e) = game.engine.renderer.set_frame_size(size.into()) {
Log::writeln(MessageKind::Error, format!("Unable to set frame size: {:?}", e));
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
+7 -1
View File
@@ -13,6 +13,7 @@ pub mod shared;
use crate::shared::create_camera;
use rg3d::engine::resource_manager::MaterialSearchOptions;
use rg3d::scene::base::BaseBuilder;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
core::{
algebra::{UnitQuaternion, Vector3},
@@ -190,7 +191,12 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
engine.renderer.set_frame_size(size.into());
if let Err(e) = engine.renderer.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { input, .. } => {
// Handle key input events via `WindowEvent`, not via `DeviceEvent` (#32)
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

+4 -1
View File
@@ -12,6 +12,7 @@ pub mod shared;
use crate::shared::{create_ui, fix_shadows_distance, Game, GameScene};
use rg3d::core::algebra::Vector2;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
animation::AnimationSignal,
event::{Event, VirtualKeyCode, WindowEvent},
@@ -232,7 +233,9 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
game.engine.renderer.set_frame_size(size.into());
if let Err(e) = game.engine.renderer.set_frame_size(size.into()) {
Log::writeln(MessageKind::Error, format!("Unable to set frame size: {:?}", e));
}
// Root UI node should be resized too, otherwise progress bar will stay
// in wrong position after resize.
+14 -3
View File
@@ -12,6 +12,7 @@ pub mod shared;
use crate::shared::create_camera;
use rg3d::engine::resource_manager::MaterialSearchOptions;
use rg3d::utils::log::{Log, MessageKind};
use rg3d::{
animation::Animation,
core::{
@@ -472,10 +473,15 @@ fn main() {
));
// Due to some weird bug in winit it does not send Resized event.
engine.renderer.set_frame_size((
if let Err(e) = engine.renderer.set_frame_size((
video_mode.size().width,
video_mode.size().height,
));
)) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
}
}
@@ -498,7 +504,12 @@ fn main() {
// It is very important to handle Resized event from window, because
// renderer knows nothing about window size - it must be notified
// directly when window size has changed.
engine.renderer.set_frame_size(size.into());
if let Err(e) = engine.renderer.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
WindowEvent::KeyboardInput { input, .. } => {
if let Some(key_code) = input.virtual_keycode {
+24
View File
@@ -192,6 +192,30 @@ impl Color {
Self { r, g, b, a }
}
#[must_use]
pub fn srgb_to_linear(self) -> Self {
let r = ((self.r as f32 / 255.0).powf(2.2).clamp(0.0, 1.0) * 255.0) as u8;
let g = ((self.g as f32 / 255.0).powf(2.2).clamp(0.0, 1.0) * 255.0) as u8;
let b = ((self.b as f32 / 255.0).powf(2.2).clamp(0.0, 1.0) * 255.0) as u8;
Self::from_rgba(r, g, b, self.a)
}
#[must_use]
pub fn srgb_to_linear_f32(self) -> Vector4<f32> {
let r = (self.r as f32 / 255.0).powf(2.2).clamp(0.0, 1.0);
let g = (self.g as f32 / 255.0).powf(2.2).clamp(0.0, 1.0);
let b = (self.b as f32 / 255.0).powf(2.2).clamp(0.0, 1.0);
Vector4::new(r, g, b, self.a as f32 / 255.0)
}
#[must_use]
pub fn linear_to_srgb(self) -> Self {
let r = ((self.r as f32 / 255.0).powf(1.0 / 2.2).clamp(0.0, 1.0) * 255.0) as u8;
let g = ((self.g as f32 / 255.0).powf(1.0 / 2.2).clamp(0.0, 1.0) * 255.0) as u8;
let b = ((self.b as f32 / 255.0).powf(1.0 / 2.2).clamp(0.0, 1.0) * 255.0) as u8;
Self::from_rgba(r, g, b, self.a)
}
pub fn as_frgba(self) -> Vector4<f32> {
Vector4::new(
f32::from(self.r) / 255.0,
+7 -1
View File
@@ -4,6 +4,7 @@
//! Once you get familiar with the engine, you should **not** use the framework because it is too
//! limiting and may slow you down.
use crate::utils::log::{Log, MessageKind};
use crate::{
core::instant::Instant,
engine::{error::EngineError, Engine},
@@ -134,7 +135,12 @@ impl<State: GameState> Framework<State> {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(size) => {
engine.renderer.set_frame_size(size.into());
if let Err(e) = engine.renderer.set_frame_size(size.into()) {
Log::writeln(
MessageKind::Error,
format!("Unable to set frame size: {:?}", e),
);
}
}
_ => (),
}
+4 -4
View File
@@ -569,7 +569,7 @@ impl DeferredLightRenderer {
|program_binding| {
program_binding
.set_matrix4(&self.ambient_light_shader.wvp_matrix, &frame_matrix)
.set_color(&self.ambient_light_shader.ambient_color, &ambient_color)
.set_linear_color(&self.ambient_light_shader.ambient_color, &ambient_color)
.set_texture(
&self.ambient_light_shader.diffuse_texture,
&gbuffer_diffuse_map,
@@ -833,7 +833,7 @@ impl DeferredLightRenderer {
.set_vector3(&shader.light_direction, &emit_direction)
.set_f32(&shader.light_radius, light_radius)
.set_matrix4(&shader.inv_view_proj_matrix, &inv_view_projection)
.set_color(&shader.light_color, &light.color())
.set_linear_color(&shader.light_color, &light.color())
.set_f32(
&shader.half_hotspot_cone_angle_cos,
(spot_light.hotspot_cone_angle() * 0.5).cos(),
@@ -880,7 +880,7 @@ impl DeferredLightRenderer {
.set_vector3(&shader.light_position, &light_position)
.set_f32(&shader.light_radius, light_radius)
.set_matrix4(&shader.inv_view_proj_matrix, &inv_view_projection)
.set_color(&shader.light_color, &light.color())
.set_linear_color(&shader.light_color, &light.color())
.set_matrix4(&shader.wvp_matrix, &frame_matrix)
.set_vector3(&shader.camera_position, &camera_global_position)
.set_f32(&shader.shadow_bias, point_light.shadow_bias())
@@ -919,7 +919,7 @@ impl DeferredLightRenderer {
program_binding
.set_vector3(&shader.light_direction, &emit_direction)
.set_matrix4(&shader.inv_view_proj_matrix, &inv_view_projection)
.set_color(&shader.light_color, &light.color())
.set_linear_color(&shader.light_color, &light.color())
.set_matrix4(&shader.wvp_matrix, &frame_matrix)
.set_vector3(&shader.camera_position, &camera_global_position)
.set_texture(&shader.depth_sampler, &gbuffer_depth_map)
+1 -1
View File
@@ -124,7 +124,7 @@ impl ForwardRenderer {
&(view_projection * instance.world_transform),
)
.set_bool(&self.shader.use_skeletal_animation, batch.is_skinned)
.set_color(&self.shader.color, &instance.color)
.set_linear_color(&self.shader.color, &instance.color)
.set_matrix4_array(
&self.shader.bone_matrices,
instance.bone_matrices.as_slice(),
+12 -1
View File
@@ -251,7 +251,18 @@ impl<'a> GpuProgramBinding<'a> {
}
#[inline(always)]
pub fn set_color(self, location: &UniformLocation, value: &Color) -> Self {
pub fn set_linear_color(self, location: &UniformLocation, value: &Color) -> Self {
unsafe {
let srgb_a = value.srgb_to_linear_f32();
self.state
.gl
.uniform_4_f32(Some(&location.id), srgb_a.x, srgb_a.y, srgb_a.z, srgb_a.w);
}
self
}
#[inline(always)]
pub fn set_srgb_color(self, location: &UniformLocation, value: &Color) -> Self {
unsafe {
let rgba = value.as_frgba();
self.state
+56 -28
View File
@@ -75,7 +75,9 @@ pub enum PixelKind {
D16,
D24S8,
RGBA8,
SRGBA8,
RGB8,
SRGB8,
BGRA8,
BGR8,
RG8,
@@ -92,6 +94,8 @@ pub enum PixelKind {
RGBA32F,
R8RGTC,
RG8RGTC,
R11G11B10F,
RGB10A2,
}
impl From<TexturePixelKind> for PixelKind {
@@ -129,14 +133,18 @@ impl PixelKind {
match self {
Self::RGBA16 | Self::RGB16 | Self::RGBA32F => 8,
Self::RGBA8
| Self::SRGBA8
| Self::RGB8
| Self::SRGB8
| Self::BGRA8
| Self::BGR8
| Self::RG16
| Self::R16
| Self::D24S8
| Self::D32F
| Self::F32 => 4,
| Self::F32
| Self::R11G11B10F
| Self::RGB10A2 => 4,
Self::RG8 | Self::D16 | Self::F16 => 2,
Self::R8 | Self::R8UI => 1,
Self::DXT1RGB
@@ -161,7 +169,9 @@ impl PixelKind {
Self::RGBA16
| Self::RGB16
| Self::RGBA8
| Self::SRGBA8
| Self::RGB8
| Self::SRGB8
| Self::BGRA8
| Self::BGR8
| Self::RG16
@@ -174,34 +184,39 @@ impl PixelKind {
| Self::F16
| Self::R8
| Self::R8UI
| Self::RGBA32F => false,
| Self::RGBA32F
| Self::R11G11B10F
| Self::RGB10A2 => false,
}
}
pub fn element_kind(self) -> PixelElementKind {
match self {
PixelKind::F32 | PixelKind::F16 | PixelKind::RGBA32F | PixelKind::D32F => {
Self::F32 | Self::F16 | Self::RGBA32F | Self::D32F | Self::R11G11B10F => {
PixelElementKind::Float
}
PixelKind::D16
| PixelKind::D24S8
| PixelKind::RGBA8
| PixelKind::RGB8
| PixelKind::BGRA8
| PixelKind::BGR8
| PixelKind::RG8
| PixelKind::RG16
| PixelKind::R8
| PixelKind::R16
| PixelKind::RGB16
| PixelKind::RGBA16
| PixelKind::DXT1RGB
| PixelKind::DXT1RGBA
| PixelKind::DXT3RGBA
| PixelKind::DXT5RGBA
| PixelKind::R8RGTC
| PixelKind::RG8RGTC => PixelElementKind::NormalizedUnsignedInteger,
PixelKind::R8UI => PixelElementKind::UnsignedInteger,
Self::D16
| Self::D24S8
| Self::RGBA8
| Self::SRGBA8
| Self::RGB8
| Self::SRGB8
| Self::BGRA8
| Self::BGR8
| Self::RG8
| Self::RG16
| Self::R8
| Self::R16
| Self::RGB16
| Self::RGBA16
| Self::DXT1RGB
| Self::DXT1RGBA
| Self::DXT3RGBA
| Self::DXT5RGBA
| Self::R8RGTC
| Self::RG8RGTC
| Self::RGB10A2 => PixelElementKind::NormalizedUnsignedInteger,
Self::R8UI => PixelElementKind::UnsignedInteger,
}
}
}
@@ -232,12 +247,15 @@ fn image_3d_size_bytes(pixel_kind: PixelKind, width: usize, height: usize, depth
PixelKind::RGBA16 => 8 * pixel_count,
PixelKind::RGB16 => 6 * pixel_count,
PixelKind::RGBA8
| PixelKind::SRGBA8
| PixelKind::BGRA8
| PixelKind::RG16
| PixelKind::D24S8
| PixelKind::D32F
| PixelKind::F32 => 4 * pixel_count,
PixelKind::RGB8 | PixelKind::BGR8 => 3 * pixel_count,
| PixelKind::F32
| PixelKind::R11G11B10F
| PixelKind::RGB10A2 => 4 * pixel_count,
PixelKind::RGB8 | PixelKind::SRGB8 | PixelKind::BGR8 => 3 * pixel_count,
PixelKind::RG8 | PixelKind::R16 | PixelKind::D16 | PixelKind::F16 => 2 * pixel_count,
PixelKind::R8 | PixelKind::R8UI => pixel_count,
PixelKind::DXT1RGB | PixelKind::DXT1RGBA | PixelKind::R8RGTC => {
@@ -258,12 +276,15 @@ fn image_2d_size_bytes(pixel_kind: PixelKind, width: usize, height: usize) -> us
PixelKind::RGBA16 => 8 * pixel_count,
PixelKind::RGB16 => 6 * pixel_count,
PixelKind::RGBA8
| PixelKind::SRGBA8
| PixelKind::BGRA8
| PixelKind::RG16
| PixelKind::D24S8
| PixelKind::D32F
| PixelKind::F32 => 4 * pixel_count,
PixelKind::RGB8 | PixelKind::BGR8 => 3 * pixel_count,
| PixelKind::F32
| PixelKind::R11G11B10F
| PixelKind::RGB10A2 => 4 * pixel_count,
PixelKind::RGB8 | PixelKind::SRGB8 | PixelKind::BGR8 => 3 * pixel_count,
PixelKind::RG8 | PixelKind::R16 | PixelKind::D16 | PixelKind::F16 => 2 * pixel_count,
PixelKind::R8 | PixelKind::R8UI => pixel_count,
PixelKind::DXT1RGB | PixelKind::DXT1RGBA | PixelKind::R8RGTC => {
@@ -283,12 +304,15 @@ fn image_1d_size_bytes(pixel_kind: PixelKind, length: usize) -> usize {
PixelKind::RGBA16 => 8 * length,
PixelKind::RGB16 => 6 * length,
PixelKind::RGBA8
| PixelKind::SRGBA8
| PixelKind::BGRA8
| PixelKind::RG16
| PixelKind::D24S8
| PixelKind::D32F
| PixelKind::F32 => 4 * length,
PixelKind::RGB8 | PixelKind::BGR8 => 3 * length,
| PixelKind::F32
| PixelKind::R11G11B10F
| PixelKind::RGB10A2 => 4 * length,
PixelKind::RGB8 | PixelKind::SRGB8 | PixelKind::BGR8 => 3 * length,
PixelKind::RG8 | PixelKind::R16 | PixelKind::D16 | PixelKind::F16 => 2 * length,
PixelKind::R8 | PixelKind::R8UI => length,
PixelKind::DXT1RGB | PixelKind::DXT1RGBA | PixelKind::R8RGTC => {
@@ -612,7 +636,9 @@ impl<'a> TextureBinding<'a> {
glow::DEPTH24_STENCIL8,
),
PixelKind::RGBA8 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::RGBA8),
PixelKind::SRGBA8 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::SRGB8_ALPHA8),
PixelKind::RGB8 => (glow::UNSIGNED_BYTE, glow::RGB, glow::RGB8),
PixelKind::SRGB8 => (glow::UNSIGNED_BYTE, glow::RGB, glow::SRGB8),
PixelKind::RG8 => (glow::UNSIGNED_BYTE, glow::RG, glow::RG8),
PixelKind::R8 => (glow::UNSIGNED_BYTE, glow::RED, glow::R8),
PixelKind::R8UI => (glow::UNSIGNED_BYTE, glow::RED_INTEGER, glow::R8UI),
@@ -622,6 +648,7 @@ impl<'a> TextureBinding<'a> {
PixelKind::R16 => (glow::UNSIGNED_SHORT, glow::RED, glow::R16),
PixelKind::RGB16 => (glow::UNSIGNED_SHORT, glow::RGB, glow::RGB16),
PixelKind::RGBA16 => (glow::UNSIGNED_SHORT, glow::RGBA, glow::RGBA16),
PixelKind::RGB10A2 => (glow::UNSIGNED_BYTE, glow::RGBA, glow::RGB10_A2),
PixelKind::DXT1RGB => (0, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT),
PixelKind::DXT1RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT),
PixelKind::DXT3RGBA => (0, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT),
@@ -629,6 +656,7 @@ impl<'a> TextureBinding<'a> {
PixelKind::R8RGTC => (0, 0, COMPRESSED_RED_RGTC1),
PixelKind::RG8RGTC => (0, 0, COMPRESSED_RG_RGTC2),
PixelKind::RGBA32F => (glow::FLOAT, glow::RGBA, glow::RGBA32F),
PixelKind::R11G11B10F => (glow::FLOAT, glow::RGB, glow::R11F_G11F_B10F),
};
let is_compressed = pixel_kind.is_compressed();
@@ -287,4 +287,14 @@ vec2 S_ComputeParallaxTextureCoordinates(in sampler2D heightTexture, vec3 eyeVec
float weight = nextH / (nextH - prevH);
return prev * weight + currentTexCoords * (1.0 - weight);
}
vec4 S_LinearToSRGB(vec4 color) {
vec3 rgb = pow(color.rgb, vec3(1.0 / 2.2));
return vec4(rgb, color.a);
}
vec4 S_SRGBToLinear(vec4 color) {
vec3 rgb = pow(color.rgb, vec3(2.2));
return vec4(rgb, color.a);
}
+101
View File
@@ -0,0 +1,101 @@
use crate::renderer::framework::framebuffer::FrameBuffer;
use crate::{
core::{
algebra::{Matrix4, Vector3},
math::Rect,
},
renderer::framework::{
error::FrameworkError,
framebuffer::{CullFace, DrawParameters},
gpu_program::{GpuProgram, UniformLocation},
gpu_texture::GpuTexture,
state::PipelineState,
},
renderer::{GeometryCache, RenderPassStatistics},
scene::mesh::surface::SurfaceData,
};
use std::{cell::RefCell, rc::Rc};
struct Shader {
pub program: GpuProgram,
pub wvp_matrix: UniformLocation,
pub screen_texture: UniformLocation,
}
impl Shader {
pub fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
let fragment_source = include_str!("shaders/gamma_fs.glsl");
let vertex_source = include_str!("shaders/flat_vs.glsl");
let program =
GpuProgram::from_source(state, "GammaShader", vertex_source, fragment_source)?;
Ok(Self {
wvp_matrix: program.uniform_location(state, "worldViewProjection")?,
screen_texture: program.uniform_location(state, "screenTexture")?,
program,
})
}
}
pub struct GammaCorrectionPass {
shader: Shader,
quad: SurfaceData,
}
impl GammaCorrectionPass {
pub fn new(state: &mut PipelineState) -> Result<Self, FrameworkError> {
Ok(Self {
shader: Shader::new(state)?,
quad: SurfaceData::make_unit_xy_quad(),
})
}
pub(in crate) fn render(
&self,
state: &mut PipelineState,
viewport: Rect<i32>,
frame_texture: Rc<RefCell<GpuTexture>>,
frame_buffer: &mut FrameBuffer,
geom_cache: &mut GeometryCache,
) -> RenderPassStatistics {
let mut statistics = RenderPassStatistics::default();
let quad = geom_cache.get(state, &self.quad);
let frame_matrix = Matrix4::new_orthographic(
0.0,
viewport.w() as f32,
viewport.h() as f32,
0.0,
-1.0,
1.0,
) * Matrix4::new_nonuniform_scaling(&Vector3::new(
viewport.w() as f32,
viewport.h() as f32,
0.0,
));
statistics += frame_buffer.draw(
quad,
state,
viewport,
&self.shader.program,
&DrawParameters {
cull_face: CullFace::Back,
culling: false,
color_write: Default::default(),
depth_write: false,
stencil_test: false,
depth_test: false,
blend: false,
},
|program_binding| {
program_binding
.set_matrix4(&self.shader.wvp_matrix, &frame_matrix)
.set_texture(&self.shader.screen_texture, &frame_texture);
},
);
statistics
}
}
+14 -4
View File
@@ -1,3 +1,10 @@
//! GBuffer Layout:
//!
//! RT0: sRGBA8 - Diffuse color.
//! RT1: RGBA8 - Normal (xyz) + specular (w)
//! RT2: RGBA8 - Ambient light + emission
//! RT3: R8UI - Decal mask
use crate::{
core::{
algebra::{Matrix4, Vector2, Vector4},
@@ -87,7 +94,7 @@ impl GBuffer {
let mut diffuse_texture = GpuTexture::new(
state,
GpuTextureKind::Rectangle { width, height },
PixelKind::RGBA8,
PixelKind::SRGBA8,
MinificationFilter::Nearest,
MagnificationFilter::Nearest,
1,
@@ -187,7 +194,7 @@ impl GBuffer {
let frame_texture = GpuTexture::new(
state,
GpuTextureKind::Rectangle { width, height },
PixelKind::RGBA8,
PixelKind::RGB10A2,
MinificationFilter::Linear,
MagnificationFilter::Linear,
1,
@@ -434,7 +441,10 @@ impl GBuffer {
initial_view_projection
};
program_binding
.set_color(shader.diffuse_color.as_ref().unwrap(), &instance.color)
.set_linear_color(
shader.diffuse_color.as_ref().unwrap(),
&instance.color,
)
.set_matrix4(
shader.wvp_matrix.as_ref().unwrap(),
&(view_projection * instance.world_transform),
@@ -535,7 +545,7 @@ impl GBuffer {
.set_texture(&shader.normal_texture, &normal_texture)
.set_texture(&shader.decal_mask, &decal_mask)
.set_u32(&shader.layer_index, decal.layer() as u32)
.set_color(&shader.color, &decal.color());
.set_linear_color(&shader.color, &decal.color());
},
);
}
+8 -2
View File
@@ -240,7 +240,10 @@ impl LightVolumeRenderer {
.set_vector3(&shader.light_position, &position)
.set_vector3(&shader.light_direction, &direction)
.set_texture(&shader.depth_sampler, &depth_map)
.set_vector3(&shader.light_color, &light.color().as_frgba().xyz())
.set_vector3(
&shader.light_color,
&light.color().srgb_to_linear_f32().xyz(),
)
.set_vector3(&shader.scatter_factor, &light.scatter());
},
)
@@ -321,7 +324,10 @@ impl LightVolumeRenderer {
.set_vector3(&shader.light_position, &position)
.set_texture(&shader.depth_sampler, &depth_map)
.set_f32(&shader.light_radius, point.radius())
.set_vector3(&shader.light_color, &light.color().as_frgba().xyz())
.set_vector3(
&shader.light_color,
&light.color().srgb_to_linear_f32().xyz(),
)
.set_vector3(&shader.scatter_factor, &light.scatter());
},
)
+112 -8
View File
@@ -19,6 +19,7 @@ mod deferred_light_renderer;
mod flat_shader;
mod forward_renderer;
mod fxaa;
mod gamma;
mod gbuffer;
mod light_volume;
mod particle_system_renderer;
@@ -29,6 +30,7 @@ mod ssao;
mod ui_renderer;
use crate::renderer::cache::CacheEntry;
use crate::renderer::gamma::GammaCorrectionPass;
use crate::scene::camera::Camera;
use crate::{
core::{
@@ -416,6 +418,71 @@ impl TextureUploadSender {
}
}
struct PostProcessingChain {
framebuffers: [FrameBuffer; 2],
swap: bool,
}
struct PostProcessingPair<'a> {
screen_texture: Rc<RefCell<GpuTexture>>,
framebuffer: &'a mut FrameBuffer,
}
impl PostProcessingChain {
fn new(state: &mut PipelineState, width: usize, height: usize) -> Result<Self, FrameworkError> {
fn make_framebuffer(
state: &mut PipelineState,
width: usize,
height: usize,
) -> Result<FrameBuffer, FrameworkError> {
let frame_texture = GpuTexture::new(
state,
GpuTextureKind::Rectangle { width, height },
PixelKind::RGB10A2,
MinificationFilter::Nearest,
MagnificationFilter::Nearest,
1,
None,
)?;
FrameBuffer::new(
state,
None,
vec![Attachment {
kind: AttachmentKind::Color,
texture: Rc::new(RefCell::new(frame_texture)),
}],
)
}
Ok(Self {
framebuffers: [
make_framebuffer(state, width, height)?,
make_framebuffer(state, width, height)?,
],
swap: false,
})
}
fn begin_pass(&mut self) -> PostProcessingPair {
let out = if self.swap {
PostProcessingPair {
screen_texture: self.framebuffers[0].color_attachments()[0].texture.clone(),
framebuffer: &mut self.framebuffers[1],
}
} else {
PostProcessingPair {
screen_texture: self.framebuffers[1].color_attachments()[0].texture.clone(),
framebuffer: &mut self.framebuffers[0],
}
};
self.swap = !self.swap;
out
}
}
/// See module docs.
pub struct Renderer {
backbuffer: FrameBuffer,
@@ -453,6 +520,8 @@ pub struct Renderer {
renderer2d: Renderer2d,
texture_upload_receiver: Receiver<Texture>,
texture_upload_sender: Sender<Texture>,
post_processing_chain: PostProcessingChain,
gamma_correction_pass: GammaCorrectionPass,
// TextureId -> FrameBuffer mapping. This mapping is used for temporal frame buffers
// like ones used to render UI instances.
ui_frame_buffers: HashMap<usize, FrameBuffer>,
@@ -694,6 +763,12 @@ impl Renderer {
fxaa_renderer: FxaaRenderer::new(&mut state)?,
statistics: Statistics::default(),
renderer2d: Renderer2d::new(&mut state)?,
gamma_correction_pass: GammaCorrectionPass::new(&mut state)?,
post_processing_chain: PostProcessingChain::new(
&mut state,
frame_size.0 as usize,
frame_size.1 as usize,
)?,
texture_upload_receiver,
texture_upload_sender,
state,
@@ -738,12 +813,20 @@ impl Renderer {
///
/// Input values will be set to 1 pixel if new size is 0. Rendering cannot
/// be performed into 0x0 texture.
pub fn set_frame_size(&mut self, new_size: (u32, u32)) {
self.deferred_light_renderer
.set_frame_size(&mut self.state, new_size)
.unwrap();
pub fn set_frame_size(&mut self, new_size: (u32, u32)) -> Result<(), FrameworkError> {
self.frame_size.0 = new_size.0.max(1);
self.frame_size.1 = new_size.1.max(1);
self.deferred_light_renderer
.set_frame_size(&mut self.state, new_size)?;
self.post_processing_chain = PostProcessingChain::new(
&mut self.state,
self.frame_size.0 as usize,
self.frame_size.1 as usize,
)?;
Ok(())
}
/// Returns current (width, height) pair of back buffer size.
@@ -906,6 +989,15 @@ impl Renderer {
let backbuffer_width = self.frame_size.0 as f32;
let backbuffer_height = self.frame_size.1 as f32;
let post_processing_pair = self.post_processing_chain.begin_pass();
post_processing_pair.framebuffer.clear(
&mut self.state,
window_viewport,
Some(Color::BLACK),
None,
None,
);
for (scene_handle, scene) in scenes.pair_iter().filter(|(_, s)| s.enabled) {
let graph = &scene.graph;
@@ -1086,12 +1178,12 @@ impl Renderer {
state,
viewport,
gbuffer.frame_texture(),
&mut self.backbuffer,
post_processing_pair.framebuffer,
&mut self.geometry_cache,
);
} else {
let shader = &self.flat_shader;
self.statistics.geometry += self.backbuffer.draw(
self.statistics.geometry += post_processing_pair.framebuffer.draw(
self.geometry_cache.get(state, &self.quad),
state,
viewport,
@@ -1131,14 +1223,26 @@ impl Renderer {
self.statistics += self.renderer2d.render(
&mut self.state,
&mut self.backbuffer,
post_processing_pair.framebuffer,
Vector2::new(backbuffer_width, backbuffer_height),
scenes2d,
&mut self.texture_cache,
self.white_dummy.clone(),
)?;
// Render UI on top of everything.
drop(post_processing_pair);
let post_processing_pair = self.post_processing_chain.begin_pass();
self.statistics += self.gamma_correction_pass.render(
&mut self.state,
window_viewport,
post_processing_pair.screen_texture,
&mut self.backbuffer,
&mut self.geometry_cache,
);
// Render UI on top of everything without gamma correction.
self.statistics += self.ui_renderer.render(UiRenderContext {
state: &mut self.state,
viewport: window_viewport,
+2 -2
View File
@@ -165,7 +165,7 @@ impl BatchStorage {
batch.instances.push(Instance {
gpu_data: InstanceData {
color: sprite.color(),
color: sprite.color().srgb_to_linear(),
world_matrix: sprite.global_transform()
* Matrix4::new_scaling(sprite.size()),
},
@@ -307,7 +307,7 @@ impl Renderer2d {
if viewport_f32.intersects_circle(position, radius) {
let light_num = light_count as usize;
let color = light.color().as_frgb();
let color = light.color().srgb_to_linear().as_frgb();
light_position_direction[light_num] =
Vector4::new(position.x, position.y, direction.x, direction.y);
@@ -36,5 +36,5 @@ void main()
lighting += lightColor * (distanceAttenuation * directionalAttenuation);
}
FragColor = vec4(lighting, 1.0) * color * texture(diffuseTexture, texCoord);
FragColor = vec4(lighting, 1.0) * color * S_SRGBToLinear(texture(diffuseTexture, texCoord));
}
+10
View File
@@ -0,0 +1,10 @@
#version 330 core
uniform sampler2D screenTexture;
in vec2 texCoord;
out vec4 fragColor;
void main() {
fragColor = S_LinearToSRGB(texture(screenTexture, texCoord));
}
+1 -1
View File
@@ -22,6 +22,6 @@ void main()
float sceneDepth = toProjSpace(texture(depthBufferTexture, gl_FragCoord.xy * invScreenSize).r);
float fragmentDepth = toProjSpace(gl_FragCoord.z);
float depthOpacity = smoothstep((sceneDepth - fragmentDepth) * softBoundarySharpnessFactor, 0.0, 1.0);
FragColor = color * texture(diffuseTexture, texCoord).r;
FragColor = color * S_SRGBToLinear(texture(diffuseTexture, texCoord)).r;
FragColor.a *= depthOpacity;
}
@@ -35,5 +35,5 @@ void main()
}
}
FragColor = vec4(lightColor * scatter, 1.0);
FragColor = vec4(lightColor * pow(scatter, vec3(2.2)), 1.0);
}
+1 -1
View File
@@ -8,5 +8,5 @@ in vec3 texCoord;
void main()
{
FragColor = texture(cubemapTexture, texCoord);
FragColor = S_SRGBToLinear(texture(cubemapTexture, texCoord));
}
+1 -1
View File
@@ -59,5 +59,5 @@ void main()
}
}
FragColor = vec4(lightColor * scatter, 1.0);
FragColor = vec4(lightColor * pow(scatter, vec3(2.2)), 1.0);
}
+1 -1
View File
@@ -9,5 +9,5 @@ in vec2 texCoord;
void main()
{
FragColor = color * texture(diffuseTexture, texCoord).r;
FragColor = color * S_SRGBToLinear(texture(diffuseTexture, texCoord)).r;
}
+11 -2
View File
@@ -1,6 +1,10 @@
#version 330 core
// IMPORTANT: UI is rendered in sRGB color space!
uniform sampler2D diffuseTexture;
uniform bool isRenderTarget;
uniform bool isFont;
uniform vec4 solidColor;
uniform float opacity;
@@ -71,13 +75,18 @@ void main()
fragColor = mix(gradientColors[current], gradientColors[next], mix_factor);
}
vec4 diffuseColor = texture(diffuseTexture, texCoord);
if (isRenderTarget) {
diffuseColor = S_LinearToSRGB(diffuseColor);
}
if (isFont)
{
fragColor.a *= texture(diffuseTexture, texCoord).r;
fragColor.a *= diffuseColor.r;
}
else
{
fragColor *= texture(diffuseTexture, texCoord);
fragColor *= diffuseColor;
}
fragColor.a *= opacity;
+3 -2
View File
@@ -29,7 +29,8 @@ 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, "FlatShader", vertex_source, fragment_source)?;
let program =
GpuProgram::from_source(state, "SpriteShader", vertex_source, fragment_source)?;
Ok(Self {
view_projection_matrix: program.uniform_location(state, "viewProjectionMatrix")?,
world_matrix: program.uniform_location(state, "worldMatrix")?,
@@ -147,7 +148,7 @@ impl SpriteRenderer {
.set_vector3(&self.shader.camera_up_vector, &camera_up)
.set_vector3(&self.shader.camera_side_vector, &camera_side)
.set_f32(&self.shader.size, sprite.size())
.set_color(&self.shader.color, &sprite.color())
.set_linear_color(&self.shader.color, &sprite.color())
.set_f32(&self.shader.rotation, sprite.rotation());
},
);
+10 -4
View File
@@ -48,6 +48,7 @@ struct UiShader {
bounds_min: UniformLocation,
bounds_max: UniformLocation,
opacity: UniformLocation,
is_render_target: UniformLocation,
}
impl UiShader {
@@ -70,6 +71,7 @@ impl UiShader {
bounds_max: program.uniform_location(state, "boundsMax")?,
resolution: program.uniform_location(state, "resolution")?,
opacity: program.uniform_location(state, "opacity")?,
is_render_target: program.uniform_location(state, "isRenderTarget")?,
program,
})
}
@@ -179,6 +181,9 @@ impl UiRenderer {
for cmd in drawing_context.get_commands() {
let mut diffuse_texture = white_dummy.clone();
let mut is_font_texture = false;
// Render targets are in linear color space, so we must convert them to sRGB
// before drawing on screen.
let mut is_render_target = false;
let mut clip_bounds = cmd.clip_bounds;
clip_bounds.position.x = clip_bounds.position.x.floor();
@@ -287,9 +292,9 @@ impl UiRenderer {
}
CommandTexture::Texture(texture) => {
if let Ok(texture) = texture.clone().0.downcast::<Mutex<TextureState>>() {
if let Some(texture) =
texture_cache.get(state, &Texture(Resource::from(texture)))
{
let resource = Resource::from(texture);
is_render_target = resource.data_ref().is_render_target();
if let Some(texture) = texture_cache.get(state, &Texture(resource)) {
diffuse_texture = texture;
}
}
@@ -334,6 +339,7 @@ impl UiRenderer {
.set_vector2(&shader.bounds_min, &cmd.bounds.position)
.set_vector2(&shader.bounds_max, &bounds_max)
.set_bool(&shader.is_font, is_font_texture)
.set_bool(&shader.is_render_target, is_render_target)
.set_i32(
&shader.brush_type,
match cmd.brush {
@@ -342,7 +348,7 @@ impl UiRenderer {
Brush::RadialGradient { .. } => 2,
},
)
.set_color(
.set_srgb_color(
&shader.solid_color,
&match cmd.brush {
Brush::Solid(color) => color,
+8
View File
@@ -158,6 +158,7 @@ pub struct TextureData {
anisotropy: f32,
serialize_content: bool,
data_hash: u64,
is_render_target: bool,
}
impl ResourceData for TextureData {
@@ -223,6 +224,7 @@ impl Default for TextureData {
anisotropy: 16.0,
serialize_content: false,
data_hash: 0,
is_render_target: false,
}
}
}
@@ -254,6 +256,7 @@ impl Texture {
anisotropy: 1.0,
serialize_content: false,
data_hash: 0,
is_render_target: true,
})))
}
@@ -966,6 +969,11 @@ impl TextureData {
&self.bytes
}
/// Returns true if the texture is used as render target.
pub fn is_render_target(&self) -> bool {
self.is_render_target
}
/// Max samples for anisotropic filtering. Default value is 16.0 (max).
/// However real value passed to GPU will be clamped to maximum supported
/// by current GPU. To disable anisotropic filtering set this to 1.0.
+6
View File
@@ -235,6 +235,12 @@ impl BaseLight {
self.scatter
}
/// Returns current scatter factor in linear color space.
#[inline]
pub fn scatter_linear(&self) -> Vector3<f32> {
self.scatter.map(|v| v.powf(2.2))
}
/// Enables or disables light scattering.
#[inline]
pub fn enable_scatter(&mut self, state: bool) {
+6 -4
View File
@@ -334,12 +334,14 @@ impl ParticleSystem {
for (i, particle_index) in sorted_particles.iter().enumerate() {
let particle = self.particles.get(*particle_index as usize).unwrap();
let linear_color = particle.color.srgb_to_linear();
draw_data.vertices.push(Vertex {
position: particle.position,
tex_coord: Vector2::default(),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
color: linear_color,
});
draw_data.vertices.push(Vertex {
@@ -347,7 +349,7 @@ impl ParticleSystem {
tex_coord: Vector2::new(1.0, 0.0),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
color: linear_color,
});
draw_data.vertices.push(Vertex {
@@ -355,7 +357,7 @@ impl ParticleSystem {
tex_coord: Vector2::new(1.0, 1.0),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
color: linear_color,
});
draw_data.vertices.push(Vertex {
@@ -363,7 +365,7 @@ impl ParticleSystem {
tex_coord: Vector2::new(0.0, 1.0),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
color: linear_color,
});
let base_index = (i * 4) as u32;
+3 -3
View File
@@ -277,7 +277,7 @@ impl Lightmap {
.up_vector()
.try_normalize(std::f32::EPSILON)
.unwrap_or_else(Vector3::y),
color: light.color().as_frgb(),
color: light.color().srgb_to_linear().as_frgb(),
}))
}
Light::Spot(spot) => lights.push(LightDefinition::Spot(SpotLightDefinition {
@@ -285,7 +285,7 @@ impl Lightmap {
intensity: 1.0,
edge0: ((spot.hotspot_cone_angle() + spot.falloff_angle_delta()) * 0.5).cos(),
edge1: (spot.hotspot_cone_angle() * 0.5).cos(),
color: light.color().as_frgb(),
color: light.color().srgb_to_linear().as_frgb(),
direction: light
.up_vector()
.try_normalize(std::f32::EPSILON)
@@ -298,7 +298,7 @@ impl Lightmap {
handle,
intensity: 1.0,
position: light.global_position(),
color: light.color().as_frgb(),
color: light.color().srgb_to_linear().as_frgb(),
radius: point.radius(),
sqr_radius: point.radius() * point.radius(),
})),