ColorSpaceNode.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * @author sunag / http://www.sunag.com.br/
  3. */
  4. import { TempNode } from '../core/TempNode.js';
  5. import { ConstNode } from '../core/ConstNode.js';
  6. import { FunctionNode } from '../core/FunctionNode.js';
  7. function ColorSpaceNode( input, method ) {
  8. TempNode.call( this, 'v4' );
  9. this.input = input;
  10. this.method = method || ColorSpaceNode.LINEAR;
  11. };
  12. ColorSpaceNode.Nodes = (function() {
  13. // 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/
  14. var LinearToLinear = new FunctionNode( [
  15. "vec4 LinearToLinear( in vec4 value ) {",
  16. " return value;",
  17. "}"
  18. ].join( "\n" ) );
  19. var GammaToLinear = new FunctionNode( [
  20. "vec4 GammaToLinear( in vec4 value, in float gammaFactor ) {",
  21. " return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );",
  22. "}"
  23. ].join( "\n" ));
  24. var LinearToGamma = new FunctionNode( [
  25. "vec4 LinearToGamma( in vec4 value, in float gammaFactor ) {",
  26. " return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );",
  27. "}"
  28. ].join( "\n" ));
  29. var sRGBToLinear = new FunctionNode( [
  30. "vec4 sRGBToLinear( in vec4 value ) {",
  31. " 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 );",
  32. "}"
  33. ].join( "\n" ));
  34. var LinearTosRGB = new FunctionNode( [
  35. "vec4 LinearTosRGB( in vec4 value ) {",
  36. " return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );",
  37. "}"
  38. ].join( "\n" ));
  39. var RGBEToLinear = new FunctionNode( [
  40. "vec4 RGBEToLinear( in vec4 value ) {",
  41. " return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );",
  42. "}"
  43. ].join( "\n" ));
  44. var LinearToRGBE = new FunctionNode( [
  45. "vec4 LinearToRGBE( in vec4 value ) {",
  46. " float maxComponent = max( max( value.r, value.g ), value.b );",
  47. " float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );",
  48. " return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );",
  49. // return vec4( value.brg, ( 3.0 + 128.0 ) / 256.0 );
  50. "}"
  51. ].join( "\n" ));
  52. // reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
  53. var RGBMToLinear = new FunctionNode( [
  54. "vec3 RGBMToLinear( in vec4 value, in float maxRange ) {",
  55. " return vec4( value.xyz * value.w * maxRange, 1.0 );",
  56. "}"
  57. ].join( "\n" ) );
  58. var LinearToRGBM = new FunctionNode( [
  59. "vec3 LinearToRGBM( in vec4 value, in float maxRange ) {",
  60. " float maxRGB = max( value.x, max( value.g, value.b ) );",
  61. " float M = clamp( maxRGB / maxRange, 0.0, 1.0 );",
  62. " M = ceil( M * 255.0 ) / 255.0;",
  63. " return vec4( value.rgb / ( M * maxRange ), M );",
  64. "}"
  65. ].join( "\n" ) );
  66. // reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
  67. var RGBDToLinear = new FunctionNode( [
  68. "vec3 RGBDToLinear( in vec4 value, in float maxRange ) {",
  69. " return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );",
  70. "}"
  71. ].join( "\n" ) );
  72. var LinearToRGBD = new FunctionNode( [
  73. "vec3 LinearToRGBD( in vec4 value, in float maxRange ) {",
  74. " float maxRGB = max( value.x, max( value.g, value.b ) );",
  75. " float D = max( maxRange / maxRGB, 1.0 );",
  76. " D = min( floor( D ) / 255.0, 1.0 );",
  77. " return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );",
  78. "}"
  79. ].join( "\n" ) );
  80. // LogLuv reference: http://graphicrants.blogspot.ca/2009/04/rgbm-color-encoding.html
  81. // M matrix, for encoding
  82. var cLogLuvM = new ConstNode("const mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );");
  83. var LinearToLogLuv = new FunctionNode( [
  84. "vec4 LinearToLogLuv( in vec4 value ) {",
  85. " vec3 Xp_Y_XYZp = value.rgb * cLogLuvM;",
  86. " Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));",
  87. " vec4 vResult;",
  88. " vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;",
  89. " float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;",
  90. " vResult.w = fract(Le);",
  91. " vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;",
  92. " return vResult;",
  93. "}"
  94. ].join( "\n" ), [ cLogLuvM ] );
  95. // Inverse M matrix, for decoding
  96. var cLogLuvInverseM = new ConstNode("const mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );");
  97. var LogLuvToLinear = new FunctionNode( [
  98. "vec4 LogLuvToLinear( in vec4 value ) {",
  99. " float Le = value.z * 255.0 + value.w;",
  100. " vec3 Xp_Y_XYZp;",
  101. " Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);",
  102. " Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;",
  103. " Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;",
  104. " vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;",
  105. " return vec4( max(vRGB, 0.0), 1.0 );",
  106. "}"
  107. ].join( "\n" ), [ cLogLuvInverseM ] );
  108. return {
  109. LinearToLinear: LinearToLinear,
  110. GammaToLinear: GammaToLinear,
  111. LinearToGamma: LinearToGamma,
  112. sRGBToLinear: sRGBToLinear,
  113. LinearTosRGB: LinearTosRGB,
  114. RGBEToLinear: RGBEToLinear,
  115. LinearToRGBE: LinearToRGBE,
  116. RGBMToLinear: RGBMToLinear,
  117. LinearToRGBM: LinearToRGBM,
  118. RGBDToLinear: RGBDToLinear,
  119. LinearToRGBD: LinearToRGBD,
  120. cLogLuvM: cLogLuvM,
  121. LinearToLogLuv: LinearToLogLuv,
  122. cLogLuvInverseM: cLogLuvInverseM,
  123. LogLuvToLinear: LogLuvToLinear
  124. };
  125. })();
  126. ColorSpaceNode.LINEAR_TO_LINEAR = 'LinearToLinear';
  127. ColorSpaceNode.GAMMA_TO_LINEAR = 'GammaToLinear';
  128. ColorSpaceNode.LINEAR_TO_GAMMA = 'LinearToGamma';
  129. ColorSpaceNode.SRGB_TO_LINEAR = 'sRGBToLinear';
  130. ColorSpaceNode.LINEAR_TO_SRGB = 'LinearTosRGB';
  131. ColorSpaceNode.RGBE_TO_LINEAR = 'RGBEToLinear';
  132. ColorSpaceNode.LINEAR_TO_RGBE = 'LinearToRGBE';
  133. ColorSpaceNode.RGBM_TO_LINEAR = 'RGBMToLinear';
  134. ColorSpaceNode.LINEAR_TO_RGBM = 'LinearToRGBM';
  135. ColorSpaceNode.RGBD_TO_LINEAR = 'RGBDToLinear';
  136. ColorSpaceNode.LINEAR_TO_RGBD = 'LinearToRGBD';
  137. ColorSpaceNode.LINEAR_TO_LOG_LUV = 'LinearToLogLuv';
  138. ColorSpaceNode.LOG_LUV_TO_LINEAR = 'LogLuvToLinear';
  139. ColorSpaceNode.prototype = Object.create( TempNode.prototype );
  140. ColorSpaceNode.prototype.constructor = ColorSpaceNode;
  141. ColorSpaceNode.prototype.nodeType = "ColorAdjustment";
  142. ColorSpaceNode.prototype.generate = function ( builder, output ) {
  143. var input = builder.context.input || this.input.build( builder, 'v4' ),
  144. encodingMethod = builder.context.encoding !== undefined ? this.getEncodingMethod( builder.context.encoding ) : [ this.method ],
  145. factor = this.factor ? this.factor.build( builder, 'f' ) : encodingMethod[1];
  146. var method = builder.include( ColorSpaceNode.Nodes[encodingMethod[0]] );
  147. if (factor) {
  148. return builder.format( method + '( ' + input + ', ' + factor + ' )', this.getType( builder ), output );
  149. } else {
  150. return builder.format( method + '( ' + input + ' )', this.getType( builder ), output );
  151. }
  152. };
  153. ColorSpaceNode.prototype.getDecodingMethod = function( encoding ) {
  154. var components = this.getEncodingComponents( encoding );
  155. components[ 0 ] += 'ToLinear';
  156. return components;
  157. };
  158. ColorSpaceNode.prototype.getEncodingMethod = function( encoding ) {
  159. var components = this.getEncodingComponents( encoding );
  160. components[ 0 ] = 'LinearTo' + components[ 0 ];
  161. return components;
  162. };
  163. ColorSpaceNode.prototype.getEncodingComponents = function( encoding ) {
  164. switch ( encoding ) {
  165. case THREE.LinearEncoding:
  166. return [ 'Linear' ];
  167. case THREE.sRGBEncoding:
  168. return [ 'sRGB' ];
  169. case THREE.RGBEEncoding:
  170. return [ 'RGBE' ];
  171. case THREE.RGBM7Encoding:
  172. return [ 'RGBM', '7.0' ];
  173. case THREE.RGBM16Encoding:
  174. return [ 'RGBM', '16.0' ];
  175. case THREE.RGBDEncoding:
  176. return [ 'RGBD', '256.0' ];
  177. case THREE.GammaEncoding:
  178. return [ 'Gamma', 'float( GAMMA_FACTOR )' ];
  179. }
  180. };
  181. ColorSpaceNode.prototype.copy = function ( source ) {
  182. TempNode.prototype.copy.call( this, source );
  183. this.input = source.input;
  184. this.method = source.method;
  185. };
  186. ColorSpaceNode.prototype.toJSON = function ( meta ) {
  187. var data = this.getJSONNode( meta );
  188. if ( ! data ) {
  189. data = this.createJSONNode( meta );
  190. data.input = this.input.toJSON( meta ).uuid;
  191. data.method = this.method;
  192. }
  193. return data;
  194. };
  195. export { ColorSpaceNode };