mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
Improve text redering and do all color operation in gamma space (#2071)
This commit is contained in:
@@ -15,7 +15,6 @@ pub mod painter;
|
||||
pub use glow;
|
||||
pub use painter::{CallbackFn, Painter};
|
||||
mod misc_util;
|
||||
mod post_process;
|
||||
mod shader_version;
|
||||
mod vao;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ use memoffset::offset_of;
|
||||
|
||||
use crate::check_for_gl_error;
|
||||
use crate::misc_util::{compile_shader, link_program};
|
||||
use crate::post_process::PostProcess;
|
||||
use crate::shader_version::ShaderVersion;
|
||||
use crate::vao;
|
||||
|
||||
@@ -52,10 +51,8 @@ pub struct Painter {
|
||||
u_screen_size: glow::UniformLocation,
|
||||
u_sampler: glow::UniformLocation,
|
||||
is_webgl_1: bool,
|
||||
is_embedded: bool,
|
||||
vao: crate::vao::VertexArrayObject,
|
||||
srgb_textures: bool,
|
||||
post_process: Option<PostProcess>,
|
||||
vbo: glow::Buffer,
|
||||
element_array_buffer: glow::Buffer,
|
||||
|
||||
@@ -105,7 +102,6 @@ impl Painter {
|
||||
/// * failed to create buffer
|
||||
pub fn new(
|
||||
gl: Arc<glow::Context>,
|
||||
pp_fb_extent: Option<[i32; 2]>,
|
||||
shader_prefix: &str,
|
||||
shader_version: Option<ShaderVersion>,
|
||||
) -> Result<Painter, String> {
|
||||
@@ -115,8 +111,8 @@ impl Painter {
|
||||
let max_texture_side = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as usize;
|
||||
let shader_version = shader_version.unwrap_or_else(|| ShaderVersion::get(&gl));
|
||||
let is_webgl_1 = shader_version == ShaderVersion::Es100;
|
||||
let header = shader_version.version_declaration();
|
||||
tracing::debug!("Shader header: {:?}.", header);
|
||||
let shader_version_declaration = shader_version.version_declaration();
|
||||
tracing::debug!("Shader header: {:?}.", shader_version_declaration);
|
||||
|
||||
let supported_extensions = gl.supported_extensions();
|
||||
tracing::trace!("OpenGL extensions: {supported_extensions:?}");
|
||||
@@ -125,44 +121,17 @@ impl Painter {
|
||||
// EXT_sRGB, GL_ARB_framebuffer_sRGB, GL_EXT_sRGB, GL_EXT_texture_sRGB_decode, …
|
||||
extension.contains("sRGB")
|
||||
});
|
||||
tracing::debug!("SRGB Support: {:?}", srgb_textures);
|
||||
|
||||
let (post_process, srgb_support_define) = if shader_version.is_embedded() {
|
||||
// WebGL doesn't support linear framebuffer blending… but maybe we can emulate it with `PostProcess`?
|
||||
|
||||
if let Some(size) = pp_fb_extent {
|
||||
tracing::debug!("WebGL with sRGB support. Turning on post processing for linear framebuffer blending.");
|
||||
// install post process to correct sRGB color:
|
||||
(
|
||||
Some(unsafe { PostProcess::new(gl.clone(), shader_prefix, is_webgl_1, size)? }),
|
||||
"#define SRGB_SUPPORTED",
|
||||
)
|
||||
} else {
|
||||
tracing::warn!("WebGL or OpenGL ES detected but PostProcess disabled because dimension is None. Linear framebuffer blending will not be supported.");
|
||||
(None, "")
|
||||
}
|
||||
} else {
|
||||
if srgb_textures {
|
||||
(None, "#define SRGB_SUPPORTED")
|
||||
} else {
|
||||
tracing::warn!("sRGB aware texture filtering not available");
|
||||
(None, "")
|
||||
}
|
||||
};
|
||||
tracing::debug!("SRGB texture Support: {:?}", srgb_textures);
|
||||
|
||||
unsafe {
|
||||
let vert = compile_shader(
|
||||
&gl,
|
||||
glow::VERTEX_SHADER,
|
||||
&format!(
|
||||
"{}\n{}\n{}\n{}",
|
||||
header,
|
||||
"{}\n#define NEW_SHADER_INTERFACE {}\n{}\n{}",
|
||||
shader_version_declaration,
|
||||
shader_version.is_new_shader_interface() as i32,
|
||||
shader_prefix,
|
||||
if shader_version.is_new_shader_interface() {
|
||||
"#define NEW_SHADER_INTERFACE\n"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
VERT_SRC
|
||||
),
|
||||
)?;
|
||||
@@ -170,15 +139,11 @@ impl Painter {
|
||||
&gl,
|
||||
glow::FRAGMENT_SHADER,
|
||||
&format!(
|
||||
"{}\n{}\n{}\n{}\n{}",
|
||||
header,
|
||||
"{}\n#define NEW_SHADER_INTERFACE {}\n#define SRGB_TEXTURES {}\n{}\n{}",
|
||||
shader_version_declaration,
|
||||
shader_version.is_new_shader_interface() as i32,
|
||||
srgb_textures as i32,
|
||||
shader_prefix,
|
||||
srgb_support_define,
|
||||
if shader_version.is_new_shader_interface() {
|
||||
"#define NEW_SHADER_INTERFACE\n"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
FRAG_SRC
|
||||
),
|
||||
)?;
|
||||
@@ -236,10 +201,8 @@ impl Painter {
|
||||
u_screen_size,
|
||||
u_sampler,
|
||||
is_webgl_1,
|
||||
is_embedded: shader_version.is_embedded(),
|
||||
vao,
|
||||
srgb_textures,
|
||||
post_process,
|
||||
vbo,
|
||||
element_array_buffer,
|
||||
textures: Default::default(),
|
||||
@@ -268,8 +231,11 @@ impl Painter {
|
||||
/// So if in a [`egui::Shape::Callback`] you need to use an offscreen FBO, you should
|
||||
/// then restore to this afterwards with
|
||||
/// `gl.bind_framebuffer(glow::FRAMEBUFFER, painter.intermediate_fbo());`
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn intermediate_fbo(&self) -> Option<glow::Framebuffer> {
|
||||
self.post_process.as_ref().map(|pp| pp.fbo())
|
||||
// We don't currently ever render to an offscreen buffer,
|
||||
// but we may want to start to in order to do anti-aliasing on web, for instance.
|
||||
None
|
||||
}
|
||||
|
||||
unsafe fn prepare_painting(
|
||||
@@ -298,7 +264,7 @@ impl Painter {
|
||||
);
|
||||
|
||||
if !cfg!(target_arch = "wasm32") {
|
||||
self.gl.enable(glow::FRAMEBUFFER_SRGB);
|
||||
self.gl.disable(glow::FRAMEBUFFER_SRGB);
|
||||
check_for_gl_error!(&self.gl, "FRAMEBUFFER_SRGB");
|
||||
}
|
||||
|
||||
@@ -372,17 +338,6 @@ impl Painter {
|
||||
crate::profile_function!();
|
||||
self.assert_not_destroyed();
|
||||
|
||||
if let Some(ref mut post_process) = self.post_process {
|
||||
unsafe {
|
||||
post_process.begin(screen_size_px[0] as i32, screen_size_px[1] as i32);
|
||||
post_process.bind();
|
||||
self.gl.disable(glow::SCISSOR_TEST);
|
||||
self.gl
|
||||
.viewport(0, 0, screen_size_px[0] as i32, screen_size_px[1] as i32);
|
||||
// use the same clear-color as was set for the screen framebuffer.
|
||||
self.gl.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
let size_in_pixels = unsafe { self.prepare_painting(screen_size_px, pixels_per_point) };
|
||||
|
||||
for egui::ClippedPrimitive {
|
||||
@@ -435,12 +390,7 @@ impl Painter {
|
||||
check_for_gl_error!(&self.gl, "callback");
|
||||
|
||||
// Restore state:
|
||||
unsafe {
|
||||
if let Some(ref mut post_process) = self.post_process {
|
||||
post_process.bind();
|
||||
}
|
||||
self.prepare_painting(screen_size_px, pixels_per_point)
|
||||
};
|
||||
unsafe { self.prepare_painting(screen_size_px, pixels_per_point) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -450,10 +400,6 @@ impl Painter {
|
||||
self.vao.unbind(&self.gl);
|
||||
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||
|
||||
if let Some(ref post_process) = self.post_process {
|
||||
post_process.end();
|
||||
}
|
||||
|
||||
self.gl.disable(glow::SCISSOR_TEST);
|
||||
|
||||
check_for_gl_error!(&self.gl, "painting");
|
||||
@@ -532,13 +478,8 @@ impl Painter {
|
||||
"Mismatch between texture size and texel count"
|
||||
);
|
||||
|
||||
let gamma = if self.is_embedded && self.post_process.is_none() {
|
||||
1.0 / 2.2
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let data: Vec<u8> = image
|
||||
.srgba_pixels(gamma)
|
||||
.srgba_pixels(None)
|
||||
.flat_map(|a| a.to_array())
|
||||
.collect();
|
||||
|
||||
@@ -684,9 +625,6 @@ impl Painter {
|
||||
if !self.destroyed {
|
||||
unsafe {
|
||||
self.destroy_gl();
|
||||
if let Some(ref post_process) = self.post_process {
|
||||
post_process.destroy();
|
||||
}
|
||||
}
|
||||
self.destroyed = true;
|
||||
}
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
#![allow(unsafe_code)]
|
||||
use crate::check_for_gl_error;
|
||||
use crate::misc_util::{compile_shader, link_program};
|
||||
use crate::vao::BufferInfo;
|
||||
use glow::HasContext as _;
|
||||
|
||||
/// Uses a framebuffer to render everything in linear color space and convert it back to `sRGB`
|
||||
/// in a separate "post processing" step
|
||||
pub(crate) struct PostProcess {
|
||||
gl: std::sync::Arc<glow::Context>,
|
||||
pos_buffer: glow::Buffer,
|
||||
index_buffer: glow::Buffer,
|
||||
vao: crate::vao::VertexArrayObject,
|
||||
is_webgl_1: bool,
|
||||
color_texture: glow::Texture,
|
||||
depth_renderbuffer: Option<glow::Renderbuffer>,
|
||||
texture_size: (i32, i32),
|
||||
fbo: glow::Framebuffer,
|
||||
program: glow::Program,
|
||||
}
|
||||
|
||||
impl PostProcess {
|
||||
pub(crate) unsafe fn new(
|
||||
gl: std::sync::Arc<glow::Context>,
|
||||
shader_prefix: &str,
|
||||
is_webgl_1: bool,
|
||||
[width, height]: [i32; 2],
|
||||
) -> Result<PostProcess, String> {
|
||||
gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
|
||||
|
||||
let fbo = gl.create_framebuffer()?;
|
||||
|
||||
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo));
|
||||
|
||||
// ----------------------------------------------
|
||||
// Set up color tesxture:
|
||||
|
||||
let color_texture = gl.create_texture()?;
|
||||
gl.bind_texture(glow::TEXTURE_2D, Some(color_texture));
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_WRAP_S,
|
||||
glow::CLAMP_TO_EDGE as i32,
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_WRAP_T,
|
||||
glow::CLAMP_TO_EDGE as i32,
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_MIN_FILTER,
|
||||
glow::NEAREST as i32,
|
||||
);
|
||||
gl.tex_parameter_i32(
|
||||
glow::TEXTURE_2D,
|
||||
glow::TEXTURE_MAG_FILTER,
|
||||
glow::NEAREST as i32,
|
||||
);
|
||||
|
||||
let (internal_format, format) = if is_webgl_1 {
|
||||
(glow::SRGB_ALPHA, glow::SRGB_ALPHA)
|
||||
} else {
|
||||
(glow::SRGB8_ALPHA8, glow::RGBA)
|
||||
};
|
||||
|
||||
gl.tex_image_2d(
|
||||
glow::TEXTURE_2D,
|
||||
0,
|
||||
internal_format as i32,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format,
|
||||
glow::UNSIGNED_BYTE,
|
||||
None,
|
||||
);
|
||||
crate::check_for_gl_error_even_in_release!(&gl, "post process texture initialization");
|
||||
|
||||
gl.framebuffer_texture_2d(
|
||||
glow::FRAMEBUFFER,
|
||||
glow::COLOR_ATTACHMENT0,
|
||||
glow::TEXTURE_2D,
|
||||
Some(color_texture),
|
||||
0,
|
||||
);
|
||||
gl.bind_texture(glow::TEXTURE_2D, None);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Depth buffer - we only need this when embedding 3D within egui using `egui::PaintCallback`.
|
||||
// TODO(emilk): add a setting to enable/disable the depth buffer.
|
||||
|
||||
let with_depth_buffer = true;
|
||||
let depth_renderbuffer = if with_depth_buffer {
|
||||
let depth_renderbuffer = gl.create_renderbuffer()?;
|
||||
gl.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_renderbuffer));
|
||||
gl.renderbuffer_storage(glow::RENDERBUFFER, glow::DEPTH_COMPONENT16, width, height);
|
||||
gl.bind_renderbuffer(glow::RENDERBUFFER, None);
|
||||
Some(depth_renderbuffer)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
let vert_shader = compile_shader(
|
||||
&gl,
|
||||
glow::VERTEX_SHADER,
|
||||
&format!(
|
||||
"{}\n{}",
|
||||
shader_prefix,
|
||||
include_str!("shader/post_vertex_100es.glsl")
|
||||
),
|
||||
)?;
|
||||
let frag_shader = compile_shader(
|
||||
&gl,
|
||||
glow::FRAGMENT_SHADER,
|
||||
&format!(
|
||||
"{}\n{}",
|
||||
shader_prefix,
|
||||
include_str!("shader/post_fragment_100es.glsl")
|
||||
),
|
||||
)?;
|
||||
let program = link_program(&gl, [vert_shader, frag_shader].iter())?;
|
||||
|
||||
let positions: Vec<f32> = vec![0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
|
||||
|
||||
let indices: Vec<u8> = vec![0, 1, 2, 1, 2, 3];
|
||||
|
||||
let pos_buffer = gl.create_buffer()?;
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(pos_buffer));
|
||||
gl.buffer_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
bytemuck::cast_slice(&positions),
|
||||
glow::STATIC_DRAW,
|
||||
);
|
||||
|
||||
let a_pos_loc = gl
|
||||
.get_attrib_location(program, "a_pos")
|
||||
.ok_or_else(|| "failed to get location of a_pos".to_owned())?;
|
||||
let vao = crate::vao::VertexArrayObject::new(
|
||||
&gl,
|
||||
pos_buffer,
|
||||
vec![BufferInfo {
|
||||
location: a_pos_loc,
|
||||
vector_size: 2,
|
||||
data_type: glow::FLOAT,
|
||||
normalized: false,
|
||||
stride: 0,
|
||||
offset: 0,
|
||||
}],
|
||||
);
|
||||
|
||||
let index_buffer = gl.create_buffer()?;
|
||||
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer));
|
||||
gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, &indices, glow::STATIC_DRAW);
|
||||
|
||||
gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||
crate::check_for_gl_error_even_in_release!(&gl, "post process initialization");
|
||||
|
||||
Ok(PostProcess {
|
||||
gl,
|
||||
pos_buffer,
|
||||
index_buffer,
|
||||
vao,
|
||||
is_webgl_1,
|
||||
color_texture,
|
||||
depth_renderbuffer,
|
||||
texture_size: (width, height),
|
||||
fbo,
|
||||
program,
|
||||
})
|
||||
}
|
||||
|
||||
/// What we render to.
|
||||
pub(crate) fn fbo(&self) -> glow::Framebuffer {
|
||||
self.fbo
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn begin(&mut self, width: i32, height: i32) {
|
||||
if (width, height) != self.texture_size {
|
||||
self.gl
|
||||
.bind_texture(glow::TEXTURE_2D, Some(self.color_texture));
|
||||
self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
|
||||
let (internal_format, format) = if self.is_webgl_1 {
|
||||
(glow::SRGB_ALPHA, glow::SRGB_ALPHA)
|
||||
} else {
|
||||
(glow::SRGB8_ALPHA8, glow::RGBA)
|
||||
};
|
||||
self.gl.tex_image_2d(
|
||||
glow::TEXTURE_2D,
|
||||
0,
|
||||
internal_format as i32,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format,
|
||||
glow::UNSIGNED_BYTE,
|
||||
None,
|
||||
);
|
||||
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
||||
|
||||
if let Some(depth_renderbuffer) = self.depth_renderbuffer {
|
||||
self.gl
|
||||
.bind_renderbuffer(glow::RENDERBUFFER, Some(depth_renderbuffer));
|
||||
self.gl.renderbuffer_storage(
|
||||
glow::RENDERBUFFER,
|
||||
glow::DEPTH_COMPONENT16,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
self.gl.bind_renderbuffer(glow::RENDERBUFFER, None);
|
||||
}
|
||||
|
||||
self.texture_size = (width, height);
|
||||
}
|
||||
|
||||
check_for_gl_error!(&self.gl, "PostProcess::begin");
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn bind(&self) {
|
||||
self.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo));
|
||||
|
||||
self.gl.framebuffer_texture_2d(
|
||||
glow::FRAMEBUFFER,
|
||||
glow::COLOR_ATTACHMENT0,
|
||||
glow::TEXTURE_2D,
|
||||
Some(self.color_texture),
|
||||
0,
|
||||
);
|
||||
|
||||
self.gl.framebuffer_renderbuffer(
|
||||
glow::FRAMEBUFFER,
|
||||
glow::DEPTH_ATTACHMENT,
|
||||
glow::RENDERBUFFER,
|
||||
self.depth_renderbuffer,
|
||||
);
|
||||
|
||||
check_for_gl_error!(&self.gl, "PostProcess::bind");
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn end(&self) {
|
||||
self.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
|
||||
self.gl.disable(glow::SCISSOR_TEST);
|
||||
|
||||
self.gl.use_program(Some(self.program));
|
||||
|
||||
self.gl.active_texture(glow::TEXTURE0);
|
||||
self.gl
|
||||
.bind_texture(glow::TEXTURE_2D, Some(self.color_texture));
|
||||
let u_sampler_loc = self
|
||||
.gl
|
||||
.get_uniform_location(self.program, "u_sampler")
|
||||
.unwrap();
|
||||
self.gl.uniform_1_i32(Some(&u_sampler_loc), 0);
|
||||
self.vao.bind(&self.gl);
|
||||
|
||||
self.gl
|
||||
.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer));
|
||||
self.gl
|
||||
.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_BYTE, 0);
|
||||
self.vao.unbind(&self.gl);
|
||||
self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
|
||||
self.gl.bind_texture(glow::TEXTURE_2D, None);
|
||||
self.gl.use_program(None);
|
||||
|
||||
check_for_gl_error!(&self.gl, "PostProcess::end");
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn destroy(&self) {
|
||||
self.gl.delete_buffer(self.pos_buffer);
|
||||
self.gl.delete_buffer(self.index_buffer);
|
||||
self.gl.delete_program(self.program);
|
||||
self.gl.delete_framebuffer(self.fbo);
|
||||
self.gl.delete_texture(self.color_texture);
|
||||
if let Some(depth_renderbuffer) = self.depth_renderbuffer {
|
||||
self.gl.delete_renderbuffer(depth_renderbuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,73 +4,38 @@
|
||||
|
||||
uniform sampler2D u_sampler;
|
||||
|
||||
#ifdef NEW_SHADER_INTERFACE
|
||||
in vec4 v_rgba;
|
||||
#if NEW_SHADER_INTERFACE
|
||||
in vec4 v_rgba_in_gamma;
|
||||
in vec2 v_tc;
|
||||
out vec4 f_color;
|
||||
// a dirty hack applied to support webGL2
|
||||
#define gl_FragColor f_color
|
||||
#define texture2D texture
|
||||
#else
|
||||
varying vec4 v_rgba;
|
||||
varying vec4 v_rgba_in_gamma;
|
||||
varying vec2 v_tc;
|
||||
#endif
|
||||
|
||||
#ifdef SRGB_SUPPORTED
|
||||
void main() {
|
||||
// The texture sampler is sRGB aware, and OpenGL already expects linear rgba output
|
||||
// so no need for any sRGB conversions here:
|
||||
gl_FragColor = v_rgba * texture2D(u_sampler, v_tc);
|
||||
}
|
||||
// 0-1 sRGB gamma from 0-1 linear
|
||||
vec3 srgb_gamma_from_linear(vec3 rgb) {
|
||||
bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
|
||||
vec3 lower = rgb * vec3(12.92);
|
||||
vec3 higher = vec3(1.055) * pow(rgb, vec3(1.0 / 2.4)) - vec3(0.055);
|
||||
return mix(higher, lower, vec3(cutoff));
|
||||
}
|
||||
|
||||
// 0-1 sRGBA gamma from 0-1 linear
|
||||
vec4 srgba_gamma_from_linear(vec4 rgba) {
|
||||
return vec4(srgb_gamma_from_linear(rgba.rgb), rgba.a);
|
||||
}
|
||||
|
||||
void main() {
|
||||
#if SRGB_TEXTURES
|
||||
vec4 texture_in_gamma = srgba_gamma_from_linear(texture2D(u_sampler, v_tc));
|
||||
#else
|
||||
// 0-255 sRGB from 0-1 linear
|
||||
vec3 srgb_from_linear(vec3 rgb) {
|
||||
bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
|
||||
vec3 lower = rgb * vec3(3294.6);
|
||||
vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025);
|
||||
return mix(higher, lower, vec3(cutoff));
|
||||
}
|
||||
|
||||
vec4 srgba_from_linear(vec4 rgba) {
|
||||
return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a);
|
||||
}
|
||||
|
||||
// 0-1 linear from 0-255 sRGB
|
||||
vec3 linear_from_srgb(vec3 srgb) {
|
||||
bvec3 cutoff = lessThan(srgb, vec3(10.31475));
|
||||
vec3 lower = srgb / vec3(3294.6);
|
||||
vec3 higher = pow((srgb + vec3(14.025)) / vec3(269.025), vec3(2.4));
|
||||
return mix(higher, lower, vec3(cutoff));
|
||||
}
|
||||
|
||||
vec4 linear_from_srgba(vec4 srgba) {
|
||||
return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// We must decode the colors, since WebGL1 doesn't come with sRGBA textures:
|
||||
vec4 texture_rgba = linear_from_srgba(texture2D(u_sampler, v_tc) * 255.0);
|
||||
/// Multiply vertex color with texture color (in linear space).
|
||||
gl_FragColor = v_rgba * texture_rgba;
|
||||
|
||||
// WebGL1 doesn't support linear blending in the framebuffer,
|
||||
// so we do a hack here where we change the premultiplied alpha
|
||||
// to do the multiplication in gamma space instead:
|
||||
|
||||
// Unmultiply alpha:
|
||||
if (gl_FragColor.a > 0.0) {
|
||||
gl_FragColor.rgb /= gl_FragColor.a;
|
||||
}
|
||||
|
||||
// Empiric tweak to make e.g. shadows look more like they should:
|
||||
gl_FragColor.a *= sqrt(gl_FragColor.a);
|
||||
|
||||
// To gamma:
|
||||
gl_FragColor = srgba_from_linear(gl_FragColor) / 255.0;
|
||||
|
||||
// Premultiply alpha, this time in gamma space:
|
||||
if (gl_FragColor.a > 0.0) {
|
||||
gl_FragColor.rgb *= gl_FragColor.a;
|
||||
}
|
||||
}
|
||||
vec4 texture_in_gamma = texture2D(u_sampler, v_tc);
|
||||
#endif
|
||||
|
||||
// We multiply the colors in gamma space, because that's the only way to get text to look right.
|
||||
gl_FragColor = v_rgba_in_gamma * texture_in_gamma;
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
precision mediump float;
|
||||
uniform sampler2D u_sampler;
|
||||
varying vec2 v_tc;
|
||||
|
||||
// 0-255 sRGB from 0-1 linear
|
||||
vec3 srgb_from_linear(vec3 rgb) {
|
||||
bvec3 cutoff = lessThan(rgb, vec3(0.0031308));
|
||||
vec3 lower = rgb * vec3(3294.6);
|
||||
vec3 higher = vec3(269.025) * pow(rgb, vec3(1.0 / 2.4)) - vec3(14.025);
|
||||
return mix(higher, lower, vec3(cutoff));
|
||||
}
|
||||
|
||||
// 0-255 sRGBA from 0-1 linear
|
||||
vec4 srgba_from_linear(vec4 rgba) {
|
||||
return vec4(srgb_from_linear(rgba.rgb), 255.0 * rgba.a);
|
||||
}
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(u_sampler, v_tc);
|
||||
|
||||
gl_FragColor = srgba_from_linear(gl_FragColor) / 255.0;
|
||||
|
||||
#ifdef APPLY_BRIGHTENING_GAMMA
|
||||
gl_FragColor = vec4(pow(gl_FragColor.rgb, vec3(1.0/2.2)), gl_FragColor.a);
|
||||
#endif
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
precision mediump float;
|
||||
attribute vec2 a_pos;
|
||||
varying vec2 v_tc;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(a_pos * 2. - 1., 0.0, 1.0);
|
||||
v_tc = a_pos;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifdef NEW_SHADER_INTERFACE
|
||||
#if NEW_SHADER_INTERFACE
|
||||
#define I in
|
||||
#define O out
|
||||
#define V(x) x
|
||||
@@ -16,28 +16,15 @@ uniform vec2 u_screen_size;
|
||||
I vec2 a_pos;
|
||||
I vec4 a_srgba; // 0-255 sRGB
|
||||
I vec2 a_tc;
|
||||
O vec4 v_rgba;
|
||||
O vec4 v_rgba_in_gamma;
|
||||
O vec2 v_tc;
|
||||
|
||||
// 0-1 linear from 0-255 sRGB
|
||||
vec3 linear_from_srgb(vec3 srgb) {
|
||||
bvec3 cutoff = lessThan(srgb, vec3(10.31475));
|
||||
vec3 lower = srgb / vec3(3294.6);
|
||||
vec3 higher = pow((srgb + vec3(14.025)) / vec3(269.025), vec3(2.4));
|
||||
return mix(higher, lower, V(cutoff));
|
||||
}
|
||||
|
||||
vec4 linear_from_srgba(vec4 srgba) {
|
||||
return vec4(linear_from_srgb(srgba.rgb), srgba.a / 255.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(
|
||||
2.0 * a_pos.x / u_screen_size.x - 1.0,
|
||||
1.0 - 2.0 * a_pos.y / u_screen_size.y,
|
||||
0.0,
|
||||
1.0);
|
||||
// egui encodes vertex colors in gamma space, so we must decode the colors here:
|
||||
v_rgba = linear_from_srgba(a_srgba);
|
||||
v_rgba_in_gamma = a_srgba / 255.0;
|
||||
v_tc = a_tc;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ impl EguiGlow {
|
||||
gl: std::sync::Arc<glow::Context>,
|
||||
shader_version: Option<ShaderVersion>,
|
||||
) -> Self {
|
||||
let painter = crate::Painter::new(gl, None, "", shader_version)
|
||||
let painter = crate::Painter::new(gl, "", shader_version)
|
||||
.map_err(|error| {
|
||||
tracing::error!("error occurred in initializing painter:\n{}", error);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user