diff --git a/gl_engine/Framebuffer.cpp b/gl_engine/Framebuffer.cpp index 0e20d001..77328ddf 100644 --- a/gl_engine/Framebuffer.cpp +++ b/gl_engine/Framebuffer.cpp @@ -43,14 +43,16 @@ QOpenGLTexture::TextureFormat internal_format_qt(Framebuffer::ColourFormat f) return QOpenGLTexture::TextureFormat::RGB8_UNorm; case Framebuffer::ColourFormat::RGBA8: return QOpenGLTexture::TextureFormat::RGBA8_UNorm; + // case Framebuffer::ColourFormat::SRGBA8: + // return QOpenGLTexture::TextureFormat::SRGB8_Alpha8; case Framebuffer::ColourFormat::RG16UI: return QOpenGLTexture::TextureFormat::RG16U; - case Framebuffer::ColourFormat::Float32: - return QOpenGLTexture::TextureFormat::R32F; - case Framebuffer::ColourFormat::RGB16F: - return QOpenGLTexture::TextureFormat::RGB16F; - case Framebuffer::ColourFormat::RGBA16F: - return QOpenGLTexture::TextureFormat::RGBA16F; + // case Framebuffer::ColourFormat::Float32: + // return QOpenGLTexture::TextureFormat::R32F; + // case Framebuffer::ColourFormat::RGB16F: + // return QOpenGLTexture::TextureFormat::RGB16F; + // case Framebuffer::ColourFormat::RGBA16F: + // return QOpenGLTexture::TextureFormat::RGBA16F; case Framebuffer::ColourFormat::R32UI: return QOpenGLTexture::TextureFormat::R32U; case Framebuffer::ColourFormat::RGBA32F: @@ -69,14 +71,16 @@ GLenum format(Framebuffer::ColourFormat f) return GL_RGB; case Framebuffer::ColourFormat::RGBA8: return GL_RGBA; + // case Framebuffer::ColourFormat::SRGBA8: + // return GL_RGBA; case Framebuffer::ColourFormat::RG16UI: return QOpenGLTexture::PixelFormat::RG_Integer; - case Framebuffer::ColourFormat::Float32: // reading Float32 is inefficient, see read_colour_attachment() for details. - return GL_RED; - case Framebuffer::ColourFormat::RGB16F: - return GL_RGB; - case Framebuffer::ColourFormat::RGBA16F: - return GL_RGBA; + // case Framebuffer::ColourFormat::Float32: // reading Float32 is inefficient, see read_colour_attachment() for details. + // return GL_RED; + // case Framebuffer::ColourFormat::RGB16F: + // return GL_RGB; + // case Framebuffer::ColourFormat::RGBA16F: + // return GL_RGBA; case Framebuffer::ColourFormat::R32UI: return GL_RED_INTEGER; case Framebuffer::ColourFormat::RGBA32F: @@ -114,16 +118,17 @@ GLenum type(Framebuffer::ColourFormat f) switch (f) { case Framebuffer::ColourFormat::R8: case Framebuffer::ColourFormat::RGBA8: + // case Framebuffer::ColourFormat::SRGBA8: case Framebuffer::ColourFormat::RGB8: return GL_UNSIGNED_BYTE; case Framebuffer::ColourFormat::RG16UI: return QOpenGLTexture::PixelType::UInt16; - case Framebuffer::ColourFormat::Float32: + // case Framebuffer::ColourFormat::Float32: case Framebuffer::ColourFormat::RGBA32F: return GL_FLOAT; - case Framebuffer::ColourFormat::RGB16F: - case Framebuffer::ColourFormat::RGBA16F: - return GL_HALF_FLOAT; + // case Framebuffer::ColourFormat::RGB16F: + // case Framebuffer::ColourFormat::RGBA16F: + // return GL_HALF_FLOAT; case Framebuffer::ColourFormat::R32UI: return GL_UNSIGNED_INT; } @@ -159,8 +164,8 @@ QImage::Format qimage_format(Framebuffer::ColourFormat f) return QImage::Format_RGBA8888; case Framebuffer::ColourFormat::RGB8: return QImage::Format_RGB888; - case Framebuffer::ColourFormat::RGB16F: - return QImage::Format_RGB16; + // case Framebuffer::ColourFormat::RGB16F: + // return QImage::Format_RGB16; default: throw std::logic_error("unsupported, QImage does not support the color format of the texture"); } @@ -304,9 +309,9 @@ T Framebuffer::read_colour_attachment_pixel(unsigned int index, const glm::dvec2 case Framebuffer::ColourFormat::R8: case Framebuffer::ColourFormat::RGB8: case Framebuffer::ColourFormat::RG16UI: // unsupported on android emulator (and webassembly linux firefox?) - case Framebuffer::ColourFormat::Float32: - case Framebuffer::ColourFormat::RGB16F: - case Framebuffer::ColourFormat::RGBA16F: + // case Framebuffer::ColourFormat::Float32: + // case Framebuffer::ColourFormat::RGB16F: + // case Framebuffer::ColourFormat::RGBA16F: case Framebuffer::ColourFormat::R32UI: // fails on linux firefox // unsupported or untested. // you really should add a unit test if you move something down to the supported section @@ -314,6 +319,7 @@ T Framebuffer::read_colour_attachment_pixel(unsigned int index, const glm::dvec2 assert(false); return {}; case Framebuffer::ColourFormat::RGBA8: + // case Framebuffer::ColourFormat::SRGBA8: assert(sizeof(T) == 4); if (sizeof(T) != 4) return {}; diff --git a/gl_engine/Framebuffer.h b/gl_engine/Framebuffer.h index 79b4bad4..b63c755b 100644 --- a/gl_engine/Framebuffer.h +++ b/gl_engine/Framebuffer.h @@ -50,12 +50,13 @@ class Framebuffer R8, RGB8, RGBA8, + // SRGBA8, // not supported as a framebuffer on android RG16UI, - RGB16F, // NOT COLOR RENDERABLE ON OPENGLES - RGBA16F, // NOT COLOR RENDERABLE ON OPENGLES + // RGB16F, // NOT COLOR RENDERABLE ON OPENGLES + // RGBA16F, // NOT COLOR RENDERABLE ON OPENGLES R32UI, - Float32, // NOT COLOR RENDERABLE ON OPENGLES - RGBA32F, // NOT COLOR RENDERABLE ON OPENGLES (weirdly it works, maybe because of extension, that qt activates?) + // Float32, // NOT COLOR RENDERABLE ON OPENGLES + RGBA32F, // NOT COLOR RENDERABLE ON OPENGLES (weirdly it works, maybe because of extension, that qt activates?) }; private: diff --git a/gl_engine/Texture.cpp b/gl_engine/Texture.cpp index 50819172..3334306b 100644 --- a/gl_engine/Texture.cpp +++ b/gl_engine/Texture.cpp @@ -48,6 +48,8 @@ GlParams gl_tex_params(gl_engine::Texture::Format format) return { GLint(gl_engine::Texture::compressed_texture_format()), 0, 0, 0, 0, true }; case F::RGBA8: return { GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, 4, 1, true }; + case F::SRGBA8: + return { GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, 4, 1, true }; case F::RGBA8UI: return { GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, 4, 1 }; case F::RGBA32F: @@ -135,12 +137,13 @@ void gl_engine::Texture::upload(const nucleus::utils::ColourTexture& texture) f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); const auto width = GLsizei(texture.width()); const auto height = GLsizei(texture.height()); + const auto p = gl_tex_params(m_format); if (m_format == Format::CompressedRGBA8) { assert(m_min_filter != Filter::MipMapLinear); const auto format = gl_engine::Texture::compressed_texture_format(); f->glCompressedTexImage2D(GLenum(m_target), 0, format, width, height, 0, GLsizei(texture.n_bytes()), texture.data()); - } else if (m_format == Format::RGBA8) { - f->glTexImage2D(GLenum(m_target), 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.data()); + } else if (m_format == Format::RGBA8 || m_format == Format::SRGBA8) { + f->glTexImage2D(GLenum(m_target), 0, p.internal_format, width, height, 0, p.format, p.type, texture.data()); if (m_min_filter == Filter::MipMapLinear) f->glGenerateMipmap(GLenum(m_target)); } else { @@ -163,7 +166,7 @@ void gl_engine::Texture::upload(const nucleus::utils::ColourTexture& texture, un if (m_format == Format::CompressedRGBA8) { const auto format = gl_engine::Texture::compressed_texture_format(); f->glCompressedTexSubImage3D(GLenum(m_target), 0, 0, 0, GLint(array_index), width, height, 1, format, GLsizei(texture.n_bytes()), texture.data()); - } else if (m_format == Format::RGBA8) { + } else if (m_format == Format::RGBA8 || m_format == Format::SRGBA8) { f->glTexSubImage3D(GLenum(m_target), 0, 0, 0, GLint(array_index), width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, texture.data()); } else { assert(false); @@ -188,7 +191,7 @@ void gl_engine::Texture::upload(const nucleus::utils::MipmappedColourTexture& mi const auto format = gl_engine::Texture::compressed_texture_format(); f->glCompressedTexSubImage3D( GLenum(m_target), mip_level, 0, 0, GLint(array_index), width, height, 1, format, GLsizei(texture.n_bytes()), texture.data()); - } else if (m_format == Format::RGBA8) { + } else if (m_format == Format::RGBA8 || m_format == Format::SRGBA8) { f->glTexSubImage3D(GLenum(m_target), mip_level, 0, 0, GLint(array_index), width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, texture.data()); } else { assert(false); @@ -274,18 +277,18 @@ GLenum gl_engine::Texture::compressed_texture_format() const ext = gl.getExtension("WEBGL_compressed_texture_etc"); if (ext === null) return 0; - return ext.COMPRESSED_RGB8_ETC2; + return ext.COMPRESSED_SRGB8_ETC2; }); // qDebug() << "gl_engine::Texture::compressed_texture_format: gl_texture_format from js: " << gl_texture_format; // clang-format on if (gl_texture_format == 0) { - gl_texture_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // not on mobile + gl_texture_format = GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; // not on mobile } return gl_texture_format; #elif defined(__ANDROID__) - return GL_COMPRESSED_RGB8_ETC2; + return GL_COMPRESSED_SRGB8_ETC2; #else - return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT; #endif } diff --git a/gl_engine/Texture.h b/gl_engine/Texture.h index 697d1820..0632f4ac 100644 --- a/gl_engine/Texture.h +++ b/gl_engine/Texture.h @@ -33,6 +33,7 @@ class Texture { enum class Target : GLenum { _2d = GL_TEXTURE_2D, _2dArray = GL_TEXTURE_2D_ARRAY }; // no 1D textures in webgl enum class Format { RGBA8, // normalised on gpu + SRGBA8, // normalised on gpu CompressedRGBA8, // normalised on gpu, compression format depends on desktop/mobile RGBA8UI, RGBA32F, diff --git a/gl_engine/shaders/compose.frag b/gl_engine/shaders/compose.frag index fb886f34..add96b95 100644 --- a/gl_engine/shaders/compose.frag +++ b/gl_engine/shaders/compose.frag @@ -260,4 +260,6 @@ void main() { } } + // srgb framebuffer support is patchy. encoding manually here. + out_Color = vec4(pow(out_Color.rgb, vec3(1.0/2.2)), out_Color.a); } diff --git a/unittests/gl_engine/texture.cpp b/unittests/gl_engine/texture.cpp index b923c7cf..4180e3cf 100644 --- a/unittests/gl_engine/texture.cpp +++ b/unittests/gl_engine/texture.cpp @@ -291,9 +291,9 @@ TEST_CASE("gl texture") const auto g = qGreen(result_pixel); const auto b = qBlue(result_pixel); - diff += std::abs(r - ref_pixel.x) / 255.0; - diff += std::abs(g - ref_pixel.y) / 255.0; - diff += std::abs(b - ref_pixel.z) / 255.0; + diff += std::abs(r / 255.0 - std::pow(ref_pixel.x / 255.0, 2.2)); + diff += std::abs(g / 255.0 - std::pow(ref_pixel.y / 255.0, 2.2)); + diff += std::abs(b / 255.0 - std::pow(ref_pixel.z / 255.0, 2.2)); } } CAPTURE(resolution); @@ -381,7 +381,7 @@ TEST_CASE("gl texture") for (auto texture_type : texture_types) { CAPTURE(texture_type.first); CAPTURE(texture_type.second); - const auto format = (texture_type.first == ColourTexture::Format::Uncompressed_RGBA) ? gl_engine::Texture::Format::RGBA8 + const auto format = (texture_type.first == ColourTexture::Format::Uncompressed_RGBA) ? gl_engine::Texture::Format::SRGBA8 : gl_engine::Texture::Format::CompressedRGBA8; const auto use_mipmaps = texture_type.second; gl_engine::Texture opengl_texture(gl_engine::Texture::Target::_2dArray, format); @@ -433,9 +433,9 @@ TEST_CASE("gl texture") double diff = 0; for (int i = 0; i < render_result.width(); ++i) { for (int j = 0; j < render_result.height(); ++j) { - diff += std::abs(qRed(render_result.pixel(i, j)) - test_raster.pixel({ i, j }).x) / 255.0; - diff += std::abs(qGreen(render_result.pixel(i, j)) - test_raster.pixel({ i, j }).y) / 255.0; - diff += std::abs(qBlue(render_result.pixel(i, j)) - test_raster.pixel({ i, j }).z) / 255.0; + diff += std::abs(qRed(render_result.pixel(i, j)) / 255.0 - std::pow(test_raster.pixel({ i, j }).x / 255.0, 2.2)); + diff += std::abs(qGreen(render_result.pixel(i, j)) / 255.0 - std::pow(test_raster.pixel({ i, j }).y / 255.0, 2.2)); + diff += std::abs(qBlue(render_result.pixel(i, j)) / 255.0 - std::pow(test_raster.pixel({ i, j }).z / 255.0, 2.2)); } } CHECK(diff / (256 * 256 * 3) < 0.017); @@ -446,12 +446,12 @@ TEST_CASE("gl texture") double diff = 0; for (int i = 0; i < render_result.width(); ++i) { for (int j = 0; j < render_result.height(); ++j) { - diff += std::abs(qRed(render_result.pixel(i, j)) - 42) / 255.0; - diff += std::abs(qGreen(render_result.pixel(i, j)) - 142) / 255.0; - diff += std::abs(qBlue(render_result.pixel(i, j)) - 242) / 255.0; + diff += std::abs(qRed(render_result.pixel(i, j)) / 255.0 - std::pow(42 / 255.0, 2.2)); + diff += std::abs(qGreen(render_result.pixel(i, j)) / 255.0 - std::pow(142 / 255.0, 2.2)); + diff += std::abs(qBlue(render_result.pixel(i, j)) / 255.0 - std::pow(242 / 255.0, 2.2)); } } - CHECK(diff / (256 * 256 * 3) < 0.017); + CHECK(diff / (256 * 256 * 3) < 0.02); } { const QImage render_result = framebuffer.read_colour_attachment(2); @@ -459,12 +459,12 @@ TEST_CASE("gl texture") double diff = 0; for (int i = 0; i < render_result.width(); ++i) { for (int j = 0; j < render_result.height(); ++j) { - diff += std::abs(qRed(render_result.pixel(i, j)) - 222) / 255.0; - diff += std::abs(qGreen(render_result.pixel(i, j)) - 111) / 255.0; - diff += std::abs(qBlue(render_result.pixel(i, j)) - 0) / 255.0; + diff += std::abs(qRed(render_result.pixel(i, j)) / 255.0 - std::pow(222 / 255.0, 2.2)) / 255.0; + diff += std::abs(qGreen(render_result.pixel(i, j)) / 255.0 - std::pow(111 / 255.0, 2.2)) / 255.0; + diff += std::abs(qBlue(render_result.pixel(i, j)) / 255.0 - std::pow(0 / 255.0, 2.2)) / 255.0; } } - CHECK(diff / (256 * 256 * 3) < 0.017); + CHECK(diff / (256 * 256 * 3) < 0.02); } } }