diff --git a/docs/api/textures/Texture.html b/docs/api/textures/Texture.html index 2cb7f85963ead9..1ec2e2c791f416 100644 --- a/docs/api/textures/Texture.html +++ b/docs/api/textures/Texture.html @@ -128,6 +128,11 @@

[property:boolean premultiplyAlpha]

False by default, which is the norm for PNG images. Set to true if the RGB values have been stored premultiplied by alpha. +

[property:number encoding]

+
+ Set to THREE.LinearEncoding by default, but supports sRGB, RGBE, RGBM, RGBD, LogLuv and Gamma corrected encodings. IMPORTANT: If this value is changed on a texture after the material has been used, it is necessary to trigger a Material.needsUpdate for this value to be realized in the shader. +
+

[property:object onUpdate]

A callback function, called when the texture is updated (e.g., when needsUpdate has been set to true and then the texture is used). diff --git a/src/Three.js b/src/Three.js index 13ad4071792e8f..1345fa28922125 100644 --- a/src/Three.js +++ b/src/Three.js @@ -305,3 +305,14 @@ THREE.WrapAroundEnding = 2402; THREE.TrianglesDrawMode = 0; THREE.TriangleStripDrawMode = 1; THREE.TriangleFanDrawMode = 2; + +// Texture Encodings + +THREE.LinearEncoding = 3000; // No encoding at all. +THREE.sRGBEncoding = 3001; +THREE.RGBEEncoding = 3002; // AKA Radiance +THREE.LogLuvEncoding = 3003; +THREE.RGBM7Encoding = 3004; +THREE.RGBM16Encoding = 3005; +THREE.RGBDEncoding = 3006; // MaxRange is 256 +THREE.GammaEncoding = 3007; // uses GAMMA_FACTOR diff --git a/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl b/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl index 62cee1ce0894d8..c0951b050bda16 100644 --- a/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl @@ -2,7 +2,7 @@ vec4 emissiveColor = texture2D( emissiveMap, vUv ); - emissiveColor.rgb = inputToLinear( emissiveColor.rgb ); + emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb; totalEmissiveLight *= emissiveColor.rgb; diff --git a/src/renderers/shaders/ShaderChunk/encodings.glsl b/src/renderers/shaders/ShaderChunk/encodings.glsl new file mode 100644 index 00000000000000..3a4ef0471e0d3f --- /dev/null +++ b/src/renderers/shaders/ShaderChunk/encodings.glsl @@ -0,0 +1,93 @@ +// For a discussion of what this is, please read this: http://lousodrome.net/blog/light/2013/05/26/gamma-correct-and-hdr-rendering-in-a-32-bits-buffer/ + +// These encodings should have the same integer values as THREE.LinearEncoding, THREE.sRGBEncoding, etc... +#define ENCODING_Linear 3000 +#define ENCODING_sRGB 3001 +#define ENCODING_RGBE 3002 +#define ENCODING_LogLuv 3003 +#define ENCODING_RGBM7 3004 +#define ENCODING_RGBM16 3005 +#define ENCODING_RGBD 3006 +#define ENCODING_Gamma 3007 + +vec4 LinearToLinear( in vec4 value ) { + return value; +} + +vec4 GammaToLinear( in vec4 value, in float gammaFactor ) { + return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w ); +} +vec4 LinearTosGamma( in vec4 value, in float gammaFactor ) { + return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w ); +} + +vec4 sRGBToLinear( in vec4 value ) { + return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w ); +} +vec4 LinearTosRGB( in vec4 value ) { + return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w ); +} + +vec4 RGBEToLinear( in vec4 value ) { + return vec4( value.xyz * exp2( value.w*256.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 ); +} + +// reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html +vec4 RGBMToLinear( in vec4 value, in float maxRange ) { + return vec4( value.xyz * value.w * maxRange, 1.0 ); +} +vec4 LinearToRGBM( in vec4 value, in float maxRange ) { + float maxRGB = max( value.x, max( value.g, value.b ) ); + float M = maxRGB / maxRange; + M = ceil( M * 255.0 ) / 255.0; + return vec4( value.rgb / ( M * maxRange ), M ); +} + +// reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html +vec4 RGBDToLinear( in vec4 value, in float maxRange ) { + return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 ); +} +vec4 LinearToRGBD( in vec4 value, in float maxRange ) { + float maxRGB = max( value.x, max( value.g, value.b ) ); + float D = max( maxRange / maxRGB, 1.0 ); + D = saturate( floor( D ) / 255.0 ); + return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D ); +} + +// LogLuv reference: http://graphicrants.blogspot.ca/2009/04/rgbm-color-encoding.html + +// M matrix, for encoding +const mat3 cLogLuvM = mat3( + 0.2209, 0.3390, 0.4184, + 0.1138, 0.6780, 0.7319, + 0.0102, 0.1130, 0.2969); +vec4 LinearToLogLuv( in vec4 value ) { + vec3 Xp_Y_XYZp = value.rgb * cLogLuvM; + Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6)); + vec4 vResult; + vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z; + float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0; + vResult.w = fract(Le); + vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0; + return vResult; +} + +// Inverse M matrix, for decoding +const mat3 cLogLuvInverseM = mat3( + 6.0014, -2.7008, -1.7996, + -1.3320, 3.1029, -5.7721, + 0.3008, -1.0882, 5.6268); +vec4 LogLuvToLinear( in vec4 value ) { + float Le = value.z * 255.0 + value.w; + vec3 Xp_Y_XYZp; + Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0); + Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y; + Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z; + vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM; + return vec4( max(vRGB, 0.0), 1.0 ); +} diff --git a/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl b/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl index 1d125de494f377..bb24b40d9f1fb4 100644 --- a/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl @@ -43,7 +43,7 @@ vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 ); #endif - envColor.xyz = inputToLinear( envColor.xyz ); + envColor = envMapTexelToLinear( envColor ); #ifdef ENVMAP_BLENDING_MULTIPLY diff --git a/src/renderers/shaders/ShaderChunk/lights_pars.glsl b/src/renderers/shaders/ShaderChunk/lights_pars.glsl index 4fb115af5520b7..fe1c20e6fe2543 100644 --- a/src/renderers/shaders/ShaderChunk/lights_pars.glsl +++ b/src/renderers/shaders/ShaderChunk/lights_pars.glsl @@ -185,7 +185,7 @@ #endif - envMapColor.rgb = inputToLinear( envMapColor.rgb ); + envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; return PI * envMapColor.rgb * envMapIntensity; @@ -277,7 +277,7 @@ #endif - envMapColor.rgb = inputToLinear( envMapColor.rgb ); + envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb; return envMapColor.rgb * envMapIntensity; diff --git a/src/renderers/shaders/ShaderChunk/map_fragment.glsl b/src/renderers/shaders/ShaderChunk/map_fragment.glsl index 8620c75239937c..b5fb51b16c7ecf 100644 --- a/src/renderers/shaders/ShaderChunk/map_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/map_fragment.glsl @@ -2,8 +2,7 @@ vec4 texelColor = texture2D( map, vUv ); - texelColor.xyz = inputToLinear( texelColor.xyz ); - + texelColor = mapTexelToLinear( texelColor ); diffuseColor *= texelColor; #endif diff --git a/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl b/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl index 754690cc63ce82..d9a03464ba71ec 100644 --- a/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl @@ -2,4 +2,4 @@ uniform sampler2D map; -#endif \ No newline at end of file +#endif diff --git a/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl b/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl index 1bc3cbe59852ef..166b5ec30d4b41 100644 --- a/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl +++ b/src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl @@ -1,5 +1,6 @@ #ifdef USE_MAP - diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy ); - + vec4 mapTexel= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy ); + diffuseColor *= mapTexelToLinear( mapTexel ); + #endif diff --git a/src/renderers/shaders/ShaderLib.js b/src/renderers/shaders/ShaderLib.js index fa27a57805cabb..f22b73fd41306d 100644 --- a/src/renderers/shaders/ShaderLib.js +++ b/src/renderers/shaders/ShaderLib.js @@ -71,6 +71,7 @@ THREE.ShaderLib = { "#endif", '#include ', + '#include ', '#include ', '#include ', '#include ', @@ -199,6 +200,7 @@ THREE.ShaderLib = { "#endif", '#include ', + '#include ', '#include ', '#include ', '#include ', @@ -363,6 +365,7 @@ THREE.ShaderLib = { "uniform float opacity;", '#include ', + '#include ', '#include ', '#include ', '#include ', @@ -526,6 +529,7 @@ THREE.ShaderLib = { "#endif", '#include ', + '#include ', '#include ', '#include ', '#include ', @@ -630,6 +634,7 @@ THREE.ShaderLib = { "uniform float opacity;", '#include ', + '#include ', '#include ', '#include ', '#include ', diff --git a/src/renderers/shaders/UniformsLib.js b/src/renderers/shaders/UniformsLib.js index d50f89fad55827..1c7f189e9c0cdb 100644 --- a/src/renderers/shaders/UniformsLib.js +++ b/src/renderers/shaders/UniformsLib.js @@ -38,7 +38,7 @@ THREE.UniformsLib = { emissivemap: { - "emissiveMap": { type: "t", value: null } + "emissiveMap": { type: "t", value: null }, }, diff --git a/src/renderers/webgl/WebGLProgram.js b/src/renderers/webgl/WebGLProgram.js index 38f62e72c9cdb2..25db38731ad5a8 100644 --- a/src/renderers/webgl/WebGLProgram.js +++ b/src/renderers/webgl/WebGLProgram.js @@ -7,6 +7,37 @@ THREE.WebGLProgram = ( function () { var arrayStructRe = /^([\w\d_]+)\[(\d+)\]\.([\w\d_]+)$/; var arrayRe = /^([\w\d_]+)\[0\]$/; + function getTexelDecodingFunction( functionName, encoding ) { + var code = "vec4 " + functionName + "( vec4 value ) { return "; + switch( encoding ) { + case THREE.LinearEncoding: + code += "value"; + break; + case THREE.sRGBEncoding: + code += "sRGBToLinear( value )"; + break; + case THREE.RGBEEncoding: + code += "RGBEToLinear( value )"; + break; + case THREE.RGBM7Encoding: + code += "RGBMToLinear( value, 7.0 )"; + break; + case THREE.RGBM16Encoding: + code += "RGBMToLinear( value, 16.0 )"; + break; + case THREE.RGBDEncoding: + code += "RGBDToLinear( value, 256.0 )"; + break; + case THREE.GammaEncoding: + code += "GammaToLinear( value, float( GAMMA_FACTOR ) )"; + break; + default: + throw new Error( "unsupported encoding: " + encoding ); + } + code += "; }"; + return code; + } + function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; @@ -420,13 +451,16 @@ THREE.WebGLProgram = ( function () { ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', + parameters.mapEncoding ? getTexelDecodingFunction( "mapTexelToLinear", material.map.encoding ) : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapTypeDefine : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.envMap ? '#define ' + envMapBlendingDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.envMapEncoding ? getTexelDecodingFunction( "envMapTexelToLinear", material.envMap.encoding ) : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.emissiveMapEncoding ? getTexelDecodingFunction( "emissiveMapTexelToLinear", material.emissiveMap.encoding ) : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', diff --git a/src/renderers/webgl/WebGLPrograms.js b/src/renderers/webgl/WebGLPrograms.js index ca8dfee78788ac..3b8443f2dfa387 100644 --- a/src/renderers/webgl/WebGLPrograms.js +++ b/src/renderers/webgl/WebGLPrograms.js @@ -15,8 +15,8 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) { }; var parameterNames = [ - "precision", "supportsVertexTextures", "map", "envMap", "envMapMode", - "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "displacementMap", "specularMap", + "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", + "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", @@ -96,11 +96,14 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) { supportsVertexTextures: capabilities.vertexTextures, map: !! material.map, + mapEncoding: ( !! material.map ) ? material.map.encoding : false, envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, + envMapEncoding: ( !! material.envMap ) ? material.envMap.encoding : false, lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, + emissiveMapEncoding: ( !! material.emissiveMap ) ? material.emissiveMap.encoding : false, bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, displacementMap: !! material.displacementMap, diff --git a/src/textures/Texture.js b/src/textures/Texture.js index 44a57efc569a85..e99e97c0f98f85 100644 --- a/src/textures/Texture.js +++ b/src/textures/Texture.js @@ -35,7 +35,14 @@ THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, f this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + + + // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. + // + // Also changing the encoding after already used by a Material will not automatically make the Material + // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. + this.encoding = THREE.LinearEncoding; this.version = 0; this.onUpdate = null; @@ -86,6 +93,7 @@ THREE.Texture.prototype = { this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; + this.encoding = source.encoding; return this; diff --git a/utils/build/includes/common.json b/utils/build/includes/common.json index 6112f3ce401627..cb01a361b75bb2 100644 --- a/utils/build/includes/common.json +++ b/utils/build/includes/common.json @@ -137,6 +137,7 @@ "src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl", "src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl", "src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl", + "src/renderers/shaders/ShaderChunk/encodings.glsl", "src/renderers/shaders/ShaderChunk/envmap_fragment.glsl", "src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl", "src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl",