ColorSpaceNode.js 8.2 KB

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