|
@@ -1,130 +1,181 @@
|
|
export default /* glsl */`
|
|
export default /* glsl */`
|
|
#ifdef ENVMAP_TYPE_CUBE_UV
|
|
#ifdef ENVMAP_TYPE_CUBE_UV
|
|
|
|
|
|
-#define cubeUV_textureSize (1024.0)
|
|
|
|
-
|
|
|
|
-int getFaceFromDirection(vec3 direction) {
|
|
|
|
- vec3 absDirection = abs(direction);
|
|
|
|
- int face = -1;
|
|
|
|
- if( absDirection.x > absDirection.z ) {
|
|
|
|
- if(absDirection.x > absDirection.y )
|
|
|
|
- face = direction.x > 0.0 ? 0 : 3;
|
|
|
|
- else
|
|
|
|
- face = direction.y > 0.0 ? 1 : 4;
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- if(absDirection.z > absDirection.y )
|
|
|
|
- face = direction.z > 0.0 ? 2 : 5;
|
|
|
|
- else
|
|
|
|
- face = direction.y > 0.0 ? 1 : 4;
|
|
|
|
- }
|
|
|
|
- return face;
|
|
|
|
-}
|
|
|
|
-#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)
|
|
|
|
-#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))
|
|
|
|
-
|
|
|
|
-vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {
|
|
|
|
- float scale = exp2(cubeUV_maxLods1 - roughnessLevel);
|
|
|
|
- float dxRoughness = dFdx(roughness);
|
|
|
|
- float dyRoughness = dFdy(roughness);
|
|
|
|
- vec3 dx = dFdx( vec * scale * dxRoughness );
|
|
|
|
- vec3 dy = dFdy( vec * scale * dyRoughness );
|
|
|
|
- float d = max( dot( dx, dx ), dot( dy, dy ) );
|
|
|
|
- // Clamp the value to the max mip level counts. hard coded to 6 mips
|
|
|
|
- d = clamp(d, 1.0, cubeUV_rangeClamp);
|
|
|
|
- float mipLevel = 0.5 * log2(d);
|
|
|
|
- return vec2(floor(mipLevel), fract(mipLevel));
|
|
|
|
|
|
+#define cubeUV_maxMipLevel 8.0
|
|
|
|
+#define cubeUV_minMipLevel 4.0
|
|
|
|
+#define cubeUV_maxTileSize 256.0
|
|
|
|
+#define cubeUV_minTileSize 16.0
|
|
|
|
+
|
|
|
|
+// These shader functions convert between the UV coordinates of a single face of
|
|
|
|
+// a cubemap, the 0-5 integer index of a cube face, and the direction vector for
|
|
|
|
+// sampling a textureCube (not generally normalized).
|
|
|
|
+
|
|
|
|
+vec3 getDirection(vec2 uv, float face) {
|
|
|
|
+ uv = 2.0 * uv - 1.0;
|
|
|
|
+ vec3 direction = vec3(uv, 1.0);
|
|
|
|
+ if (face == 0.0) {
|
|
|
|
+ direction = direction.zyx;
|
|
|
|
+ direction.z *= -1.0;
|
|
|
|
+ } else if (face == 1.0) {
|
|
|
|
+ direction = direction.xzy;
|
|
|
|
+ direction.z *= -1.0;
|
|
|
|
+ } else if (face == 3.0) {
|
|
|
|
+ direction = direction.zyx;
|
|
|
|
+ direction.x *= -1.0;
|
|
|
|
+ } else if (face == 4.0) {
|
|
|
|
+ direction = direction.xzy;
|
|
|
|
+ direction.y *= -1.0;
|
|
|
|
+ } else if (face == 5.0) {
|
|
|
|
+ direction.xz *= -1.0;
|
|
|
|
+ }
|
|
|
|
+ return direction;
|
|
}
|
|
}
|
|
|
|
|
|
-#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)
|
|
|
|
-#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)
|
|
|
|
-
|
|
|
|
-vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {
|
|
|
|
- mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;
|
|
|
|
- float a = 16.0 * cubeUV_rcpTextureSize;
|
|
|
|
-
|
|
|
|
- vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );
|
|
|
|
- vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;
|
|
|
|
- // float powScale = exp2(roughnessLevel + mipLevel);
|
|
|
|
- float powScale = exp2_packed.x * exp2_packed.y;
|
|
|
|
- // float scale = 1.0 / exp2(roughnessLevel + 2.0 + mipLevel);
|
|
|
|
- float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;
|
|
|
|
- // float mipOffset = 0.75*(1.0 - 1.0/exp2(mipLevel))/exp2(roughnessLevel);
|
|
|
|
- float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;
|
|
|
|
-
|
|
|
|
- bool bRes = mipLevel == 0.0;
|
|
|
|
- scale = bRes && (scale < a) ? a : scale;
|
|
|
|
-
|
|
|
|
- vec3 r;
|
|
|
|
- vec2 offset;
|
|
|
|
- int face = getFaceFromDirection(direction);
|
|
|
|
-
|
|
|
|
- float rcpPowScale = 1.0 / powScale;
|
|
|
|
-
|
|
|
|
- if( face == 0) {
|
|
|
|
- r = vec3(direction.x, -direction.z, direction.y);
|
|
|
|
- offset = vec2(0.0+mipOffset,0.75 * rcpPowScale);
|
|
|
|
- offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;
|
|
|
|
- }
|
|
|
|
- else if( face == 1) {
|
|
|
|
- r = vec3(direction.y, direction.x, direction.z);
|
|
|
|
- offset = vec2(scale+mipOffset, 0.75 * rcpPowScale);
|
|
|
|
- offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;
|
|
|
|
- }
|
|
|
|
- else if( face == 2) {
|
|
|
|
- r = vec3(direction.z, direction.x, direction.y);
|
|
|
|
- offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);
|
|
|
|
- offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;
|
|
|
|
- }
|
|
|
|
- else if( face == 3) {
|
|
|
|
- r = vec3(direction.x, direction.z, direction.y);
|
|
|
|
- offset = vec2(0.0+mipOffset,0.5 * rcpPowScale);
|
|
|
|
- offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;
|
|
|
|
- }
|
|
|
|
- else if( face == 4) {
|
|
|
|
- r = vec3(direction.y, direction.x, -direction.z);
|
|
|
|
- offset = vec2(scale+mipOffset, 0.5 * rcpPowScale);
|
|
|
|
- offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- r = vec3(direction.z, -direction.x, direction.y);
|
|
|
|
- offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);
|
|
|
|
- offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;
|
|
|
|
- }
|
|
|
|
- r = normalize(r);
|
|
|
|
- float texelOffset = 0.5 * cubeUV_rcpTextureSize;
|
|
|
|
- vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;
|
|
|
|
- vec2 base = offset + vec2( texelOffset );
|
|
|
|
- return base + s * ( scale - 2.0 * texelOffset );
|
|
|
|
|
|
+float getFace(vec3 direction) {
|
|
|
|
+ vec3 absDirection = abs(direction);
|
|
|
|
+ float face = -1.0;
|
|
|
|
+ if (absDirection.x > absDirection.z) {
|
|
|
|
+ if (absDirection.x > absDirection.y)
|
|
|
|
+ face = direction.x > 0.0 ? 0.0 : 3.0;
|
|
|
|
+ else
|
|
|
|
+ face = direction.y > 0.0 ? 1.0 : 4.0;
|
|
|
|
+ } else {
|
|
|
|
+ if (absDirection.z > absDirection.y)
|
|
|
|
+ face = direction.z > 0.0 ? 2.0 : 5.0;
|
|
|
|
+ else
|
|
|
|
+ face = direction.y > 0.0 ? 1.0 : 4.0;
|
|
|
|
+ }
|
|
|
|
+ return face;
|
|
}
|
|
}
|
|
|
|
|
|
-#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)
|
|
|
|
-
|
|
|
|
-vec4 textureCubeUV( sampler2D envMap, vec3 reflectedDirection, float roughness ) {
|
|
|
|
- float roughnessVal = roughness* cubeUV_maxLods3;
|
|
|
|
- float r1 = floor(roughnessVal);
|
|
|
|
- float r2 = r1 + 1.0;
|
|
|
|
- float t = fract(roughnessVal);
|
|
|
|
- vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);
|
|
|
|
- float s = mipInfo.y;
|
|
|
|
- float level0 = mipInfo.x;
|
|
|
|
- float level1 = level0 + 1.0;
|
|
|
|
- level1 = level1 > 5.0 ? 5.0 : level1;
|
|
|
|
-
|
|
|
|
- // round to nearest mipmap if we are not interpolating.
|
|
|
|
- level0 += min( floor( s + 0.5 ), 5.0 );
|
|
|
|
|
|
+vec2 getUV(vec3 direction, float face) {
|
|
|
|
+ vec2 uv;
|
|
|
|
+ if (face == 0.0) {
|
|
|
|
+ uv = vec2(-direction.z, direction.y) / abs(direction.x);
|
|
|
|
+ } else if (face == 1.0) {
|
|
|
|
+ uv = vec2(direction.x, -direction.z) / abs(direction.y);
|
|
|
|
+ } else if (face == 2.0) {
|
|
|
|
+ uv = direction.xy / abs(direction.z);
|
|
|
|
+ } else if (face == 3.0) {
|
|
|
|
+ uv = vec2(direction.z, direction.y) / abs(direction.x);
|
|
|
|
+ } else if (face == 4.0) {
|
|
|
|
+ uv = direction.xz / abs(direction.y);
|
|
|
|
+ } else {
|
|
|
|
+ uv = vec2(-direction.x, direction.y) / abs(direction.z);
|
|
|
|
+ }
|
|
|
|
+ return 0.5 * (uv + 1.0);
|
|
|
|
+}
|
|
|
|
|
|
- // Tri linear interpolation.
|
|
|
|
- vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);
|
|
|
|
- vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));
|
|
|
|
|
|
+vec3 bilinearCubeUV(sampler2D envMap, vec3 direction, float mipInt) {
|
|
|
|
+ float face = getFace(direction);
|
|
|
|
+ float filterInt = max(cubeUV_minMipLevel - mipInt, 0.0);
|
|
|
|
+ mipInt = max(mipInt, cubeUV_minMipLevel);
|
|
|
|
+ float faceSize = exp2(mipInt);
|
|
|
|
+
|
|
|
|
+ float texelSize = 1.0 / (3.0 * cubeUV_maxTileSize);
|
|
|
|
+
|
|
|
|
+ vec2 uv = getUV(direction, face) * (faceSize - 1.0);
|
|
|
|
+ vec2 f = fract(uv);
|
|
|
|
+ uv += 0.5 - f;
|
|
|
|
+ if (face > 2.0) {
|
|
|
|
+ uv.y += faceSize;
|
|
|
|
+ face -= 3.0;
|
|
|
|
+ }
|
|
|
|
+ uv.x += face * faceSize;
|
|
|
|
+ if(mipInt < cubeUV_maxMipLevel){
|
|
|
|
+ uv.y += 2.0 * cubeUV_maxTileSize;
|
|
|
|
+ }
|
|
|
|
+ uv.y += filterInt * 2.0 * cubeUV_minTileSize;
|
|
|
|
+ uv.x += 3.0 * max(0.0, cubeUV_maxTileSize - 2.0 * faceSize);
|
|
|
|
+ uv *= texelSize;
|
|
|
|
+
|
|
|
|
+ vec3 tl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
|
|
|
|
+ uv.x += texelSize;
|
|
|
|
+ vec3 tr = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
|
|
|
|
+ uv.y += texelSize;
|
|
|
|
+ vec3 br = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
|
|
|
|
+ uv.x -= texelSize;
|
|
|
|
+ vec3 bl = envMapTexelToLinear(texture2D(envMap, uv)).rgb;
|
|
|
|
+ vec3 tm = mix(tl, tr, f.x);
|
|
|
|
+ vec3 bm = mix(bl, br, f.x);
|
|
|
|
+ return mix(tm, bm, f.y);
|
|
|
|
+}
|
|
|
|
|
|
- vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);
|
|
|
|
- vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));
|
|
|
|
|
|
+// These defines must match with PMREMGenerator
|
|
|
|
+
|
|
|
|
+#define r0 1.0
|
|
|
|
+#define v0 0.339
|
|
|
|
+#define m0 -2.0
|
|
|
|
+#define r1 0.8
|
|
|
|
+#define v1 0.276
|
|
|
|
+#define m1 -1.0
|
|
|
|
+#define r4 0.4
|
|
|
|
+#define v4 0.046
|
|
|
|
+#define m4 2.0
|
|
|
|
+#define r5 0.305
|
|
|
|
+#define v5 0.016
|
|
|
|
+#define m5 3.0
|
|
|
|
+#define r6 0.21
|
|
|
|
+#define v6 0.0038
|
|
|
|
+#define m6 4.0
|
|
|
|
+
|
|
|
|
+float roughnessToVariance(float roughness) {
|
|
|
|
+ float variance = 0.0;
|
|
|
|
+ if (roughness >= r1) {
|
|
|
|
+ variance = (r0 - roughness) * (v1 - v0) / (r0 - r1) + v0;
|
|
|
|
+ } else if (roughness >= r4) {
|
|
|
|
+ variance = (r1 - roughness) * (v4 - v1) / (r1 - r4) + v1;
|
|
|
|
+ } else if (roughness >= r5) {
|
|
|
|
+ variance = (r4 - roughness) * (v5 - v4) / (r4 - r5) + v4;
|
|
|
|
+ } else {
|
|
|
|
+ float roughness2 = roughness * roughness;
|
|
|
|
+ variance = 1.79 * roughness2 * roughness2;
|
|
|
|
+ }
|
|
|
|
+ return variance;
|
|
|
|
+}
|
|
|
|
|
|
- vec4 result = mix(color10, color20, t);
|
|
|
|
|
|
+float varianceToRoughness(float variance) {
|
|
|
|
+ float roughness = 0.0;
|
|
|
|
+ if (variance >= v1) {
|
|
|
|
+ roughness = (v0 - variance) * (r1 - r0) / (v0 - v1) + r0;
|
|
|
|
+ } else if (variance >= v4) {
|
|
|
|
+ roughness = (v1 - variance) * (r4 - r1) / (v1 - v4) + r1;
|
|
|
|
+ } else if (variance >= v5) {
|
|
|
|
+ roughness = (v4 - variance) * (r5 - r4) / (v4 - v5) + r4;
|
|
|
|
+ } else {
|
|
|
|
+ roughness = pow(0.559 * variance, 0.25);// 0.559 = 1.0 / 1.79
|
|
|
|
+ }
|
|
|
|
+ return roughness;
|
|
|
|
+}
|
|
|
|
|
|
- return vec4(result.rgb, 1.0);
|
|
|
|
|
|
+float roughnessToMip(float roughness) {
|
|
|
|
+ float mip = 0.0;
|
|
|
|
+ if (roughness >= r1) {
|
|
|
|
+ mip = (r0 - roughness) * (m1 - m0) / (r0 - r1) + m0;
|
|
|
|
+ } else if (roughness >= r4) {
|
|
|
|
+ mip = (r1 - roughness) * (m4 - m1) / (r1 - r4) + m1;
|
|
|
|
+ } else if (roughness >= r5) {
|
|
|
|
+ mip = (r4 - roughness) * (m5 - m4) / (r4 - r5) + m4;
|
|
|
|
+ } else if (roughness >= r6) {
|
|
|
|
+ mip = (r5 - roughness) * (m6 - m5) / (r5 - r6) + m5;
|
|
|
|
+ } else {
|
|
|
|
+ mip = -2.0 * log2(1.16 * roughness);// 1.16 = 1.79^0.25
|
|
|
|
+ }
|
|
|
|
+ return mip;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+vec4 textureCubeUV(sampler2D envMap, vec3 sampleDir, float roughness) {
|
|
|
|
+ float mip = clamp(roughnessToMip(roughness), m0, cubeUV_maxMipLevel);
|
|
|
|
+ float mipF = fract(mip);
|
|
|
|
+ float mipInt = floor(mip);
|
|
|
|
+
|
|
|
|
+ vec3 color0 = bilinearCubeUV(envMap, sampleDir, mipInt);
|
|
|
|
+ if (mipF == 0.0) {
|
|
|
|
+ return vec4(color0, 1.0);
|
|
|
|
+ } else {
|
|
|
|
+ vec3 color1 = bilinearCubeUV(envMap, sampleDir, mipInt + 1.0);
|
|
|
|
+ return vec4(mix(color0, color1, mipF), 1.0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
#endif
|
|
#endif
|
|
`;
|
|
`;
|