diff options
Diffstat (limited to 'res/effectlib/gles2/sampleProbe.glsllib')
-rw-r--r-- | res/effectlib/gles2/sampleProbe.glsllib | 224 |
1 files changed, 33 insertions, 191 deletions
diff --git a/res/effectlib/gles2/sampleProbe.glsllib b/res/effectlib/gles2/sampleProbe.glsllib index f785918..fb202e5 100644 --- a/res/effectlib/gles2/sampleProbe.glsllib +++ b/res/effectlib/gles2/sampleProbe.glsllib @@ -39,6 +39,8 @@ #define QT3DS_ENABLE_IBL_FOV 0 #endif +#define USE_RGBE + uniform sampler2D light_probe; uniform vec4 light_probe_props; uniform vec4 light_probe_rotation; @@ -75,17 +77,9 @@ mat3 tangentFrame( vec3 N, vec3 p ) // get edge vectors of the pixel triangle vec3 dp1 = dFdx( p ); vec3 dp2 = dFdy( p ); - // Using dPdu and dPdv would be nicer, but the nature of our materials - // are not ones with intrinsic UVs, so we can't really go there. -// vec2 duv1 = dFdx( uv ); -// vec2 duv2 = dFdy( uv ); - // solve the linear system vec3 dp2perp = cross( dp2, N ); vec3 dp1perp = cross( N, dp1 ); -// vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; -// vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; - vec3 T = normalize(dp1perp); vec3 B = normalize(dp2perp); return mat3( T , B , N ); @@ -99,6 +93,16 @@ vec2 transformSample( vec2 origUV, vec4 probeRot, vec2 probeOfs ) return retUV; } +vec3 textureProbe(sampler2D lightProbe, vec2 coord, float lod) +{ +#ifdef USE_RGBE + vec4 ret = textureLod(lightProbe, coord, lod); + return ret.rgb * pow(2.0, ret.a * 255.0 - 128.0); +#else + return textureLod(lightProbe, coord, lod).rgb; +#endif +} + // This is broken out into its own routine so that if we get some other // format image than a lat-long, then we can account for that by changing // the code here alone. @@ -142,7 +146,7 @@ vec4 getTopLayerSample( vec3 inDir, float lodShift, vec3 lodOffsets ) vec3 getProbeSample( vec3 smpDir, float lodShift, vec3 normal ) { vec2 smpUV = getProbeSampleUV( smpDir, light_probe_rotation, light_probe_offset.xy ); - return textureLod( light_probe, smpUV , lodShift ).xyz; + return textureProbe( light_probe, smpUV , lodShift ); } vec3 getProbeWeightedSample( vec3 smpDir, float lodShift, float roughness, vec3 normal ) @@ -184,10 +188,10 @@ vec3 getProbeWeightedSample( vec3 smpDir, float lodShift, float roughness, vec3 lodShift = max( lodShift, minLod ); - vec3 retVal = 0.4 * textureLod( light_probe, smpUV , lodShift ).xyz; - retVal += 0.2 * textureLod( light_probe, smpUV , max(minLod, lodShift+lodOffsets.x) ).xyz; - retVal += 0.3 * textureLod( light_probe, smpUV , lodShift+lodOffsets.y ).xyz; - retVal += 0.1 * textureLod( light_probe, smpUV , lodShift+lodOffsets.z ).xyz; + vec3 retVal = 0.4 * textureProbe( light_probe, smpUV , lodShift ); + retVal += 0.2 * textureProbe( light_probe, smpUV , max(minLod, lodShift+lodOffsets.x) ); + retVal += 0.3 * textureProbe( light_probe, smpUV , lodShift+lodOffsets.y ); + retVal += 0.1 * textureProbe( light_probe, smpUV , lodShift+lodOffsets.z ); #if QT3DS_ENABLE_LIGHT_PROBE_2 vec4 topSmp = getTopLayerSample( smpDir, lodShift, lodOffsets ); @@ -257,9 +261,9 @@ vec3 getProbeAnisoSample( vec3 smpDir, float roughU, float roughV, mat3 tanFrame wt = sigma / (sigma + float(i * i)); vec2 uv0 = getProbeSampleUV(normalize(smpDir + smpDirOfs * float(i)), light_probe_rotation, light_probe_offset.xy); vec2 uv1 = getProbeSampleUV(normalize(smpDir - smpDirOfs * float(i)), light_probe_rotation, light_probe_offset.xy); - result.xyz += wt * textureLod( light_probe, uv0 , lodMin ).xyz; + result.xyz += wt * textureProbe( light_probe, uv0 , lodMin ); result.w += wt; - result.xyz += wt * textureLod( light_probe, uv1 , lodMin ).xyz; + result.xyz += wt * textureProbe( light_probe, uv1 , lodMin ); result.w += wt; } @@ -272,78 +276,7 @@ vec4 sampleDiffuse( mat3 tanFrame ) if ( light_probe_props.w < 0.005 ) return vec4( 0.0 ); -// if ( light_probe_offset.w > 0.5 ) -// { - // The LOD offset comes from the assumption that a full diffuse convolution - // has a support of pi/2, which translates into x pixels, and the base 2 log - // gives us this LOD... Technically, "x" pixels depends on what the original - // texture resolution was, which is why we use light_probe_offset.w, which holds - // the number of mip levels the texture has. - - return vec4( light_probe_props.w * getProbeWeightedSample( tanFrame[2], light_probe_offset.w - 2.65149613, 1.0, tanFrame[2] ), 1.0 ); -// } - - /* - // PKC -- the code below is for full-blown IBL, which we'll skip for now - - // Hand-calculated Hammersley points for t = 2, n = 33 - // I exclude the 0,0 first point, hence why n=33 and not 32 - // Nice thing about 2d Hammersley points is that any subset is - // also stratified, so even if I have 1000 points and truncate - // anywhere, I'm fine. Each of these represent the y of an xy - // while x for the kth point is always (k+1)/n. - float kernel[32]; - kernel[0] = 0.5; kernel[1] = 0.25; - kernel[2] = 0.75; kernel[3] = 0.125; - kernel[4] = 0.625; kernel[5] = 0.375; - kernel[6] = 0.875; kernel[7] = 0.0625; - kernel[8] = 0.5625; kernel[9] = 0.3125; - kernel[10] = 0.8125; kernel[11] = 0.1875; - kernel[12] = 0.6875; kernel[13] = 0.4375; - kernel[14] = 0.9375; kernel[15] = 0.03125; - kernel[16] = 0.53125; kernel[17] = 0.28125; - kernel[18] = 0.78125; kernel[19] = 0.15625; - kernel[20] = 0.65625; kernel[21] = 0.40625; - kernel[22] = 0.90625; kernel[23] = 0.09375; - kernel[24] = 0.59375; kernel[25] = 0.34375; - kernel[26] = 0.84375; kernel[27] = 0.28175; - kernel[28] = 0.71875; kernel[29] = 0.46875; - kernel[30] = 0.96875; kernel[31] = 0.015625; - - float phiShift = noise1d(gl_FragCoord.xy) - 0.5; - - vec3 ret = vec3(0, 0, 0); - - int ct = 24; - float step = 25.0; - - // Importance sampling a cosine-weighted distribution. Since this - // matches the BSDF exactly, we are just going to assume that the PDF - // and the BSDF cancel out in sampling, so we just need to accumulate - // texture colors. The noise function puts randomized "twist" into - // the sampled directions. - for( int i = 0; i < ct; ++i ) - { - vec3 localDir; - float phi = 6.28318530718 * (kernel[i] + phiShift); - float cosTheta = sqrt( float(i+1) / step); - localDir.z = sqrt(1.0 - cosTheta*cosTheta); - localDir.x = cos(phi) * cosTheta; - localDir.y = sin(phi) * cosTheta; - vec3 smpDir = tanFrame[0]*localDir.x + tanFrame[1]*localDir.y + tanFrame[2]*localDir.z; - - - float lodShift = light_probe_offset.w - 2 + log2( 3.1415926535 / (localDir.z * step) ); - vec3 smpColor = getProbeSample( smpDir, lodShift, tanFrame[2] ); - - // The assumption here is that the BSDF and the sampling PDF are identical - // so they cancel out and therefore, we don't need to include it here. - ret += smpColor; - } - - ret *= aoFactor / 24.0; - return ret; - */ + return vec4( light_probe_props.w * getProbeWeightedSample( tanFrame[2], light_probe_offset.w - 2.65149613, 1.0, tanFrame[2] ), 1.0 ); } vec4 sampleDiffuseCustomMaterial( vec3 normal, vec3 worldPos, float aoFactor ) @@ -358,114 +291,23 @@ vec4 sampleGlossyAniso( mat3 tanFrame, vec3 viewDir, float roughU, float roughV if ( light_probe_props.w < 0.005 ) return vec4( 0.0 ); - // PKC : If we do the full IBL sampling, it's useful to square the roughnesses because - // it makes the effect of roughness feel more linear in the low end. This isn't necessary - // for fast IBL. -// float sigmaU = clamp(roughU*roughU, 0.0001, 1.0); -// float sigmaV = clamp(roughV*roughV, 0.0001, 1.0); float sigmaU = smoothstep( 0.0, 1.0, clamp(roughU, 0.0001, 1.0) ); float sigmaV = smoothstep( 0.0, 1.0, clamp(roughV, 0.0001, 1.0) ); vec3 ret = vec3(0, 0, 0); -// if ( light_probe_offset.w > 0.5 ) -// { - vec3 smpDir = reflect( -viewDir, tanFrame[2] ); - float sigma = sqrt(sigmaU * sigmaV); - - // Compute the Geometric occlusion/self-shadowing term - float NdotL = clamp( dot( smpDir, tanFrame[2] ), 0.0, 0.999995); - float k = sigma * 0.31830988618; // roughness / pi - float Gl = clamp( (NdotL / (NdotL*(1.0-k) + k) + (1.0 - k*k)) * 0.5, 0.0, 1.0 ); - - vec3 outColor; - - outColor = getProbeAnisoSample( smpDir, sigmaU, sigmaV, tanFrame ); - - return vec4( light_probe_props.w * Gl * outColor, 1.0 ); -// } - - // PKC -- the code below is for full-blown IBL, which we'll skip for now - -/* - float step = clamp( ceil(32.0 * sqrt(max(sigmaU, sigmaV))), 4.0, 32.0 ); - int actualCt = int(step); - float phiShift = noise1d(gl_FragCoord.xy) - 0.5; - - // Hand-calculated Hammersley points for t = 2, n = 33 - // I exclude the 0,0 first point, hence why n=33 and not 32 - // Nice thing about 2d Hammersley points is that any subset is - // also stratified, so even if I have 1000 points and truncate - // anywhere, I'm fine. Each of these represent the y of an xy - // while x for the kth point is always (k+1)/n. - float kernel[32]; - kernel[0] = 0.5; kernel[1] = 0.25; - kernel[2] = 0.75; kernel[3] = 0.125; - kernel[4] = 0.625; kernel[5] = 0.375; - kernel[6] = 0.875; kernel[7] = 0.0625; - kernel[8] = 0.5625; kernel[9] = 0.3125; - kernel[10] = 0.8125; kernel[11] = 0.1875; - kernel[12] = 0.6875; kernel[13] = 0.4375; - kernel[14] = 0.9375; kernel[15] = 0.03125; - kernel[16] = 0.53125; kernel[17] = 0.28125; - kernel[18] = 0.78125; kernel[19] = 0.15625; - kernel[20] = 0.65625; kernel[21] = 0.40625; - kernel[22] = 0.90625; kernel[23] = 0.09375; - kernel[24] = 0.59375; kernel[25] = 0.34375; - kernel[26] = 0.84375; kernel[27] = 0.28175; - kernel[28] = 0.71875; kernel[29] = 0.46875; - kernel[30] = 0.96875; kernel[31] = 0.015625; - - float thetaI = acos( dot(viewDir, tanFrame[2]) ); - - // NOTE : The model I'm using here is actually based on the KGGX model used in - // physGlossyBSDF. This is my own variation on the original GGX which uses something - // closer to a pure Cauchy distribution in tangent space, but also supports anisotropy. - for (int i = 0; i < actualCt; ++i) - { - vec3 localDir; - - float phi = 6.28318530718 * (kernel[i] + phiShift); - float u = float(i + 1) / (step + 1.0); - float rU = cos(phi) * sigmaU; - float rV = sin(phi) * sigmaV; - float sigma = sqrt(rU * rU + rV * rV); - - float boundA = atan( ((thetaI - 1.57079632679) * 0.5) / sigma ); - float boundB = atan( ((thetaI + 1.57079632679) * 0.5) / sigma ); - float t = (1.0 - u) * boundA + u * boundB; - float thetaH = tan( t ) * sigma; - - float cosThetaH = cos( thetaH ); - float sinThetaH = sin( thetaH ); - localDir.z = cosThetaH; - localDir.y = sin(phi) * sinThetaH; - localDir.x = cos(phi) * sinThetaH; - - vec3 halfDir = tanFrame[0]*localDir.x + tanFrame[1]*localDir.y + tanFrame[2]*localDir.z; - halfDir = normalize(halfDir); - vec3 smpDir = reflect( -viewDir, halfDir ); - - vec2 scaledXY = localDir.xy / vec2(sigmaU, sigmaV); - float PDF = (sigmaU*sigmaV) / (sigmaU*sigmaV + dot(scaledXY, scaledXY)); - vec3 Haf = smpDir + viewDir; // We need the unnormalized half vecter as well as the normalized one - float HdotL = dot(halfDir, smpDir); - // normalize the PDF to compute the filter support - // This gives us the ideal miplevel at which to sample the texture map. - PDF *= dot(Haf, Haf) / (4.0 * dot(Haf, smpDir) * HdotL * sigmaU*sigmaV * (boundB-boundA)*(boundB-boundA)); - - // Again assuming that the pdf and BSDF are equivalent -- that's not generally valid, - // but it saves a lot of ALU cycles. - float lodShift = log2( 512.0 * sigma / PDF ); - - float k = sigma * 0.31830988618; // roughness / pi - float Gl = clamp( (HdotL / (HdotL*(1.0-k) + k) + (1.0 - k*k)) * 0.5, 0.0, 1.0 ); - - vec3 smpColor = Gl * getProbeSample( smpDir, lodShift, tanFrame[2] ); - ret += smpColor; - } - ret /= float(actualCt); - return vec4(ret, 1.0); -*/ + vec3 smpDir = reflect( -viewDir, tanFrame[2] ); + float sigma = sqrt(sigmaU * sigmaV); + + // Compute the Geometric occlusion/self-shadowing term + float NdotL = clamp( dot( smpDir, tanFrame[2] ), 0.0, 0.999995); + float k = sigma * 0.31830988618; // roughness / pi + float Gl = clamp( (NdotL / (NdotL*(1.0-k) + k) + (1.0 - k*k)) * 0.5, 0.0, 1.0 ); + + vec3 outColor; + + outColor = getProbeAnisoSample( smpDir, sigmaU, sigmaV, tanFrame ); + + return vec4( light_probe_props.w * Gl * outColor, 1.0 ); } vec4 sampleGlossy( mat3 tanFrame, vec3 viewDir, float roughness ) |