Browse Source

Merge pull request #8117 from bhouston/texture_encoding

Allow textures to be encoded/decoded in Linear,sRGB,RGBE,RGBM, etc
Mr.doob 9 years ago
parent
commit
0c7dbd5703

+ 5 - 0
docs/api/textures/Texture.html

@@ -128,6 +128,11 @@
 		False by default, which is the norm for PNG images. Set to true if the RGB values have been stored premultiplied by alpha.
 		</div>
 
+		<h3>[property:number encoding]</h3>
+		<div>
+		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.
+		</div>
+
 		<h3>[property:object onUpdate]</h3>
 		<div>
 		A callback function, called when the texture is updated (e.g., when needsUpdate has been set to true and then the texture is used).

+ 11 - 0
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

+ 1 - 1
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;
 

+ 93 - 0
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 );
+}

+ 1 - 1
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
 

+ 2 - 2
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;
 

+ 1 - 2
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

+ 1 - 1
src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl

@@ -2,4 +2,4 @@
 
 	uniform sampler2D map;
 
-#endif
+#endif

+ 3 - 2
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

+ 5 - 0
src/renderers/shaders/ShaderLib.js

@@ -71,6 +71,7 @@ THREE.ShaderLib = {
 			"#endif",
 
 			'#include <common>',
+			'#include <encodings>',
 			'#include <color_pars_fragment>',
 			'#include <uv_pars_fragment>',
 			'#include <uv2_pars_fragment>',
@@ -199,6 +200,7 @@ THREE.ShaderLib = {
 			"#endif",
 
 			'#include <common>',
+			'#include <encodings>',
 			'#include <color_pars_fragment>',
 			'#include <uv_pars_fragment>',
 			'#include <uv2_pars_fragment>',
@@ -363,6 +365,7 @@ THREE.ShaderLib = {
 			"uniform float opacity;",
 
 			'#include <common>',
+			'#include <encodings>',
 			'#include <color_pars_fragment>',
 			'#include <uv_pars_fragment>',
 			'#include <uv2_pars_fragment>',
@@ -526,6 +529,7 @@ THREE.ShaderLib = {
 			"#endif",
 
 			'#include <common>',
+			'#include <encodings>',
 			'#include <color_pars_fragment>',
 			'#include <uv_pars_fragment>',
 			'#include <uv2_pars_fragment>',
@@ -630,6 +634,7 @@ THREE.ShaderLib = {
 			"uniform float opacity;",
 
 			'#include <common>',
+			'#include <encodings>',
 			'#include <color_pars_fragment>',
 			'#include <map_particle_pars_fragment>',
 			'#include <fog_pars_fragment>',

+ 1 - 1
src/renderers/shaders/UniformsLib.js

@@ -38,7 +38,7 @@ THREE.UniformsLib = {
 
 	emissivemap: {
 
-		"emissiveMap": { type: "t", value: null }
+		"emissiveMap": { type: "t", value: null },
 
 	},
 

+ 34 - 0
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' : '',

+ 5 - 2
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,

+ 9 - 1
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;
 

+ 1 - 0
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",