On Github cedricpinson / ParisWebGL2014
by Cedric Pinson / @trigrou @sketchfab
Micro geometry
Reflection
Refraction
Light is reflected
Refracted light is absorbed
Light is reflected
Light is refracted
Diffuse / Specular
F: Fresnel (F_Schlick)
D: Normal distribution function (D_GGX)
G: Geometry function (G_SmithGGX)
Use enfironment as lightsource
image from hdri-hub.com [7]precompute irradiance [1]
cubemap / spherical harmonics
image from hdri-hub.com [7]for ( int i = 0; i < nbSamples; i++ ) { vec2 u = getSample(i); vec3 h = importanceSampleCos(u); vec3 l = h; vec3 dir = iblTransform * l; vec3 env = textureCubeLod( ibl, dir, 0.0 ); contrib += env / PI; }
prefilter roughness or more [2]
importance sampling [3]
image from hdri-hub.com [7]for ( int i = 0; i < nbSamples; i++ ) { vec2 u = getSample(i); vec3 h = importanceSampleGGX(u); vec3 l = reflect(v, h); float g = g_smithGGX( roughness, NoV, NoL); vec3 f = f_schlick( f0, NoH); float d = d_ggx( NoH, roughness); vec3 dir = iblTransform * l; vec3 env = textureCubeLod( ibl, dir, 0.0 ); contrib += env * g * f * d / ( 4.0 * NoV * NoL ); }
Use texture lod as approximation
Need to use something like RGBE [8]
Need to do trilinear manually
Can't use once cubemap
Can use multi cubemap [9]
// Ok, this is ugly, but there is an explanation ... vec3 ComputeEnvColor(float roughness, vec3 refl) { float a = roughness * roughness * 6.0; if ( a < 1.0) return mix(textureCube(envMap0, refl).rgb, textureCube(envMap1, refl).rgb, a); if ( a < 2.0) return mix(textureCube(envMap1, refl).rgb, textureCube(envMap2, refl).rgb, a - 1.0); if ( a < 3.0) return mix(textureCube(envMap2, refl).rgb, textureCube(envMap3, refl).rgb, a - 2.0); if ( a < 4.0) return mix(textureCube(envMap3, refl).rgb, textureCube(envMap4, refl).rgb, a - 3.0); if ( a < 5.0) return mix(textureCube(envMap4, refl).rgb, textureCube(envMap5, refl).rgb, a - 4.0); return textureCube(envMap5, refl).rgb; }code from alexandre pestana [9]
No ARB_seamless_cube_map extension [10]
AMD cubemap gen has code to fixup [1]
image from Ignacio Castaño post [12]
Can use panorama with lod inline
Panorama suffer from pole low resolution
vec4 computeUVForMipmap( const in float level, const in vec2 uv, const in vec2 size, const in float maxLOD ) { float widthForLevel = pow( 2.0, maxLOD-level); // the height locally for the level in pixel vec2 sizeForLevel = vec2( widthForLevel, widthForLevel - 2.0 ); // -2.0 avoid bleeding on the top // globally the texture is square so width = height for texture size float globalOffsetVInTexel = size.x - sizeForLevel.x; // in texel float oneOnSizeX = 1.0 / size.x; // we will need to transform our original uv to the mipmap level space vec2 ratio = sizeForLevel * oneOnSizeX; // u = u * ratioU // v = v * ratioV + offsetY / height vec2 uvGlobal = uv * ratio; float globalOffsetV = globalOffsetVInTexel * oneOnSizeX; uvGlobal.y += globalOffsetV; // zw contains the max box of the local mip level return vec4( uvGlobal.x, uvGlobal.y, ratio.x, ratio.y + globalOffsetV ); }
Panorama example
Sketchfab siggraph prototype [11]