@zephyr3d/scene 0.9.24 → 0.9.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/dist/animation/hair/gpu_hair_simulation.js +1090 -0
  2. package/dist/animation/hair/gpu_hair_simulation.js.map +1 -0
  3. package/dist/animation/joint_dynamics/controller.js +7 -5
  4. package/dist/animation/joint_dynamics/controller.js.map +1 -1
  5. package/dist/animation/joint_dynamics/joint_dynamics_system.js +81 -5
  6. package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
  7. package/dist/animation/morphtrack.js +3 -4
  8. package/dist/animation/morphtrack.js.map +1 -1
  9. package/dist/animation/skeleton.js +58 -24
  10. package/dist/animation/skeleton.js.map +1 -1
  11. package/dist/animation/spring/multi_chain_spring_system.js +577 -179
  12. package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
  13. package/dist/animation/spring/spring_collider.js +4 -3
  14. package/dist/animation/spring/spring_collider.js.map +1 -1
  15. package/dist/animation/spring/spring_node_pose_tracker.js +40 -0
  16. package/dist/animation/spring/spring_node_pose_tracker.js.map +1 -0
  17. package/dist/animation/spring/spring_particle.js +2 -0
  18. package/dist/animation/spring/spring_particle.js.map +1 -1
  19. package/dist/animation/spring/spring_system.js +11 -3
  20. package/dist/animation/spring/spring_system.js.map +1 -1
  21. package/dist/animation/spring_modifier.js +10 -0
  22. package/dist/animation/spring_modifier.js.map +1 -1
  23. package/dist/app/engine.js +53 -43
  24. package/dist/app/engine.js.map +1 -1
  25. package/dist/app/scriptingsystem.js +119 -6
  26. package/dist/app/scriptingsystem.js.map +1 -1
  27. package/dist/app/scriptregistry.js +3 -2
  28. package/dist/app/scriptregistry.js.map +1 -1
  29. package/dist/asset/assetmanager.js +284 -67
  30. package/dist/asset/assetmanager.js.map +1 -1
  31. package/dist/asset/loaders/zhair/zhair_format.js +288 -0
  32. package/dist/asset/loaders/zhair/zhair_format.js.map +1 -0
  33. package/dist/asset/loaders/zhair/zhair_loader.js +221 -0
  34. package/dist/asset/loaders/zhair/zhair_loader.js.map +1 -0
  35. package/dist/asset/model.js +178 -67
  36. package/dist/asset/model.js.map +1 -1
  37. package/dist/camera/camera.js +193 -21
  38. package/dist/camera/camera.js.map +1 -1
  39. package/dist/camera/perspectivecamera.js +3 -1
  40. package/dist/camera/perspectivecamera.js.map +1 -1
  41. package/dist/index.d.ts +4040 -1290
  42. package/dist/index.js +16 -6
  43. package/dist/index.js.map +1 -1
  44. package/dist/material/eye.js +932 -0
  45. package/dist/material/eye.js.map +1 -0
  46. package/dist/material/hair.js +917 -0
  47. package/dist/material/hair.js.map +1 -0
  48. package/dist/material/hairstrand.js +842 -0
  49. package/dist/material/hairstrand.js.map +1 -0
  50. package/dist/material/hairstrand_data.js +162 -0
  51. package/dist/material/hairstrand_data.js.map +1 -0
  52. package/dist/material/lambert.js +5 -2
  53. package/dist/material/lambert.js.map +1 -1
  54. package/dist/material/material.js +338 -300
  55. package/dist/material/material.js.map +1 -1
  56. package/dist/material/meshmaterial.js +120 -19
  57. package/dist/material/meshmaterial.js.map +1 -1
  58. package/dist/material/mixins/albedocolor.js +1 -1
  59. package/dist/material/mixins/albedocolor.js.map +1 -1
  60. package/dist/material/mixins/lightmodel/blinnphong.js +5 -3
  61. package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
  62. package/dist/material/mixins/lightmodel/lambert.js +5 -4
  63. package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
  64. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +3 -3
  65. package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
  66. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
  67. package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
  68. package/dist/material/mixins/lightmodel/pbrspecularglossness.js +3 -3
  69. package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
  70. package/dist/material/mixins/lit.js +73 -18
  71. package/dist/material/mixins/lit.js.map +1 -1
  72. package/dist/material/mixins/pbr/common.js +4 -4
  73. package/dist/material/mixins/pbr/common.js.map +1 -1
  74. package/dist/material/mixins/texture.js +48 -11
  75. package/dist/material/mixins/texture.js.map +1 -1
  76. package/dist/material/mtoon.js +6 -5
  77. package/dist/material/mtoon.js.map +1 -1
  78. package/dist/material/pbrblueprint.js +146 -28
  79. package/dist/material/pbrblueprint.js.map +1 -1
  80. package/dist/material/pbrblueprint_instance.js +501 -0
  81. package/dist/material/pbrblueprint_instance.js.map +1 -0
  82. package/dist/material/pbrmr.js +15 -16
  83. package/dist/material/pbrmr.js.map +1 -1
  84. package/dist/material/shader/helper.js +766 -456
  85. package/dist/material/shader/helper.js.map +1 -1
  86. package/dist/material/shader/terrain_blend.js +55 -0
  87. package/dist/material/shader/terrain_blend.js.map +1 -0
  88. package/dist/material/skin.js +192 -32
  89. package/dist/material/skin.js.map +1 -1
  90. package/dist/material/spriteblueprint.js +16 -1
  91. package/dist/material/spriteblueprint.js.map +1 -1
  92. package/dist/material/subsurfaceprofile.js +86 -23
  93. package/dist/material/subsurfaceprofile.js.map +1 -1
  94. package/dist/material/water.js +3 -1
  95. package/dist/material/water.js.map +1 -1
  96. package/dist/posteffect/bloom.js +167 -7
  97. package/dist/posteffect/bloom.js.map +1 -1
  98. package/dist/posteffect/coloradjust.js +2 -1
  99. package/dist/posteffect/coloradjust.js.map +1 -1
  100. package/dist/posteffect/compositor.js.map +1 -1
  101. package/dist/posteffect/fxaa.js +2 -1
  102. package/dist/posteffect/fxaa.js.map +1 -1
  103. package/dist/posteffect/grayscale.js +3 -1
  104. package/dist/posteffect/grayscale.js.map +1 -1
  105. package/dist/posteffect/motionblur.js +4 -1
  106. package/dist/posteffect/motionblur.js.map +1 -1
  107. package/dist/posteffect/posteffect.js.map +1 -1
  108. package/dist/posteffect/skinsss.js +368 -69
  109. package/dist/posteffect/skinsss.js.map +1 -1
  110. package/dist/posteffect/ssgi.js +16 -9
  111. package/dist/posteffect/ssgi.js.map +1 -1
  112. package/dist/posteffect/ssr.js +3 -0
  113. package/dist/posteffect/ssr.js.map +1 -1
  114. package/dist/posteffect/sss.js +91 -34
  115. package/dist/posteffect/sss.js.map +1 -1
  116. package/dist/posteffect/taa.js.map +1 -1
  117. package/dist/posteffect/tonemap.js +44 -1
  118. package/dist/posteffect/tonemap.js.map +1 -1
  119. package/dist/render/abuffer_oit.js +45 -39
  120. package/dist/render/abuffer_oit.js.map +1 -1
  121. package/dist/render/cluster_light.js +4 -4
  122. package/dist/render/cluster_light.js.map +1 -1
  123. package/dist/render/cull_visitor.js +19 -0
  124. package/dist/render/cull_visitor.js.map +1 -1
  125. package/dist/render/depthpass.js +36 -2
  126. package/dist/render/depthpass.js.map +1 -1
  127. package/dist/render/drawable_mixin.js +26 -1
  128. package/dist/render/drawable_mixin.js.map +1 -1
  129. package/dist/render/dualdepthpeeling_oit.js +52 -51
  130. package/dist/render/dualdepthpeeling_oit.js.map +1 -1
  131. package/dist/render/envlight.js +1 -1
  132. package/dist/render/envlight.js.map +1 -1
  133. package/dist/render/fbm_wavegenerator.js +3 -0
  134. package/dist/render/fbm_wavegenerator.js.map +1 -1
  135. package/dist/render/gerstner_wavegenerator.js +3 -0
  136. package/dist/render/gerstner_wavegenerator.js.map +1 -1
  137. package/dist/render/globalbindgroup_allocator.js +3 -0
  138. package/dist/render/globalbindgroup_allocator.js.map +1 -1
  139. package/dist/render/oit.js +29 -0
  140. package/dist/render/oit.js.map +1 -0
  141. package/dist/render/render_queue.js +3 -0
  142. package/dist/render/render_queue.js.map +1 -1
  143. package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
  144. package/dist/render/rendergraph/executor.js +2 -0
  145. package/dist/render/rendergraph/executor.js.map +1 -1
  146. package/dist/render/rendergraph/forward_plus_builder.js +61 -1
  147. package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  148. package/dist/render/rendergraph/rendergraph.js.map +1 -1
  149. package/dist/render/rendergraph/texture_affinity_cache.js +1 -1
  150. package/dist/render/rendergraph/texture_affinity_cache.js.map +1 -1
  151. package/dist/render/rendergraph/types.js.map +1 -1
  152. package/dist/render/shadow_mask_pass.js +73 -8
  153. package/dist/render/shadow_mask_pass.js.map +1 -1
  154. package/dist/render/sky.js +166 -126
  155. package/dist/render/sky.js.map +1 -1
  156. package/dist/render/weightedblended_oit.js +36 -35
  157. package/dist/render/weightedblended_oit.js.map +1 -1
  158. package/dist/scene/basesprite.js +8 -0
  159. package/dist/scene/basesprite.js.map +1 -1
  160. package/dist/scene/batchgroup.js +3 -0
  161. package/dist/scene/batchgroup.js.map +1 -1
  162. package/dist/scene/environment.js +4 -1
  163. package/dist/scene/environment.js.map +1 -1
  164. package/dist/scene/hair.js +963 -0
  165. package/dist/scene/hair.js.map +1 -0
  166. package/dist/scene/light.js +5 -1
  167. package/dist/scene/light.js.map +1 -1
  168. package/dist/scene/mesh.js +481 -47
  169. package/dist/scene/mesh.js.map +1 -1
  170. package/dist/scene/meshdrawable.js +6 -0
  171. package/dist/scene/meshdrawable.js.map +1 -1
  172. package/dist/scene/msdftext.js +3 -0
  173. package/dist/scene/msdftext.js.map +1 -1
  174. package/dist/scene/msdftextsprite.js +3 -0
  175. package/dist/scene/msdftextsprite.js.map +1 -1
  176. package/dist/scene/particlesys.js +6 -0
  177. package/dist/scene/particlesys.js.map +1 -1
  178. package/dist/scene/scene.js +3 -0
  179. package/dist/scene/scene.js.map +1 -1
  180. package/dist/scene/scene_node.js +6 -1
  181. package/dist/scene/scene_node.js.map +1 -1
  182. package/dist/scene/terrain-cm/grass.js +273 -249
  183. package/dist/scene/terrain-cm/grass.js.map +1 -1
  184. package/dist/scene/terrain-cm/grassmaterial.js +3 -0
  185. package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
  186. package/dist/scene/terrain-cm/terrain-cm.js +6 -0
  187. package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
  188. package/dist/scene/terrain-cm/texture_cache.js +309 -0
  189. package/dist/scene/terrain-cm/texture_cache.js.map +1 -0
  190. package/dist/scene/water.js +6 -0
  191. package/dist/scene/water.js.map +1 -1
  192. package/dist/shaders/atmosphere.js.map +1 -1
  193. package/dist/shaders/diffusion.js +54 -0
  194. package/dist/shaders/diffusion.js.map +1 -0
  195. package/dist/shaders/shadow.js +33 -21
  196. package/dist/shaders/shadow.js.map +1 -1
  197. package/dist/shaders/temporal.js +71 -33
  198. package/dist/shaders/temporal.js.map +1 -1
  199. package/dist/shadow/dom.js +546 -0
  200. package/dist/shadow/dom.js.map +1 -0
  201. package/dist/shadow/pcf_opt.js +3 -5
  202. package/dist/shadow/pcf_opt.js.map +1 -1
  203. package/dist/shadow/pcf_pd.js +1 -1
  204. package/dist/shadow/pcf_pd.js.map +1 -1
  205. package/dist/shadow/receiver_bias.js +41 -0
  206. package/dist/shadow/receiver_bias.js.map +1 -0
  207. package/dist/shadow/shader.js +20 -60
  208. package/dist/shadow/shader.js.map +1 -1
  209. package/dist/shadow/shadow_impl.js +89 -0
  210. package/dist/shadow/shadow_impl.js.map +1 -1
  211. package/dist/shadow/shadow_region.js +50 -50
  212. package/dist/shadow/shadow_region.js.map +1 -1
  213. package/dist/shadow/shadowmapper.js +150 -37
  214. package/dist/shadow/shadowmapper.js.map +1 -1
  215. package/dist/shapes/shape.js +11 -2
  216. package/dist/shapes/shape.js.map +1 -1
  217. package/dist/shapes/sphere.js +50 -9
  218. package/dist/shapes/sphere.js.map +1 -1
  219. package/dist/utility/blueprint/common/constants.js +112 -2
  220. package/dist/utility/blueprint/common/constants.js.map +1 -1
  221. package/dist/utility/blueprint/material/func.js +142 -49
  222. package/dist/utility/blueprint/material/func.js.map +1 -1
  223. package/dist/utility/blueprint/material/ir.js +490 -25
  224. package/dist/utility/blueprint/material/ir.js.map +1 -1
  225. package/dist/utility/blueprint/material/texture.js +898 -4
  226. package/dist/utility/blueprint/material/texture.js.map +1 -1
  227. package/dist/utility/serialization/manager.js +431 -100
  228. package/dist/utility/serialization/manager.js.map +1 -1
  229. package/dist/utility/serialization/scene/animation.js +370 -3
  230. package/dist/utility/serialization/scene/animation.js.map +1 -1
  231. package/dist/utility/serialization/scene/batch.js +4 -0
  232. package/dist/utility/serialization/scene/batch.js.map +1 -1
  233. package/dist/utility/serialization/scene/camera.js +133 -1
  234. package/dist/utility/serialization/scene/camera.js.map +1 -1
  235. package/dist/utility/serialization/scene/common.js +26 -5
  236. package/dist/utility/serialization/scene/common.js.map +1 -1
  237. package/dist/utility/serialization/scene/hair.js +1141 -0
  238. package/dist/utility/serialization/scene/hair.js.map +1 -0
  239. package/dist/utility/serialization/scene/light.js +84 -4
  240. package/dist/utility/serialization/scene/light.js.map +1 -1
  241. package/dist/utility/serialization/scene/material.js +1867 -166
  242. package/dist/utility/serialization/scene/material.js.map +1 -1
  243. package/dist/utility/serialization/scene/mesh.js +231 -14
  244. package/dist/utility/serialization/scene/mesh.js.map +1 -1
  245. package/dist/utility/serialization/scene/node.js +141 -7
  246. package/dist/utility/serialization/scene/node.js.map +1 -1
  247. package/dist/utility/serialization/scene/particle.js +4 -0
  248. package/dist/utility/serialization/scene/particle.js.map +1 -1
  249. package/dist/utility/serialization/scene/primitive.js +18 -0
  250. package/dist/utility/serialization/scene/primitive.js.map +1 -1
  251. package/dist/utility/serialization/scene/sprite.js +4 -0
  252. package/dist/utility/serialization/scene/sprite.js.map +1 -1
  253. package/dist/utility/serialization/scene/terrain.js +92 -69
  254. package/dist/utility/serialization/scene/terrain.js.map +1 -1
  255. package/dist/utility/serialization/scene/text.js +4 -0
  256. package/dist/utility/serialization/scene/text.js.map +1 -1
  257. package/dist/utility/serialization/scene/water.js +4 -0
  258. package/dist/utility/serialization/scene/water.js.map +1 -1
  259. package/dist/utility/serialization/types.js.map +1 -1
  260. package/dist/utility/shprojector.js +96 -95
  261. package/dist/utility/shprojector.js.map +1 -1
  262. package/dist/values.js +105 -58
  263. package/dist/values.js.map +1 -1
  264. package/package.json +3 -3
@@ -6,9 +6,12 @@ import '../material/unlit.js';
6
6
  import '../material/particle.js';
7
7
  import { SubsurfaceProfile } from '../material/subsurfaceprofile.js';
8
8
  import '../material/skin.js';
9
+ import '../material/eye.js';
9
10
  import '@zephyr3d/device';
10
11
  import { LIGHT_TYPE_DIRECTIONAL, LIGHT_TYPE_POINT, LIGHT_TYPE_SPOT, LIGHT_TYPE_RECT } from '../values.js';
11
12
  import '../material/meshmaterial.js';
13
+ import '../material/hair.js';
14
+ import '../material/hairstrand.js';
12
15
  import '../material/grassmaterial.js';
13
16
  import '../material/terrain-cm.js';
14
17
  import '../material/pbrmr.js';
@@ -32,11 +35,12 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
32
35
  * @internal
33
36
  */ class SSS extends AbstractPostEffect {
34
37
  static _profileLUTWidth = 256;
35
- static _profileLUTHeight = 7;
38
+ static _profileLUTHeight = 8;
36
39
  static _profileRadiusEncodeMax = 8;
37
40
  static _profileWorldScaleEncodeMax = 4;
38
41
  static _profileNormalScaleEncodeMax = 2;
39
42
  static _profileExtinctionEncodeMax = 4;
43
+ static _profileSpecularRadiusEncodeMax = 4;
40
44
  static _debugViewMap = {
41
45
  none: 0,
42
46
  scatter_mask: 1,
@@ -187,6 +191,7 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
187
191
  bindGroup.setValue('targetSize', new Vector4(targetWidth, targetHeight, targetWidth, targetHeight));
188
192
  bindGroup.setValue('sampleOffsets', this.createSampleOffsets(kernelRadius));
189
193
  bindGroup.setValue('blurScale', ctx.camera.sssBlurScale);
194
+ bindGroup.setValue('sssStrength', ctx.camera.sssStrength);
190
195
  bindGroup.setValue('blurStdDev', sssSettings.blurStdDev);
191
196
  bindGroup.setValue('depthCutoff', sssSettings.blurDepthCutoff);
192
197
  bindGroup.setValue('normalCutoff', sssSettings.normalCutoff);
@@ -368,6 +373,7 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
368
373
  this.targetSize = pb.vec4().uniform(0);
369
374
  this.sampleOffsets = pb.vec4[sampleCount + 1]().uniform(0);
370
375
  this.blurScale = pb.float().uniform(0);
376
+ this.sssStrength = pb.float().uniform(0);
371
377
  this.blurStdDev = pb.float().uniform(0);
372
378
  this.depthCutoff = pb.float().uniform(0);
373
379
  this.normalCutoff = pb.float().uniform(0);
@@ -487,7 +493,8 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
487
493
  this.$l.profileScatterScaleNorm = pb.clamp(pb.div(this.profileScatterScale, SSS._profileWorldScaleEncodeMax), 0, 1);
488
494
  this.$l.profileStrengthNorm = pb.clamp(pb.mul(this.profileStrengthScale, 0.5), 0, 1);
489
495
  this.$l.depthThreshold = pb.div(pb.mul(this.depthCutoff, pb.add(0.45, pb.mul(this.widthStrength, 0.42)), pb.mix(0.88, 1.16, this.profileWorldScaleNorm)), pb.max(pb.sub(pb.log(pb.add(this.cameraNearFar.y, 1)), pb.log(pb.add(this.cameraNearFar.x, 1))), 1e-4));
490
- this.$l.normalThreshold = pb.clamp(pb.add(pb.max(pb.sub(this.normalCutoff, pb.mul(this.widthStrength, 0.12)), 0.58), pb.sub(pb.mul(this.profileNormalScale, 0.08), 0.08), pb.neg(pb.mul(this.profileBoundaryBleed, 0.04))), 0.52, 0.985);
496
+ this.$l.normalDetailSoftening = pb.mul(pb.clamp(this.sssStrength, 0, 1), pb.add(0.14, pb.mul(this.widthStrength, 0.1)));
497
+ this.$l.normalThreshold = pb.clamp(pb.add(pb.max(pb.sub(this.normalCutoff, pb.add(this.normalDetailSoftening, pb.mul(this.widthStrength, 0.12))), 0.48), pb.sub(pb.mul(this.profileNormalScale, 0.08), 0.08), pb.neg(pb.mul(this.profileBoundaryBleed, 0.04))), 0.44, 0.92);
491
498
  this.$l.centerNormal = this.decodeNormal(this.uv);
492
499
  this.$l.profileRadius = pb.mul(this.readProfileRadius(this.centerSlot), this.profileWorldScale, pb.mix(0.76, 1.44, this.profileScatterScaleNorm), pb.mix(0.92, 1.08, this.profileStrengthNorm));
493
500
  this.$l.profileFalloff = this.readProfileFalloff(this.centerSlot);
@@ -683,15 +690,17 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
683
690
  this.$l.raw = pb.textureSampleLevel(this.profileLUTTex, this.uv, 0);
684
691
  this.$return(pb.vec4(pb.mul(this.raw.x, SSS._profileExtinctionEncodeMax), pb.mul(this.raw.y, SSS._profileWorldScaleEncodeMax), pb.mul(this.raw.z, 2), this.raw.w));
685
692
  });
686
- pb.func('readProfileSlot', [
687
- pb.vec4('param')
693
+ pb.func('readProfileSpecularSettings', [
694
+ pb.float('slot')
688
695
  ], function() {
689
- this.$return(pb.floor(pb.add(pb.mul(this.param.r, 255), 0.5)));
696
+ this.$l.uv = pb.vec2(pb.div(pb.add(this.slot, 0.5), SSS._profileLUTWidth), SSS.profileRowV(7));
697
+ this.$l.raw = pb.textureSampleLevel(this.profileLUTTex, this.uv, 0);
698
+ this.$return(pb.vec2(this.raw.x, pb.mul(this.raw.y, SSS._profileSpecularRadiusEncodeMax)));
690
699
  });
691
- pb.func('readProfilePreset', [
700
+ pb.func('readProfileSlot', [
692
701
  pb.vec4('param')
693
702
  ], function() {
694
- this.$return(pb.floor(pb.add(pb.mul(this.param.b, 255), 0.5)));
703
+ this.$return(pb.floor(pb.add(pb.mul(this.param.r, 255), 0.5)));
695
704
  });
696
705
  pb.func('decodeNormal', [
697
706
  pb.vec2('uv')
@@ -707,6 +716,26 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
707
716
  ], function() {
708
717
  this.$return(ShaderHelper.sampleLinearDepth(this, this.depthTex, this.uv, 0));
709
718
  });
719
+ pb.func('sampleCompatibleSpecular', [
720
+ pb.vec2('uv'),
721
+ pb.float('profileSlot'),
722
+ pb.float('centerLogDepth')
723
+ ], function() {
724
+ this.$l.sampleUV = pb.clamp(this.uv, pb.vec2(0), pb.vec2(1));
725
+ this.$l.sampleParam = pb.textureSampleLevel(this.paramTex, this.sampleUV, 0);
726
+ this.$l.sampleSlot = this.readProfileSlot(this.sampleParam);
727
+ this.$l.sampleDepth01 = this.readDepth01(this.sampleUV);
728
+ this.$if(pb.or(pb.or(pb.lessThanEqual(this.sampleParam.a, 0.5), pb.notEqual(this.sampleSlot, this.profileSlot)), pb.greaterThanEqual(this.sampleDepth01, 1)), function() {
729
+ this.$return(pb.vec4(0));
730
+ });
731
+ this.$l.sampleLogDepth = pb.log(pb.add(pb.mul(this.sampleDepth01, this.cameraNearFar.y), 1));
732
+ this.$if(pb.greaterThan(pb.abs(pb.sub(this.sampleLogDepth, this.centerLogDepth)), 0.022), function() {
733
+ this.$return(pb.vec4(0));
734
+ });
735
+ this.$l.sampleScene = pb.textureSampleLevel(this.colorTex, this.sampleUV, 0).rgb;
736
+ this.$l.sampleDiffuse = pb.textureSampleLevel(this.diffuseTex, this.sampleUV, 0).rgb;
737
+ this.$return(pb.vec4(pb.max(pb.sub(this.sampleScene, this.sampleDiffuse), pb.vec3(0)), 1));
738
+ });
710
739
  pb.func('estimateScreenThinnessComponents', [
711
740
  pb.vec2('uv'),
712
741
  pb.float('depth01'),
@@ -838,10 +867,9 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
838
867
  this.$l.scatterSoftnessAuthor = pb.clamp(pb.mul(pb.sub(this.param.a, 0.75), 4), 0, 1);
839
868
  this.$l.roughnessInfo = pb.textureSampleLevel(this.roughnessTex, this.uv, 0);
840
869
  this.$l.profileStrength = this.max3(this.profile.rgb);
870
+ this.$l.automaticTransmission = pb.lessThan(this.profile.a, 0);
841
871
  this.$l.materialTransmissionMask = pb.clamp(this.profile.a, 0, 1);
842
- this.debugTransmissionMask = this.materialTransmissionMask;
843
872
  this.$l.profileSlot = this.readProfileSlot(this.param);
844
- this.$l.profilePreset = this.readProfilePreset(this.param);
845
873
  this.$l.depth01 = this.readDepth01(this.uv);
846
874
  this.$l.result = this.baseColor.rgb;
847
875
  // Transmission shadow. On the PCSS path the shadow uses implicit
@@ -871,6 +899,7 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
871
899
  this.$l.profileSettingsA = this.readProfileSettingsA(this.profileSlot);
872
900
  this.$l.profileTransmissionTint = this.readProfileTransmissionTint(this.profileSlot);
873
901
  this.$l.profileSettingsB = this.readProfileSettingsB(this.profileSlot);
902
+ this.$l.profileSpecularSettings = this.readProfileSpecularSettings(this.profileSlot);
874
903
  this.$l.profileWorldScale = pb.clamp(this.profileSettingsA.x, 0.05, SSS._profileWorldScaleEncodeMax);
875
904
  this.$l.profileBoundaryBleed = pb.clamp(this.profileSettingsA.y, 0, 1);
876
905
  this.$l.profileNormalScale = pb.clamp(this.profileSettingsA.z, 0, SSS._profileNormalScaleEncodeMax);
@@ -878,6 +907,8 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
878
907
  this.$l.profileExtinctionScale = pb.clamp(this.profileSettingsB.x, 0, SSS._profileExtinctionEncodeMax);
879
908
  this.$l.profileScatterScale = pb.clamp(this.profileSettingsB.y, 0.05, SSS._profileWorldScaleEncodeMax);
880
909
  this.$l.profileStrengthScale = pb.clamp(this.profileSettingsB.z, 0.05, 2);
910
+ this.$l.profileSpecularSoftness = pb.clamp(this.profileSpecularSettings.x, 0, 1);
911
+ this.$l.profileSpecularRadius = pb.clamp(this.profileSpecularSettings.y, 0, SSS._profileSpecularRadiusEncodeMax);
881
912
  this.$l.profileWorldScaleNorm = pb.clamp(pb.div(this.profileWorldScale, SSS._profileWorldScaleEncodeMax), 0, 1);
882
913
  this.$l.profileScatterScaleNorm = pb.clamp(pb.div(this.profileScatterScale, SSS._profileWorldScaleEncodeMax), 0, 1);
883
914
  this.$l.profileStrengthNorm = pb.clamp(pb.mul(this.profileStrengthScale, 0.5), 0, 1);
@@ -896,7 +927,6 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
896
927
  this.$l.presetResponse = this.readProfilePresetResponse(this.profileSlot);
897
928
  this.$l.presetFrontScatter = this.presetResponse.x;
898
929
  this.$l.presetSoftness = this.presetResponse.y;
899
- this.$l.presetEnergyBoost = this.presetResponse.z;
900
930
  this.$l.presetTransmissionBoost = this.presetResponse.w;
901
931
  this.$l.presetTint = pb.mul(this.profileTintBase, this.presetTintBias, pb.mix(pb.vec3(1), this.profileTransmissionTint, pb.mul(this.profileBoundaryBleed, 0.22)));
902
932
  this.$l.tint = pb.div(pb.max(this.presetTint, pb.vec3(1e-4)), pb.max(this.max3(this.presetTint), 1e-4));
@@ -918,6 +948,11 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
918
948
  this.$l.depthThinness = this.thinnessComponents.x;
919
949
  this.$l.curvatureThinness = this.thinnessComponents.y;
920
950
  this.$l.screenThinness = this.thinnessComponents.z;
951
+ this.$l.backLightFacing = pb.clamp(pb.dot(pb.neg(this.normalWS), this.sunDirWS), 0, 1);
952
+ this.$l.backLightGate = pb.pow(this.backLightFacing, pb.max(this.transmissionPower, 0.1));
953
+ this.$l.automaticThinMask = pb.mul(pb.smoothStep(0.24, 0.72, this.screenThinness), pb.mix(0.58, 1, this.backLightGate));
954
+ this.materialTransmissionMask = this.$choice(this.automaticTransmission, this.automaticThinMask, this.materialTransmissionMask);
955
+ this.debugTransmissionMask = this.materialTransmissionMask;
921
956
  this.debugThinness = this.screenThinness;
922
957
  this.debugThinnessLayers = pb.vec3(this.depthThinness, this.curvatureThinness, 0);
923
958
  this.$l.wrappedFront = pb.smoothStep(pb.add(-0.32, pb.mul(this.profileSpread, 0.08)), pb.add(0.42, pb.mul(this.radiusResponse, 0.16)), pb.add(this.frontLit, pb.mul(this.radiusResponse, 0.18)));
@@ -933,7 +968,6 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
933
968
  this.$l.edgeThinBoost = pb.clamp(pb.mul(this.screenThinness, pb.add(0.14, pb.mul(this.materialThicknessField, 0.22))), 0, 0.32);
934
969
  this.$l.curvatureThinBoost = pb.clamp(pb.mul(this.curvatureThinness, pb.add(pb.add(0.08, pb.mul(this.materialThicknessField, 0.12)), pb.mul(pb.sub(this.profileNormalScale, 1), 0.08))), 0, 0.18);
935
970
  this.$l.scatterMask = pb.clamp(pb.add(this.surfaceScatterMask, pb.add(pb.add(pb.mul(this.materialScatterBoost, 0.28), this.presetFrontScatter), pb.add(pb.mul(pb.clamp(pb.sub(1, pb.max(this.frontLit, 0)), 0, 1), pb.add(pb.mul(this.materialScatterBoost, 0.22), pb.mul(this.presetFrontScatter, 0.7))), pb.mul(this.screenThinness, pb.add(0.12, pb.mul(this.presetSoftness, 0.08), pb.mul(this.presetFrontScatter, 0.08))), pb.mul(this.materialTransmissionSoftness, 0.1)))), pb.add(pb.add(0.05, pb.mul(this.materialScatterBoost, 0.12)), pb.mul(this.presetFrontScatter, 0.3)), 1);
936
- this.$l.scatterMix = pb.add(pb.add(0.54, pb.mul(this.radiusResponse, 0.94)), pb.add(pb.add(pb.mul(this.avgFalloff, 0.22), pb.mul(this.profileSpread, 0.08)), pb.add(pb.mul(this.materialScatterBoost, 0.3), this.presetSoftness)));
937
971
  this.$l.scatterStrength = pb.clamp(pb.mul(pb.max(pb.mul(this.profileStrength, pb.mix(0.88, 1.24, this.profileStrengthNorm)), pb.mul(this.widthStrength, 0.45)), this.sssStrength, this.profileEnergyScale, pb.add(pb.add(0.62, pb.mul(this.avgFalloff, 0.56)), pb.add(pb.add(pb.mul(this.profileSpread, 0.28), pb.mul(this.screenThinness, 0.18)), pb.add(pb.add(pb.mul(this.materialScatterBoost, 0.42), pb.mul(this.presetSoftness, 0.5)), pb.mul(this.materialTransmissionSoftness, 0.22))))), 0, pb.add(pb.add(pb.add(1.05, pb.mul(this.materialScatterBoost, 1.1)), pb.add(pb.mul(this.presetSoftness, 0.8), pb.mul(this.profileEnergyScale, 0.24))), pb.mul(this.screenThinness, 0.22)));
938
972
  this.$l.blurred = pb.textureSampleLevel(this.blurTex, this.uv, 0).rgb;
939
973
  this.$l.roughness = pb.float(1);
@@ -953,26 +987,44 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
953
987
  this.$l.skinTint = pb.normalize(pb.max(pb.mix(this.baseDiffuseSafe, pb.mul(this.baseDiffuseSafe, pb.mix(this.tint, this.profileTransmissionTint, pb.mul(this.profileBoundaryBleed, 0.2))), this.epidermalMix), pb.vec3(1e-4)));
954
988
  this.$l.deepTint = pb.normalize(pb.max(pb.mix(this.skinTint, pb.mul(this.tint, pb.mix(pb.vec3(1), this.profileTransmissionTint, pb.clamp(pb.add(pb.mul(this.profileBoundaryBleed, 0.35), pb.mul(pb.sub(1, pb.clamp(pb.div(this.profileExtinctionScale, SSS._profileExtinctionEncodeMax), 0, 1)), 0.28)), 0, 0.8))), this.deepMix), pb.vec3(1e-4)));
955
989
  this.$l.baseSpecular = pb.max(pb.sub(this.baseColor.rgb, this.baseDiffuseColor), pb.vec3(0));
956
- this.$l.scatterBlend = pb.clamp(pb.mul(this.scatterStrength, pb.add(0.12, pb.mul(this.scatterMix, 0.24)), this.scatterMask), 0, pb.add(pb.add(0.7, pb.mul(this.materialScatterBoost, 0.22)), this.$choice(pb.equal(this.profilePreset, 1), 0.12, this.$choice(pb.equal(this.profilePreset, 2), 0.08, 0))));
990
+ this.$l.softenedSpecular = this.baseSpecular;
991
+ this.$if(pb.notEqual(this.hasDiffuseTex, 0), function() {
992
+ // The wide SSS pass contains diffuse lighting only. Preserve broad
993
+ // highlights, but diffuse the pixel-scale normal detail that would
994
+ // otherwise be added back unchanged with the specular component.
995
+ this.$l.specularRadius = pb.clamp(pb.mul(this.profileSpecularRadius, pb.mix(0.9, 1.1, this.radiusResponse)), 0, pb.mul(SSS._profileSpecularRadiusEncodeMax, 1.1));
996
+ this.$l.specularTexel = pb.div(pb.vec2(this.specularRadius), this.targetSize.xy);
997
+ this.$l.centerLogDepth = pb.log(pb.add(pb.mul(this.depth01, this.cameraNearFar.y), 1));
998
+ this.$l.specularSum = this.baseSpecular;
999
+ this.$l.specularWeight = pb.float(1);
1000
+ const sampleSpecularAt = (offsetX, offsetY)=>{
1001
+ this.$l.specularNeighbor = this.sampleCompatibleSpecular(pb.add(this.uv, pb.mul(this.specularTexel, pb.vec2(offsetX, offsetY))), this.profileSlot, this.centerLogDepth);
1002
+ this.specularSum = pb.add(this.specularSum, pb.mul(this.specularNeighbor.rgb, this.specularNeighbor.a, 0.72));
1003
+ this.specularWeight = pb.add(this.specularWeight, pb.mul(this.specularNeighbor.a, 0.72));
1004
+ };
1005
+ sampleSpecularAt(1, 0);
1006
+ sampleSpecularAt(-1, 0);
1007
+ sampleSpecularAt(0, 1);
1008
+ sampleSpecularAt(0, -1);
1009
+ this.$l.filteredSpecular = pb.div(this.specularSum, pb.max(this.specularWeight, 1));
1010
+ this.$l.specularSoftnessBase = pb.clamp(pb.mul(this.profileSpecularSoftness, this.profileStrength, this.sssStrength), 0, 0.95);
1011
+ this.$l.specularSoftness = pb.sub(1, pb.mul(pb.sub(1, this.specularSoftnessBase), pb.sub(1, this.specularSoftnessBase)));
1012
+ this.softenedSpecular = pb.mix(this.baseSpecular, this.filteredSpecular, this.specularSoftness);
1013
+ });
1014
+ this.$l.scatterBlendBase = pb.clamp(pb.mul(this.profileStrength, this.sssStrength, pb.add(0.28, pb.add(pb.mul(this.radiusResponse, 0.42), pb.mul(this.widthStrength, 0.3))), pb.mix(0.72, 1, this.scatterMask)), 0, 0.92);
1015
+ // Diffusion should visibly suppress high-frequency normal lighting.
1016
+ // A squared complement keeps low strengths controllable while making
1017
+ // a fully enabled skin profile approach the blurred diffuse result.
1018
+ this.$l.scatterBlend = pb.clamp(pb.sub(1, pb.mul(pb.sub(1, this.scatterBlendBase), pb.sub(1, this.scatterBlendBase))), 0, 0.97);
957
1019
  this.$l.blurredLuma = pb.dot(this.blurred, pb.vec3(0.2126, 0.7152, 0.0722));
958
- this.$l.profileBlurred = pb.mix(this.blurred, pb.mul(this.blurred, pb.mix(pb.vec3(1), this.skinTint, pb.clamp(pb.add(pb.add(0.2, pb.mul(this.avgFalloff, 0.26)), pb.add(pb.mul(this.materialScatterBoost, 0.16), pb.mul(this.presetSoftness, 0.18))), 0, 0.72))), pb.clamp(pb.add(pb.add(0.14, pb.mul(this.radiusResponse, 0.18)), pb.add(pb.mul(this.avgFalloff, 0.08), pb.add(pb.mul(this.materialScatterBoost, 0.16), pb.mul(this.presetSoftness, 0.16)))), 0, 0.58));
959
- this.$l.warmBlur = pb.mul(pb.max(this.blurredLuma, pb.mul(this.baseDiffuseLuma, 0.82)), this.deepTint);
960
- this.profileBlurred = pb.mix(this.profileBlurred, this.warmBlur, pb.clamp(pb.add(pb.add(0.1, pb.mul(this.avgFalloff, 0.08)), pb.add(pb.mul(this.materialScatterBoost, 0.12), pb.mul(this.presetSoftness, 0.08))), 0.08, 0.32));
961
- this.$l.blurredClamped = pb.min(this.profileBlurred, pb.add(this.baseDiffuseColor, pb.mul(this.deepTint, pb.mul(this.scatterMask, pb.add(pb.add(0.06, pb.mul(this.radiusResponse, 0.18)), pb.add(pb.mul(this.avgFalloff, 0.08), pb.add(pb.mul(this.materialScatterBoost, 0.2), pb.mul(this.presetEnergyBoost, 0.18))))))));
1020
+ this.$l.profileBlurred = this.blurred;
1021
+ this.$l.blurredClamped = this.profileBlurred;
962
1022
  this.$l.scattered = pb.mix(this.baseDiffuseColor, this.blurredClamped, this.scatterBlend);
963
- this.$l.terminatorLift = pb.mul(this.baseDiffuseColor, this.deepTint, this.scatterStrength, this.terminatorWrap, pb.add(pb.add(0.014, pb.mul(this.radiusResponse, 0.022)), pb.add(pb.mul(this.profileSpread, 0.012), pb.add(pb.mul(this.materialScatterBoost, 0.02), pb.mul(this.presetSoftness, 0.012)))), pb.sub(1, pb.mul(this.specMask, 0.45)));
964
1023
  this.$l.multiScatterLift = pb.mul(pb.max(pb.sub(this.blurredClamped, this.baseDiffuseColor), pb.vec3(0)), this.scatterStrength, this.multiScatter, this.scatterMask, pb.add(0.08, pb.mul(this.radiusResponse, pb.add(pb.add(0.09, pb.mul(this.materialScatterBoost, 0.08)), pb.add(pb.mul(this.presetSoftness, 0.06), pb.add(pb.mul(pb.sub(this.profileScatterScale, 1), 0.04), pb.mul(this.profileDiffusionSoftness, 0.03)))))));
965
- this.$l.subdermalFill = pb.mul(this.deepTint, this.scatterStrength, pb.add(pb.mul(this.shadowMask, pb.add(pb.add(0.026, pb.mul(this.radiusResponse, 0.08)), pb.add(pb.mul(this.profileSpread, 0.045), pb.mul(this.materialScatterBoost, 0.05)))), pb.add(pb.add(0.004, pb.mul(this.avgFalloff, 0.012)), pb.add(pb.mul(this.profileSpread, 0.004), pb.mul(this.materialScatterBoost, 0.008)))), this.scatterMask, pb.sub(1, pb.mul(this.specMask, 0.62)));
966
- this.$l.veilColor = pb.mix(this.blurredClamped, pb.mul(this.blurredClamped, this.skinTint), 0.16);
967
- this.$l.softVeil = pb.mul(this.veilColor, this.scatterStrength, this.scatterMask, pb.add(pb.add(pb.add(0.002, pb.mul(this.materialScatterBoost, 0.004)), pb.mul(this.presetSoftness, 0.004)), pb.mul(this.radiusResponse, pb.add(pb.add(0.007, pb.mul(this.avgFalloff, 0.007)), pb.add(pb.mul(this.profileSpread, 0.003), pb.add(pb.mul(this.materialScatterBoost, 0.006), pb.mul(this.presetSoftness, 0.004)))))), pb.sub(1, pb.mul(this.specMask, 0.55)));
968
- this.$l.scatteredDiffuse = pb.add(this.scattered, pb.add(this.terminatorLift, pb.add(this.multiScatterLift, pb.add(this.subdermalFill, this.softVeil))));
969
- this.$l.energyLimit = pb.add(this.baseDiffuseColor, pb.mul(this.deepTint, this.scatterStrength, this.scatterMask, pb.add(pb.add(pb.add(0.1, pb.mul(this.radiusResponse, 0.18)), pb.add(pb.mul(this.avgFalloff, 0.08), pb.mul(this.profileSpread, 0.04))), pb.add(pb.mul(this.materialScatterBoost, 0.28), this.presetEnergyBoost))));
970
- this.$l.scatterBudget = pb.max(pb.sub(this.energyLimit, this.baseDiffuseColor), pb.vec3(0));
971
- this.$l.scatterDelta = pb.max(pb.sub(this.scatteredDiffuse, this.baseDiffuseColor), pb.vec3(0));
972
- this.$l.scatteredDiffuse = pb.add(this.baseDiffuseColor, pb.min(this.scatterDelta, this.scatterBudget));
1024
+ this.$l.scatteredDiffuse = pb.add(this.scattered, this.multiScatterLift);
973
1025
  this.$l.transmissionBudget = pb.mul(this.deepTint, this.scatterStrength, this.materialTransmissionResponse, this.transmissionStrength, this.profileTransmissionScale, pb.add(pb.add(pb.add(0.12, pb.mul(this.radiusResponse, 0.2)), pb.mul(this.materialTransmissionSoftness, 0.2)), pb.add(pb.mul(this.avgFalloff, 0.12), pb.add(pb.mul(this.profileSpread, 0.12), pb.add(pb.mul(this.materialScatterBoost, 0.12), pb.mul(this.presetTransmissionBoost, 0.22))))), pb.mix(0.82, 1.18, this.profileScatterScaleNorm));
974
1026
  this.$l.diffuseHeadroom = pb.mul(this.transmissionBudget, pb.add(pb.add(1.65, pb.mul(this.materialTransmissionResponse, 0.9)), pb.add(pb.add(pb.mul(this.materialTransmissionSoftness, 0.55), pb.mul(this.profileSpread, 0.2)), pb.mul(this.profileTransmissionScale, 0.22))));
975
- this.result = pb.add(this.baseSpecular, this.scatteredDiffuse);
1027
+ this.result = pb.add(this.softenedSpecular, this.scatteredDiffuse);
976
1028
  this.$if(pb.greaterThan(this.transmissionStrength, 0), function() {
977
1029
  this.$l.viewDirVS = pb.normalize(pb.neg(this.viewPos));
978
1030
  this.$l.viewDirWS = pb.normalize(pb.mul(this.invViewMatrix, pb.vec4(this.viewDirVS, 0)).xyz);
@@ -984,8 +1036,9 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
984
1036
  this.debugTransmissionShadow = this.transmissionShadow;
985
1037
  this.debugProfileBoundary = pb.clamp(pb.vec3(this.profileBoundaryBleed, this.transmissionShadowSoftness, this.transmissionShadowFloor), pb.vec3(0), pb.vec3(1));
986
1038
  this.debugProfileTransmission = pb.clamp(pb.vec3(pb.div(this.profileTransmissionScale, 1.35), this.materialTransmissionResponse, this.transmissionShadow), pb.vec3(0), pb.vec3(1));
987
- this.$l.backLit = pb.mul(pb.pow(pb.clamp(pb.dot(pb.neg(this.normalWS), this.sunDirWS), 0, 1), 1.5), this.transmissionLightAttenuation);
988
- this.$l.thicknessBackScatter = pb.clamp(pb.add(pb.add(pb.mul(this.backLit, 0.82), pb.mul(this.rim, 0.24)), pb.mul(this.materialThicknessField, 0.08)), 0, 1);
1039
+ // Keep warm thin-shell lighting on the back-facing side of the surface.
1040
+ this.$l.backLit = pb.mul(this.backLightGate, this.transmissionLightAttenuation);
1041
+ this.$l.thicknessBackScatter = pb.clamp(pb.mul(this.backLit, pb.add(0.82, pb.mul(this.rim, 0.18))), 0, 1);
989
1042
  this.$l.thinPotential = pb.clamp(pb.add(pb.add(pb.mul(this.materialThicknessField, 0.78), this.edgeThinBoost), pb.add(this.curvatureThinBoost, pb.mul(pb.sqrt(pb.max(pb.mul(this.depthThinness, this.curvatureThinness), 0)), pb.add(pb.add(0.08, pb.mul(this.profileSpread, 0.06)), pb.mul(pb.sub(this.profileDistribution, 0.5), 0.06))))), 0, 1);
990
1043
  this.$l.transmissionFocus = pb.clamp(pb.mul(pb.add(pb.mul(this.materialThicknessField, 0.72), pb.mul(this.thinPotential, 0.38)), pb.add(pb.add(0.22, pb.mul(this.thicknessBackScatter, 0.72)), pb.mul(this.backLit, 0.08))), 0, 1);
991
1044
  this.$l.thinRegion = pb.clamp(pb.add(pb.add(pb.mul(this.materialThicknessField, 0.86), pb.mul(this.transmissionFocus, 0.74)), pb.add(pb.add(pb.mul(this.edgeThinBoost, 0.9), pb.mul(this.curvatureThinBoost, 0.5)), pb.add(pb.mul(this.backLit, 0.1), pb.mul(this.profileSpread, 0.05)))), 0, 1);
@@ -1010,13 +1063,11 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
1010
1063
  this.$l.transmittedDiffuseLuma = pb.dot(this.transmittedDiffuse, pb.vec3(0.2126, 0.7152, 0.0722));
1011
1064
  this.$l.visibleTransmissionMask = pb.clamp(pb.add(pb.add(pb.mul(this.materialThicknessField, 0.72), pb.mul(this.thinRegion, 0.34)), pb.add(pb.mul(this.thicknessBackScatter, 0.28), pb.add(pb.mul(this.materialTransmissionSoftness, 0.08), pb.add(pb.mul(this.profileTransmissionScale, 0.12), pb.mul(this.profileDiffusionSoftness, 0.06))))), 0, 1);
1012
1065
  this.$l.visibleTransmissionChroma = pb.clamp(pb.add(pb.add(0.66, pb.mul(this.visibleTransmissionMask, 0.32)), pb.add(pb.mul(this.thicknessBackScatter, 0.24), pb.mul(this.presetTransmissionBoost, 0.14))), 0.52, 1);
1013
- this.$l.visibleTransmissionTint = pb.normalize(pb.max(pb.mix(this.warmTransmissionTint, pb.mul(this.profileTransmissionTint, pb.vec3(1.0, 0.14, 0.08)), pb.clamp(pb.add(pb.add(0.5, pb.mul(this.visibleTransmissionMask, 0.22)), pb.mul(this.thicknessBackScatter, 0.18)), 0, 0.92)), pb.vec3(1e-4)));
1066
+ this.$l.visibleTransmissionTint = pb.normalize(pb.max(pb.mix(this.warmTransmissionTint, this.profileTransmissionTint, pb.clamp(pb.add(pb.mul(this.visibleTransmissionMask, 0.24), pb.mul(this.thicknessBackScatter, 0.2)), 0, 0.6)), pb.vec3(1e-4)));
1014
1067
  this.$l.visibleTransmissionWarm = pb.mix(this.transmittedDiffuse, pb.mul(this.visibleTransmissionTint, this.transmittedDiffuseLuma), this.visibleTransmissionChroma);
1015
- this.$l.visibleTransmissionGain = pb.clamp(pb.add(pb.add(2.35, pb.mul(this.visibleTransmissionMask, 1.85)), pb.add(pb.mul(this.thicknessBackScatter, 1.3), pb.add(pb.mul(this.materialTransmissionSoftness, 0.42), pb.mul(this.profileTransmissionScale, 0.72)))), 2, 6.4);
1068
+ this.$l.visibleTransmissionGain = pb.clamp(pb.add(pb.add(0.78, pb.mul(this.visibleTransmissionMask, 0.72)), pb.add(pb.mul(this.thicknessBackScatter, 0.48), pb.add(pb.mul(this.materialTransmissionSoftness, 0.18), pb.mul(this.profileTransmissionScale, 0.24)))), 0.7, 2.4);
1016
1069
  this.$l.visibleTransmissionHeadroom = pb.add(pb.mul(this.transmissionHeadroom, pb.add(pb.add(2.15, pb.mul(this.visibleTransmissionMask, 1.1)), pb.mul(this.thicknessBackScatter, 0.72))), pb.mul(this.visibleTransmissionTint, this.visibleTransmissionMask, this.transmittedDiffuseLuma, pb.add(pb.add(0.12, pb.mul(this.thicknessBackScatter, 0.18)), pb.mul(this.materialThicknessField, 0.14))));
1017
- this.$l.visibleTransmission = pb.min(pb.mul(this.visibleTransmissionWarm, this.visibleTransmissionGain, this.transmissionShadow), this.visibleTransmissionHeadroom);
1018
- this.$l.visibleTransmissionReveal = pb.clamp(pb.mul(this.visibleTransmissionMask, this.transmissionShadow, pb.add(pb.add(0.1, pb.mul(this.thicknessBackScatter, 0.25)), pb.mul(this.materialThicknessField, 0.12))), 0, 0.38);
1019
- this.result = pb.mix(this.result, pb.mul(this.result, pb.vec3(0.84, 0.68, 0.64)), this.visibleTransmissionReveal);
1070
+ this.$l.visibleTransmission = pb.min(pb.mul(this.visibleTransmissionWarm, this.visibleTransmissionGain, this.transmissionShadow, this.backLightGate), this.visibleTransmissionHeadroom);
1020
1071
  this.debugThinLighting = this.visibleTransmission;
1021
1072
  this.result = pb.add(this.result, this.visibleTransmission);
1022
1073
  });
@@ -1079,7 +1130,8 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
1079
1130
  }
1080
1131
  ensureProfileLUT(ctx) {
1081
1132
  const device = ctx.device;
1082
- if (!SSS._profileLUT || SSS._profileLUT.device !== device) {
1133
+ if (!SSS._profileLUT || SSS._profileLUT.device !== device || SSS._profileLUT.width !== SSS._profileLUTWidth || SSS._profileLUT.height !== SSS._profileLUTHeight) {
1134
+ SSS._profileLUT?.dispose();
1083
1135
  SSS._profileLUT = device.createTexture2D('rgba8unorm', SSS._profileLUTWidth, SSS._profileLUTHeight, {
1084
1136
  mipmapping: false
1085
1137
  });
@@ -1131,6 +1183,11 @@ import { AbstractPostEffect, PostEffectLayer } from './posteffect.js';
1131
1183
  data[settingsBOffset + 1] = encodeScaled(profile.scale, SSS._profileWorldScaleEncodeMax);
1132
1184
  data[settingsBOffset + 2] = encodeUnit(profile.strength / 2);
1133
1185
  data[settingsBOffset + 3] = 255;
1186
+ const specularSettingsOffset = (SSS._profileLUTWidth * 7 + slot) * 4;
1187
+ data[specularSettingsOffset + 0] = encodeUnit(profile.specularDetailSoftness);
1188
+ data[specularSettingsOffset + 1] = encodeScaled(profile.specularDetailRadius, SSS._profileSpecularRadiusEncodeMax);
1189
+ data[specularSettingsOffset + 2] = 0;
1190
+ data[specularSettingsOffset + 3] = 255;
1134
1191
  }
1135
1192
  SSS._profileLUT.update(data, 0, 0, SSS._profileLUTWidth, SSS._profileLUTHeight);
1136
1193
  SSS._profileLUTVersion = SubsurfaceProfile.version;