Gamma correct image scaling (#7368)

Fixes the gamma error noted in #7367 for both Metal and OpenGL by using
sRGB image formats for the textures.

This branch also includes the commits from #7367, so it'd probably be
best to review and merge that first.
This commit is contained in:
Mitchell Hashimoto
2025-05-15 15:36:12 -07:00
committed by GitHub
6 changed files with 31 additions and 10 deletions

View File

@@ -70,6 +70,9 @@ pub const InternalFormat = enum(c_int) {
rgb = c.GL_RGB,
rgba = c.GL_RGBA,
srgb = c.GL_SRGB,
srgba = c.GL_SRGB_ALPHA,
// There are so many more that I haven't filled in.
_,
};

View File

@@ -96,6 +96,7 @@ pub const MTLVertexStepFunction = enum(c_ulong) {
pub const MTLPixelFormat = enum(c_ulong) {
r8unorm = 10,
rgba8unorm = 70,
rgba8unorm_srgb = 71,
rgba8uint = 73,
bgra8unorm = 80,
bgra8unorm_srgb = 81,

View File

@@ -441,7 +441,7 @@ pub const Image = union(enum) {
};
// Set our properties
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.rgba8unorm));
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.rgba8unorm_srgb));
desc.setProperty("width", @as(c_ulong, @intCast(p.width)));
desc.setProperty("height", @as(c_ulong, @intCast(p.height)));

View File

@@ -368,8 +368,8 @@ pub const Image = union(enum) {
internal: gl.Texture.InternalFormat,
format: gl.Texture.Format,
} = switch (self.*) {
.pending_rgb, .replace_rgb => .{ .internal = .rgb, .format = .rgb },
.pending_rgba, .replace_rgba => .{ .internal = .rgba, .format = .rgba },
.pending_rgb, .replace_rgb => .{ .internal = .srgb, .format = .rgb },
.pending_rgba, .replace_rgba => .{ .internal = .srgba, .format = .rgba },
else => unreachable,
};

View File

@@ -668,12 +668,12 @@ fragment float4 image_fragment(
float4 rgba = image.sample(textureSampler, in.tex_coord);
return load_color(
uchar4(rgba * 255.0),
// We assume all images are sRGB regardless of the configured colorspace
// TODO: Maybe support wide gamut images?
false,
uniforms.use_linear_blending
);
if (!uniforms.use_linear_blending) {
rgba = unlinearize(rgba);
}
rgba.rgb *= rgba.a;
return rgba;
}

View File

@@ -6,7 +6,24 @@ layout(location = 0) out vec4 out_FragColor;
uniform sampler2D image;
// Converts a color from linear to sRGB gamma encoding.
vec4 unlinearize(vec4 linear) {
bvec3 cutoff = lessThan(linear.rgb, vec3(0.0031308));
vec3 higher = pow(linear.rgb, vec3(1.0/2.4)) * vec3(1.055) - vec3(0.055);
vec3 lower = linear.rgb * vec3(12.92);
return vec4(mix(higher, lower, cutoff), linear.a);
}
void main() {
vec4 color = texture(image, tex_coord);
// Our texture is stored with an sRGB internal format,
// which means that the values are linearized when we
// sample the texture, but for now we actually want to
// output the color with gamma compression, so we do
// that.
color = unlinearize(color);
out_FragColor = vec4(color.rgb * color.a, color.a);
}