Jelajahi Sumber

Merge pull request #8117 from bhouston/texture_encoding

Allow textures to be encoded/decoded in Linear,sRGB,RGBE,RGBM, etc
Mr.doob 9 tahun lalu
induk
melakukan
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.
 		False by default, which is the norm for PNG images. Set to true if the RGB values have been stored premultiplied by alpha.
 		</div>
 		</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>
 		<h3>[property:object onUpdate]</h3>
 		<div>
 		<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).
 		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.TrianglesDrawMode = 0;
 THREE.TriangleStripDrawMode = 1;
 THREE.TriangleStripDrawMode = 1;
 THREE.TriangleFanDrawMode = 2;
 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 );
 	vec4 emissiveColor = texture2D( emissiveMap, vUv );
 
 
-	emissiveColor.rgb = inputToLinear( emissiveColor.rgb );
+	emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;
 
 
 	totalEmissiveLight *= 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 );
 		vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );
 	#endif
 	#endif
 
 
-	envColor.xyz = inputToLinear( envColor.xyz );
+	envColor = envMapTexelToLinear( envColor );
 
 
 	#ifdef ENVMAP_BLENDING_MULTIPLY
 	#ifdef ENVMAP_BLENDING_MULTIPLY
 
 

+ 2 - 2
src/renderers/shaders/ShaderChunk/lights_pars.glsl

@@ -185,7 +185,7 @@
 
 
 		#endif
 		#endif
 
 
-		envMapColor.rgb = inputToLinear( envMapColor.rgb );
+		envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;
 
 
 		return PI * envMapColor.rgb * envMapIntensity;
 		return PI * envMapColor.rgb * envMapIntensity;
 
 
@@ -277,7 +277,7 @@
 
 
 		#endif
 		#endif
 
 
-		envMapColor.rgb = inputToLinear( envMapColor.rgb );
+		envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;
 
 
 		return envMapColor.rgb * envMapIntensity;
 		return envMapColor.rgb * envMapIntensity;
 
 

+ 1 - 2
src/renderers/shaders/ShaderChunk/map_fragment.glsl

@@ -2,8 +2,7 @@
 
 
 	vec4 texelColor = texture2D( map, vUv );
 	vec4 texelColor = texture2D( map, vUv );
 
 
-	texelColor.xyz = inputToLinear( texelColor.xyz );
-
+	texelColor = mapTexelToLinear( texelColor );
 	diffuseColor *= texelColor;
 	diffuseColor *= texelColor;
 
 
 #endif
 #endif

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

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

+ 3 - 2
src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl

@@ -1,5 +1,6 @@
 #ifdef USE_MAP
 #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
 #endif

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

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

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

@@ -38,7 +38,7 @@ THREE.UniformsLib = {
 
 
 	emissivemap: {
 	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 arrayStructRe = /^([\w\d_]+)\[(\d+)\]\.([\w\d_]+)$/;
 	var arrayRe = /^([\w\d_]+)\[0\]$/;
 	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 ) {
 	function generateExtensions( extensions, parameters, rendererExtensions ) {
 
 
 		extensions = extensions || {};
 		extensions = extensions || {};
@@ -420,13 +451,16 @@ THREE.WebGLProgram = ( function () {
 				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
 				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',
 
 
 				parameters.map ? '#define USE_MAP' : '',
 				parameters.map ? '#define USE_MAP' : '',
+				parameters.mapEncoding ? getTexelDecodingFunction( "mapTexelToLinear", material.map.encoding ) : '',
 				parameters.envMap ? '#define USE_ENVMAP' : '',
 				parameters.envMap ? '#define USE_ENVMAP' : '',
 				parameters.envMap ? '#define ' + envMapTypeDefine : '',
 				parameters.envMap ? '#define ' + envMapTypeDefine : '',
 				parameters.envMap ? '#define ' + envMapModeDefine : '',
 				parameters.envMap ? '#define ' + envMapModeDefine : '',
 				parameters.envMap ? '#define ' + envMapBlendingDefine : '',
 				parameters.envMap ? '#define ' + envMapBlendingDefine : '',
 				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
 				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
+				parameters.envMapEncoding ? getTexelDecodingFunction( "envMapTexelToLinear", material.envMap.encoding ) : '',
 				parameters.aoMap ? '#define USE_AOMAP' : '',
 				parameters.aoMap ? '#define USE_AOMAP' : '',
 				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
 				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
+				parameters.emissiveMapEncoding ? getTexelDecodingFunction( "emissiveMapTexelToLinear", material.emissiveMap.encoding ) : '',
 				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 				parameters.normalMap ? '#define USE_NORMALMAP' : '',
 				parameters.normalMap ? '#define USE_NORMALMAP' : '',
 				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 				parameters.specularMap ? '#define USE_SPECULARMAP' : '',

+ 5 - 2
src/renderers/webgl/WebGLPrograms.js

@@ -15,8 +15,8 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 	};
 	};
 
 
 	var parameterNames = [
 	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",
 		"roughnessMap", "metalnessMap",
 		"alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
 		"alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
@@ -96,11 +96,14 @@ THREE.WebGLPrograms = function ( renderer, capabilities ) {
 			supportsVertexTextures: capabilities.vertexTextures,
 			supportsVertexTextures: capabilities.vertexTextures,
 
 
 			map: !! material.map,
 			map: !! material.map,
+			mapEncoding: ( !! material.map ) ? material.map.encoding : false,
 			envMap: !! material.envMap,
 			envMap: !! material.envMap,
 			envMapMode: material.envMap && material.envMap.mapping,
 			envMapMode: material.envMap && material.envMap.mapping,
+			envMapEncoding: ( !! material.envMap ) ? material.envMap.encoding : false,
 			lightMap: !! material.lightMap,
 			lightMap: !! material.lightMap,
 			aoMap: !! material.aoMap,
 			aoMap: !! material.aoMap,
 			emissiveMap: !! material.emissiveMap,
 			emissiveMap: !! material.emissiveMap,
+			emissiveMapEncoding: ( !! material.emissiveMap ) ? material.emissiveMap.encoding : false,
 			bumpMap: !! material.bumpMap,
 			bumpMap: !! material.bumpMap,
 			normalMap: !! material.normalMap,
 			normalMap: !! material.normalMap,
 			displacementMap: !! material.displacementMap,
 			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.generateMipmaps = true;
 	this.premultiplyAlpha = false;
 	this.premultiplyAlpha = false;
 	this.flipY = true;
 	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.version = 0;
 	this.onUpdate = null;
 	this.onUpdate = null;
@@ -86,6 +93,7 @@ THREE.Texture.prototype = {
 		this.premultiplyAlpha = source.premultiplyAlpha;
 		this.premultiplyAlpha = source.premultiplyAlpha;
 		this.flipY = source.flipY;
 		this.flipY = source.flipY;
 		this.unpackAlignment = source.unpackAlignment;
 		this.unpackAlignment = source.unpackAlignment;
+		this.encoding = source.encoding;
 
 
 		return this;
 		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/displacementmap_pars_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/emissivemap_pars_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_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl",
 	"src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl",