diff --git a/examples/files.js b/examples/files.js index 9bbf2d459e8843686cc7cad5758f45179e391e4f..0efe36dd30ac7685f19e169e33fe6a7219249819 100644 --- a/examples/files.js +++ b/examples/files.js @@ -119,6 +119,7 @@ var files = { "webgl_materials_cubemap_refraction", "webgl_materials_displacementmap", "webgl_materials_envmaps", + "webgl_materials_envmaps_hdr", "webgl_materials_grass", "webgl_materials_lightmap", "webgl_materials_nodes", diff --git a/examples/js/Encodings.js b/examples/js/Encodings.js new file mode 100644 index 0000000000000000000000000000000000000000..21c43d20008f62bf770389e34e7293c382ceb245 --- /dev/null +++ b/examples/js/Encodings.js @@ -0,0 +1,26 @@ +/** + * @author Ben Houston / http://clara.io / bhouston + * @author Prashant Sharma / spidersharma03 + */ + +THREE.Encodings = function() { + if( THREE.toHalf === undefined ) throw new Error("THREE.Encodings is required for HDRCubeMapLoader when loading half data."); +} + +THREE.Encodings.RGBEByteToRGBFloat = function( sourceArray, sourceOffset, destArray, destOffset ) { + var e = sourceArray[sourceOffset+3]; + var scale = Math.pow(2.0, e - 128.0) / 255.0; + + destArray[destOffset+0] = sourceArray[sourceOffset+0] * scale; + destArray[destOffset+1] = sourceArray[sourceOffset+1] * scale; + destArray[destOffset+2] = sourceArray[sourceOffset+2] * scale; +} + +THREE.Encodings.RGBEByteToRGBHalf = function( sourceArray, sourceOffset, destArray, destOffset ) { + var e = sourceArray[sourceOffset+3]; + var scale = Math.pow(2.0, e - 128.0) / 255.0; + + destArray[destOffset+0] = THREE.toHalf( sourceArray[sourceOffset+0] * scale ); + destArray[destOffset+1] = THREE.toHalf( sourceArray[sourceOffset+1] * scale ); + destArray[destOffset+2] = THREE.toHalf( sourceArray[sourceOffset+2] * scale ); +} diff --git a/examples/js/Half.js b/examples/js/Half.js new file mode 100644 index 0000000000000000000000000000000000000000..8e8b7ca73028d6087a75acab6f564cbe35c2fd10 --- /dev/null +++ b/examples/js/Half.js @@ -0,0 +1,51 @@ +/** + * Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410 + */ + +THREE.toHalf = (function() { + var floatView = new Float32Array(1); + var int32View = new Int32Array(floatView.buffer); + + /* This method is faster than the OpenEXR implementation (very often + * used, eg. in Ogre), with the additional benefit of rounding, inspired + * by James Tursa?s half-precision code. */ + return function toHalf(val) { + + floatView[0] = val; + var x = int32View[0]; + + var bits = (x >> 16) & 0x8000; /* Get the sign */ + var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */ + var e = (x >> 23) & 0xff; /* Using int is faster here */ + + /* If zero, or denormal, or exponent underflows too much for a denormal + * half, return signed zero. */ + if (e < 103) { + return bits; + } + + /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ + if (e > 142) { + bits |= 0x7c00; + /* If exponent was 0xff and one mantissa bit was set, it means NaN, + * not Inf, so make sure we set one mantissa bit too. */ + bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff); + return bits; + } + + /* If exponent underflows but not too much, return a denormal */ + if (e < 113) { + m |= 0x0800; + /* Extra rounding may overflow and set mantissa to 0 and exponent + * to 1, which is OK. */ + bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1); + return bits; + } + + bits |= ((e - 112) << 10) | (m >> 1); + /* Extra rounding. An overflow will set mantissa to 0 and increment + * the exponent, which is OK. */ + bits += m & 1; + return bits; + }; +}()); diff --git a/examples/js/loaders/HDRCubeMapLoader.js b/examples/js/loaders/HDRCubeMapLoader.js new file mode 100644 index 0000000000000000000000000000000000000000..c65cd6d042c06b69eb0f464e9b6ebeeb967acac2 --- /dev/null +++ b/examples/js/loaders/HDRCubeMapLoader.js @@ -0,0 +1,83 @@ +/** +* @author Prashant Sharma / spidersharma03 +* @author Ben Houston / http://clara.io / bhouston +*/ + +THREE.HDRCubeMapLoader = function (manager) { + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + // override in sub classes + this.hdrLoader = new THREE.RGBELoader(); + + if( THREE.Encodings === undefined ) throw new Error( "HDRCubeMapLoader requires THREE.Encodings" ); +} + +THREE.HDRCubeMapLoader.prototype.load = function(type, urls, onLoad, onProgress, onError) { + var texture = new THREE.CubeTexture( [] ); + + texture.type = type; + texture.encoding = (type === THREE.UnsignedByteType) ? THREE.RGBEEncoding : THREE.LinearEncoding; + texture.format = (type === THREE.UnsignedByteType ) ? THREE.RGBAFormat : THREE.RGBFormat; + texture.minFilter = (texture.encoding === THREE.RGBEEncoding ) ? THREE.NearestFilter : THREE.LinearFilter; + texture.magFilter = (texture.encoding === THREE.RGBEEncoding ) ? THREE.NearestFilter : THREE.LinearFilter; + texture.generateMipmaps = (texture.encoding !== THREE.RGBEEncoding ); + texture.anisotropy = 0; + + var scope = this.hdrLoader; + + var loaded = 0; + + function loadHDRData(i, onLoad, onProgress, onError) { + var loader = new THREE.XHRLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + + loader.load( urls[i], function ( buffer ) { + loaded++; + + var texData = scope._parser( buffer ); + + if ( ! texData ) return; + + if(type === THREE.FloatType) { + var numElements = ( texData.data.length / 4 )*3; + var floatdata = new Float32Array( numElements ); + for( var j=0; j 16 ) + c *= 2; + var nMips = size > 16 ? 6 : 1; + var mipOffsetX = 0; + var mipOffsetY = 0; + var mipSize = size; + + for ( var j = 0; j < nMips; j ++ ) { + + // Mip Maps + for ( var k = 0; k < 6; k ++ ) { + + // 6 Cube Faces + var material = this.getShader(); + material.uniforms[ "envMap" ].value = this.cubeLods[ i ]; + material.envMap = this.cubeLods[ i ] + material.uniforms[ "faceIndex" ].value = k; + material.uniforms[ "mapSize" ].value = mipSize; + var color = material.uniforms[ "testColor" ].value; + //color.copy(testColor[j]); + var planeMesh = new THREE.Mesh( + new THREE.PlaneGeometry( mipSize, mipSize, 0 ), + material ); + planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX; + planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY; + planeMesh.material.side = THREE.DoubleSide; + this.scene.add( planeMesh ); + this.objects.push( planeMesh ); + + } + mipOffsetY += 1.75 * mipSize; + mipOffsetX += 1.25 * mipSize; + mipSize /= 2; + + } + offset2 += 2 * size; + if ( size > 16 ) + size /= 2; + + } + +}; + +THREE.PMREMCubeUVPacker.prototype = { + + constructor : THREE.PMREMCubeUVPacker, + + update: function( renderer ) { + + var gammaInput = renderer.gammaInput; + var gammaOutput = renderer.gammaOutput; + renderer.gammaInput = false; + renderer.gammaOutput = false; + + renderer.render( this.scene, this.camera, this.CubeUVRenderTarget, true ); + + renderer.gammaInput = renderer.gammaInput; + renderer.gammaOutput = renderer.gammaOutput; + }, + + getShader: function() { + + var shaderMaterial = new THREE.ShaderMaterial( { + + uniforms: { + "faceIndex": { type: 'i', value: 0 }, + "mapSize": { type: 'f', value: 0 }, + "envMap": { type: 't', value: null }, + "testColor": { type: 'v3', value: new THREE.Vector3( 1, 1, 1 ) } + }, + + vertexShader: + "precision highp float;\ + varying vec2 vUv;\ + void main() {\ + vUv = uv;\ + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\ + }", + + fragmentShader: + "precision highp float;\ + varying vec2 vUv;\ + uniform samplerCube envMap;\ + uniform float mapSize;\ + uniform vec3 testColor;\ + uniform int faceIndex;\ + \ + void main() {\ + vec3 sampleDirection;\ + vec2 uv = vUv;\ + uv = uv * 2.0 - 1.0;\ + uv.y *= -1.0;\ + if(faceIndex == 0) {\ + sampleDirection = normalize(vec3(1.0, uv.y, -uv.x));\ + }\ + else if(faceIndex == 1) {\ + sampleDirection = normalize(vec3(uv.x, 1.0, uv.y));\ + }\ + else if(faceIndex == 2) {\ + sampleDirection = normalize(vec3(uv.x, uv.y, 1.0));\ + }\ + else if(faceIndex == 3) {\ + sampleDirection = normalize(vec3(-1.0, uv.y, uv.x));\ + }\ + else if(faceIndex == 4) {\ + sampleDirection = normalize(vec3(uv.x, -1.0, -uv.y));\ + }\ + else {\ + sampleDirection = normalize(vec3(-uv.x, uv.y, -1.0));\ + }\ + vec4 color = envMapTexelToLinear( textureCube( envMap, sampleDirection ) );\ + gl_FragColor = linearToOutputTexel( color * vec4(testColor, 1.0) );\ + }", + + blending: THREE.CustomBlending, + blendSrc: THREE.OneFactor, + blendDst: THREE.ZeroFactor, + blendSrcAlpha: THREE.OneFactor, + blendDstAlpha: THREE.ZeroFactor, + blendEquation: THREE.AddEquation + }); + + return shaderMaterial; + + } + +}; diff --git a/examples/js/pmrem/PMREMGenerator.js b/examples/js/pmrem/PMREMGenerator.js new file mode 100644 index 0000000000000000000000000000000000000000..6bf13e0db23753c57e6e54a97e0eb10d870b8d9b --- /dev/null +++ b/examples/js/pmrem/PMREMGenerator.js @@ -0,0 +1,232 @@ +/** + * @author Prashant Sharma / spidersharma03 + * @author Ben Houston / bhouston, https://clara.io + */ + + THREE.PMREMGenerator = function( cubeTexture ) { + if ( cubeTexture instanceof THREE.CubeTexture ) { + + if ( cubeTexture.images[ 0 ] === undefined ) + console.error( "CubeTexture Not Initialized" ); + if(cubeTexture.images[ 0 ] instanceof THREE.DataTexture) { + this.resolution = cubeTexture.images[ 0 ].image.width; + } + else { + this.resolution = cubeTexture.images[ 0 ].width; + } + + } + else if ( cubeTexture instanceof THREE.WebGLRenderTargetCube ) { + if ( cubeTexture === undefined ) console.error( "Render Target Not Initialized" ); + this.resolution = cubeTexture.width; + } + else { + console.error( "Wrong Input to PMREMGenerator" ); + } + this.sourceTexture = cubeTexture; + + this.cubeLods = []; + + var size = this.resolution; + var params = { format: this.sourceTexture.format, magFilter: this.sourceTexture.magFilter, minFilter: this.sourceTexture.minFilter, type: this.sourceTexture.type }; + + this.numLods = Math.log2( size ) - 2; + for ( var i = 0; i < this.numLods; i ++ ) { + var renderTarget = new THREE.WebGLRenderTargetCube( size, size, params ); + renderTarget.texture.generateMipmaps = this.sourceTexture.generateMipmaps; + renderTarget.texture.anisotropy = this.sourceTexture.anisotropy; + renderTarget.texture.encoding = this.sourceTexture.encoding; + renderTarget.texture.minFilter = this.sourceTexture.minFilter; + renderTarget.texture.magFilter = this.sourceTexture.magFilter; + this.cubeLods.push( renderTarget ); + size = Math.max( 16, size / 2 ); + } + + this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0.0, 1000 ); + + this.shader = this.getShader(); + this.planeMesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2, 0 ), this.shader ); + this.planeMesh.material.side = THREE.DoubleSide; + this.scene = new THREE.Scene(); + this.scene.add( this.planeMesh ); + this.scene.add( this.camera ); + + this.shader.uniforms[ "envMap" ].value = this.sourceTexture; + this.shader.envMap = this.sourceTexture; +}; + +THREE.PMREMGenerator.prototype = { + + constructor : THREE.PMREMGenerator, + + update: function( renderer ) { + + this.shader.uniforms[ "envMap" ].value = this.sourceTexture; + this.shader.envMap = this.sourceTexture; + + var gammaInput = renderer.gammaInput; + var gammaOutput = renderer.gammaOutput; + renderer.gammaInput = false; + renderer.gammaOutput = false; + for ( var i = 0; i < this.numLods; i ++ ) { + + var r = i / ( this.numLods - 1 ); + this.shader.uniforms[ "roughness" ].value = r * 0.9; + var size = this.cubeLods[ i ].width; + this.shader.uniforms[ "mapSize" ].value = size; + this.renderToCubeMapTarget( renderer, this.cubeLods[ i ] ); + if ( i < 5 ) + this.shader.uniforms[ "envMap" ].value = this.cubeLods[ i ]; + + } + + renderer.gammaInput = renderer.gammaInput; + renderer.gammaOutput = renderer.gammaOutput; + + }, + + renderToCubeMapTarget: function( renderer, renderTarget ) { + + for ( var i = 0; i < 6; i ++ ) { + this.renderToCubeMapTargetFace( renderer, renderTarget, i ) + } + + }, + + renderToCubeMapTargetFace: function( renderer, renderTarget, faceIndex ) { + renderTarget.activeCubeFace = faceIndex; + this.shader.uniforms[ "faceIndex" ].value = faceIndex; + renderer.render( this.scene, this.camera, renderTarget, true ); + + }, + + getShader: function() { + + return new THREE.ShaderMaterial( { + + uniforms: { + "faceIndex": { type: 'i', value: 0 }, + "roughness": { type: 'f', value: 0.5 }, + "mapSize": { type: 'f', value: 0.5 }, + "envMap": { type: 't', value: null }, + "testColor": { type: 'v3', value: new THREE.Vector3( 1, 1, 1 ) } + }, + + vertexShader: + "varying vec2 vUv;\n\ + void main() {\n\ + vUv = uv;\n\ + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ + }", + + fragmentShader: + "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\ + float sinTheta = sqrt(1.0 - cosTheta * cosTheta);\n\ + vec3 sampleDir = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);\n\ + return vecSpace * sampleDir;\n\ + }\n\ + vec3 ImportanceSampleGGX( vec2 uv, mat3 vecSpace, float Roughness )\n\ + {\n\ + float a = Roughness * Roughness;\n\ + float Phi = 2.0 * PI * uv.x;\n\ + float CosTheta = sqrt( (1.0 - uv.y) / ( 1.0 + (a*a - 1.0) * uv.y ) );\n\ + float SinTheta = sqrt( 1.0 - CosTheta * CosTheta );\n\ + return vecSpace * vec3(SinTheta * cos( Phi ), SinTheta * sin( Phi ), CosTheta);\n\ + }\n\ + mat3 matrixFromVector(vec3 n) {\n\ + float a = 1.0 / (1.0 + n.z);\n\ + float b = -n.x * n.y * a;\n\ + vec3 b1 = vec3(1.0 - n.x * n.x * a, b, -n.x);\n\ + vec3 b2 = vec3(b, 1.0 - n.y * n.y * a, -n.y);\n\ + return mat3(b1, b2, n);\n\ + }\n\ + \n\ + vec4 testColorMap(float Roughness) {\n\ + vec4 color;\n\ + if(faceIndex == 0)\n\ + color = vec4(1.0,0.0,0.0,1.0);\n\ + else if(faceIndex == 1)\n\ + color = vec4(0.0,1.0,0.0,1.0);\n\ + else if(faceIndex == 2)\n\ + color = vec4(0.0,0.0,1.0,1.0);\n\ + else if(faceIndex == 3)\n\ + color = vec4(1.0,1.0,0.0,1.0);\n\ + else if(faceIndex == 4)\n\ + color = vec4(0.0,1.0,1.0,1.0);\n\ + else\n\ + color = vec4(1.0,0.0,1.0,1.0);\n\ + color *= ( 1.0 - Roughness );\n\ + return color;\n\ + }\n\ + void main() {\n\ + vec3 sampleDirection;\n\ + vec2 uv = vUv*2.0 - 1.0;\n\ + float offset = -1.0/mapSize;\n\ + const float a = -1.0;\n\ + const float b = 1.0;\n\ + float c = -1.0 + offset;\n\ + float d = 1.0 - offset;\n\ + float bminusa = b - a;\n\ + uv.x = (uv.x - a)/bminusa * d - (uv.x - b)/bminusa * c;\n\ + uv.y = (uv.y - a)/bminusa * d - (uv.y - b)/bminusa * c;\n\ + if (faceIndex==0) {\n\ + sampleDirection = vec3(1.0, -uv.y, -uv.x);\n\ + }\n\ + else if (faceIndex==1) {\n\ + sampleDirection = vec3(-1.0, -uv.y, uv.x);\n\ + } else if (faceIndex==2) {\n\ + sampleDirection = vec3(uv.x, 1.0, uv.y);\n\ + } else if (faceIndex==3) {\n\ + sampleDirection = vec3(uv.x, -1.0, -uv.y);\n\ + } else if (faceIndex==4) {\n\ + sampleDirection = vec3(uv.x, -uv.y, 1.0);\n\ + } else {\n\ + sampleDirection = vec3(-uv.x, -uv.y, -1.0);\n\ + }\n\ + mat3 vecSpace = matrixFromVector(normalize(sampleDirection));\n\ + vec3 rgbColor = vec3(0.0);\n\ + const int NumSamples = 1024;\n\ + vec3 vect;\n\ + float weight = 0.0;\n\ + for(int i=0; i + + + threejs webgl - materials + + + + + + +
+
threejs - High dynamic range (RGBE) Image-based Lighting (IBL) using run-time generated pre-filtered roughness mipmaps (PMREM)
+ Created by Prashant Sharma and Ben Houston.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Three.js b/src/Three.js index 6a1e13c88098b50e2deff72655385ddb0c45aba8..1c91569900d61e45d19096308784783f162cae6f 100644 --- a/src/Three.js +++ b/src/Three.js @@ -219,6 +219,8 @@ THREE.EquirectangularReflectionMapping = 303; THREE.EquirectangularRefractionMapping = 304; THREE.SphericalReflectionMapping = 305; +THREE.CubeUVReflectionMapping = 306; +THREE.CubeUVRefractionMapping = 307; // Wrapping modes diff --git a/src/renderers/shaders/ShaderChunk/bsdfs.glsl b/src/renderers/shaders/ShaderChunk/bsdfs.glsl index 449fbce25dd77b35ca3453f3fc41ce53d506767d..7fcf38825eafc6edfb24094a423fbf0df5586e10 100644 --- a/src/renderers/shaders/ShaderChunk/bsdfs.glsl +++ b/src/renderers/shaders/ShaderChunk/bsdfs.glsl @@ -148,3 +148,7 @@ vec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in Ge float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) { return ( 2.0 / square( ggxRoughness + 0.0001 ) - 2.0 ); } + +float BlinnExponentToGGXRoughness( const in float blinnExponent ) { + return sqrt( 2.0 / ( blinnExponent + 2.0 ) ); +} diff --git a/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl b/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl new file mode 100644 index 0000000000000000000000000000000000000000..b5d21ea6e45174d9ab52125394a2f844702e1ffa --- /dev/null +++ b/src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl @@ -0,0 +1,138 @@ +#ifdef ENVMAP_TYPE_CUBE_UV + +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; +} + +vec2 MipLevelInfo( vec3 vec, float textureSize, float roughnessLevel, float roughness ) { + float s = log2(textureSize*0.25) - 1.0; + float scale = pow(2.0, s - 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 + float rangeClamp = pow(2.0, (6.0 - 1.0) * 2.0); + d = clamp(d, 1.0, rangeClamp); + float mipLevel = 0.5 * log2(d); + return vec2(floor(mipLevel), fract(mipLevel)); +} + +vec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel, float textureSize) { + float maxLods = log2(textureSize*0.25) - 2.0; + mipLevel = roughnessLevel > maxLods - 3.0 ? 0.0 : mipLevel; + float a = 16.0/textureSize; + float powScale = pow(2.0,roughnessLevel + mipLevel); + float scale = 1.0/pow(2.0,roughnessLevel + 2.0 + mipLevel); + float mipOffset = 0.75*(1.0 - 1.0/pow(2.0, mipLevel))/pow(2.0,roughnessLevel); + bool bRes = mipLevel == 0.0; + scale = bRes && (scale < a) ? a : scale; + + vec3 r; + vec2 offset; + int face = getFaceFromDirection(direction); + + if( face == 0) { + r = vec3(direction.x, -direction.z, direction.y); + offset = vec2(0.0+mipOffset,0.75/powScale); + 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/powScale); + 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/powScale); + 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/powScale); + 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/powScale); + 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/powScale); + offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y; + } + r = normalize(r); + float texelOffset = 0.5/textureSize; + float s1 = (r.y/abs(r.x) + 1.0)*0.5; + float s2 = (r.z/abs(r.x) + 1.0)*0.5; + vec2 uv = offset + vec2(s1*scale, s2*scale); + float min_x = offset.x + texelOffset; float max_x = offset.x + scale - texelOffset; + float min_y = offset.y + texelOffset; + float max_y = offset.y + scale - texelOffset; + float delx = max_x - min_x; + float dely = max_y - min_y; + uv.x = min_x + s1*delx; + uv.y = min_y + s2*dely; + return uv; +} + +vec3 convertRGBEToRGB(vec4 rgbe) { + float d = pow(2.0, rgbe.w*256.0 - 128.0); + return vec3(rgbe) * d; +} + +vec3 tonemap(vec3 RGB) { + float LogAvgLum = 0.08;//0.08 + float key = 1.0; + float Ywhite = 1e3; + Ywhite *= Ywhite; + float sat = 1.0; + float Ylum = dot(RGB ,vec3(0.2126, 0.7152, 0.0722)); + float Y = key/LogAvgLum * Ylum ; + float Yd = Y * ( 1.0 + Y/Ywhite)/( 1.0 + Y) ; + return Yd * pow(RGB/Ylum ,vec3(sat, sat, sat)); + } + +vec4 textureCubeUV(vec3 reflectedDirection, float roughness, float textureSize) { + float maxLods = log2(textureSize*0.25) - 3.0; + float roughnessVal = roughness*maxLods; + float r1 = floor(roughnessVal); + float r2 = r1 + 1.0; + float t = fract(roughnessVal); + vec2 mipInfo = MipLevelInfo(reflectedDirection, textureSize, r1, roughness); + float s = mipInfo.y; + float level0 = mipInfo.x; + float level1 = level0 + 1.0; + level1 = level1 > 5.0 ? 5.0 : level1; + // Tri linear interpolation. + vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0, textureSize); + vec2 uv_11 = getCubeUV(reflectedDirection, r1, level1, textureSize); + vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0, textureSize); + vec2 uv_21 = getCubeUV(reflectedDirection, r2, level1, textureSize); + vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10)); + vec4 color11 = envMapTexelToLinear(texture2D(envMap, uv_11)); + vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20)); + vec4 color21 = envMapTexelToLinear(texture2D(envMap, uv_21)); + vec4 c1 = mix(color10 , color11, s); + vec4 c2 = mix(color20 , color21, s); + vec4 c3 = mix(c1 , c2, t); + return vec4(c3.rgb, 1.0); +} + +#endif diff --git a/src/renderers/shaders/ShaderChunk/encodings.glsl b/src/renderers/shaders/ShaderChunk/encodings.glsl index 2533fa7f37a3defd6d74f650750923ef16a271f3..d63b9fd50ad3d6e8e9fa25af21e48ebccf02f600 100644 --- a/src/renderers/shaders/ShaderChunk/encodings.glsl +++ b/src/renderers/shaders/ShaderChunk/encodings.glsl @@ -19,12 +19,13 @@ vec4 LinearTosRGB( in vec4 value ) { } vec4 RGBEToLinear( in vec4 value ) { - return vec4( value.xyz * exp2( value.w*256.0 - 128.0 ), 1.0 ); + return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 ); } vec4 LinearToRGBE( in vec4 value ) { - float maxComponent = max(max(value.r, value.g), value.b ); - float fExp = ceil( log2(maxComponent) ); - return vec4( value.rgb / exp2(fExp), (fExp + 128.0) / 255.0 ); + float maxComponent = max( max( value.r, value.g ), value.b ); + float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 ); + return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 ); +// return vec4( value.brg, ( 3.0 + 128.0 ) / 256.0 ); } // reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html @@ -33,7 +34,7 @@ vec4 RGBMToLinear( in vec4 value, in float maxRange ) { } vec4 LinearToRGBM( in vec4 value, in float maxRange ) { float maxRGB = max( value.x, max( value.g, value.b ) ); - float M = maxRGB / maxRange; + float M = clamp( maxRGB / maxRange, 0.0, 1.0 ); M = ceil( M * 255.0 ) / 255.0; return vec4( value.rgb / ( M * maxRange ), M ); } diff --git a/src/renderers/shaders/ShaderChunk/lights_pars.glsl b/src/renderers/shaders/ShaderChunk/lights_pars.glsl index fe1c20e6fe2543c23b633cca92a3300185ecb5cb..c27f1ae261505b90f1b3a8938607c4619c107dd9 100644 --- a/src/renderers/shaders/ShaderChunk/lights_pars.glsl +++ b/src/renderers/shaders/ShaderChunk/lights_pars.glsl @@ -179,6 +179,11 @@ #endif + #elif defined( ENVMAP_TYPE_CUBE_UV ) + + vec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz ); + vec4 envMapColor = textureCubeUV(queryVec, 1.0, 1024.0); + #else vec3 envMapColor = vec3( 0.0 ); @@ -245,6 +250,11 @@ #endif + #elif defined( ENVMAP_TYPE_CUBE_UV ) + + vec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ); + vec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent), 1024.0); + #elif defined( ENVMAP_TYPE_EQUIREC ) vec2 sampleUV; diff --git a/src/renderers/shaders/ShaderChunk/lights_template.glsl b/src/renderers/shaders/ShaderChunk/lights_template.glsl index d47aef318e3c298a0ac359f2e74b38ed7328436f..1099c60ced052fa5e116e7c9671e22c4f7681c0c 100644 --- a/src/renderers/shaders/ShaderChunk/lights_template.glsl +++ b/src/renderers/shaders/ShaderChunk/lights_template.glsl @@ -101,12 +101,12 @@ IncidentLight directLight; #endif - // #if defined( USE_ENVMAP ) && defined( STANDARD ) + #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) // TODO, replace 8 with the real maxMIPLevel - // irradiance += getLightProbeIndirectIrradiance( /*lightProbe,*/ geometry, 8 ); // comment out until seams are fixed + irradiance += getLightProbeIndirectIrradiance( /*lightProbe,*/ geometry, 8 ); - // #endif + #endif RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); diff --git a/src/renderers/shaders/ShaderLib/meshstandard_frag.glsl b/src/renderers/shaders/ShaderLib/meshstandard_frag.glsl index 4c13ebfc731821b76bc6cfa08fc9ede1eea4ddf4..f658bcfa65647d0d1772eab4f5271580e5390521 100644 --- a/src/renderers/shaders/ShaderLib/meshstandard_frag.glsl +++ b/src/renderers/shaders/ShaderLib/meshstandard_frag.glsl @@ -29,6 +29,7 @@ varying vec3 vViewPosition; #include #include #include +#include #include #include #include diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 330501249b9e78a31d6092100d3f1ad5229e90a6..b21655019c98ed42f4b50bc34a02926b84e75447 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -51,7 +51,7 @@ THREE.WebGLProgram = ( function () { extensions = extensions || {}; var chunks = [ - ( extensions.derivatives || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '', @@ -279,6 +279,11 @@ THREE.WebGLProgram = ( function () { envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; break; + case THREE.CubeUVReflectionMapping: + case THREE.CubeUVRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; + case THREE.EquirectangularReflectionMapping: case THREE.EquirectangularRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index 9eff101a1427c62242a66793b95fe7c6e325cb64..3c048200536633e09bcaf1d3997bd7c67c6292ed 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -70,18 +70,17 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) { function getTextureEncodingFromMap( map, gammaOverrideLinear ) { var encoding; - if( ! map ) { encoding = THREE.LinearEncoding; } - else if( map instanceof THREE.Texture ) { + else if( map instanceof THREE.Texture || map instanceof THREE.CubeTexture ) { encoding = map.encoding; } - else if( map instanceof THREE.WebGLRenderTarget ) { + else if( map instanceof THREE.WebGLRenderTarget || map instanceof THREE.THREE.WebGLRenderTargetCube ) { encoding = map.texture.encoding; @@ -129,6 +128,8 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) { envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), + envMapCubeUV: (!!material.envMap) && ((material.envMap.mapping === THREE.CubeUVReflectionMapping) || + (material.envMap.mapping === THREE.CubeUVRefractionMapping)), lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, diff --git a/utils/build/includes/common.json b/utils/build/includes/common.json index db065a79cc67c94a446d419e2043438287a283ce..5db2a709ec81eb47087e3e72fee4c9f6d1389770 100644 --- a/utils/build/includes/common.json +++ b/utils/build/includes/common.json @@ -132,6 +132,7 @@ "src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl", "src/renderers/shaders/ShaderChunk/color_vertex.glsl", "src/renderers/shaders/ShaderChunk/common.glsl", + "src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl", "src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl", "src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl", "src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl",