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",