UNPKG

pex-renderer

Version:

Physically Based Renderer for Pex

103 lines (91 loc) 3.4 kB
const SHADERS = require('../chunks/index.js') module.exports = /* glsl */ ` precision highp float; // Variables varying vec2 vTexCoord; uniform float uTextureSize; uniform sampler2D uSource; uniform int uSourceEncoding; uniform sampler2D uHammersleyPointSetMap; uniform int uNumSamples; uniform float uLevel; uniform float uSourceMipmapLevel; uniform float uSourceRoughnessLevel; uniform float uRoughnessLevel; uniform int uOutputEncoding; // Includes ${SHADERS.math.PI} ${SHADERS.math.saturate} ${SHADERS.octMap} ${SHADERS.octMapUvToDir} ${SHADERS.rgbm} ${SHADERS.gamma} ${SHADERS.encodeDecode} //Sampled from a texture generated by code based on //http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html vec2 Hammersley(int i, int N) { return texture2D(uHammersleyPointSetMap, vec2(0.5, (float(i) + 0.5)/float(N))).rg; } //Based on Real Shading in Unreal Engine 4 vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N) { //this is mapping 2d point to a hemisphere but additionally we add spread by roughness float a = Roughness * Roughness; a *= 0.75; // to prevent overblurring as we sample from previous roughness level with smaller number of samples float Phi = 2.0 * PI * Xi.x; float CosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); float SinTheta = sqrt(1.0 - CosTheta * CosTheta); vec3 H; H.x = SinTheta * cos(Phi); H.y = SinTheta * sin(Phi); H.z = CosTheta; //Tangent space vectors vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); vec3 tangent = normalize(cross(up, N)); vec3 bitangent = normalize(cross(N, tangent)); //Tangent to World Space return tangent * H.x + bitangent * H.y + N * H.z; } //TODO: optimize this using sign() //Source: http://webglinsights.github.io/downloads/WebGL-Insights-Chapter-16.pdf vec4 textureOctMapLod(sampler2D tex, vec2 uv) { float width = 2048.0; float maxLevel = 11.0; // this should come from log of size float levelSizeInPixels = pow(2.0, 1.0 + uSourceMipmapLevel + uSourceRoughnessLevel); float levelSize = max(64.0, width / levelSizeInPixels); float roughnessLevelWidth = width / pow(2.0, 1.0 + uSourceRoughnessLevel); float vOffset = (width - pow(2.0, maxLevel - uSourceRoughnessLevel)); float hOffset = 2.0 * roughnessLevelWidth - pow(2.0, log2(2.0 * roughnessLevelWidth) - uSourceMipmapLevel); // trying to fix oveflow from atlas.. uv = (uv * levelSize + 0.5) / (levelSize + 1.0); uv *= levelSize; uv = (uv + vec2(hOffset, vOffset)) / width; return texture2D(uSource, uv); } vec3 PrefilterEnvMap( float roughness, vec3 R ) { vec3 N = R; vec3 V = R; vec3 PrefilteredColor = vec3(0.0); const int NumSamples = 512; float TotalWeight = 0.0; for( int i = 0; i < NumSamples; i++ ) { if (i > uNumSamples) { break; } vec2 Xi = Hammersley( i, uNumSamples ); vec3 H = ImportanceSampleGGX( Xi, roughness, N ); vec3 L = 2.0 * dot( V, H ) * H - V; float NoL = saturate( dot( N, L ) ); if( NoL > 0.0 ) { vec4 color = textureOctMapLod(uSource, envMapOctahedral(L)); PrefilteredColor += NoL * decode(color, uSourceEncoding).rgb; TotalWeight += NoL; } } return PrefilteredColor / TotalWeight; } void main() { vec3 normal = octMapUVToDir(vTexCoord); vec3 color = PrefilterEnvMap(uRoughnessLevel / 5.0, normal); gl_FragColor = encode(vec4(color, 1.0), uOutputEncoding); } `