Search code examples
webgl2

How do you implement a gamma correct workflow in WebGL2?


I am having trouble utilizing sRGB textures with WebGL2. I am attempting to load a texture and display it as a fullscreen quad, but the image is displaying incorrectly (too dark.)

The texture loading code is the following.

const texture0 = await (() => {
    const image = new Image()
    const promise = new Promise(resolve => image.onload = () => {
        const texture = gl.createTexture()
        gl.bindTexture(gl.TEXTURE_2D, texture)
        gl.texStorage2D(gl.TEXTURE_2D, 1, gl.SRGB8_ALPHA8, 256, 256)
        gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 256, 256, gl.RGBA, gl.UNSIGNED_BYTE, image) 
        resolve(texture)
    })
    image.src = "./images/texture.png"
    return promise
})()

I'm thinking maybe it has to do with the encoding of the framebuffer, but I do not see an equivalent to glEnable(GL_FRAMEBUFFER_SRGB) in WebGL.


Solution

  • Ok, answering my own question. It seems that EXT_sRGB adds a framebuffer attachment parameter, FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT, that allows you to see whether or not a framebuffer will perform linear to sRGB conversion. This functionality is included by default in WebGL2. However, there is currently no way to specify whether or not the default framebuffer will be linear or sRGB. In the case of Chrome v67 it seems that the default framebuffer is linear, so you have to perform the conversion manually in your fragment shader.

    There is an extension in OpenGL ES 3.0, EXT_sRGB_write_control, that would allow you to use glEnable(GL_FRAMEBUFFER_SRGB_EXT). However, this is not currently available in WebGL.