MaterialXHextile.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import {
  2. abs,
  3. add,
  4. clamp,
  5. cos,
  6. dFdx,
  7. dFdy,
  8. div,
  9. dot,
  10. element,
  11. floor,
  12. fract,
  13. max,
  14. mix,
  15. mul,
  16. pow,
  17. sin,
  18. step,
  19. sub,
  20. vec2,
  21. vec3,
  22. } from 'three/tsl';
  23. const HEXTILE_SQRT3_2 = Math.sqrt( 3 ) * 2;
  24. const HEXTILE_EPSILON = 1e-6;
  25. const HEXTILE_PI_OVER_180 = Math.PI / 180;
  26. function toRadians( degrees ) {
  27. return mul( degrees, HEXTILE_PI_OVER_180 );
  28. }
  29. function mxHextileHash( point ) {
  30. const x = element( point, 0 );
  31. const y = element( point, 1 );
  32. const p3Base = vec3( x, y, x );
  33. const p3Scaled = mul( p3Base, vec3( 0.1031, 0.103, 0.0973 ) );
  34. const p3Fract = fract( p3Scaled );
  35. const p3YZX = vec3( element( p3Fract, 1 ), element( p3Fract, 2 ), element( p3Fract, 0 ) );
  36. const p3Offset = add( p3YZX, 33.33 );
  37. const p3 = add( p3Fract, dot( p3Fract, p3Offset ) );
  38. const lhs = add( vec2( element( p3, 0 ), element( p3, 0 ) ), vec2( element( p3, 1 ), element( p3, 2 ) ) );
  39. const rhs = vec2( element( p3, 2 ), element( p3, 1 ) );
  40. return fract( mul( lhs, rhs ) );
  41. }
  42. function mxSchlickGain( x, r ) {
  43. const rr = clamp( r, 0.001, 0.999 );
  44. const a = mul( sub( div( 1, rr ), 2 ), sub( 1, mul( 2, x ) ) );
  45. const low = div( x, add( a, 1 ) );
  46. const high = div( sub( a, x ), sub( a, 1 ) );
  47. return mix( low, high, step( 0.5, x ) );
  48. }
  49. function normalizeBlendWeights( weights ) {
  50. const wx = element( weights, 0 );
  51. const wy = element( weights, 1 );
  52. const wz = element( weights, 2 );
  53. const sum = add( add( wx, wy ), wz );
  54. return div( weights, sum );
  55. }
  56. function mxRotate2d( point, sine, cosine ) {
  57. return vec2( sub( mul( cosine, element( point, 0 ) ), mul( sine, element( point, 1 ) ) ), add( mul( sine, element( point, 0 ) ), mul( cosine, element( point, 1 ) ) ) );
  58. }
  59. function toTileCenter( tileId ) {
  60. const scaled = div( tileId, HEXTILE_SQRT3_2 );
  61. const sx = element( scaled, 0 );
  62. const sy = element( scaled, 1 );
  63. return vec2( add( sx, mul( 0.5, sy ) ), mul( 0.8660254, sy ) );
  64. }
  65. export function mxHextileComputeBlendWeights( luminanceWeights, tileWeights, falloff ) {
  66. const weighted = mul( luminanceWeights, pow( tileWeights, vec3( 7, 7, 7 ) ) );
  67. const normalized = normalizeBlendWeights( weighted );
  68. const gained = vec3(
  69. mxSchlickGain( element( normalized, 0 ), falloff ),
  70. mxSchlickGain( element( normalized, 1 ), falloff ),
  71. mxSchlickGain( element( normalized, 2 ), falloff ),
  72. );
  73. const gainedNormalized = normalizeBlendWeights( gained );
  74. const applyFalloff = step( HEXTILE_EPSILON, abs( sub( falloff, 0.5 ) ) );
  75. return mix( normalized, gainedNormalized, applyFalloff );
  76. }
  77. export function mxHextileCoord( coord, rotation, rotationRange, scale, scaleRange, offset, offsetRange ) {
  78. const st = mul( coord, HEXTILE_SQRT3_2 );
  79. const stSkewed = vec2( add( element( st, 0 ), mul( - 0.57735027, element( st, 1 ) ) ), mul( 1.15470054, element( st, 1 ) ) );
  80. const stFrac = fract( stSkewed );
  81. const tx = element( stFrac, 0 );
  82. const ty = element( stFrac, 1 );
  83. const tz = sub( sub( 1, tx ), ty );
  84. const s = step( 0, sub( 0, tz ) );
  85. const s2 = sub( mul( 2, s ), 1 );
  86. const w1 = mul( sub( 0, tz ), s2 );
  87. const w2 = sub( s, mul( ty, s2 ) );
  88. const w3 = sub( s, mul( tx, s2 ) );
  89. const baseId = floor( stSkewed );
  90. const oneMinusS = sub( 1, s );
  91. const id1 = add( baseId, vec2( s, s ) );
  92. const id2 = add( baseId, vec2( s, oneMinusS ) );
  93. const id3 = add( baseId, vec2( oneMinusS, s ) );
  94. const ctr1 = toTileCenter( id1 );
  95. const ctr2 = toTileCenter( id2 );
  96. const ctr3 = toTileCenter( id3 );
  97. const seedOffset = vec2( 0.12345, 0.12345 );
  98. const rand1 = mxHextileHash( add( id1, seedOffset ) );
  99. const rand2 = mxHextileHash( add( id2, seedOffset ) );
  100. const rand3 = mxHextileHash( add( id3, seedOffset ) );
  101. const rr = vec2( toRadians( element( rotationRange, 0 ) ), toRadians( element( rotationRange, 1 ) ) );
  102. const rrMin = element( rr, 0 );
  103. const rrMax = element( rr, 1 );
  104. const randX = vec3( element( rand1, 0 ), element( rand2, 0 ), element( rand3, 0 ) );
  105. const rotations = mix( vec3( rrMin, rrMin, rrMin ), vec3( rrMax, rrMax, rrMax ), mul( randX, rotation ) );
  106. const randY = vec3( element( rand1, 1 ), element( rand2, 1 ), element( rand3, 1 ) );
  107. const scaleMin = element( scaleRange, 0 );
  108. const scaleMax = element( scaleRange, 1 );
  109. const randomScale = mix( vec3( scaleMin, scaleMin, scaleMin ), vec3( scaleMax, scaleMax, scaleMax ), randY );
  110. const scales = mix( vec3( 1, 1, 1 ), randomScale, scale );
  111. const offsetMin = element( offsetRange, 0 );
  112. const offsetMax = element( offsetRange, 1 );
  113. const offset1 = mix( vec2( offsetMin, offsetMin ), vec2( offsetMax, offsetMax ), mul( rand1, offset ) );
  114. const offset2 = mix( vec2( offsetMin, offsetMin ), vec2( offsetMax, offsetMax ), mul( rand2, offset ) );
  115. const offset3 = mix( vec2( offsetMin, offsetMin ), vec2( offsetMax, offsetMax ), mul( rand3, offset ) );
  116. const sampleCoord = ( center, randomOffset, rotationValue, sampleScale ) => {
  117. const delta = sub( coord, center );
  118. const rotated = mxRotate2d( delta, sin( rotationValue ), cos( rotationValue ) );
  119. const safeScale = max( sampleScale, HEXTILE_EPSILON );
  120. return add( add( div( rotated, vec2( safeScale, safeScale ) ), center ), randomOffset );
  121. };
  122. const sampleDerivative = ( derivative, rotationValue, sampleScale ) => {
  123. const rotated = mxRotate2d( derivative, sin( rotationValue ), cos( rotationValue ) );
  124. const safeScale = max( sampleScale, HEXTILE_EPSILON );
  125. return div( rotated, vec2( safeScale, safeScale ) );
  126. };
  127. const ddx = dFdx( coord );
  128. const ddy = dFdy( coord );
  129. return {
  130. coords: [
  131. sampleCoord( ctr1, offset1, element( rotations, 0 ), element( scales, 0 ) ),
  132. sampleCoord( ctr2, offset2, element( rotations, 1 ), element( scales, 1 ) ),
  133. sampleCoord( ctr3, offset3, element( rotations, 2 ), element( scales, 2 ) ),
  134. ],
  135. ddx: [
  136. sampleDerivative( ddx, element( rotations, 0 ), element( scales, 0 ) ),
  137. sampleDerivative( ddx, element( rotations, 1 ), element( scales, 1 ) ),
  138. sampleDerivative( ddx, element( rotations, 2 ), element( scales, 2 ) ),
  139. ],
  140. ddy: [
  141. sampleDerivative( ddy, element( rotations, 0 ), element( scales, 0 ) ),
  142. sampleDerivative( ddy, element( rotations, 1 ), element( scales, 1 ) ),
  143. sampleDerivative( ddy, element( rotations, 2 ), element( scales, 2 ) ),
  144. ],
  145. weights: vec3( w1, w2, w3 ),
  146. };
  147. }
粤ICP备19079148号