Skip to content

Commit

Permalink
Properly handle sRGB and gamma correct output
Browse files Browse the repository at this point in the history
Fix up the sRGB handling to properly light in linear space.  Also fixed
sRGB on platforms that don't support the SRGB extension.
  • Loading branch information
snagy committed Oct 19, 2017
1 parent 49b5c27 commit 80f3df9
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 16 deletions.
6 changes: 2 additions & 4 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function loadCubeMap(gl, envMap, type, state) {
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
// todo: should this be srgb? or rgba? what's the HDR scale on this?
gl.texImage2D(face, j, state.sRGBifAvailable, state.sRGBifAvailable, gl.UNSIGNED_BYTE, image);
gl.texImage2D(face, j, state.hasSRGBExt ? state.hasSRGBExt.SRGB_EXT : gl.RGBA, state.hasSRGBExt ? state.hasSRGBExt.SRGB_EXT : gl.RGBA, gl.UNSIGNED_BYTE, image);
};
}

Expand Down Expand Up @@ -142,8 +142,6 @@ function init(vertSource, fragSource) {

var ctx2d = canvas2d.getContext("2d");

var hasSRGBExt = gl.getExtension('EXT_SRGB');

glState = {
uniforms: {},
attributes: {},
Expand All @@ -152,7 +150,7 @@ function init(vertSource, fragSource) {
scene: null,
hasLODExtension:gl.getExtension('EXT_shader_texture_lod'),
hasDerivativesExtension:gl.getExtension('OES_standard_derivatives'),
sRGBifAvailable: (hasSRGBExt ? hasSRGBExt.SRGB_EXT : gl.RGBA)
hasSRGBExt:gl.getExtension('EXT_SRGB')
};

var projectionMatrix = mat4.create();
Expand Down
12 changes: 8 additions & 4 deletions mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ class Mesh {

this.vertSource = globalState.vertSource;
this.fragSource = globalState.fragSource;
this.sRGBifAvailable = globalState.sRGBifAvailable;
this.hasSRGBExt = globalState.hasSRGBExt;

if(!this.hasSRGBExt) {
this.defines.MANUAL_SRGB = 1;
}

++scene.pendingBuffers;

Expand Down Expand Up @@ -226,7 +230,7 @@ class Mesh {
vals: baseColorFactor
};
if (pbrMat && pbrMat.baseColorTexture && gltf.textures.length > pbrMat.baseColorTexture.index) {
imageInfos['baseColor'] = this.getImageInfo(gl, gltf, pbrMat.baseColorTexture.index, 'uniform1i', 'u_BaseColorSampler', this.sRGBifAvailable);
imageInfos['baseColor'] = this.getImageInfo(gl, gltf, pbrMat.baseColorTexture.index, 'uniform1i', 'u_BaseColorSampler', this.hasSRGBExt ? this.hasSRGBExt.SRGB_EXT : gl.RGBA);
this.defines.HAS_BASECOLORMAP = 1;
}
else if (this.localState.uniforms['u_BaseColorSampler']) {
Expand Down Expand Up @@ -262,12 +266,12 @@ class Mesh {
// brdfLUT
var brdfLUT = 'textures/brdfLUT.png';
samplerIndex = this.scene.getNextSamplerIndex();
imageInfos['brdfLUT'] = { 'uri': brdfLUT, 'samplerIndex': samplerIndex, 'colorSpace': gl.RGBA, 'clamp': true };
imageInfos['brdfLUT'] = { 'uri': brdfLUT, 'samplerIndex': samplerIndex, 'colorSpace': this.hasSRGBExt ? this.hasSRGBExt.SRGB_EXT : gl.RGBA, 'clamp': true };
this.localState.uniforms['u_brdfLUT'] = { 'funcName': 'uniform1i', 'vals': [samplerIndex] };

// Emissive
if (this.material && this.material.emissiveTexture) {
imageInfos['emissive'] = this.getImageInfo(gl, gltf, this.material.emissiveTexture.index, 'uniform1i', 'u_EmissiveSampler', this.sRGBifAvailable);
imageInfos['emissive'] = this.getImageInfo(gl, gltf, this.material.emissiveTexture.index, 'uniform1i', 'u_EmissiveSampler', this.hasSRGBExt ? this.hasSRGBExt.SRGB_EXT : gl.RGBA);
this.defines.HAS_EMISSIVEMAP = 1;
var emissiveFactor = defined(this.material.emissiveFactor) ? this.material.emissiveFactor : [0.0, 0.0, 0.0];
this.localState.uniforms['u_EmissiveFactor'] = {
Expand Down
2 changes: 1 addition & 1 deletion scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class Scene {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,/*imageInfo.colorSpace, imageInfo.colorSpace,*/ gl.UNSIGNED_BYTE, image);
gl.texImage2D(gl.TEXTURE_2D, 0, imageInfo.colorSpace, imageInfo.colorSpace, gl.UNSIGNED_BYTE, image);

scene.pendingTextures--;
scene.drawScene(gl);
Expand Down
29 changes: 22 additions & 7 deletions shaders/pbr-frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ struct PBRInfo
const float M_PI = 3.141592653589793;
const float c_MinRoughness = 0.04;

vec4 SRGBtoLINEAR(vec4 srgbIn)
{
#ifdef MANUAL_SRGB
#ifdef SRGB_FAST_APPROXIMATION
vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
#else //SRGB_FAST_APPROXIMATION
vec3 bLess = step(vec3(0.03928),srgbIn.xyz);
vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
#endif //SRGB_FAST_APPROXIMATION
return vec4(linOut,srgbIn.w);;
#else //MANUAL_SRGB
return srgbIn;
#endif //MANUAL_SRGB
}

// Find the normal for this fragment, pulling either from a predefined normal map
// or from the interpolated mesh normal and tangent attributes.
vec3 getNormal()
Expand Down Expand Up @@ -131,13 +146,13 @@ vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
float mipCount = 9.0; // resolution of 512x512
float lod = (pbrInputs.perceptualRoughness * mipCount);
// retrieve a scale and bias to F0. See [1], Figure 3
vec3 brdf = texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness)).rgb;
vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;
vec3 brdf = SRGBtoLINEAR(texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
vec3 diffuseLight = SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb;

#ifdef USE_TEX_LOD
vec3 specularLight = textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod).rgb;
vec3 specularLight = SRGBtoLINEAR(textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod)).rgb;
#else
vec3 specularLight = textureCube(u_SpecularEnvSampler, reflection).rgb;
vec3 specularLight = SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb;
#endif

vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
Expand Down Expand Up @@ -212,7 +227,7 @@ void main()

// The albedo may be defined from a base texture or a flat color
#ifdef HAS_BASECOLORMAP
vec4 baseColor = texture2D(u_BaseColorSampler, v_UV) * u_BaseColorFactor;
vec4 baseColor = SRGBtoLINEAR(texture2D(u_BaseColorSampler, v_UV)) * u_BaseColorFactor;
#else
vec4 baseColor = u_BaseColorFactor;
#endif
Expand Down Expand Up @@ -280,7 +295,7 @@ void main()
#endif

#ifdef HAS_EMISSIVEMAP
vec3 emissive = texture2D(u_EmissiveSampler, v_UV).rgb * u_EmissiveFactor;
vec3 emissive = SRGBtoLINEAR(texture2D(u_EmissiveSampler, v_UV)).rgb * u_EmissiveFactor;
color += emissive;
#endif

Expand All @@ -296,5 +311,5 @@ void main()
color = mix(color, vec3(metallic), u_ScaleDiffBaseMR.z);
color = mix(color, vec3(perceptualRoughness), u_ScaleDiffBaseMR.w);

gl_FragColor = vec4(color, baseColor.a);
gl_FragColor = vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
}

0 comments on commit 80f3df9

Please sign in to comment.