ColorSpaceNode.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import {
  2. GammaEncoding,
  3. LinearEncoding,
  4. RGBEEncoding,
  5. RGBM7Encoding,
  6. RGBM16Encoding,
  7. RGBDEncoding,
  8. sRGBEncoding
  9. } from '../../../../build/three.module.js';
  10. import { TempNode } from '../core/TempNode.js';
  11. import { FloatNode } from '../inputs/FloatNode.js';
  12. import { FunctionNode } from '../core/FunctionNode.js';
  13. import { ExpressionNode } from '../core/ExpressionNode.js';
  14. class ColorSpaceNode extends TempNode {
  15. constructor( input, method ) {
  16. super( 'v4' );
  17. this.input = input;
  18. this.method = method || ColorSpaceNode.LINEAR_TO_LINEAR;
  19. }
  20. generate( builder, output ) {
  21. const input = this.input.build( builder, 'v4' );
  22. const outputType = this.getType( builder );
  23. const methodNode = ColorSpaceNode.Nodes[ this.method ];
  24. const method = builder.include( methodNode );
  25. if ( method === ColorSpaceNode.LINEAR_TO_LINEAR ) {
  26. return builder.format( input, outputType, output );
  27. } else {
  28. if ( methodNode.inputs.length === 2 ) {
  29. const factor = this.factor.build( builder, 'f' );
  30. return builder.format( method + '( ' + input + ', ' + factor + ' )', outputType, output );
  31. } else {
  32. return builder.format( method + '( ' + input + ' )', outputType, output );
  33. }
  34. }
  35. }
  36. fromEncoding( encoding ) {
  37. const components = ColorSpaceNode.getEncodingComponents( encoding );
  38. this.method = 'LinearTo' + components[ 0 ];
  39. this.factor = components[ 1 ];
  40. }
  41. fromDecoding( encoding ) {
  42. const components = ColorSpaceNode.getEncodingComponents( encoding );
  43. this.method = components[ 0 ] + 'ToLinear';
  44. this.factor = components[ 1 ];
  45. }
  46. copy( source ) {
  47. super.copy( source );
  48. this.input = source.input;
  49. this.method = source.method;
  50. return this;
  51. }
  52. toJSON( meta ) {
  53. let data = this.getJSONNode( meta );
  54. if ( ! data ) {
  55. data = this.createJSONNode( meta );
  56. data.input = this.input.toJSON( meta ).uuid;
  57. data.method = this.method;
  58. }
  59. return data;
  60. }
  61. }
  62. ColorSpaceNode.Nodes = ( function () {
  63. // 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/
  64. const LinearToLinear = new FunctionNode( /* glsl */`
  65. vec4 LinearToLinear( in vec4 value ) {
  66. return value;
  67. }`
  68. );
  69. const GammaToLinear = new FunctionNode( /* glsl */`
  70. vec4 GammaToLinear( in vec4 value, in float gammaFactor ) {
  71. return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );
  72. }`
  73. );
  74. const LinearToGamma = new FunctionNode( /* glsl */`
  75. vec4 LinearToGamma( in vec4 value, in float gammaFactor ) {
  76. return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );
  77. }`
  78. );
  79. const sRGBToLinear = new FunctionNode( /* glsl */`
  80. vec4 sRGBToLinear( in vec4 value ) {
  81. 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 );
  82. }`
  83. );
  84. const LinearTosRGB = new FunctionNode( /* glsl */`
  85. vec4 LinearTosRGB( in vec4 value ) {
  86. 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 );
  87. }`
  88. );
  89. const RGBEToLinear = new FunctionNode( /* glsl */`
  90. vec4 RGBEToLinear( in vec4 value ) {
  91. return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );
  92. }`
  93. );
  94. const LinearToRGBE = new FunctionNode( /* glsl */`
  95. vec4 LinearToRGBE( in vec4 value ) {
  96. float maxComponent = max( max( value.r, value.g ), value.b );
  97. float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );
  98. return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );
  99. }`
  100. );
  101. // reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
  102. const RGBMToLinear = new FunctionNode( /* glsl */`
  103. vec3 RGBMToLinear( in vec4 value, in float maxRange ) {
  104. return vec4( value.xyz * value.w * maxRange, 1.0 );
  105. }`
  106. );
  107. const LinearToRGBM = new FunctionNode( /* glsl */`
  108. vec3 LinearToRGBM( in vec4 value, in float maxRange ) {
  109. float maxRGB = max( value.x, max( value.g, value.b ) );
  110. float M = clamp( maxRGB / maxRange, 0.0, 1.0 );
  111. M = ceil( M * 255.0 ) / 255.0;
  112. return vec4( value.rgb / ( M * maxRange ), M );
  113. }`
  114. );
  115. // reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
  116. const RGBDToLinear = new FunctionNode( /* glsl */`
  117. vec3 RGBDToLinear( in vec4 value, in float maxRange ) {
  118. return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );
  119. }`
  120. );
  121. const LinearToRGBD = new FunctionNode( /* glsl */`
  122. vec3 LinearToRGBD( in vec4 value, in float maxRange ) {
  123. float maxRGB = max( value.x, max( value.g, value.b ) );
  124. float D = max( maxRange / maxRGB, 1.0 );
  125. D = clamp( floor( D ) / 255.0, 0.0, 1.0 );
  126. return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );
  127. }`
  128. );
  129. return {
  130. LinearToLinear: LinearToLinear,
  131. GammaToLinear: GammaToLinear,
  132. LinearToGamma: LinearToGamma,
  133. sRGBToLinear: sRGBToLinear,
  134. LinearTosRGB: LinearTosRGB,
  135. RGBEToLinear: RGBEToLinear,
  136. LinearToRGBE: LinearToRGBE,
  137. RGBMToLinear: RGBMToLinear,
  138. LinearToRGBM: LinearToRGBM,
  139. RGBDToLinear: RGBDToLinear,
  140. LinearToRGBD: LinearToRGBD
  141. };
  142. } )();
  143. ColorSpaceNode.LINEAR_TO_LINEAR = 'LinearToLinear';
  144. ColorSpaceNode.GAMMA_TO_LINEAR = 'GammaToLinear';
  145. ColorSpaceNode.LINEAR_TO_GAMMA = 'LinearToGamma';
  146. ColorSpaceNode.SRGB_TO_LINEAR = 'sRGBToLinear';
  147. ColorSpaceNode.LINEAR_TO_SRGB = 'LinearTosRGB';
  148. ColorSpaceNode.RGBE_TO_LINEAR = 'RGBEToLinear';
  149. ColorSpaceNode.LINEAR_TO_RGBE = 'LinearToRGBE';
  150. ColorSpaceNode.RGBM_TO_LINEAR = 'RGBMToLinear';
  151. ColorSpaceNode.LINEAR_TO_RGBM = 'LinearToRGBM';
  152. ColorSpaceNode.RGBD_TO_LINEAR = 'RGBDToLinear';
  153. ColorSpaceNode.LINEAR_TO_RGBD = 'LinearToRGBD';
  154. ColorSpaceNode.getEncodingComponents = function ( encoding ) {
  155. switch ( encoding ) {
  156. case LinearEncoding:
  157. return [ 'Linear' ];
  158. case sRGBEncoding:
  159. return [ 'sRGB' ];
  160. case RGBEEncoding:
  161. return [ 'RGBE' ];
  162. case RGBM7Encoding:
  163. return [ 'RGBM', new FloatNode( 7.0 ).setReadonly( true ) ];
  164. case RGBM16Encoding:
  165. return [ 'RGBM', new FloatNode( 16.0 ).setReadonly( true ) ];
  166. case RGBDEncoding:
  167. return [ 'RGBD', new FloatNode( 256.0 ).setReadonly( true ) ];
  168. case GammaEncoding:
  169. return [ 'Gamma', new ExpressionNode( 'float( GAMMA_FACTOR )', 'f' ) ];
  170. }
  171. };
  172. ColorSpaceNode.prototype.nodeType = 'ColorSpace';
  173. ColorSpaceNode.prototype.hashProperties = [ 'method' ];
  174. export { ColorSpaceNode };