ColorAdjustmentNode.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import { TempNode } from '../core/TempNode.js';
  2. import { FunctionNode } from '../core/FunctionNode.js';
  3. import { LuminanceNode } from './LuminanceNode.js';
  4. class ColorAdjustmentNode extends TempNode {
  5. constructor( rgb, adjustment, method ) {
  6. super( 'v3' );
  7. this.rgb = rgb;
  8. this.adjustment = adjustment;
  9. this.method = method || ColorAdjustmentNode.SATURATION;
  10. }
  11. generate( builder, output ) {
  12. const rgb = this.rgb.build( builder, 'v3' ),
  13. adjustment = this.adjustment.build( builder, 'f' );
  14. switch ( this.method ) {
  15. case ColorAdjustmentNode.BRIGHTNESS:
  16. return builder.format( '( ' + rgb + ' + ' + adjustment + ' )', this.getType( builder ), output );
  17. break;
  18. case ColorAdjustmentNode.CONTRAST:
  19. return builder.format( '( ' + rgb + ' * ' + adjustment + ' )', this.getType( builder ), output );
  20. break;
  21. }
  22. const method = builder.include( ColorAdjustmentNode.Nodes[ this.method ] );
  23. return builder.format( method + '( ' + rgb + ', ' + adjustment + ' )', this.getType( builder ), output );
  24. }
  25. copy( source ) {
  26. super.copy( source );
  27. this.rgb = source.rgb;
  28. this.adjustment = source.adjustment;
  29. this.method = source.method;
  30. return this;
  31. }
  32. toJSON( meta ) {
  33. let data = this.getJSONNode( meta );
  34. if ( ! data ) {
  35. data = this.createJSONNode( meta );
  36. data.rgb = this.rgb.toJSON( meta ).uuid;
  37. data.adjustment = this.adjustment.toJSON( meta ).uuid;
  38. data.method = this.method;
  39. }
  40. return data;
  41. }
  42. }
  43. ColorAdjustmentNode.Nodes = ( function () {
  44. const hue = new FunctionNode( /* glsl */`
  45. vec3 hue(vec3 rgb, float adjustment) {
  46. const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);
  47. const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);
  48. vec3 yiq = RGBtoYIQ * rgb;
  49. float hue = atan(yiq.z, yiq.y) + adjustment;
  50. float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);
  51. return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));
  52. }`
  53. );
  54. // Algorithm from Chapter 16 of OpenGL Shading Language
  55. const saturation = new FunctionNode( /* glsl */`
  56. vec3 saturation(vec3 rgb, float adjustment) {
  57. vec3 intensity = vec3( luminance( rgb ) );
  58. return mix( intensity, rgb, adjustment );
  59. }`
  60. , [ LuminanceNode.Nodes.luminance ] ); // include LuminanceNode function
  61. // Shader by Evan Wallace adapted by @lo-th
  62. const vibrance = new FunctionNode( /* glsl */`
  63. vec3 vibrance(vec3 rgb, float adjustment) {
  64. float average = (rgb.r + rgb.g + rgb.b) / 3.0;
  65. float mx = max(rgb.r, max(rgb.g, rgb.b));
  66. float amt = (mx - average) * (-3.0 * adjustment);
  67. return mix(rgb.rgb, vec3(mx), amt);
  68. }`
  69. );
  70. return {
  71. hue: hue,
  72. saturation: saturation,
  73. vibrance: vibrance
  74. };
  75. } )();
  76. ColorAdjustmentNode.SATURATION = 'saturation';
  77. ColorAdjustmentNode.HUE = 'hue';
  78. ColorAdjustmentNode.VIBRANCE = 'vibrance';
  79. ColorAdjustmentNode.BRIGHTNESS = 'brightness';
  80. ColorAdjustmentNode.CONTRAST = 'contrast';
  81. ColorAdjustmentNode.prototype.nodeType = 'ColorAdjustment';
  82. ColorAdjustmentNode.prototype.hashProperties = [ 'method' ];
  83. export { ColorAdjustmentNode };