Browse Source

Add high quality rand() function to common.glsl and an example to show off the noise textures. (#8603)

* create procedural glsl and an example to show off the noise textures.

* add to files.js

* adopt noise procedural in PMREMGenerator.

* fix page title per @arose's suggestion.

* more adoption of procedural.glsl

* remove procedural.glsl, add a single line to common.glsl.  Update other files as needed.

* cleanup shaders to use common.glsl

* tabify.

* avoid collapsing into a single function - @WestLangley recommendations

* more adoption of tabs.

* fix tabs.
Ben Houston (Clara.io) 9 years ago
parent
commit
db765284f5

+ 1 - 0
examples/files.js

@@ -190,6 +190,7 @@ var files = {
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_msaa",
 		"webgl_postprocessing_nodes",
+		"webgl_postprocessing_procedural",
 		"webgl_postprocessing_smaa",
 		"webgl_postprocessing_ssao",
 		"webgl_postprocessing_taa",

+ 4 - 7
examples/js/pmrem/PMREMGenerator.js

@@ -150,22 +150,19 @@ THREE.PMREMGenerator.prototype = {
 				}",
 
 			fragmentShader:
-				"varying vec2 vUv;\n\
+				"#include <common>\n\
+				varying vec2 vUv;\n\
 				uniform int faceIndex;\n\
 				uniform float roughness;\n\
 				uniform samplerCube envMap;\n\
 				uniform float mapSize;\n\
 				uniform vec3 testColor;\n\
 				\n\
-				float rnd(vec2 uv) {\n\
-					return fract(sin(dot(uv, vec2(12.9898, 78.233) * 2.0)) * 43758.5453);\n\
-				}\n\
 				float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\
 					float a = ggxRoughness + 0.0001;\n\
 					a *= a;\n\
 					return ( 2.0 / a - 2.0 );\n\
 				}\n\
-				const float PI = 3.14159265358979;\n\
 				vec3 ImportanceSamplePhong(vec2 uv, mat3 vecSpace, float specPow) {\n\
 					float phi = uv.y * 2.0 * PI;\n\
 					float cosTheta = pow(1.0 - uv.x, 1.0 / (specPow + 1.0));\n\
@@ -238,8 +235,8 @@ THREE.PMREMGenerator.prototype = {
 					for(int i=0; i<NumSamples; i++) {\n\
 						float sini = sin(float(i));\n\
 						float cosi = cos(float(i));\n\
-						float rand = rnd(vec2(sini, cosi));\n\
-						vect = ImportanceSampleGGX(vec2(float(i) / float(NumSamples), rand), vecSpace, roughness);\n\
+						float r = rand(vec2(sini, cosi));\n\
+						vect = ImportanceSampleGGX(vec2(float(i) / float(NumSamples), r), vecSpace, roughness);\n\
 						float dotProd = dot(vect, normalize(sampleDirection));\n\
 						weight += dotProd;\n\
 						vec3 color = envMapTexelToLinear(textureCube(envMap,vect)).rgb;\n\

+ 3 - 17
examples/js/shaders/BokehShader2.js

@@ -64,6 +64,8 @@ THREE.BokehShader = {
 
 	fragmentShader: [
 
+		"#include <common>",
+
 		"varying vec2 vUv;",
 
 		"uniform sampler2D tColor;",
@@ -71,8 +73,6 @@ THREE.BokehShader = {
 		"uniform float textureWidth;",
 		"uniform float textureHeight;",
 
-		"const float PI = 3.14159265;",
-
 		"uniform float focalDepth;  //focal distance value in meters, but you may use autofocus option below",
 		"uniform float focalLength; //focal length in mm",
 		"uniform float fstop; //f-stop value",
@@ -226,20 +226,6 @@ THREE.BokehShader = {
 			"return col+mix(vec3(0.0),col,thresh*blur);",
 		"}",
 
-		"vec2 rand(vec2 coord) {",
-			"// generating noise / pattern texture for dithering",
-
-			"float noiseX = ((fract(1.0-coord.s*(textureWidth/2.0))*0.25)+(fract(coord.t*(textureHeight/2.0))*0.75))*2.0-1.0;",
-			"float noiseY = ((fract(1.0-coord.s*(textureWidth/2.0))*0.75)+(fract(coord.t*(textureHeight/2.0))*0.25))*2.0-1.0;",
-
-			"if (noise) {",
-				"noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0;",
-				"noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0;",
-			"}",
-
-			"return vec2(noiseX,noiseY);",
-		"}",
-
 		"vec3 debugFocus(vec3 col, float blur, float depth) {",
 			"float edge = 0.002*depth; //distance based edge smoothing",
 			"float m = clamp(smoothstep(0.0,edge,blur),0.0,1.0);",
@@ -320,7 +306,7 @@ THREE.BokehShader = {
 
 			"// calculation of pattern for dithering",
 
-			"vec2 noise = rand(vUv.xy)*dithering*blur;",
+			"vec2 noise = vec2(rand(vUv.xy), rand( vUv.xy + vec2( 0.4, 0.6 ) ) )*dithering*blur;",
 
 			"// getting blur x and y step factor",
 

+ 4 - 4
examples/js/shaders/FilmShader.js

@@ -48,6 +48,8 @@ THREE.FilmShader = {
 
 	fragmentShader: [
 
+		"#include <common>",
+		
 		// control parameter
 		"uniform float time;",
 
@@ -72,12 +74,10 @@ THREE.FilmShader = {
 			"vec4 cTextureScreen = texture2D( tDiffuse, vUv );",
 
 			// make some noise
-			"float x = vUv.x * vUv.y * time *  1000.0;",
-			"x = mod( x, 13.0 ) * mod( x, 123.0 );",
-			"float dx = mod( x, 0.01 );",
+			"float dx = rand( vUv + time );",
 
 			// add noise
-			"vec3 cResult = cTextureScreen.rgb + cTextureScreen.rgb * clamp( 0.1 + dx * 100.0, 0.0, 1.0 );",
+			"vec3 cResult = cTextureScreen.rgb + cTextureScreen.rgb * clamp( 0.1 + dx, 0.0, 1.0 );",
 
 			// get us a sine and cosine
 			"vec2 sc = vec2( sin( vUv.y * sCount ), cos( vUv.y * sCount ) );",

+ 11 - 15
examples/js/shaders/OceanShaders.js

@@ -46,9 +46,9 @@ THREE.ShaderLib[ 'ocean_subtransform' ] = {
 	},
 	fragmentShader: [
 		//GPU FFT using a Stockham formulation
-		'precision highp float;',
 
-		'const float PI = 3.14159265359;',
+		'precision highp float;',
+		'#include <common>',
 
 		'uniform sampler2D u_input;',
 		'uniform float u_transformSize;',
@@ -96,8 +96,8 @@ THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
 	},
 	fragmentShader: [
 		'precision highp float;',
+		'#include <common>',
 
-		'const float PI = 3.14159265359;',
 		'const float G = 9.81;',
 		'const float KM = 370.0;',
 		'const float CM = 0.23;',
@@ -106,12 +106,8 @@ THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
 		'uniform float u_resolution;',
 		'uniform float u_size;',
 
-		'float square (float x) {',
-			'return x * x;',
-		'}',
-
 		'float omega (float k) {',
-			'return sqrt(G * k * (1.0 + square(k / KM)));',
+			'return sqrt(G * k * (1.0 + pow2(k / KM)));',
 		'}',
 
 		'float tanh (float x) {',
@@ -130,24 +126,24 @@ THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
 			'float l_wind = length(u_wind);',
 
 			'float Omega = 0.84;',
-			'float kp = G * square(Omega / l_wind);',
+			'float kp = G * pow2(Omega / l_wind);',
 
 			'float c = omega(k) / k;',
 			'float cp = omega(kp) / kp;',
 
-			'float Lpm = exp(-1.25 * square(kp / k));',
+			'float Lpm = exp(-1.25 * pow2(kp / k));',
 			'float gamma = 1.7;',
 			'float sigma = 0.08 * (1.0 + 4.0 * pow(Omega, -3.0));',
-			'float Gamma = exp(-square(sqrt(k / kp) - 1.0) / 2.0 * square(sigma));',
+			'float Gamma = exp(-pow2(sqrt(k / kp) - 1.0) / 2.0 * pow2(sigma));',
 			'float Jp = pow(gamma, Gamma);',
 			'float Fp = Lpm * Jp * exp(-Omega / sqrt(10.0) * (sqrt(k / kp) - 1.0));',
 			'float alphap = 0.006 * sqrt(Omega);',
 			'float Bl = 0.5 * alphap * cp / c * Fp;',
 
-			'float z0 = 0.000037 * square(l_wind) / G * pow(l_wind / cp, 0.9);',
+			'float z0 = 0.000037 * pow2(l_wind) / G * pow(l_wind / cp, 0.9);',
 			'float uStar = 0.41 * l_wind / log(10.0 / z0);',
 			'float alpham = 0.01 * ((uStar < CM) ? (1.0 + log(uStar / CM)) : (1.0 + 3.0 * log(uStar / CM)));',
-			'float Fm = exp(-0.25 * square(k / KM - 1.0));',
+			'float Fm = exp(-0.25 * pow2(k / KM - 1.0));',
 			'float Bh = 0.5 * alpham * CM / c * Fm * Lpm;',
 
 			'float a0 = log(2.0) / 4.0;',
@@ -180,8 +176,8 @@ THREE.ShaderLib[ 'ocean_phase' ] = {
 	},
 	fragmentShader: [
 		'precision highp float;',
+		'#include <common>',
 
-		'const float PI = 3.14159265359;',
 		'const float G = 9.81;',
 		'const float KM = 370.0;',
 
@@ -224,8 +220,8 @@ THREE.ShaderLib[ 'ocean_spectrum' ] = {
 	},
 	fragmentShader: [
 		'precision highp float;',
+		'#include <common>',
 
-		'const float PI = 3.14159265359;',
 		'const float G = 9.81;',
 		'const float KM = 370.0;',
 

+ 3 - 9
examples/js/shaders/TriangleBlurShader.js

@@ -34,6 +34,8 @@ THREE.TriangleBlurShader = {
 
 	fragmentShader: [
 
+		"#include <common>",
+
 		"#define ITERATIONS 10.0",
 
 		"uniform sampler2D texture;",
@@ -41,14 +43,6 @@ THREE.TriangleBlurShader = {
 
 		"varying vec2 vUv;",
 
-		"float random( vec3 scale, float seed ) {",
-
-			// use the fragment position for a different seed per-pixel
-
-			"return fract( sin( dot( gl_FragCoord.xyz + seed, scale ) ) * 43758.5453 + seed );",
-
-		"}",
-
 		"void main() {",
 
 			"vec4 color = vec4( 0.0 );",
@@ -57,7 +51,7 @@ THREE.TriangleBlurShader = {
 
 			// randomize the lookup values to hide the fixed number of samples
 
-			"float offset = random( vec3( 12.9898, 78.233, 151.7182 ), 0.0 );",
+			"float offset = rand( vUv );",
 
 			"for ( float t = -ITERATIONS; t <= ITERATIONS; t ++ ) {",
 

+ 182 - 0
examples/webgl_postprocessing_procedural.html

@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing procedural effects</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				margin: 0px;
+				background-color: #000;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				margin: 0px;
+				text-align:center;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - Procedural Effects Example by <a href="https://clara.io" target="_blank">Ben Houston</a><br/><br/>
+		</div>
+
+		<script id="procedural-vert" type="x-shader/x-vertex">
+			varying vec2 vUv;
+
+			void main() {
+				vUv = uv;
+				gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+			}
+		</script>
+		<script id="noiseRandom1D-frag" type="x-shader/x-fragment">
+		#include <common>
+		#include <packing>
+
+			varying vec2 vUv;
+
+			void main() {
+				gl_FragColor.xyz = vec3( rand( vUv ) );
+				gl_FragColor.w = 1.0;
+			}
+		</script>
+		<script id="noiseRandom2D-frag" type="x-shader/x-fragment">
+			#include <common>
+			#include <packing>
+
+			varying vec2 vUv;
+
+			void main() {
+				vec2 rand2 = vec2( rand( vUv ), rand( vUv + vec2( 0.4, 0.6 ) ) );
+				gl_FragColor.xyz = mix( mix( vec3( 1.0, 1.0, 1.0 ), vec3( 0.0, 0.0, 1.0 ), rand2.x ), vec3( 0.0 ), rand2.y );
+				gl_FragColor.w = 1.0;
+			}
+		</script>
+		<script id="noiseRandom3D-frag" type="x-shader/x-fragment">
+			#include <common>
+			#include <packing>
+
+			varying vec2 vUv;
+
+			void main() {
+				vec3 rand3 = vec3( rand( vUv ), rand( vUv + vec2( 0.4, 0.6 ) ), rand( vUv + vec2( 0.6, 0.4 ) ) );
+				gl_FragColor.xyz = rand3;
+				gl_FragColor.w = 1.0;
+			}
+		</script>
+
+		<div id="container"></div>
+
+		<script src="../build/three.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+
+		<script>
+
+			var camera, postScene, renderer, postMaterial, noiseRandom1DMaterial, noiseRandon2DMaterial, noiseRandom3DMaterial, postQuad;
+			var gui, stats, texture;
+
+			var params = { procedure: "noiseRandom3D" };
+
+			init();
+			animate();
+			initGui();
+
+			// Init gui
+			function initGui() {
+
+				var gui = new dat.GUI();
+				gui.add( params, 'procedure', [ 'noiseRandom1D', 'noiseRandom2D', 'noiseRandom3D' ] );
+
+			}
+
+			function init() {
+
+				container = document.getElementById( "container" );
+
+				renderer = new THREE.WebGLRenderer( { antialias: false } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// Setup post processing stage
+				postCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
+				noiseRandom1DMaterial = new THREE.ShaderMaterial({
+					vertexShader: document.querySelector('#procedural-vert').textContent.trim(),
+					fragmentShader: document.querySelector('#noiseRandom1D-frag').textContent.trim(),
+				});
+				noiseRandom2DMaterial = new THREE.ShaderMaterial({
+					vertexShader: document.querySelector('#procedural-vert').textContent.trim(),
+					fragmentShader: document.querySelector('#noiseRandom2D-frag').textContent.trim(),
+				});
+				noiseRandom3DMaterial = new THREE.ShaderMaterial({
+					vertexShader: document.querySelector('#procedural-vert').textContent.trim(),
+					fragmentShader: document.querySelector('#noiseRandom3D-frag').textContent.trim(),
+				});
+				postMaterial = noiseRandom3DMaterial;
+				var postPlane = new THREE.PlaneGeometry(2, 2);
+				postQuad = new THREE.Mesh(postPlane, postMaterial);
+				postScene = new THREE.Scene();
+				postScene.add(postQuad);
+
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				var width = window.innerWidth;
+				var height = window.innerHeight;
+
+				postCamera.aspect = width / height;
+				postCamera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			function animate() {
+
+				this.index = this.index || 0;
+
+				requestAnimationFrame( animate );
+
+				this.index ++;
+
+				switch ( params.procedure ) {
+
+					case 'noiseRandom1D': postMaterial = noiseRandom1DMaterial; break;
+					case 'noiseRandom2D': postMaterial = noiseRandom2DMaterial; break;
+					case 'noiseRandom3D': postMaterial = noiseRandom3DMaterial; break;
+
+				}
+
+				postQuad.material = postMaterial;
+
+
+				// render post FX
+				renderer.render( postScene, postCamera );
+
+				stats.update();
+
+			}
+
+		</script>
+		<div>
+	</body>
+</html>

+ 9 - 3
src/renderers/shaders/ShaderChunk/common.glsl

@@ -1,5 +1,5 @@
-#define PI 3.14159
-#define PI2 6.28318
+#define PI 3.14159265359
+#define PI2 6.28318530718
 #define RECIPROCAL_PI 0.31830988618
 #define RECIPROCAL_PI2 0.15915494
 #define LOG2 1.442695
@@ -12,7 +12,13 @@ float pow2( const in float x ) { return x*x; }
 float pow3( const in float x ) { return x*x*x; }
 float pow4( const in float x ) { float x2 = x*x; return x2*x2; }
 float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }
-
+// expects values in the range of [0,1]x[0,1], returns values in the [0,1] range.
+// do not collapse into a single function per: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
+highp float rand( const in vec2 uv ) {
+	const highp float a = 12.9898, b = 78.233, c = 43758.5453;
+	highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
+	return fract(sin(sn) * c);
+}
 
 struct IncidentLight {
 	vec3 color;

+ 99 - 99
src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl

@@ -3,126 +3,126 @@
 const float 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;
+	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;
 }
 float cubeUV_maxLods1 = log2(cubeUV_textureSize*0.25) - 1.0;
 float 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));
+	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));
 }
 
 float cubeUV_maxLods2 = log2(cubeUV_textureSize*0.25) - 2.0;
 const float 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 );
+	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 cubeUV_maxLods3 = log2(cubeUV_textureSize*0.25) - 3.0;
 
 vec4 textureCubeUV(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;
+	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 );
+	// round to nearest mipmap if we are not interpolating.
+	level0 += min( floor( s + 0.5 ), 5.0 );
 
-    // Tri linear interpolation.
-    vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);
-    vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));
+	// Tri linear interpolation.
+	vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);
+	vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));
 
-    vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);
-    vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));
+	vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);
+	vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));
 
-    vec4 result = mix(color10, color20, t);
+	vec4 result = mix(color10, color20, t);
 
-    return vec4(result.rgb, 1.0);
+	return vec4(result.rgb, 1.0);
 }
 
 #endif