NormalMapNode.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import {
  2. BackSide
  3. } from '../../../../build/three.module.js';
  4. import { TempNode } from '../core/TempNode.js';
  5. import { Vector2Node } from '../inputs/Vector2Node.js';
  6. import { FunctionNode } from '../core/FunctionNode.js';
  7. import { UVNode } from '../accessors/UVNode.js';
  8. import { NormalNode } from '../accessors/NormalNode.js';
  9. import { PositionNode } from '../accessors/PositionNode.js';
  10. class NormalMapNode extends TempNode {
  11. constructor( value, scale ) {
  12. super( 'v3' );
  13. this.value = value;
  14. this.scale = scale || new Vector2Node( 1, 1 );
  15. }
  16. generate( builder, output ) {
  17. if ( builder.isShader( 'fragment' ) ) {
  18. const perturbNormal2Arb = builder.include( NormalMapNode.Nodes.perturbNormal2Arb );
  19. this.normal = this.normal || new NormalNode();
  20. this.position = this.position || new PositionNode( PositionNode.VIEW );
  21. this.uv = this.uv || new UVNode();
  22. let scale = this.scale.build( builder, 'v2' );
  23. if ( builder.material.side === BackSide ) {
  24. scale = '-' + scale;
  25. }
  26. return builder.format( perturbNormal2Arb + '( -' + this.position.build( builder, 'v3' ) + ', ' +
  27. this.normal.build( builder, 'v3' ) + ', ' +
  28. this.value.build( builder, 'v3' ) + ', ' +
  29. this.uv.build( builder, 'v2' ) + ', ' +
  30. scale + ' )', this.getType( builder ), output );
  31. } else {
  32. console.warn( 'THREE.NormalMapNode is not compatible with ' + builder.shader + ' shader.' );
  33. return builder.format( 'vec3( 0.0 )', this.getType( builder ), output );
  34. }
  35. }
  36. copy( source ) {
  37. super.copy( source );
  38. this.value = source.value;
  39. this.scale = source.scale;
  40. return this;
  41. }
  42. toJSON( meta ) {
  43. let data = this.getJSONNode( meta );
  44. if ( ! data ) {
  45. data = this.createJSONNode( meta );
  46. data.value = this.value.toJSON( meta ).uuid;
  47. data.scale = this.scale.toJSON( meta ).uuid;
  48. }
  49. return data;
  50. }
  51. }
  52. NormalMapNode.Nodes = ( function () {
  53. const perturbNormal2Arb = new FunctionNode(
  54. // Per-Pixel Tangent Space Normal Mapping
  55. // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
  56. `vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 vUv, vec2 normalScale ) {
  57. // Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
  58. vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
  59. vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
  60. vec2 st0 = dFdx( vUv.st );
  61. vec2 st1 = dFdy( vUv.st );
  62. float scale = sign( st1.t * st0.s - st0.t * st1.s ); // we do not care about the magnitude
  63. vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );
  64. vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );
  65. vec3 N = normalize( surf_norm );
  66. vec3 mapN = map * 2.0 - 1.0;
  67. mapN.xy *= normalScale;
  68. #ifdef DOUBLE_SIDED
  69. // Workaround for Adreno GPUs gl_FrontFacing bug. See #15850 and #10331
  70. if ( dot( cross( S, T ), N ) < 0.0 ) mapN.xy *= - 1.0;
  71. #else
  72. mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );
  73. #endif
  74. mat3 tsn = mat3( S, T, N );
  75. return normalize( tsn * mapN );
  76. }`, null, { derivatives: true } );
  77. return {
  78. perturbNormal2Arb: perturbNormal2Arb
  79. };
  80. } )();
  81. NormalMapNode.prototype.nodeType = 'NormalMap';
  82. export { NormalMapNode };