@zephyr3d/scene 0.1.0

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 (236) hide show
  1. package/dist/animation/animation.js +173 -0
  2. package/dist/animation/animation.js.map +1 -0
  3. package/dist/animation/animationset.js +95 -0
  4. package/dist/animation/animationset.js.map +1 -0
  5. package/dist/animation/animationtrack.js +38 -0
  6. package/dist/animation/animationtrack.js.map +1 -0
  7. package/dist/animation/eulerrotationtrack.js +33 -0
  8. package/dist/animation/eulerrotationtrack.js.map +1 -0
  9. package/dist/animation/rotationtrack.js +37 -0
  10. package/dist/animation/rotationtrack.js.map +1 -0
  11. package/dist/animation/scaletrack.js +36 -0
  12. package/dist/animation/scaletrack.js.map +1 -0
  13. package/dist/animation/skeleton.js +97 -0
  14. package/dist/animation/skeleton.js.map +1 -0
  15. package/dist/animation/translationtrack.js +36 -0
  16. package/dist/animation/translationtrack.js.map +1 -0
  17. package/dist/animation/usertrack.js +47 -0
  18. package/dist/animation/usertrack.js.map +1 -0
  19. package/dist/app.js +173 -0
  20. package/dist/app.js.map +1 -0
  21. package/dist/asset/assetmanager.js +476 -0
  22. package/dist/asset/assetmanager.js.map +1 -0
  23. package/dist/asset/builtin.js +373 -0
  24. package/dist/asset/builtin.js.map +1 -0
  25. package/dist/asset/loaders/dds/dds.js +472 -0
  26. package/dist/asset/loaders/dds/dds.js.map +1 -0
  27. package/dist/asset/loaders/dds/dds_loader.js +38 -0
  28. package/dist/asset/loaders/dds/dds_loader.js.map +1 -0
  29. package/dist/asset/loaders/gltf/gltf_loader.js +981 -0
  30. package/dist/asset/loaders/gltf/gltf_loader.js.map +1 -0
  31. package/dist/asset/loaders/gltf/helpers.js +314 -0
  32. package/dist/asset/loaders/gltf/helpers.js.map +1 -0
  33. package/dist/asset/loaders/hdr/hdr.js +175 -0
  34. package/dist/asset/loaders/hdr/hdr.js.map +1 -0
  35. package/dist/asset/loaders/image/tga_Loader.js +117 -0
  36. package/dist/asset/loaders/image/tga_Loader.js.map +1 -0
  37. package/dist/asset/loaders/image/webimage_loader.js +50 -0
  38. package/dist/asset/loaders/image/webimage_loader.js.map +1 -0
  39. package/dist/asset/loaders/loader.js +45 -0
  40. package/dist/asset/loaders/loader.js.map +1 -0
  41. package/dist/asset/model.js +264 -0
  42. package/dist/asset/model.js.map +1 -0
  43. package/dist/blitter/blitter.js +389 -0
  44. package/dist/blitter/blitter.js.map +1 -0
  45. package/dist/blitter/box.js +118 -0
  46. package/dist/blitter/box.js.map +1 -0
  47. package/dist/blitter/copy.js +22 -0
  48. package/dist/blitter/copy.js.map +1 -0
  49. package/dist/blitter/depthlimitedgaussion.js +166 -0
  50. package/dist/blitter/depthlimitedgaussion.js.map +1 -0
  51. package/dist/blitter/gaussianblur.js +229 -0
  52. package/dist/blitter/gaussianblur.js.map +1 -0
  53. package/dist/camera/base.js +90 -0
  54. package/dist/camera/base.js.map +1 -0
  55. package/dist/camera/camera.js +358 -0
  56. package/dist/camera/camera.js.map +1 -0
  57. package/dist/camera/fps.js +246 -0
  58. package/dist/camera/fps.js.map +1 -0
  59. package/dist/camera/orbit.js +157 -0
  60. package/dist/camera/orbit.js.map +1 -0
  61. package/dist/camera/orthocamera.js +126 -0
  62. package/dist/camera/orthocamera.js.map +1 -0
  63. package/dist/camera/perspectivecamera.js +133 -0
  64. package/dist/camera/perspectivecamera.js.map +1 -0
  65. package/dist/index.d.ts +8402 -0
  66. package/dist/index.js +87 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/input/inputmgr.js +242 -0
  69. package/dist/input/inputmgr.js.map +1 -0
  70. package/dist/material/blinn.js +75 -0
  71. package/dist/material/blinn.js.map +1 -0
  72. package/dist/material/grassmaterial.js +221 -0
  73. package/dist/material/grassmaterial.js.map +1 -0
  74. package/dist/material/lambert.js +52 -0
  75. package/dist/material/lambert.js.map +1 -0
  76. package/dist/material/lightmodel.js +2074 -0
  77. package/dist/material/lightmodel.js.map +1 -0
  78. package/dist/material/lit.js +578 -0
  79. package/dist/material/lit.js.map +1 -0
  80. package/dist/material/material.js +458 -0
  81. package/dist/material/material.js.map +1 -0
  82. package/dist/material/meshmaterial.js +311 -0
  83. package/dist/material/meshmaterial.js.map +1 -0
  84. package/dist/material/mixins/albedocolor.js +130 -0
  85. package/dist/material/mixins/albedocolor.js.map +1 -0
  86. package/dist/material/mixins/texture.js +110 -0
  87. package/dist/material/mixins/texture.js.map +1 -0
  88. package/dist/material/mixins/vertexcolor.js +45 -0
  89. package/dist/material/mixins/vertexcolor.js.map +1 -0
  90. package/dist/material/pbr.js +27 -0
  91. package/dist/material/pbr.js.map +1 -0
  92. package/dist/material/standard.js +282 -0
  93. package/dist/material/standard.js.map +1 -0
  94. package/dist/material/terrainlightmodel.js +259 -0
  95. package/dist/material/terrainlightmodel.js.map +1 -0
  96. package/dist/material/terrainmaterial.js +139 -0
  97. package/dist/material/terrainmaterial.js.map +1 -0
  98. package/dist/material/unlit.js +29 -0
  99. package/dist/material/unlit.js.map +1 -0
  100. package/dist/posteffect/bloom.js +398 -0
  101. package/dist/posteffect/bloom.js.map +1 -0
  102. package/dist/posteffect/compositor.js +264 -0
  103. package/dist/posteffect/compositor.js.map +1 -0
  104. package/dist/posteffect/fxaa.js +291 -0
  105. package/dist/posteffect/fxaa.js.map +1 -0
  106. package/dist/posteffect/grayscale.js +87 -0
  107. package/dist/posteffect/grayscale.js.map +1 -0
  108. package/dist/posteffect/posteffect.js +165 -0
  109. package/dist/posteffect/posteffect.js.map +1 -0
  110. package/dist/posteffect/sao.js +327 -0
  111. package/dist/posteffect/sao.js.map +1 -0
  112. package/dist/posteffect/tonemap.js +112 -0
  113. package/dist/posteffect/tonemap.js.map +1 -0
  114. package/dist/posteffect/water.js +535 -0
  115. package/dist/posteffect/water.js.map +1 -0
  116. package/dist/render/clipmap.js +462 -0
  117. package/dist/render/clipmap.js.map +1 -0
  118. package/dist/render/cluster_light.js +329 -0
  119. package/dist/render/cluster_light.js.map +1 -0
  120. package/dist/render/cull_visitor.js +124 -0
  121. package/dist/render/cull_visitor.js.map +1 -0
  122. package/dist/render/depth_pass.js +47 -0
  123. package/dist/render/depth_pass.js.map +1 -0
  124. package/dist/render/envlight.js +282 -0
  125. package/dist/render/envlight.js.map +1 -0
  126. package/dist/render/forward.js +186 -0
  127. package/dist/render/forward.js.map +1 -0
  128. package/dist/render/forward_pass.js +137 -0
  129. package/dist/render/forward_pass.js.map +1 -0
  130. package/dist/render/helper.js +38 -0
  131. package/dist/render/helper.js.map +1 -0
  132. package/dist/render/primitive.js +246 -0
  133. package/dist/render/primitive.js.map +1 -0
  134. package/dist/render/render_queue.js +163 -0
  135. package/dist/render/render_queue.js.map +1 -0
  136. package/dist/render/renderpass.js +151 -0
  137. package/dist/render/renderpass.js.map +1 -0
  138. package/dist/render/renderscheme.js +61 -0
  139. package/dist/render/renderscheme.js.map +1 -0
  140. package/dist/render/scatteringlut.js +634 -0
  141. package/dist/render/scatteringlut.js.map +1 -0
  142. package/dist/render/shadowmap_pass.js +70 -0
  143. package/dist/render/shadowmap_pass.js.map +1 -0
  144. package/dist/render/sky.js +881 -0
  145. package/dist/render/sky.js.map +1 -0
  146. package/dist/render/temporalcache.js +222 -0
  147. package/dist/render/temporalcache.js.map +1 -0
  148. package/dist/render/watermesh.js +835 -0
  149. package/dist/render/watermesh.js.map +1 -0
  150. package/dist/scene/environment.js +146 -0
  151. package/dist/scene/environment.js.map +1 -0
  152. package/dist/scene/graph_node.js +69 -0
  153. package/dist/scene/graph_node.js.map +1 -0
  154. package/dist/scene/light.js +436 -0
  155. package/dist/scene/light.js.map +1 -0
  156. package/dist/scene/mesh.js +215 -0
  157. package/dist/scene/mesh.js.map +1 -0
  158. package/dist/scene/model.js +111 -0
  159. package/dist/scene/model.js.map +1 -0
  160. package/dist/scene/octree.js +651 -0
  161. package/dist/scene/octree.js.map +1 -0
  162. package/dist/scene/octree_update_visitor.js +16 -0
  163. package/dist/scene/octree_update_visitor.js.map +1 -0
  164. package/dist/scene/raycast_visitor.js +72 -0
  165. package/dist/scene/raycast_visitor.js.map +1 -0
  166. package/dist/scene/scene.js +225 -0
  167. package/dist/scene/scene.js.map +1 -0
  168. package/dist/scene/scene_node.js +299 -0
  169. package/dist/scene/scene_node.js.map +1 -0
  170. package/dist/scene/terrain/grass.js +277 -0
  171. package/dist/scene/terrain/grass.js.map +1 -0
  172. package/dist/scene/terrain/heightfield.js +391 -0
  173. package/dist/scene/terrain/heightfield.js.map +1 -0
  174. package/dist/scene/terrain/patch.js +530 -0
  175. package/dist/scene/terrain/patch.js.map +1 -0
  176. package/dist/scene/terrain/quadtree.js +430 -0
  177. package/dist/scene/terrain/quadtree.js.map +1 -0
  178. package/dist/scene/terrain/terrain.js +258 -0
  179. package/dist/scene/terrain/terrain.js.map +1 -0
  180. package/dist/scene/xform.js +224 -0
  181. package/dist/scene/xform.js.map +1 -0
  182. package/dist/shaders/builtins.js +110 -0
  183. package/dist/shaders/builtins.js.map +1 -0
  184. package/dist/shaders/framework.js +709 -0
  185. package/dist/shaders/framework.js.map +1 -0
  186. package/dist/shaders/lighting.js +335 -0
  187. package/dist/shaders/lighting.js.map +1 -0
  188. package/dist/shaders/misc.js +405 -0
  189. package/dist/shaders/misc.js.map +1 -0
  190. package/dist/shaders/noise.js +157 -0
  191. package/dist/shaders/noise.js.map +1 -0
  192. package/dist/shaders/pbr.js +132 -0
  193. package/dist/shaders/pbr.js.map +1 -0
  194. package/dist/shaders/shadow.js +642 -0
  195. package/dist/shaders/shadow.js.map +1 -0
  196. package/dist/shaders/water.js +630 -0
  197. package/dist/shaders/water.js.map +1 -0
  198. package/dist/shadow/esm.js +235 -0
  199. package/dist/shadow/esm.js.map +1 -0
  200. package/dist/shadow/pcf_opt.js +182 -0
  201. package/dist/shadow/pcf_opt.js.map +1 -0
  202. package/dist/shadow/pcf_pd.js +190 -0
  203. package/dist/shadow/pcf_pd.js.map +1 -0
  204. package/dist/shadow/shadow_impl.js +15 -0
  205. package/dist/shadow/shadow_impl.js.map +1 -0
  206. package/dist/shadow/shadowmapper.js +709 -0
  207. package/dist/shadow/shadowmapper.js.map +1 -0
  208. package/dist/shadow/ssm.js +194 -0
  209. package/dist/shadow/ssm.js.map +1 -0
  210. package/dist/shadow/vsm.js +298 -0
  211. package/dist/shadow/vsm.js.map +1 -0
  212. package/dist/shapes/box.js +313 -0
  213. package/dist/shapes/box.js.map +1 -0
  214. package/dist/shapes/cylinder.js +74 -0
  215. package/dist/shapes/cylinder.js.map +1 -0
  216. package/dist/shapes/plane.js +48 -0
  217. package/dist/shapes/plane.js.map +1 -0
  218. package/dist/shapes/shape.js +33 -0
  219. package/dist/shapes/shape.js.map +1 -0
  220. package/dist/shapes/sphere.js +91 -0
  221. package/dist/shapes/sphere.js.map +1 -0
  222. package/dist/shapes/torus.js +100 -0
  223. package/dist/shapes/torus.js.map +1 -0
  224. package/dist/utility/aabbtree.js +390 -0
  225. package/dist/utility/aabbtree.js.map +1 -0
  226. package/dist/utility/bounding_volume.js +78 -0
  227. package/dist/utility/bounding_volume.js.map +1 -0
  228. package/dist/utility/panorama.js +163 -0
  229. package/dist/utility/panorama.js.map +1 -0
  230. package/dist/utility/pmrem.js +345 -0
  231. package/dist/utility/pmrem.js.map +1 -0
  232. package/dist/utility/shprojection.js +448 -0
  233. package/dist/utility/shprojection.js.map +1 -0
  234. package/dist/values.js +48 -0
  235. package/dist/values.js.map +1 -0
  236. package/package.json +70 -0
@@ -0,0 +1,2074 @@
1
+ import { Matrix4x4, Vector4 } from '@zephyr3d/base';
2
+ import { PBPrimitiveTypeInfo, PBPrimitiveType, PBStructTypeInfo } from '@zephyr3d/device';
3
+ import { calculateTBN, calculateTBNWithNormal } from '../shaders/misc.js';
4
+ import { Material } from './material.js';
5
+ import { fresnelSchlick, directLighting, directSheenLighting, directClearcoatLighting } from '../shaders/pbr.js';
6
+ import { ShaderFramework } from '../shaders/framework.js';
7
+ import { Application } from '../app.js';
8
+
9
+ const typeF32 = PBPrimitiveTypeInfo.getCachedTypeInfo(PBPrimitiveType.F32);
10
+ const typeF32Vec3 = PBPrimitiveTypeInfo.getCachedTypeInfo(PBPrimitiveType.F32VEC3);
11
+ const typeF32Vec4 = PBPrimitiveTypeInfo.getCachedTypeInfo(PBPrimitiveType.F32VEC4);
12
+ const typeMat3 = PBPrimitiveTypeInfo.getCachedTypeInfo(PBPrimitiveType.MAT3);
13
+ const identTexTransform = Matrix4x4.identity();
14
+ const TEX_NAME_ALBEDO = 'albedo';
15
+ const TEX_NAME_NORMAL = 'normal';
16
+ const TEX_NAME_EMISSIVE = 'emissive';
17
+ const TEX_NAME_OCCLUSION = 'occlusion';
18
+ const TEX_NAME_SPECULAR = 'specular';
19
+ const TEX_NAME_SPECULAR_COLOR = 'specularColor';
20
+ const TEX_NAME_METALLIC = 'metallic';
21
+ const TEX_NAME_SHEEN_COLOR = 'sheenColor';
22
+ const TEX_NAME_SHEEN_ROUGHNESS = 'sheenRoughness';
23
+ const TEX_NAME_SHEEN_LUT = 'sheenLut';
24
+ const TEX_NAME_CLEARCOAT_INTENSITY = 'clearcoatIntensity';
25
+ const TEX_NAME_CLEARCOAT_ROUGHNESS = 'clearcoatRoughness';
26
+ const TEX_NAME_CLEARCOAT_NORMAL = 'clearcoatNormal';
27
+ /**
28
+ * Base class for any kind of light model
29
+ * @public
30
+ */ class LightModel {
31
+ /** @internal */ static funcNameBRDFEnvConstantAmbient = 'libLM_envConstantAmbient';
32
+ /** @internal */ static funcNameBRDFEnvHemispheric = 'libLM_envHemispheric';
33
+ /** @internal */ static uniformAlbedoColor = 'libLM_USAGE_albedoColor';
34
+ /** @internal */ static uniformNormalScale = 'libLM_USAGE_normalScale';
35
+ /** @internal */ static uniformEmissiveFactor = 'libLM_USAGE_emissiveFactor';
36
+ /** @internal */ static funcNameCalcAlbedo = 'libLM_calcAlbedo';
37
+ /** @internal */ _doubleSideLighting;
38
+ /** @internal */ _albedo;
39
+ /** @internal */ _normalScale;
40
+ /** @internal */ _normalMapMode;
41
+ /** @internal */ _emissiveFactor;
42
+ /** @internal */ _hash;
43
+ /** @internal */ _hashVersion;
44
+ /** @internal */ _uniformVersion;
45
+ /** @internal */ _bindGroupTagList;
46
+ /** @internal */ _texCoordChannels;
47
+ /** @internal */ _textureOptions;
48
+ /** @internal */ _surfaceDataType;
49
+ /**
50
+ * Creates an instance of LightModel
51
+ */ constructor(){
52
+ this._albedo = Vector4.one();
53
+ this._doubleSideLighting = true;
54
+ this._normalScale = 1;
55
+ this._normalMapMode = 'tangent-space';
56
+ this._emissiveFactor = new Vector4(0, 0, 0, 1);
57
+ this._hash = null;
58
+ this._hashVersion = 0;
59
+ this._uniformVersion = 0;
60
+ this._bindGroupTagList = new WeakMap();
61
+ this._texCoordChannels = [];
62
+ this._textureOptions = {};
63
+ this._surfaceDataType = null;
64
+ }
65
+ /**
66
+ * Gets the data structure of surface data
67
+ * @param env - environment lighting object
68
+ * @returns The data structure of surface data
69
+ */ getSurfaceDataType(env) {
70
+ return this.createSurfaceDataType(env);
71
+ }
72
+ /** Whether enable double side lighting */ get doubleSideLighting() {
73
+ return this._doubleSideLighting;
74
+ }
75
+ set doubleSideLighting(val) {
76
+ if (this._doubleSideLighting !== !!val) {
77
+ this._doubleSideLighting = !!val;
78
+ this.optionChanged(true);
79
+ }
80
+ }
81
+ /** The albedo color */ get albedo() {
82
+ return this._albedo;
83
+ }
84
+ set albedo(val) {
85
+ if (!val.equalsTo(this._albedo)) {
86
+ this._albedo.set(val);
87
+ this.optionChanged(false);
88
+ }
89
+ }
90
+ /** The albedo texture */ get albedoMap() {
91
+ return (this._textureOptions[TEX_NAME_ALBEDO]?.texture) ?? null;
92
+ }
93
+ /** Sampler of the albedo texture */ get albedoSampler() {
94
+ return this._textureOptions[TEX_NAME_ALBEDO]?.sampler ?? null;
95
+ }
96
+ /** Texture coordinate index of the albedo texture */ get albedoMapTexCoord() {
97
+ return this._textureOptions[TEX_NAME_ALBEDO]?.texCoordIndex ?? null;
98
+ }
99
+ /**
100
+ * Gets the texture transform matrix for given texture coordinate index.
101
+ * @param texCoordIndex - The texture coordinate index
102
+ * @returns Texture transform matrix
103
+ */ getTexCoordTransform(texCoordIndex) {
104
+ return this._texCoordChannels[texCoordIndex]?.transform ?? null;
105
+ }
106
+ /**
107
+ * Sets the texture transform matrix for given texture coordinate index.
108
+ * @param texCoordIndex - The texture coordinate index.
109
+ * @param transform - Texture transform matrix, if null, the identity matrix will be set.
110
+ */ setTexCoordTransform(texCoordIndex, transform) {
111
+ if (!this._texCoordChannels[texCoordIndex]) {
112
+ console.error(`setTexCoordTransform(): texCoordIndex ${texCoordIndex} not in use`);
113
+ } else {
114
+ this._texCoordChannels[texCoordIndex].transform = transform ?? identTexTransform;
115
+ this.optionChanged(false);
116
+ }
117
+ }
118
+ /**
119
+ * Sets the albedo texture
120
+ * @param tex - The texture to set
121
+ * @param sampler - Sampler of the texture
122
+ * @param texCoordIndex - Texture coordinate index of the texture
123
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
124
+ */ setAlbedoMap(tex, sampler, texCoordIndex, texTransform) {
125
+ this.setTextureOptions(TEX_NAME_ALBEDO, tex, sampler, texCoordIndex, texTransform);
126
+ }
127
+ /** The normal texture */ get normalMap() {
128
+ return (this._textureOptions[TEX_NAME_NORMAL]?.texture) ?? null;
129
+ }
130
+ /** Sampler of the normal texture */ get normalSampler() {
131
+ return this._textureOptions[TEX_NAME_NORMAL]?.sampler ?? null;
132
+ }
133
+ /** Texture coordinate index of the normal texture */ get normalMapTexCoord() {
134
+ return this._textureOptions[TEX_NAME_NORMAL]?.texCoordIndex ?? null;
135
+ }
136
+ /**
137
+ * Sets the normal texture
138
+ * @param tex - The texture to set
139
+ * @param sampler - Sampler of the texture
140
+ * @param texCoordIndex - Texture coordinate index of the texture
141
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
142
+ */ setNormalMap(tex, sampler, texCoordIndex, texTransform) {
143
+ this.setTextureOptions(TEX_NAME_NORMAL, tex, sampler, texCoordIndex, texTransform);
144
+ }
145
+ /** Scale value of normal for normal mapping */ get normalScale() {
146
+ return this._normalScale;
147
+ }
148
+ set normalScale(val) {
149
+ if (val !== this._normalScale) {
150
+ this._normalScale = val;
151
+ if (this.normalMap) {
152
+ this.optionChanged(false);
153
+ }
154
+ }
155
+ }
156
+ /** The emission texture */ get emissiveMap() {
157
+ return (this._textureOptions[TEX_NAME_EMISSIVE]?.texture) ?? null;
158
+ }
159
+ /** Sampler of the emission texture */ get emissiveSampler() {
160
+ return this._textureOptions[TEX_NAME_EMISSIVE]?.sampler ?? null;
161
+ }
162
+ /** Texture coordinate index of the emission texture */ get emissiveMapTexCoord() {
163
+ return this._textureOptions[TEX_NAME_EMISSIVE]?.texCoordIndex ?? null;
164
+ }
165
+ /**
166
+ * Sets the emission texture
167
+ * @param tex - The texture to set
168
+ * @param sampler - Sampler of the texture
169
+ * @param texCoordIndex - Texture coordinate index of the texture
170
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
171
+ */ setEmissiveMap(tex, sampler, texCoordIndex, texTransform) {
172
+ this.setTextureOptions(TEX_NAME_EMISSIVE, tex, sampler, texCoordIndex, texTransform);
173
+ }
174
+ /** The emission color */ get emissiveColor() {
175
+ return this._emissiveFactor.xyz();
176
+ }
177
+ set emissiveColor(val) {
178
+ if (val.x !== this._emissiveFactor.x || val.y !== this._emissiveFactor.y || val.z !== this._emissiveFactor.z) {
179
+ this._emissiveFactor.x = val.x;
180
+ this._emissiveFactor.y = val.y;
181
+ this._emissiveFactor.z = val.z;
182
+ this.optionChanged(false);
183
+ }
184
+ }
185
+ /** Strength of emission */ get emissiveStrength() {
186
+ return this._emissiveFactor.w;
187
+ }
188
+ set emissiveStrength(val) {
189
+ if (this._emissiveFactor.w !== val) {
190
+ this._emissiveFactor.w = val;
191
+ this.optionChanged(false);
192
+ }
193
+ }
194
+ /**
195
+ * Adds a texture uniforms for the light model
196
+ * @param name - Name of the texture uniform
197
+ * @param tex - Texture to set
198
+ * @param sampler - Sampler of the texture
199
+ * @param texCoord - Texture coordinate index of the texture
200
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
201
+ */ setTextureOptions(name, tex, sampler, texCoord, texTransform) {
202
+ tex = tex ?? null;
203
+ let info = this._textureOptions[name];
204
+ if (!tex) {
205
+ if (info) {
206
+ delete this._textureOptions[name];
207
+ this.optionChanged(true);
208
+ }
209
+ return;
210
+ }
211
+ if (!info) {
212
+ info = {
213
+ texture: null,
214
+ texCoordIndex: null,
215
+ sampler: null
216
+ };
217
+ this._textureOptions[name] = info;
218
+ }
219
+ sampler = sampler ?? null;
220
+ texTransform = texTransform || identTexTransform;
221
+ let uniformChanged = false;
222
+ let hashChanged = false;
223
+ if (info.texture !== tex) {
224
+ hashChanged ||= !info.texture || !tex;
225
+ info.texture = tex;
226
+ }
227
+ if (info.sampler !== sampler) {
228
+ uniformChanged ||= !!info.texture;
229
+ info.sampler = sampler;
230
+ }
231
+ const index = this.addTexCoordChannel(texCoord, texTransform);
232
+ if (index !== info.texCoordIndex) {
233
+ info.texCoordIndex = index;
234
+ uniformChanged ||= !!info.texture;
235
+ }
236
+ if (uniformChanged || hashChanged) {
237
+ this.optionChanged(hashChanged);
238
+ }
239
+ return index;
240
+ }
241
+ /**
242
+ * Calculates the hash code of the shader program
243
+ * @returns The hash code of the shader program
244
+ */ calculateHash() {
245
+ const texChannelHash = this._texCoordChannels.map((val)=>val.srcLocation).join('');
246
+ const albedoHash = this.albedoMap ? this.albedoMapTexCoord + 1 : 0;
247
+ const normalHash = this.normalMap ? `${this.normalMapTexCoord + 1}:${this._normalMapMode === 'tangent-space' ? 1 : 0}` : 0;
248
+ const emissiveHash = this.emissiveMap ? this.emissiveMapTexCoord + 1 : 0;
249
+ return `${texChannelHash}_${albedoHash}_${normalHash}_${emissiveHash}`;
250
+ }
251
+ /**
252
+ * Setup uniforms of the shader program
253
+ * @param scope - The shader scope
254
+ * @param ctx - The drawing context
255
+ */ setupUniforms(scope, ctx) {
256
+ const pb = scope.$builder;
257
+ const that = this;
258
+ if (pb.shaderKind === 'vertex') {
259
+ for(let i = 0; i < that._texCoordChannels.length; i++){
260
+ scope[`lm_texTransform${i}`] = pb.mat4().uniform(2);
261
+ }
262
+ } else {
263
+ scope.lm_albedo = pb.vec4().uniform(2).tag(LightModel.uniformAlbedoColor);
264
+ if (this.normalMap) {
265
+ scope.lm_normalScale = pb.float().uniform(2).tag(LightModel.uniformNormalScale);
266
+ }
267
+ scope.lm_emissiveFactor = pb.vec4().uniform(2).tag(LightModel.uniformEmissiveFactor);
268
+ scope.surfaceData = pb.defineStructByType(that.getSurfaceDataType(ctx.drawEnvLight ? ctx.env.light.envLight : null))();
269
+ this.setupTextureUniforms(scope);
270
+ }
271
+ }
272
+ /**
273
+ * Updates uniforms of the shader program
274
+ * @param bindGroup - The bind group
275
+ * @param ctx - The drawing context
276
+ */ applyUniforms(bindGroup, ctx) {
277
+ for(let i = 0; i < this._texCoordChannels.length; i++){
278
+ bindGroup.setValue(`lm_texTransform${i}`, this._texCoordChannels[i].transform);
279
+ }
280
+ bindGroup.setValue('lm_albedo', this._albedo);
281
+ if (this.normalMap) {
282
+ bindGroup.setValue('lm_normalScale', this._normalScale);
283
+ }
284
+ bindGroup.setValue('lm_emissiveFactor', this._emissiveFactor);
285
+ this.applyTextureUniforms(bindGroup);
286
+ }
287
+ /**
288
+ * Retreives the surface data of the material
289
+ * @param scope - The shader scope
290
+ * @param envLight - The environment lighting object
291
+ * @param worldPos - World space position of current fragment
292
+ * @param worldNormal - World space normal of current fragment
293
+ * @param worldTangent - World space tangent of current fragment
294
+ * @param worldBinormal - World space binormal of current fragment
295
+ * @returns The surface data
296
+ */ getSurfaceData(scope, envLight, worldPos, worldNormal, worldTangent, worldBinormal) {
297
+ const funcNameGetSurfaceData = 'lib_getSurfaceData';
298
+ const pb = scope.$builder;
299
+ const that = this;
300
+ const args = [
301
+ worldPos.xyz
302
+ ];
303
+ const params = [
304
+ pb.vec3('worldPos')
305
+ ];
306
+ if (worldNormal) {
307
+ params.push(pb.vec3('worldNormal'));
308
+ args.push(worldNormal);
309
+ if (worldTangent) {
310
+ params.push(pb.vec3('worldTangent'), pb.vec3('worldBinormal'));
311
+ args.push(worldTangent, worldBinormal);
312
+ }
313
+ }
314
+ pb.func(funcNameGetSurfaceData, params, function() {
315
+ this.$l.normalInfo = that.calculateNormal(this, this.worldPos, worldNormal ? this.worldNormal : null, worldTangent ? this.worldTangent : null, worldTangent ? this.worldBinormal : null);
316
+ this.surfaceData.TBN = this.normalInfo.TBN;
317
+ this.surfaceData.normal = this.normalInfo.normal;
318
+ this.surfaceData.viewVec = pb.normalize(pb.sub(ShaderFramework.getCameraPosition(this), this.worldPos));
319
+ this.surfaceData.NdotV = pb.clamp(pb.dot(this.surfaceData.normal, this.surfaceData.viewVec), 0.0001, 1);
320
+ this.surfaceData.diffuse = that.calculateAlbedo(this);
321
+ this.surfaceData.accumDiffuse = pb.vec3(0);
322
+ this.surfaceData.accumSpecular = pb.vec3(0);
323
+ this.surfaceData.accumEmissive = that.calculateEmissive(this);
324
+ this.surfaceData.accumColor = pb.vec3(0);
325
+ that.fillSurfaceData(this, envLight);
326
+ });
327
+ pb.getGlobalScope()[funcNameGetSurfaceData](...args);
328
+ }
329
+ /**
330
+ * Gets the name of a texture uniform by given key
331
+ * @param key - key of the texture
332
+ * @returns Name of the texture uniform
333
+ */ getTextureUniformName(key) {
334
+ return `lm_${key}_Map`;
335
+ }
336
+ /**
337
+ * Calculates the texture coordinate of current fragment by given texture coordinate index
338
+ * @param scope - The shader scope
339
+ * @param index - The texture coordinate index
340
+ * @returns The texture coordinate
341
+ */ calculateTexCoord(scope, index) {
342
+ return scope.$builder.mul(scope[`lm_texTransform${index}`], scope.$builder.vec4(scope.$inputs[`texcoord${this._texCoordChannels[index].srcLocation}`], 0, 1)).xy;
343
+ }
344
+ /**
345
+ * Calculates the texture coordinate of current fragment by given texture coordinate value
346
+ * @param scope - The shader scope
347
+ * @param index - The texture coordinate index
348
+ * @returns The texture coordinate
349
+ */ calculateTexCoordNoInput(scope, index, value) {
350
+ return scope.$builder.mul(scope[`lm_texTransform${index}`], scope.$builder.vec4(value, 0, 1)).xy;
351
+ }
352
+ /**
353
+ * Calculates the emissive color for current fragment
354
+ * @param scope - The shader scope
355
+ * @returns The emissive color
356
+ */ calculateEmissive(scope) {
357
+ const pb = scope.$builder;
358
+ const emissiveMap = scope[this.getTextureUniformName(TEX_NAME_EMISSIVE)];
359
+ const emissiveFactor = scope.$query(LightModel.uniformEmissiveFactor);
360
+ if (emissiveFactor) {
361
+ const emissiveColor = pb.mul(emissiveFactor.rgb, emissiveFactor.a);
362
+ if (emissiveMap) {
363
+ const emissiveTexCoord = scope.$inputs[`texcoord${this.emissiveMapTexCoord}`];
364
+ return pb.mul(pb.textureSample(emissiveMap, emissiveTexCoord).rgb, emissiveColor).rgb;
365
+ } else {
366
+ return emissiveColor;
367
+ }
368
+ } else {
369
+ return pb.vec3(0);
370
+ }
371
+ }
372
+ /**
373
+ * Calculates the albedo color for current fragment
374
+ * @param scope - The shader scope
375
+ * @returns The albedo color
376
+ */ calculateAlbedo(scope) {
377
+ const that = this;
378
+ const pb = scope.$builder;
379
+ pb.func(LightModel.funcNameCalcAlbedo, [], function() {
380
+ const diffuseMap = this[that.getTextureUniformName(TEX_NAME_ALBEDO)];
381
+ const texCoord = diffuseMap && this.$inputs[`texcoord${that.albedoMapTexCoord}`];
382
+ const vertexColor = scope.$query(ShaderFramework.USAGE_VERTEX_COLOR);
383
+ let val = scope.$query(LightModel.uniformAlbedoColor);
384
+ if (diffuseMap && texCoord) {
385
+ const tex = pb.textureSample(diffuseMap, texCoord);
386
+ val = pb.mul(val, tex);
387
+ }
388
+ if (vertexColor) {
389
+ val = pb.mul(val, vertexColor);
390
+ }
391
+ this.$return(val);
392
+ });
393
+ return pb.getGlobalScope()[LightModel.funcNameCalcAlbedo]();
394
+ }
395
+ /**
396
+ * Samples tangent space normal map and then convert it to object space
397
+ * @param scope - The shader scope
398
+ * @param tex - Normal map
399
+ * @param texCoord - Sample texture coordinate
400
+ * @param normalScale - The normal scale
401
+ * @param TBN - The TBN matrix
402
+ * @returns Object space normal
403
+ */ sampleNormalMapWithTBN(scope, tex, texCoord, normalScale, TBN) {
404
+ const pb = scope.$builder;
405
+ const pixel = pb.sub(pb.mul(pb.textureSample(tex, texCoord).rgb, 2), pb.vec3(1));
406
+ const normalTex = pb.mul(pixel, pb.vec3(pb.vec3(normalScale).xx, 1));
407
+ return pb.normalize(pb.mul(TBN, normalTex));
408
+ }
409
+ /**
410
+ * Samples object space normal map
411
+ * @param scope - The shader scope
412
+ * @param tex - Normal map
413
+ * @param texCoord - Sample texture coordinate
414
+ * @param normalScale - The normal scale
415
+ * @returns Object space normal
416
+ */ sampleNormalMap(scope, tex, texCoord, normalScale) {
417
+ const pb = scope.$builder;
418
+ const pixel = pb.sub(pb.mul(pb.textureSample(tex, texCoord).rgb, 2), pb.vec3(1));
419
+ const normalTex = pb.mul(pixel, pb.vec3(pb.vec3(normalScale).xx, 1));
420
+ return pb.normalize(normalTex);
421
+ }
422
+ /**
423
+ * Calculate pixel normal by given TBN matrix
424
+ * @param scope - The shader scope
425
+ * @param TBN - TBN matrix
426
+ * @returns Calculated pixel normal
427
+ */ calculateNormalWithTBN(scope, texCoord, TBN) {
428
+ return this.normalMap ? this._normalMapMode === 'tangent-space' ? this.sampleNormalMapWithTBN(scope, scope[this.getTextureUniformName(TEX_NAME_NORMAL)], texCoord, scope.$query(LightModel.uniformNormalScale) || scope.$builder.float(1), TBN) : this.sampleNormalMap(scope, scope[this.getTextureUniformName(TEX_NAME_NORMAL)], texCoord, scope.$query(LightModel.uniformNormalScale) || scope.$builder.float(1)) : TBN[2];
429
+ }
430
+ /**
431
+ * Calculate the normal vector for current fragment
432
+ * @param scope - The shader scope
433
+ * @param worldPosition - World space position of current fragment
434
+ * @param worldNormal - World space normal of current fragment
435
+ * @param worldTangent - World space tangent of current fragment
436
+ * @param worldBinormal - World space binormal of current fragment
437
+ * @returns Normal vector for current fragment
438
+ */ calculateNormal(scope, worldPosition, worldNormal, worldTangent, worldBinormal) {
439
+ const pb = scope.$builder;
440
+ const uv = this.normalMap ? scope.$inputs[`texcoord${this.normalMapTexCoord}`] ?? pb.vec2(0) : this.albedoMap ? scope.$inputs[`texcoord${this.albedoMapTexCoord}`] ?? pb.vec2(0) : pb.vec2(0);
441
+ let TBN;
442
+ if (!worldNormal) {
443
+ TBN = calculateTBN(scope, worldPosition, uv, this._doubleSideLighting);
444
+ } else if (!worldTangent) {
445
+ TBN = calculateTBNWithNormal(scope, worldPosition, worldNormal, uv, this._doubleSideLighting);
446
+ } else {
447
+ TBN = pb.mat3(pb.normalize(worldTangent), pb.normalize(worldBinormal), pb.normalize(worldNormal));
448
+ }
449
+ return this.calculatePixelNormal(scope, TBN);
450
+ }
451
+ /**
452
+ * Calculates the final color of current fragment by composing individual light contribution
453
+ * @param scope - The shader scope
454
+ * @returns The final fragment color
455
+ */ finalComposite(scope) {
456
+ const funcNameFinalComposite = 'lib_finalComposite';
457
+ const pb = scope.$builder;
458
+ const that = this;
459
+ pb.func(funcNameFinalComposite, [], function() {
460
+ this.surfaceData.accumColor = pb.add(this.surfaceData.accumDiffuse, this.surfaceData.accumSpecular, this.surfaceData.accumEmissive);
461
+ that.compositeSurfaceData(this);
462
+ this.$return(Material.debugChannel ? this.surfaceData.debugColor : pb.vec4(this.surfaceData.accumColor, this.surfaceData.diffuse.a));
463
+ });
464
+ return pb.getGlobalScope()[funcNameFinalComposite]();
465
+ }
466
+ /**
467
+ * Gets the shader hash
468
+ * @returns shader hash
469
+ */ getHash() {
470
+ if (this._hash === null) {
471
+ this._hash = `${this.constructor.name}_${this.calculateHash()}`;
472
+ }
473
+ return this._hash;
474
+ }
475
+ /**
476
+ * Peeks the shader hash
477
+ * @returns shader hash
478
+ */ peekHash() {
479
+ return this._hash;
480
+ }
481
+ /**
482
+ * Composite surface data to produce the final color
483
+ * @param scope - The shader scope
484
+ */ compositeSurfaceData(scope) {
485
+ // to be overriden
486
+ const pb = scope.$builder;
487
+ switch(Material.debugChannel){
488
+ case 'normal':
489
+ {
490
+ scope.surfaceData.debugColor = pb.vec4(pb.add(pb.mul(scope.surfaceData.TBN[2], 0.5), pb.vec3(0.5)), 1);
491
+ break;
492
+ }
493
+ case 'tangent':
494
+ {
495
+ scope.surfaceData.debugColor = pb.vec4(pb.add(pb.mul(scope.surfaceData.TBN[0], 0.5), pb.vec3(0.5)), 1);
496
+ break;
497
+ }
498
+ case 'binormal':
499
+ {
500
+ scope.surfaceData.debugColor = pb.vec4(pb.add(pb.mul(scope.surfaceData.TBN[1], 0.5), pb.vec3(0.5)), 1);
501
+ break;
502
+ }
503
+ case 'shadingNormal':
504
+ {
505
+ scope.surfaceData.debugColor = pb.vec4(pb.add(pb.mul(scope.surfaceData.normal, 0.5), pb.vec3(0.5)), 1);
506
+ break;
507
+ }
508
+ }
509
+ }
510
+ /**
511
+ * Creates the surface data type
512
+ * @param env - The environment lighting object
513
+ * @returns The surface data type
514
+ */ createSurfaceDataType(env) {
515
+ const debugColor = Material.debugChannel ? [
516
+ {
517
+ name: 'debugColor',
518
+ type: typeF32Vec4
519
+ }
520
+ ] : [];
521
+ return new PBStructTypeInfo('', 'default', [
522
+ {
523
+ name: 'diffuse',
524
+ type: typeF32Vec4
525
+ },
526
+ {
527
+ name: 'normal',
528
+ type: typeF32Vec3
529
+ },
530
+ {
531
+ name: 'viewVec',
532
+ type: typeF32Vec3
533
+ },
534
+ {
535
+ name: 'NdotV',
536
+ type: typeF32
537
+ },
538
+ {
539
+ name: 'TBN',
540
+ type: typeMat3
541
+ },
542
+ {
543
+ name: 'accumDiffuse',
544
+ type: typeF32Vec3
545
+ },
546
+ {
547
+ name: 'accumSpecular',
548
+ type: typeF32Vec3
549
+ },
550
+ {
551
+ name: 'accumEmissive',
552
+ type: typeF32Vec3
553
+ },
554
+ {
555
+ name: 'accumColor',
556
+ type: typeF32Vec3
557
+ },
558
+ ...debugColor
559
+ ]);
560
+ }
561
+ /**
562
+ * Check if specified texture is being used
563
+ * @param name - The texture name
564
+ * @returns true if the texture is being used
565
+ */ isTextureUsed(name) {
566
+ return !!this._textureOptions[name]?.texture;
567
+ }
568
+ /**
569
+ * Initial fill the surface data of current fragment
570
+ * @param scope - The shader scope
571
+ * @param envLight - The environment lighting object
572
+ */ fillSurfaceData(scope, envLight) {
573
+ // to be overriden
574
+ if (Material.debugChannel) {
575
+ scope.surfaceData.debugColor = scope.$builder.vec4(scope.surfaceData.diffuse.rgb, 1);
576
+ }
577
+ }
578
+ /**
579
+ * Update all texture uniforms
580
+ * @param bindGroup - The bind group
581
+ */ applyTextureUniforms(bindGroup) {
582
+ for(const k in this._textureOptions){
583
+ if (this.isTextureUsed(k)) {
584
+ const uniformName = this.getTextureUniformName(k);
585
+ const info = this._textureOptions[k];
586
+ bindGroup.setTexture(uniformName, info.texture, info.sampler);
587
+ }
588
+ }
589
+ }
590
+ /**
591
+ * Setup all texture uniforms
592
+ * @param scope - The shader scope
593
+ */ setupTextureUniforms(scope) {
594
+ const pb = scope.$builder;
595
+ for(const k in this._textureOptions){
596
+ if (this.isTextureUsed(k)) {
597
+ const uniformName = this.getTextureUniformName(k);
598
+ const texture = this._textureOptions[k].texture;
599
+ let exp;
600
+ if (texture.isTexture2D()) {
601
+ exp = pb.tex2D().uniform(2);
602
+ } else if (texture.isTextureCube()) {
603
+ exp = pb.texCube().uniform(2);
604
+ } else if (texture.isTexture3D()) {
605
+ exp = pb.tex3D().uniform(2);
606
+ } else if (texture.isTexture2DArray()) {
607
+ exp = pb.tex2DArray().uniform(2);
608
+ } else if (texture.isTextureVideo()) {
609
+ exp = pb.texExternal().uniform(2);
610
+ } else {
611
+ throw new Error('Unsupported light model texture type');
612
+ }
613
+ if (!texture.isFilterable()) {
614
+ exp.sampleType('unfilterable-float');
615
+ }
616
+ scope[uniformName] = exp;
617
+ }
618
+ }
619
+ }
620
+ /** @internal */ addTexCoordChannel(srcLocation, transform) {
621
+ transform = transform || Matrix4x4.identity();
622
+ let index = this._texCoordChannels.findIndex((val)=>val.srcLocation === srcLocation && val.transform.equalsTo(transform));
623
+ if (index < 0) {
624
+ index = this._texCoordChannels.length;
625
+ this._texCoordChannels.push({
626
+ srcLocation,
627
+ transform: new Matrix4x4(transform)
628
+ });
629
+ }
630
+ return index;
631
+ }
632
+ /** @internal */ calculatePixelNormal(scope, TBN) {
633
+ const pixelNormal = this.calculateNormalWithTBN(scope, scope.$inputs[`texcoord${this.normalMapTexCoord}`], TBN);
634
+ const pb = scope.$builder;
635
+ return pb.defineStruct([
636
+ pb.mat3('TBN'),
637
+ pb.vec3('normal')
638
+ ])(TBN, pixelNormal);
639
+ }
640
+ /**
641
+ * Notifies that the options has changed
642
+ * @param changeHash - true if the shader need to be rebuild
643
+ */ optionChanged(changeHash) {
644
+ this._uniformVersion++;
645
+ if (changeHash) {
646
+ this._hash = null;
647
+ this._surfaceDataType = null;
648
+ this._hashVersion++;
649
+ }
650
+ }
651
+ /**
652
+ * Checks if the specified texture coordinate index is being used
653
+ * @param texCoordIndex - The texture coordinate index
654
+ * @returns true if the texture coordinate index is being used
655
+ */ isTexCoordIndexUsed(texCoordIndex) {
656
+ return typeof this._texCoordChannels[texCoordIndex]?.srcLocation === 'number';
657
+ }
658
+ /**
659
+ * Checks if the specified texture coordinate location is being used
660
+ * @param loc - The texture coordinate location
661
+ * @returns true if the texture coordinate location is being used
662
+ */ isTexCoordSrcLocationUsed(loc) {
663
+ return this._texCoordChannels.findIndex((val)=>val.srcLocation === loc) >= 0;
664
+ }
665
+ /**
666
+ * Gets the location of a given texture coordinate index
667
+ * @param texCoordIndex - The texture coordinate index
668
+ * @returns The location of the given texture coordinate index
669
+ */ getTexCoordSrcLocation(texCoordIndex) {
670
+ return this._texCoordChannels[texCoordIndex].srcLocation;
671
+ }
672
+ /**
673
+ * Checks if normal vector is being used
674
+ * @returns true if normal vector is being used
675
+ */ isNormalUsed() {
676
+ return true;
677
+ }
678
+ /**
679
+ * Updates uniforms of the shader program if needed
680
+ * @param bindGroup - The bind group
681
+ * @param ctx - The drawing context
682
+ */ applyUniformsIfOutdated(bindGroup, ctx) {
683
+ const tags = this._bindGroupTagList.get(bindGroup);
684
+ if (!tags || tags[0] !== this._uniformVersion || tags[1] !== bindGroup.cid) {
685
+ if (tags) {
686
+ tags[0] = this._uniformVersion;
687
+ tags[1] = bindGroup.cid;
688
+ } else {
689
+ this._bindGroupTagList.set(bindGroup, [
690
+ this._uniformVersion,
691
+ bindGroup.cid
692
+ ]);
693
+ }
694
+ this.applyUniforms(bindGroup, ctx);
695
+ }
696
+ }
697
+ }
698
+ /**
699
+ * Unlit light model
700
+ * @public
701
+ */ class UnlitLightModel extends LightModel {
702
+ /**
703
+ * {@inheritDoc LightModel.supportLighting}
704
+ * @override
705
+ */ supportLighting() {
706
+ return false;
707
+ }
708
+ /**
709
+ * {@inheritDoc LightModel.envBRDF}
710
+ * @override
711
+ */ envBRDF(envLight, scope) {}
712
+ /**
713
+ * {@inheritDoc LightModel.directBRDF}
714
+ * @override
715
+ */ directBRDF(scope, lightDir, attenuation) {}
716
+ /**
717
+ * {@inheritDoc LightModel.isNormalUsed}
718
+ * @override
719
+ */ isNormalUsed() {
720
+ return false;
721
+ }
722
+ /**
723
+ * {@inheritDoc LightModel.compositeSurfaceData}
724
+ * @override
725
+ */ compositeSurfaceData(scope) {
726
+ scope.surfaceData.accumColor = scope.surfaceData.diffuse.rgb;
727
+ super.compositeSurfaceData(scope);
728
+ }
729
+ }
730
+ /**
731
+ * Lambert light model
732
+ * @public
733
+ */ class LambertLightModel extends LightModel {
734
+ static funcNameBRDFEnvIBL = 'lib_lambertLM_envIBL';
735
+ static funcNameBRDFDirect = 'lib_lambertLM_direct';
736
+ /**
737
+ * {@inheritDoc LightModel.supportLighting}
738
+ * @override
739
+ */ supportLighting() {
740
+ return true;
741
+ }
742
+ /**
743
+ * {@inheritDoc LightModel.envBRDF}
744
+ * @override
745
+ */ envBRDF(envLight, scope) {
746
+ const pb = scope.$builder;
747
+ if (envLight?.hasIrradiance()) {
748
+ scope.surfaceData.accumDiffuse = pb.add(scope.surfaceData.accumDiffuse, pb.mul(envLight.getIrradiance(scope, scope.surfaceData.normal).rgb, scope.surfaceData.diffuse.rgb, ShaderFramework.getEnvLightStrength(scope)));
749
+ }
750
+ }
751
+ /**
752
+ * {@inheritDoc LightModel.directBRDF}
753
+ * @override
754
+ */ directBRDF(scope, lightDir, attenuation) {
755
+ const pb = scope.$builder;
756
+ pb.func(LambertLightModel.funcNameBRDFDirect, [
757
+ pb.vec3('attenuation')
758
+ ], function() {
759
+ this.surfaceData.accumDiffuse = pb.add(this.surfaceData.accumDiffuse, pb.mul(this.surfaceData.diffuse.rgb, this.attenuation));
760
+ });
761
+ pb.getGlobalScope()[LambertLightModel.funcNameBRDFDirect](attenuation);
762
+ }
763
+ }
764
+ /**
765
+ * Blinn-phong light model
766
+ * @public
767
+ */ class BlinnLightModel extends LightModel {
768
+ static funcNameBRDFEnvIBL = 'lib_blinnLM_envIBL';
769
+ static funcNameBRDFDirect = 'lib_blinnLM_direct';
770
+ /** @internal */ _shininess;
771
+ /**
772
+ * Creates an instance of BlinnLightModel
773
+ */ constructor(){
774
+ super();
775
+ this._shininess = 32;
776
+ }
777
+ /** Shininess */ get shininess() {
778
+ return this._shininess;
779
+ }
780
+ set shininess(val) {
781
+ this._shininess = val;
782
+ }
783
+ /**
784
+ * {@inheritDoc LightModel.supportLighting}
785
+ * @override
786
+ */ supportLighting() {
787
+ return true;
788
+ }
789
+ /**
790
+ * {@inheritDoc LightModel.setupUniforms}
791
+ * @override
792
+ */ setupUniforms(scope, ctx) {
793
+ super.setupUniforms(scope, ctx);
794
+ if (scope.$builder.shaderKind === 'fragment') {
795
+ scope.shininess = scope.$builder.float().uniform(2);
796
+ }
797
+ }
798
+ /**
799
+ * {@inheritDoc LightModel.applyUniforms}
800
+ * @override
801
+ */ applyUniforms(bindGroup, ctx) {
802
+ super.applyUniforms(bindGroup, ctx);
803
+ bindGroup.setValue('shininess', this._shininess);
804
+ }
805
+ /**
806
+ * {@inheritDoc LightModel.envBRDF}
807
+ * @override
808
+ */ envBRDF(envLight, scope) {
809
+ const pb = scope.$builder;
810
+ if (envLight?.hasIrradiance()) {
811
+ scope.surfaceData.accumDiffuse = pb.add(scope.surfaceData.accumDiffuse, pb.mul(envLight.getIrradiance(scope, scope.surfaceData.normal).rgb, scope.surfaceData.diffuse.rgb, ShaderFramework.getEnvLightStrength(scope)));
812
+ }
813
+ /*
814
+ if (envLight?.hasRadiance()) {
815
+ const refl = pb.reflect(pb.neg(scope.surfaceData.viewVec), scope.surfaceData.normal);
816
+ const roughness = pb.sub(1, pb.clamp(pb.div(scope.shininess, 64), 0, 1));
817
+ scope.surfaceData.accumSpecular = pb.add(scope.surfaceData.accumSpecular, envLight.getRadiance(scope, refl, roughness));
818
+ }
819
+ */ }
820
+ /**
821
+ * {@inheritDoc LightModel.directBRDF}
822
+ * @override
823
+ */ directBRDF(scope, lightDir, attenuation) {
824
+ const pb = scope.$builder;
825
+ pb.func(BlinnLightModel.funcNameBRDFDirect, [
826
+ pb.vec3('lightDir'),
827
+ pb.vec3('attenuation')
828
+ ], function() {
829
+ this.$l.halfVec = pb.normalize(pb.sub(this.surfaceData.viewVec, this.lightDir));
830
+ this.$l.NdotH = pb.clamp(pb.dot(this.surfaceData.normal, this.halfVec), 0, 1);
831
+ this.$l.outDiffuse = pb.mul(this.surfaceData.diffuse.rgb, this.attenuation);
832
+ this.$l.outSpecular = pb.mul(this.attenuation, pb.pow(this.NdotH, this.shininess));
833
+ this.surfaceData.accumSpecular = pb.add(this.surfaceData.accumSpecular, this.outSpecular);
834
+ this.surfaceData.accumDiffuse = pb.add(this.surfaceData.accumDiffuse, this.outDiffuse);
835
+ });
836
+ pb.getGlobalScope()[BlinnLightModel.funcNameBRDFDirect](lightDir, attenuation);
837
+ }
838
+ }
839
+ /**
840
+ * Base class for PBR light model
841
+ * @public
842
+ */ class PBRLightModelBase extends LightModel {
843
+ /** @internal */ static funcNameCalcPBRLight = 'lib_PBRLM_calcPBRLight';
844
+ /** @internal */ static funcNameIllumEnvLight = 'lib_PBRLM_illumEnvLight_pbr';
845
+ /** @internal */ static uniformF0 = 'PBRLM_f0';
846
+ /** @internal */ static uniformOcclusionStrength = 'PBRLM_occlusionStrength';
847
+ /** @internal */ static uniformSheenFactor = 'lib_PBRLM_sheenFactor';
848
+ /** @internal */ static uniformClearcoatFactor = 'lib_PBRLM_clearcoatFactor';
849
+ /** @internal */ static uniformClearcoatNormalScale = 'lib_PBRLM_clearcoatNormalScale';
850
+ /** @internal */ static ggxLut = null;
851
+ /** @internal */ _f0;
852
+ /** @internal */ _occlusionStrength;
853
+ /** @internal */ _sheen;
854
+ /** @internal */ _sheenFactor;
855
+ /** @internal */ _clearcoat;
856
+ /** @internal */ _clearcoatFactor;
857
+ /** @internal */ static getGGXLUT() {
858
+ if (!this.ggxLut) {
859
+ this.ggxLut = this.createGGXLUT(Application.instance.device, 1024);
860
+ }
861
+ return this.ggxLut;
862
+ }
863
+ /** @internal */ static createGGXLUT(device, size) {
864
+ const program = device.buildRenderProgram({
865
+ vertex (pb) {
866
+ this.$inputs.pos = pb.vec2().attrib('position');
867
+ this.$outputs.uv = pb.vec2();
868
+ pb.main(function() {
869
+ this.$builtins.position = pb.vec4(this.$inputs.pos, 0, 1);
870
+ this.$outputs.uv = pb.add(pb.mul(this.$inputs.pos.xy, 0.5), pb.vec2(0.5));
871
+ if (device.type === 'webgpu') {
872
+ this.$builtins.position.y = pb.neg(this.$builtins.position.y);
873
+ }
874
+ });
875
+ },
876
+ fragment (pb) {
877
+ this.$outputs.color = pb.vec4();
878
+ const SAMPLE_COUNT = 1024;
879
+ if (device.type === 'webgl') {
880
+ pb.func('radicalInverse_VdC', [
881
+ pb.int('bits')
882
+ ], function() {
883
+ this.$l.rand = pb.float(0);
884
+ this.$l.denom = pb.float(1);
885
+ this.$l.invBase = pb.float(0.5);
886
+ this.$l.n = this.bits;
887
+ this.$for(pb.int('i'), 0, 32, function() {
888
+ this.denom = pb.mul(this.denom, 2);
889
+ this.rand = pb.add(this.rand, pb.div(pb.mod(pb.float(this.n), 2), this.denom));
890
+ this.n = pb.div(this.n, 2);
891
+ this.$if(pb.equal(this.n, 0), function() {
892
+ this.$break();
893
+ });
894
+ });
895
+ this.$return(this.rand);
896
+ });
897
+ pb.func('hammersley2d', [
898
+ pb.int('i'),
899
+ pb.int('N')
900
+ ], function() {
901
+ this.$return(pb.vec2(pb.div(pb.float(this.i), pb.float(this.N)), this.radicalInverse_VdC(this.i)));
902
+ });
903
+ } else {
904
+ pb.func('radicalInverse_VdC', [
905
+ pb.uint('bits')
906
+ ], function() {
907
+ this.$l.n = this.bits;
908
+ this.n = pb.compOr(pb.sal(this.n, 16), pb.sar(this.n, 16));
909
+ this.n = pb.compOr(pb.sal(pb.compAnd(this.n, 0x55555555), 1), pb.sar(pb.compAnd(this.n, 0xAAAAAAAA), 1));
910
+ this.n = pb.compOr(pb.sal(pb.compAnd(this.n, 0x33333333), 2), pb.sar(pb.compAnd(this.n, 0xCCCCCCCC), 2));
911
+ this.n = pb.compOr(pb.sal(pb.compAnd(this.n, 0x0F0F0F0F), 4), pb.sar(pb.compAnd(this.n, 0xF0F0F0F0), 4));
912
+ this.n = pb.compOr(pb.sal(pb.compAnd(this.n, 0x00FF00FF), 8), pb.sar(pb.compAnd(this.n, 0xFF00FF00), 8));
913
+ this.$return(pb.mul(pb.float(this.n), 2.3283064365386963e-10));
914
+ });
915
+ pb.func('hammersley2d', [
916
+ pb.int('i'),
917
+ pb.int('N')
918
+ ], function() {
919
+ this.$return(pb.vec2(pb.div(pb.float(this.i), pb.float(this.N)), this.radicalInverse_VdC(pb.uint(this.i))));
920
+ });
921
+ }
922
+ pb.func('generateTBN', [
923
+ pb.vec3('normal')
924
+ ], function() {
925
+ this.$l.bitangent = pb.vec3(0, 1, 0);
926
+ this.$l.NoU = this.normal.y;
927
+ this.$l.epsl = 0.0000001;
928
+ this.$if(pb.lessThanEqual(pb.sub(1, pb.abs(this.normal.y)), this.epsl), function() {
929
+ this.bitangent = this.$choice(pb.greaterThan(this.normal.y, 0), pb.vec3(0, 0, 1), pb.vec3(0, 0, -1));
930
+ });
931
+ this.$l.tangent = pb.normalize(pb.cross(this.bitangent, this.normal));
932
+ this.bitangent = pb.cross(this.normal, this.tangent);
933
+ this.$return(pb.mat3(this.tangent, this.bitangent, this.normal));
934
+ });
935
+ pb.func('D_Charlie', [
936
+ pb.float('sheenRoughness'),
937
+ pb.float('NdotH')
938
+ ], function() {
939
+ this.$l.roughness = pb.max(this.sheenRoughness, 0.000001);
940
+ this.$l.invR = pb.div(1, this.roughness);
941
+ this.$l.cos2h = pb.mul(this.NdotH, this.NdotH);
942
+ this.$l.sin2h = pb.sub(1, this.cos2h);
943
+ this.$return(pb.div(pb.mul(pb.add(this.invR, 2), pb.pow(this.sin2h, pb.mul(this.invR, 0.5))), Math.PI * 2));
944
+ });
945
+ pb.func('smithGGXCorrelated', [
946
+ pb.float('NoV'),
947
+ pb.float('NoL'),
948
+ pb.float('roughness')
949
+ ], function() {
950
+ this.$l.a2 = pb.mul(this.roughness, this.roughness, this.roughness, this.roughness);
951
+ this.$l.GGXV = pb.mul(this.NoL, pb.sqrt(pb.add(pb.mul(this.NoV, this.NoV, pb.sub(1, this.a2)), this.a2)));
952
+ this.$l.GGXL = pb.mul(this.NoV, pb.sqrt(pb.add(pb.mul(this.NoL, this.NoL, pb.sub(1, this.a2)), this.a2)));
953
+ this.$return(pb.div(0.5, pb.add(this.GGXV, this.GGXL)));
954
+ });
955
+ pb.func('V_Ashikhmin', [
956
+ pb.float('NdotL'),
957
+ pb.float('NdotV')
958
+ ], function() {
959
+ this.$return(pb.clamp(pb.div(1, pb.mul(pb.sub(pb.add(this.NdotL, this.NdotV), pb.mul(this.NdotL, this.NdotV)), 4)), 0, 1));
960
+ });
961
+ pb.func('importanceSample', [
962
+ pb.vec2('xi'),
963
+ pb.vec3('normal'),
964
+ pb.float('roughness'),
965
+ pb.vec3('ggx').out(),
966
+ pb.vec3('charlie').out()
967
+ ], function() {
968
+ this.$l.alphaRoughness = pb.mul(this.roughness, this.roughness);
969
+ this.$l.cosTheta = pb.clamp(pb.sqrt(pb.div(pb.sub(1, this.xi.y), pb.add(1, pb.mul(pb.sub(pb.mul(this.alphaRoughness, this.alphaRoughness), 1), this.xi.y)))), 0, 1);
970
+ this.$l.sinTheta = pb.sqrt(pb.sub(1, pb.mul(this.cosTheta, this.cosTheta)));
971
+ this.$l.phi = pb.mul(this.xi.x, Math.PI * 2);
972
+ this.$l.TBN = this.generateTBN(this.normal);
973
+ this.$l.localSpaceDir = pb.normalize(pb.vec3(pb.mul(this.sinTheta, pb.cos(this.phi)), pb.mul(this.sinTheta, pb.sin(this.phi)), this.cosTheta));
974
+ this.ggx = pb.mul(this.TBN, this.localSpaceDir);
975
+ this.sinTheta = pb.pow(this.xi.y, pb.div(this.alphaRoughness, pb.add(pb.mul(this.alphaRoughness, 2), 1)));
976
+ this.cosTheta = pb.sqrt(pb.sub(1, pb.mul(this.sinTheta, this.sinTheta)));
977
+ this.localSpaceDir = pb.normalize(pb.vec3(pb.mul(this.sinTheta, pb.cos(this.phi)), pb.mul(this.sinTheta, pb.sin(this.phi)), this.cosTheta));
978
+ this.charlie = pb.mul(this.TBN, this.localSpaceDir);
979
+ });
980
+ pb.func('integrateBRDF', [
981
+ pb.float('NoV'),
982
+ pb.float('roughness')
983
+ ], function() {
984
+ this.$l.V = pb.vec3(pb.sub(1, pb.mul(this.NoV, this.NoV)), 0, this.NoV);
985
+ this.$l.a = pb.float(0);
986
+ this.$l.b = pb.float(0);
987
+ this.$l.c = pb.float(0);
988
+ this.$l.n = pb.vec3(0, 0, 1);
989
+ this.$for(pb.int('i'), 0, SAMPLE_COUNT, function() {
990
+ this.$l.xi = this.hammersley2d(this.i, SAMPLE_COUNT);
991
+ this.$l.ggxSample = pb.vec3();
992
+ this.$l.charlieSample = pb.vec3();
993
+ this.importanceSample(this.xi, this.n, this.roughness, this.ggxSample, this.charlieSample);
994
+ this.$l.ggxL = pb.normalize(pb.reflect(pb.neg(this.V), this.ggxSample.xyz));
995
+ this.$l.ggxNoL = pb.clamp(this.ggxL.z, 0, 1);
996
+ this.$l.ggxNoH = pb.clamp(this.ggxSample.z, 0, 1);
997
+ this.$l.ggxVoH = pb.clamp(pb.dot(this.V, this.ggxSample.xyz), 0, 1);
998
+ this.$l.charlieL = pb.normalize(pb.reflect(pb.neg(this.V), this.charlieSample.xyz));
999
+ this.$l.charlieNoL = pb.clamp(this.charlieL.z, 0, 1);
1000
+ this.$l.charlieNoH = pb.clamp(this.charlieSample.z, 0, 1);
1001
+ this.$l.charlieVoH = pb.clamp(pb.dot(this.V, this.charlieSample.xyz), 0, 1);
1002
+ this.$if(pb.greaterThan(this.ggxNoL, 0), function() {
1003
+ this.$l.pdf = pb.div(pb.mul(this.smithGGXCorrelated(this.NoV, this.ggxNoL, this.roughness), this.ggxVoH, this.ggxNoL), this.ggxNoH);
1004
+ this.$l.Fc = pb.pow(pb.sub(1, this.ggxVoH), 5);
1005
+ this.a = pb.add(this.a, pb.mul(pb.sub(1, this.Fc), this.pdf));
1006
+ this.b = pb.add(this.b, pb.mul(this.Fc, this.pdf));
1007
+ });
1008
+ this.$if(pb.greaterThan(this.charlieNoL, 0), function() {
1009
+ this.$l.sheenDistribution = this.D_Charlie(this.roughness, this.charlieNoH);
1010
+ this.$l.sheenVis = this.V_Ashikhmin(this.charlieNoL, this.NoV);
1011
+ this.c = pb.add(this.c, pb.mul(this.sheenVis, this.sheenDistribution, this.charlieNoL, this.charlieVoH));
1012
+ });
1013
+ });
1014
+ this.$return(pb.div(pb.vec3(pb.mul(this.a, 4), pb.mul(this.b, 4), pb.mul(this.c, 8 * Math.PI)), SAMPLE_COUNT));
1015
+ });
1016
+ pb.main(function() {
1017
+ this.$outputs.color = pb.vec4(this.integrateBRDF(this.$inputs.uv.x, this.$inputs.uv.y), 1);
1018
+ });
1019
+ }
1020
+ });
1021
+ const vertexLayout = device.createVertexLayout({
1022
+ vertexBuffers: [
1023
+ {
1024
+ buffer: device.createVertexBuffer('position_f32x2', new Float32Array([
1025
+ -1,
1026
+ -1,
1027
+ 1,
1028
+ -1,
1029
+ -1,
1030
+ 1,
1031
+ 1,
1032
+ 1
1033
+ ]))
1034
+ }
1035
+ ]
1036
+ });
1037
+ const rs = device.createRenderStateSet();
1038
+ rs.useRasterizerState().setCullMode('none');
1039
+ rs.useDepthState().enableTest(false).enableWrite(false);
1040
+ const tex = device.createTexture2D('rgba8unorm', size, size, {
1041
+ samplerOptions: {
1042
+ mipFilter: 'none'
1043
+ }
1044
+ });
1045
+ tex.name = 'GGXLUT';
1046
+ const fb = device.createFrameBuffer([
1047
+ tex
1048
+ ], null);
1049
+ device.pushDeviceStates();
1050
+ device.setProgram(program);
1051
+ device.setVertexLayout(vertexLayout);
1052
+ device.setRenderStates(rs);
1053
+ device.setFramebuffer(fb);
1054
+ device.draw('triangle-strip', 0, 4);
1055
+ device.popDeviceStates();
1056
+ fb.dispose();
1057
+ vertexLayout.dispose();
1058
+ program.dispose();
1059
+ return tex;
1060
+ }
1061
+ /**
1062
+ * Creates an instance of PBRLightModelBase
1063
+ */ constructor(){
1064
+ super();
1065
+ this._f0 = new Vector4(0.04, 0.04, 0.04, 1.5);
1066
+ this._sheen = false;
1067
+ this._sheenFactor = Vector4.zero();
1068
+ this._clearcoat = false;
1069
+ this._clearcoatFactor = new Vector4(0, 0, 1, 0);
1070
+ this._occlusionStrength = 1;
1071
+ }
1072
+ /** ior value */ get ior() {
1073
+ return this._f0.w;
1074
+ }
1075
+ set ior(val) {
1076
+ if (val !== this._f0.w) {
1077
+ let k = (val - 1) / (val + 1);
1078
+ k *= k;
1079
+ this._f0.setXYZW(k, k, k, val);
1080
+ this.optionChanged(false);
1081
+ }
1082
+ }
1083
+ /** occlusion strength */ get occlusionStrength() {
1084
+ return this._occlusionStrength;
1085
+ }
1086
+ set occlusionStrength(val) {
1087
+ if (this._occlusionStrength !== val) {
1088
+ this._occlusionStrength = val;
1089
+ if (this.occlusionMap) {
1090
+ this.optionChanged(false);
1091
+ }
1092
+ }
1093
+ }
1094
+ /** The occlusion texture */ get occlusionMap() {
1095
+ return (this._textureOptions[TEX_NAME_OCCLUSION]?.texture) ?? null;
1096
+ }
1097
+ /** Sampler of the occlusion texture */ get occlusionSampler() {
1098
+ return this._textureOptions[TEX_NAME_OCCLUSION]?.sampler ?? null;
1099
+ }
1100
+ /** Texture coordinate index of the occlusion texture */ get occlusionMapTexCoord() {
1101
+ return this._textureOptions[TEX_NAME_OCCLUSION]?.texCoordIndex ?? null;
1102
+ }
1103
+ /**
1104
+ * Sets the occlusion texture
1105
+ * @param tex - The texture to set
1106
+ * @param sampler - Sampler of the texture
1107
+ * @param texCoordIndex - Texture coordinate index of the texture
1108
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1109
+ */ setOcclusionMap(tex, sampler, texCoordIndex, texTransform) {
1110
+ this.setTextureOptions(TEX_NAME_OCCLUSION, tex, sampler, texCoordIndex, texTransform);
1111
+ }
1112
+ /** true if sheen lighting is being used */ get useSheen() {
1113
+ return this._sheen;
1114
+ }
1115
+ set useSheen(val) {
1116
+ if (this._sheen !== !!val) {
1117
+ this._sheen = !!val;
1118
+ this.optionChanged(true);
1119
+ }
1120
+ }
1121
+ /** Color factor for sheen lighting */ get sheenColorFactor() {
1122
+ return this._sheenFactor.xyz();
1123
+ }
1124
+ set sheenColorFactor(val) {
1125
+ if (val.x !== this._sheenFactor.x || val.y !== this._sheenFactor.y || val.z !== this._sheenFactor.z) {
1126
+ this._sheenFactor.x = val.x;
1127
+ this._sheenFactor.y = val.y;
1128
+ this._sheenFactor.z = val.z;
1129
+ if (this._sheen) {
1130
+ this.optionChanged(false);
1131
+ }
1132
+ }
1133
+ }
1134
+ /** Roughness factor for sheen lighting */ get sheenRoughnessFactor() {
1135
+ return this._sheenFactor.w;
1136
+ }
1137
+ set sheenRoughnessFactor(val) {
1138
+ if (val !== this._sheenFactor.w) {
1139
+ this._sheenFactor.w = val;
1140
+ if (this._sheen) {
1141
+ this.optionChanged(false);
1142
+ }
1143
+ }
1144
+ }
1145
+ /** Lut texture for sheen lighting */ get sheenLut() {
1146
+ return (this._textureOptions[TEX_NAME_SHEEN_LUT]?.texture) ?? null;
1147
+ }
1148
+ /**
1149
+ * Sets the lut texture for sheen lighting
1150
+ * @param tex - The texture to set
1151
+ */ setSheenLut(tex) {
1152
+ this.setTextureOptions(TEX_NAME_SHEEN_LUT, tex, null, 0, null);
1153
+ }
1154
+ /** The sheen color texture */ get sheenColorMap() {
1155
+ return (this._textureOptions[TEX_NAME_SHEEN_COLOR]?.texture) ?? null;
1156
+ }
1157
+ /** Sampler of the sheen color texture */ get sheenColorSampler() {
1158
+ return this._textureOptions[TEX_NAME_SHEEN_COLOR]?.sampler ?? null;
1159
+ }
1160
+ /** Texture coordinate index of the sheen color texture */ get sheenColorMapTexCoord() {
1161
+ return this._textureOptions[TEX_NAME_SHEEN_COLOR]?.texCoordIndex ?? null;
1162
+ }
1163
+ /**
1164
+ * Sets the sheen color texture
1165
+ * @param tex - The texture to set
1166
+ * @param sampler - Sampler of the texture
1167
+ * @param texCoordIndex - Texture coordinate index of the texture
1168
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1169
+ */ setSheenColorMap(tex, sampler, texCoordIndex, texTransform) {
1170
+ this.setTextureOptions(TEX_NAME_SHEEN_COLOR, tex, sampler, texCoordIndex, texTransform);
1171
+ }
1172
+ /** The sheen roughness texture */ get sheenRoughnessMap() {
1173
+ return (this._textureOptions[TEX_NAME_SHEEN_ROUGHNESS]?.texture) ?? null;
1174
+ }
1175
+ /** Sampler of the sheen roughness texture */ get sheenRoughnessSampler() {
1176
+ return this._textureOptions[TEX_NAME_SHEEN_ROUGHNESS]?.sampler ?? null;
1177
+ }
1178
+ get sheenRoughnessMapTexCoord() {
1179
+ return this._textureOptions[TEX_NAME_SHEEN_ROUGHNESS]?.texCoordIndex ?? null;
1180
+ }
1181
+ /**
1182
+ * Sets the sheen roughness texture
1183
+ * @param tex - The texture to set
1184
+ * @param sampler - Sampler of the texture
1185
+ * @param texCoordIndex - Texture coordinate index of the texture
1186
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1187
+ */ setSheenRoughnessMap(tex, sampler, texCoordIndex, texTransform) {
1188
+ this.setTextureOptions(TEX_NAME_SHEEN_ROUGHNESS, tex, sampler, texCoordIndex, texTransform);
1189
+ }
1190
+ /** true if the clearcoat lighting is enabled */ get useClearcoat() {
1191
+ return this._clearcoat;
1192
+ }
1193
+ set useClearcoat(val) {
1194
+ if (this._clearcoat !== !!val) {
1195
+ this._clearcoat = !!val;
1196
+ this.optionChanged(true);
1197
+ }
1198
+ }
1199
+ /** Intensity of clearcoat lighting */ get clearcoatIntensity() {
1200
+ return this._clearcoatFactor.x;
1201
+ }
1202
+ set clearcoatIntensity(val) {
1203
+ if (val !== this._clearcoatFactor.x) {
1204
+ this._clearcoatFactor.x = val;
1205
+ if (this._clearcoat) {
1206
+ this.optionChanged(false);
1207
+ }
1208
+ }
1209
+ }
1210
+ /** Roughness factor of clearcoat lighting */ get clearcoatRoughnessFactor() {
1211
+ return this._clearcoatFactor.y;
1212
+ }
1213
+ set clearcoatRoughnessFactor(val) {
1214
+ if (val !== this._clearcoatFactor.y) {
1215
+ this._clearcoatFactor.y = val;
1216
+ if (this._clearcoat) {
1217
+ this.optionChanged(false);
1218
+ }
1219
+ }
1220
+ }
1221
+ /** Normal scale of clearcoat lighting */ get clearcoatNormalScale() {
1222
+ return this._clearcoatFactor.z;
1223
+ }
1224
+ set clearcoatNormalScale(val) {
1225
+ if (val !== this._clearcoatFactor.z) {
1226
+ this._clearcoatFactor.z = val;
1227
+ if (this._clearcoat) {
1228
+ this.optionChanged(false);
1229
+ }
1230
+ }
1231
+ }
1232
+ /** The clearcoat intensity texture */ get clearcoatIntensityMap() {
1233
+ return (this._textureOptions[TEX_NAME_CLEARCOAT_INTENSITY]?.texture) ?? null;
1234
+ }
1235
+ /** Sampler of the clearcoat intensity texture */ get clearcoatIntensitySampler() {
1236
+ return this._textureOptions[TEX_NAME_CLEARCOAT_INTENSITY]?.sampler ?? null;
1237
+ }
1238
+ /** Texture coordinate index of the clearcoat intensity texture */ get clearcoatIntensityMapTexCoord() {
1239
+ return this._textureOptions[TEX_NAME_CLEARCOAT_INTENSITY]?.texCoordIndex ?? null;
1240
+ }
1241
+ /**
1242
+ * Sets the clearcoat intensity texture
1243
+ * @param tex - The texture to set
1244
+ * @param sampler - Sampler of the texture
1245
+ * @param texCoordIndex - Texture coordinate index of the texture
1246
+ * @param texTransform - Transformation matrox for texture coordinates of the texture
1247
+ */ setClearcoatIntensityMap(tex, sampler, texCoordIndex, texTransform) {
1248
+ this.setTextureOptions(TEX_NAME_CLEARCOAT_INTENSITY, tex, sampler, texCoordIndex, texTransform);
1249
+ }
1250
+ /** The clearcoat roughness texture */ get clearcoatRoughnessMap() {
1251
+ return (this._textureOptions[TEX_NAME_CLEARCOAT_ROUGHNESS]?.texture) ?? null;
1252
+ }
1253
+ /** Sampler of the clearcoat roughness texture */ get clearcoatRoughnessSampler() {
1254
+ return this._textureOptions[TEX_NAME_CLEARCOAT_ROUGHNESS]?.sampler ?? null;
1255
+ }
1256
+ /** Texture coordinate index of the clearcoat roughness texture */ get clearcoatRoughnessMapTexCoord() {
1257
+ return this._textureOptions[TEX_NAME_CLEARCOAT_ROUGHNESS]?.texCoordIndex ?? null;
1258
+ }
1259
+ /**
1260
+ * Sets the clearcoat roughness texture
1261
+ * @param tex - The texture to set
1262
+ * @param sampler - Sampler of the texture
1263
+ * @param texCoordIndex - Texture coordinate index of the texture
1264
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1265
+ */ setClearcoatRoughnessMap(tex, sampler, texCoordIndex, texTransform) {
1266
+ this.setTextureOptions(TEX_NAME_CLEARCOAT_ROUGHNESS, tex, sampler, texCoordIndex, texTransform);
1267
+ }
1268
+ /** The clearcoat normal texture */ get clearcoatNormalMap() {
1269
+ return (this._textureOptions[TEX_NAME_CLEARCOAT_NORMAL]?.texture) ?? null;
1270
+ }
1271
+ /** Sampler of the clearcoat normal texture */ get clearcoatNormalSampler() {
1272
+ return this._textureOptions[TEX_NAME_CLEARCOAT_NORMAL]?.sampler ?? null;
1273
+ }
1274
+ /** Texture coordinate index of the clearcoat normal texture */ get clearcoatNormalMapTexCoord() {
1275
+ return this._textureOptions[TEX_NAME_CLEARCOAT_NORMAL]?.texCoordIndex ?? null;
1276
+ }
1277
+ /**
1278
+ * Sets the clearcoat normal texture
1279
+ * @param tex - The texture to set
1280
+ * @param sampler - Sampler of the texture
1281
+ * @param texCoordIndex - Texture coordinate index of the texture
1282
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1283
+ */ setClearcoatNormalMap(tex, sampler, texCoordIndex, texTransform) {
1284
+ this.setTextureOptions(TEX_NAME_CLEARCOAT_NORMAL, tex, sampler, texCoordIndex, texTransform);
1285
+ }
1286
+ /**
1287
+ * {@inheritDoc LightModel.calculateHash}
1288
+ * @override
1289
+ */ calculateHash() {
1290
+ const occlusionHash = this.occlusionMap ? this.occlusionMapTexCoord + 1 : 0;
1291
+ const ccIntensityHash = this.clearcoatIntensityMap ? this.clearcoatIntensityMapTexCoord + 1 : 0;
1292
+ const ccRoughnessHash = this.clearcoatRoughnessMap ? this.clearcoatRoughnessMapTexCoord + 1 : 0;
1293
+ const ccNormalHash = this.clearcoatNormalMap ? this.clearcoatNormalMapTexCoord + 1 : 0;
1294
+ const ccHash = this.useClearcoat ? `(${ccIntensityHash}-${ccRoughnessHash}-${ccNormalHash})` : '';
1295
+ const sheenColorHash = this.sheenColorMap ? this.sheenColorMapTexCoord + 1 : 0;
1296
+ const sheenRoughnessHash = this.sheenRoughnessMap ? this.sheenRoughnessMapTexCoord + 1 : 0;
1297
+ const sheenHash = this.useSheen ? `(${sheenColorHash}-${sheenRoughnessHash})` : '';
1298
+ return `${super.calculateHash()}_${occlusionHash}_${sheenHash}_${ccHash}`;
1299
+ }
1300
+ /**
1301
+ * {@inheritDoc LightModel.setupUniforms}
1302
+ * @override
1303
+ */ setupUniforms(scope, ctx) {
1304
+ super.setupUniforms(scope, ctx);
1305
+ if (scope.$builder.shaderKind === 'fragment') {
1306
+ if (ctx.drawEnvLight) {
1307
+ scope.ggxLut = scope.$builder.tex2D().uniform(2);
1308
+ }
1309
+ scope.lm_f0 = scope.$builder.vec4().uniform(2).tag(PBRLightModelBase.uniformF0);
1310
+ if (this.occlusionMap) {
1311
+ scope.lm_occlusionStrength = scope.$builder.float().uniform(2).tag(PBRLightModelBase.uniformOcclusionStrength);
1312
+ }
1313
+ if (this._sheen) {
1314
+ scope.lm_sheenFactor = scope.$builder.vec4().uniform(2).tag(PBRLightModelBase.uniformSheenFactor);
1315
+ }
1316
+ if (this._clearcoat) {
1317
+ scope.lm_clearcoatFactor = scope.$builder.vec4().uniform(2).tag(PBRLightModelBase.uniformClearcoatFactor);
1318
+ }
1319
+ }
1320
+ }
1321
+ /**
1322
+ * {@inheritDoc LightModel.applyUniforms}
1323
+ * @override
1324
+ */ applyUniforms(bindGroup, ctx) {
1325
+ super.applyUniforms(bindGroup, ctx);
1326
+ if (ctx.drawEnvLight) {
1327
+ bindGroup.setTexture('ggxLut', PBRLightModelBase.getGGXLUT());
1328
+ }
1329
+ bindGroup.setValue('lm_f0', this._f0);
1330
+ if (this.occlusionMap) {
1331
+ bindGroup.setValue('lm_occlusionStrength', this._occlusionStrength);
1332
+ }
1333
+ if (this._sheen) {
1334
+ bindGroup.setValue('lm_sheenFactor', this._sheenFactor);
1335
+ }
1336
+ if (this._clearcoat) {
1337
+ bindGroup.setValue('lm_clearcoatFactor', this._clearcoatFactor);
1338
+ }
1339
+ }
1340
+ /**
1341
+ * {@inheritDoc LightModel.createSurfaceDataType}
1342
+ * @override
1343
+ */ createSurfaceDataType(env) {
1344
+ const type = super.createSurfaceDataType(env);
1345
+ const props = [
1346
+ {
1347
+ name: 'metallic',
1348
+ type: typeF32
1349
+ },
1350
+ {
1351
+ name: 'roughness',
1352
+ type: typeF32
1353
+ },
1354
+ {
1355
+ name: 'f0',
1356
+ type: typeF32Vec4
1357
+ },
1358
+ {
1359
+ name: 'f90',
1360
+ type: typeF32Vec3
1361
+ },
1362
+ {
1363
+ name: 'occlusion',
1364
+ type: typeF32Vec4
1365
+ },
1366
+ {
1367
+ name: 'irradiance',
1368
+ type: typeF32Vec3
1369
+ },
1370
+ {
1371
+ name: 'radiance',
1372
+ type: typeF32Vec3
1373
+ }
1374
+ ];
1375
+ if (env) {
1376
+ props.push({
1377
+ name: 'ggxLutSample',
1378
+ type: typeF32Vec4
1379
+ });
1380
+ }
1381
+ if (this._sheen) {
1382
+ props.push({
1383
+ name: 'sheenColor',
1384
+ type: typeF32Vec3
1385
+ }, {
1386
+ name: 'sheenRoughness',
1387
+ type: typeF32
1388
+ }, {
1389
+ name: 'sheenAlbedoScaling',
1390
+ type: typeF32
1391
+ }, {
1392
+ name: 'sheenContrib',
1393
+ type: typeF32Vec3
1394
+ });
1395
+ }
1396
+ if (this._clearcoat) {
1397
+ props.push({
1398
+ name: 'clearcoatFactor',
1399
+ type: typeF32Vec4
1400
+ }, {
1401
+ name: 'clearcoatNormal',
1402
+ type: typeF32Vec3
1403
+ }, {
1404
+ name: 'clearcoatNdotV',
1405
+ type: typeF32
1406
+ }, {
1407
+ name: 'clearcoatFresnel',
1408
+ type: typeF32Vec3
1409
+ }, {
1410
+ name: 'clearcoatContrib',
1411
+ type: typeF32Vec3
1412
+ }, {
1413
+ name: 'radianceClearcoat',
1414
+ type: typeF32Vec3
1415
+ });
1416
+ if (env) {
1417
+ props.push({
1418
+ name: 'clearcoatGGXLutSample',
1419
+ type: typeF32Vec4
1420
+ });
1421
+ }
1422
+ }
1423
+ return props.length > 0 ? type.extends('', props) : type;
1424
+ }
1425
+ /**
1426
+ * {@inheritDoc LightModel.fillSurfaceData}
1427
+ * @override
1428
+ */ fillSurfaceData(scope, envLight) {
1429
+ super.fillSurfaceData(scope, envLight);
1430
+ const funcNameFillSurfaceDataPBRCommon = 'lib_fillSurfaceDataPBRCommon';
1431
+ const pb = scope.$builder;
1432
+ const that = this;
1433
+ pb.func(funcNameFillSurfaceDataPBRCommon, [], function() {
1434
+ this.surfaceData.f0 = this.$query(PBRLightModelBase.uniformF0);
1435
+ this.surfaceData.f90 = pb.vec3(1);
1436
+ const strength = ShaderFramework.getEnvLightStrength(this);
1437
+ if (that.occlusionMap) {
1438
+ const occlusionStrength = this.$query(PBRLightModelBase.uniformOcclusionStrength);
1439
+ const texCoord = this.$inputs[`texcoord${that.occlusionMapTexCoord ?? that.albedoMapTexCoord}`];
1440
+ this.surfaceData.occlusion = pb.textureSample(this[that.getTextureUniformName(TEX_NAME_OCCLUSION)], texCoord);
1441
+ this.surfaceData.occlusion.r = pb.mul(pb.add(pb.mul(occlusionStrength, pb.sub(this.surfaceData.occlusion.r, 1)), 1), strength);
1442
+ } else {
1443
+ this.surfaceData.occlusion = pb.vec4(strength);
1444
+ }
1445
+ if (that.useClearcoat) {
1446
+ this.surfaceData.clearcoatFactor = this.$query(PBRLightModelBase.uniformClearcoatFactor);
1447
+ if (that.clearcoatNormalMap) {
1448
+ const clearcoatNormalMap = this[that.getTextureUniformName(TEX_NAME_CLEARCOAT_NORMAL)];
1449
+ const texCoord = this.$inputs[`texcoord${that.clearcoatNormalMapTexCoord ?? that.albedoMapTexCoord}`];
1450
+ this.$l.ccNormal = pb.sub(pb.mul(pb.textureSample(clearcoatNormalMap, texCoord).rgb, 2), pb.vec3(1));
1451
+ this.ccNormal = pb.mul(this.ccNormal, pb.vec3(this.surfaceData.clearcoatFactor.z, this.surfaceData.clearcoatFactor.z, 1));
1452
+ this.surfaceData.clearcoatNormal = pb.normalize(pb.mul(this.surfaceData.TBN, this.ccNormal));
1453
+ this.surfaceData.clearcoatNdotV = pb.clamp(pb.dot(this.surfaceData.clearcoatNormal, this.surfaceData.viewVec), 0.0001, 1);
1454
+ } else {
1455
+ this.surfaceData.clearcoatNormal = this.surfaceData.TBN[2];
1456
+ this.surfaceData.clearcoatNdotV = this.surfaceData.NdotV;
1457
+ }
1458
+ if (that.clearcoatIntensityMap) {
1459
+ const clearcoatIntensityMap = this[that.getTextureUniformName(TEX_NAME_CLEARCOAT_INTENSITY)];
1460
+ const texCoord = this.$inputs[`texcoord${that.clearcoatIntensityMapTexCoord ?? that.albedoMapTexCoord}`];
1461
+ this.surfaceData.clearcoatFactor.x = pb.mul(this.surfaceData.clearcoatFactor.x, pb.textureSample(clearcoatIntensityMap, texCoord).r);
1462
+ }
1463
+ if (that.clearcoatRoughnessMap) {
1464
+ const clearcoatRoughnessMap = this[that.getTextureUniformName(TEX_NAME_CLEARCOAT_ROUGHNESS)];
1465
+ const texCoord = this.$inputs[`texcoord${that.clearcoatRoughnessMapTexCoord ?? that.albedoMapTexCoord}`];
1466
+ this.surfaceData.clearcoatFactor.y = pb.mul(this.surfaceData.clearcoatFactor.y, pb.textureSample(clearcoatRoughnessMap, texCoord).g);
1467
+ }
1468
+ this.surfaceData.clearcoatFactor.y = pb.clamp(this.surfaceData.clearcoatFactor.y, 0, 1);
1469
+ this.surfaceData.clearcoatContrib = pb.vec3(0);
1470
+ if (envLight) {
1471
+ this.surfaceData.clearcoatGGXLutSample = pb.clamp(pb.textureSample(this.ggxLut, pb.vec2(this.surfaceData.NdotV, this.surfaceData.clearcoatFactor.y)), pb.vec4(0), pb.vec4(1));
1472
+ }
1473
+ // clearcoatFresnel/radianceClearcoat will be set after f0 and f90 are set
1474
+ }
1475
+ if (that._sheen) {
1476
+ this.$l.sheenColor = this.$query(PBRLightModelBase.uniformSheenFactor).rgb;
1477
+ this.$l.sheenRoughness = this.$query(PBRLightModelBase.uniformSheenFactor).a;
1478
+ if (that.sheenColorMap) {
1479
+ const sheenColorMap = this[that.getTextureUniformName(TEX_NAME_SHEEN_COLOR)];
1480
+ const texCoord = this.$inputs[`texcoord${that.sheenColorMapTexCoord ?? that.albedoMapTexCoord}`];
1481
+ this.$l.sheenColor = pb.mul(this.$l.sheenColor, pb.textureSample(sheenColorMap, texCoord).rgb);
1482
+ }
1483
+ if (that.sheenRoughnessMap) {
1484
+ const sheenRoughnessMap = this[that.getTextureUniformName(TEX_NAME_SHEEN_ROUGHNESS)];
1485
+ const texCoord = this.$inputs[`texcoord${that.sheenRoughnessMapTexCoord ?? that.albedoMapTexCoord}`];
1486
+ this.$l.sheenRoughness = pb.mul(this.$l.sheenRoughness, pb.textureSample(sheenRoughnessMap, texCoord).a);
1487
+ }
1488
+ if (that.sheenLut) {
1489
+ const sheenLut = this[that.getTextureUniformName(TEX_NAME_SHEEN_LUT)];
1490
+ this.$l.sheenDFG = pb.textureSample(sheenLut, pb.vec2(this.surfaceData.NdotV, this.sheenRoughness)).b;
1491
+ } else {
1492
+ this.$l.sheenDFG = 0.157;
1493
+ }
1494
+ this.surfaceData.sheenAlbedoScaling = pb.sub(1, pb.mul(pb.max(pb.max(this.sheenColor.r, this.sheenColor.g), this.sheenColor.b), this.sheenDFG));
1495
+ this.surfaceData.sheenColor = this.sheenColor;
1496
+ this.surfaceData.sheenRoughness = this.sheenRoughness;
1497
+ this.surfaceData.sheenContrib = pb.vec3(0);
1498
+ /*
1499
+ this.$l.k = pb.clamp(pb.textureSample(this.ggxLut, pb.vec2(this.surfaceData.NdotV, this.surfaceData.sheenRoughness)), pb.vec4(0), pb.vec4(1)).b;
1500
+ this.surfaceData.sheenAlbedoScaling = pb.sub(1, pb.mul(this.k, pb.max(pb.max(this.surfaceData.sheenColor.r, this.surfaceData.sheenColor.g), this.surfaceData.sheenColor.b)));
1501
+ */ }
1502
+ });
1503
+ pb.getGlobalScope()[funcNameFillSurfaceDataPBRCommon]();
1504
+ }
1505
+ iblSpecular(scope, brdf, f0, radiance, NdotV, roughness, specularWeight) {
1506
+ const pb = scope.$builder;
1507
+ const funcName = 'lib_PBRLM_iblspecular_pbr';
1508
+ pb.func(funcName, [
1509
+ pb.vec4('brdf'),
1510
+ pb.vec3('f0'),
1511
+ pb.vec3('radiance'),
1512
+ pb.float('NdotV'),
1513
+ pb.float('roughness'),
1514
+ pb.float('specularWeight')
1515
+ ], function() {
1516
+ this.$l.f_ab = this.brdf.rg;
1517
+ this.$l.Fr = pb.sub(pb.max(pb.vec3(pb.sub(1, this.roughness)), this.f0), this.f0);
1518
+ this.$l.k_S = pb.add(this.f0, pb.mul(this.Fr, pb.pow(pb.sub(1, this.NdotV), 5)));
1519
+ this.$l.FssEss = pb.add(pb.mul(this.k_S, this.f_ab.x), pb.vec3(this.f_ab.y));
1520
+ this.$return(pb.mul(this.radiance, this.FssEss, this.specularWeight));
1521
+ });
1522
+ return pb.getGlobalScope()[funcName](brdf, f0, radiance, NdotV, roughness, specularWeight);
1523
+ }
1524
+ iblDiffuse(scope, brdf, f0, diffuse, irradiance, NdotV, roughness, specularWeight) {
1525
+ const pb = scope.$builder;
1526
+ const funcName = 'lib_PBRLM_ibldiffuse_pbr';
1527
+ pb.func(funcName, [
1528
+ pb.vec4('brdf'),
1529
+ pb.vec3('f0'),
1530
+ pb.vec3('diffuse'),
1531
+ pb.vec3('irradiance'),
1532
+ pb.float('NdotV'),
1533
+ pb.float('roughness'),
1534
+ pb.float('specularWeight')
1535
+ ], function() {
1536
+ this.$l.f_ab = this.brdf.rg;
1537
+ this.$l.Fr = pb.sub(pb.max(pb.vec3(pb.sub(1, this.roughness)), this.f0), this.f0);
1538
+ this.$l.k_S = pb.add(this.f0, pb.mul(this.Fr, pb.pow(pb.sub(1, this.NdotV), 5)));
1539
+ this.$l.FssEss = pb.add(pb.mul(this.k_S, this.f_ab.x, this.specularWeight), pb.vec3(this.f_ab.y));
1540
+ this.$l.Ems = pb.sub(1, pb.add(this.f_ab.x, this.f_ab.y));
1541
+ this.$l.F_avg = pb.mul(pb.add(this.f0, pb.div(pb.sub(pb.vec3(1), this.f0), 21)), this.specularWeight);
1542
+ this.$l.FmsEms = pb.div(pb.mul(this.FssEss, this.F_avg, this.Ems), pb.sub(pb.vec3(1), pb.mul(this.F_avg, this.Ems)));
1543
+ this.$l.k_D = pb.mul(this.diffuse, pb.add(pb.sub(pb.vec3(1), this.FssEss), this.FmsEms));
1544
+ this.$return(pb.mul(pb.add(this.FmsEms, this.k_D), this.irradiance));
1545
+ });
1546
+ return pb.getGlobalScope()[funcName](brdf, f0, diffuse, irradiance, NdotV, roughness, specularWeight);
1547
+ }
1548
+ /** @internal */ illumEnvLight(scope, envLight) {
1549
+ const pb = scope.$builder;
1550
+ const that = this;
1551
+ pb.func(PBRLightModelBase.funcNameIllumEnvLight, [], function() {
1552
+ if (envLight.hasRadiance()) {
1553
+ this.$l.iblSpecular = that.iblSpecular(this, this.surfaceData.ggxLutSample, this.surfaceData.f0.rgb, this.surfaceData.radiance, this.surfaceData.NdotV, this.surfaceData.roughness, this.surfaceData.specularWeight);
1554
+ this.surfaceData.accumSpecular = pb.add(this.surfaceData.accumSpecular, pb.mul(this.iblSpecular, this.surfaceData.occlusion.r));
1555
+ }
1556
+ if (envLight.hasIrradiance()) {
1557
+ this.$l.iblDiffuse = that.iblDiffuse(this, this.surfaceData.ggxLutSample, this.surfaceData.f0.rgb, this.surfaceData.diffuse.rgb, this.surfaceData.irradiance, this.surfaceData.NdotV, this.surfaceData.roughness, this.surfaceData.specularWeight);
1558
+ this.surfaceData.accumDiffuse = pb.add(this.surfaceData.accumDiffuse, pb.mul(this.iblDiffuse, this.surfaceData.occlusion.r));
1559
+ }
1560
+ if (that._clearcoat) {
1561
+ this.$l.ccSpecular = that.iblSpecular(this, this.surfaceData.ggxLutSample, this.surfaceData.f0.rgb, this.surfaceData.radianceClearcoat, this.surfaceData.clearcoatNdotV, this.surfaceData.clearcoatFactor.y, 1);
1562
+ this.surfaceData.clearcoatContrib = pb.add(this.surfaceData.clearcoatContrib, pb.mul(this.ccSpecular, this.surfaceData.occlusion.r));
1563
+ }
1564
+ if (envLight.hasIrradiance() && that._sheen) {
1565
+ this.$l.refl = pb.reflect(pb.neg(this.surfaceData.viewVec), this.surfaceData.normal);
1566
+ this.$l.sheenEnvSample = envLight.getRadiance(this, this.refl, this.surfaceData.sheenRoughness);
1567
+ this.$l.sheenBRDF = pb.textureSample(this.ggxLut, pb.clamp(pb.vec2(this.surfaceData.NdotV, this.surfaceData.sheenRoughness), pb.vec2(0), pb.vec2(1))).b;
1568
+ //this.$l.sheenBRDF = iblSheenBRDF(this, this.surfaceData.sheenRoughness, this.surfaceData.NdotV);
1569
+ this.$l.sheenLighting = pb.mul(this.surfaceData.sheenColor.rgb, this.iblDiffuse, this.sheenBRDF);
1570
+ this.surfaceData.sheenContrib = pb.add(this.surfaceData.sheenContrib, pb.mul(this.sheenLighting, this.surfaceData.occlusion.r));
1571
+ }
1572
+ });
1573
+ pb.getGlobalScope()[PBRLightModelBase.funcNameIllumEnvLight]();
1574
+ }
1575
+ /**
1576
+ * {@inheritDoc LightModel.supportLighting}
1577
+ * @override
1578
+ */ supportLighting() {
1579
+ return true;
1580
+ }
1581
+ /**
1582
+ * {@inheritDoc LightModel.envBRDF}
1583
+ * @override
1584
+ */ envBRDF(envLight, scope) {
1585
+ this.illumEnvLight(scope, envLight);
1586
+ }
1587
+ /**
1588
+ * {@inheritDoc LightModel.directBRDF}
1589
+ * @override
1590
+ */ directBRDF(scope, lightDir, attenuation) {
1591
+ const that = this;
1592
+ const pb = scope.$builder;
1593
+ pb.func(PBRLightModelBase.funcNameCalcPBRLight, [
1594
+ pb.vec3('lightDir'),
1595
+ pb.vec3('attenuation')
1596
+ ], function() {
1597
+ this.$l.L = pb.neg(this.lightDir);
1598
+ this.$l.halfVec = pb.normalize(pb.sub(this.surfaceData.viewVec, this.lightDir));
1599
+ this.$l.NdotH = pb.clamp(pb.dot(this.surfaceData.normal, this.halfVec), 0, 1);
1600
+ this.$l.NdotL = pb.clamp(pb.dot(this.surfaceData.normal, this.L), 0, 1);
1601
+ this.$l.VdotH = pb.clamp(pb.dot(this.surfaceData.viewVec, this.halfVec), 0, 1);
1602
+ this.$l.outSpecular = pb.vec3();
1603
+ this.$l.outDiffuse = pb.vec3();
1604
+ this.$l.F = fresnelSchlick(this, this.VdotH, this.surfaceData.f0.rgb, this.surfaceData.f90);
1605
+ directLighting(this, this.surfaceData.diffuse.rgb, this.surfaceData.NdotV, this.NdotH, this.NdotL, this.F, this.surfaceData.roughness, this.surfaceData.specularWeight, this.outSpecular, this.outDiffuse);
1606
+ this.surfaceData.accumSpecular = pb.add(this.surfaceData.accumSpecular, pb.mul(this.outSpecular, this.attenuation));
1607
+ this.surfaceData.accumDiffuse = pb.add(this.surfaceData.accumDiffuse, pb.mul(this.outDiffuse, this.attenuation));
1608
+ if (that._sheen) {
1609
+ this.$l.sheenLighting = directSheenLighting(this, this.surfaceData.NdotV, this.NdotL, this.NdotH, this.surfaceData.sheenColor, this.surfaceData.sheenRoughness);
1610
+ this.surfaceData.sheenContrib = pb.add(this.surfaceData.sheenContrib, pb.mul(this.sheenLighting, this.attenuation));
1611
+ }
1612
+ if (that._clearcoat) {
1613
+ this.$l.ccNdotH = pb.clamp(pb.dot(this.surfaceData.clearcoatNormal, this.halfVec), 0, 1);
1614
+ this.$l.ccNdotL = pb.clamp(pb.dot(this.surfaceData.clearcoatNormal, this.L), 0, 1);
1615
+ this.$l.ccLighting = directClearcoatLighting(this, this.surfaceData.clearcoatNdotV, this.ccNdotH, this.ccNdotL, this.F, this.surfaceData.clearcoatFactor.y);
1616
+ this.surfaceData.clearcoatContrib = pb.add(this.surfaceData.clearcoatContrib, pb.mul(this.ccLighting, this.attenuation));
1617
+ }
1618
+ });
1619
+ pb.getGlobalScope()[PBRLightModelBase.funcNameCalcPBRLight](lightDir, attenuation);
1620
+ }
1621
+ /**
1622
+ * {@inheritDoc LightModel.compositeSurfaceData}
1623
+ * @override
1624
+ */ compositeSurfaceData(scope) {
1625
+ // to be overriden
1626
+ const pb = scope.$builder;
1627
+ switch(Material.debugChannel){
1628
+ case 'pbrBase':
1629
+ {
1630
+ break;
1631
+ }
1632
+ case 'pbrMetallic':
1633
+ {
1634
+ scope.surfaceData.debugColor = pb.vec4(scope.surfaceData.metallic, scope.surfaceData.metallic, scope.surfaceData.metallic, 1);
1635
+ break;
1636
+ }
1637
+ case 'pbrRoughness':
1638
+ {
1639
+ scope.surfaceData.debugColor = pb.vec4(scope.surfaceData.roughness, scope.surfaceData.roughness, scope.surfaceData.roughness, 1);
1640
+ break;
1641
+ }
1642
+ case 'pbrMetallicRoughness':
1643
+ {
1644
+ scope.surfaceData.debugColor = pb.vec4(pb.add(scope.surfaceData.accumDiffuse, scope.surfaceData.accumSpecular), 1);
1645
+ break;
1646
+ }
1647
+ case 'pbrSheen':
1648
+ {
1649
+ if (this._sheen) {
1650
+ scope.surfaceData.debugColor = pb.vec4(scope.surfaceData.sheenContrib, 1);
1651
+ } else {
1652
+ scope.surfaceData.debugColor = pb.vec4(0, 0, 0, 1);
1653
+ }
1654
+ break;
1655
+ }
1656
+ case 'pbrSheenColor':
1657
+ {
1658
+ if (this._sheen) {
1659
+ scope.surfaceData.debugColor = pb.vec4(scope.surfaceData.sheenColor, 1);
1660
+ } else {
1661
+ scope.surfaceData.debugColor = pb.vec4(0, 0, 0, 1);
1662
+ }
1663
+ break;
1664
+ }
1665
+ case 'pbrSheenRoughness':
1666
+ {
1667
+ if (this._sheen) {
1668
+ scope.surfaceData.debugColor = pb.vec4(scope.surfaceData.sheenRoughness, scope.surfaceData.sheenRoughness, scope.surfaceData.sheenRoughness, 1);
1669
+ } else {
1670
+ scope.surfaceData.debugColor = pb.vec4(0, 0, 0, 1);
1671
+ }
1672
+ break;
1673
+ }
1674
+ case 'pbrSheenAlbedoScaling':
1675
+ {
1676
+ if (this._sheen) {
1677
+ scope.surfaceData.debugColor = pb.vec4(scope.surfaceData.sheenAlbedoScaling, scope.surfaceData.sheenAlbedoScaling, scope.surfaceData.sheenAlbedoScaling, 1);
1678
+ } else {
1679
+ scope.surfaceData.debugColor = pb.vec4(0, 0, 0, 1);
1680
+ }
1681
+ break;
1682
+ }
1683
+ default:
1684
+ {
1685
+ if (this._sheen) {
1686
+ scope.surfaceData.accumColor = pb.add(pb.mul(scope.surfaceData.accumColor, scope.surfaceData.sheenAlbedoScaling), scope.surfaceData.sheenContrib);
1687
+ }
1688
+ if (this._clearcoat) {
1689
+ scope.surfaceData.accumColor = pb.add(pb.mul(scope.surfaceData.accumColor, pb.sub(pb.vec3(1), pb.mul(scope.surfaceData.clearcoatFresnel, scope.surfaceData.clearcoatFactor.x))), pb.mul(scope.surfaceData.clearcoatContrib, scope.surfaceData.clearcoatFactor.x));
1690
+ // surfaceData.accumColor = pb.add(pb.mul(surfaceData.clearcoatNormal, 0.5), pb.vec3(0.5));
1691
+ // surfaceData.accumColor = pb.vec3(surfaceData.clearcoatFresnel);
1692
+ }
1693
+ break;
1694
+ }
1695
+ }
1696
+ super.compositeSurfaceData(scope);
1697
+ }
1698
+ }
1699
+ /**
1700
+ * specular-glossness PBR light model
1701
+ * @public
1702
+ */ class PBRLightModelSG extends PBRLightModelBase {
1703
+ /** @internal */ static uniformSpecularFactor = 'lib_PBRSG_specularFactor';
1704
+ /** @internal */ static uniformGlossinessFactor = 'lib_PBRSG_glossinessFactor';
1705
+ /** @internal */ _specularFactor;
1706
+ /** @internal */ _glossinessFactor;
1707
+ /**
1708
+ * Creates an instance of PBRLightModelSG
1709
+ */ constructor(){
1710
+ super();
1711
+ this._specularFactor = Vector4.one();
1712
+ this._glossinessFactor = 1;
1713
+ }
1714
+ /** The specular factor */ get specularFactor() {
1715
+ return this._specularFactor;
1716
+ }
1717
+ set specularFactor(val) {
1718
+ if (val && !this._specularFactor.equalsTo(val)) {
1719
+ this._specularFactor.set(val);
1720
+ this.optionChanged(false);
1721
+ }
1722
+ }
1723
+ /** The glossness factor */ get glossinessFactor() {
1724
+ return this._glossinessFactor;
1725
+ }
1726
+ set glossinessFactor(val) {
1727
+ if (val !== this._glossinessFactor) {
1728
+ this._glossinessFactor = val;
1729
+ this.optionChanged(false);
1730
+ }
1731
+ }
1732
+ /** The specular texture */ get specularMap() {
1733
+ return (this._textureOptions[TEX_NAME_SPECULAR]?.texture) ?? null;
1734
+ }
1735
+ /** Texture coordinate index of the specular texture */ get specularMapTexCoord() {
1736
+ return this._textureOptions[TEX_NAME_SPECULAR]?.texCoordIndex ?? null;
1737
+ }
1738
+ /** Sampler of the specular texture */ get specularSampler() {
1739
+ return this._textureOptions[TEX_NAME_SPECULAR]?.sampler ?? null;
1740
+ }
1741
+ /**
1742
+ * Sets the specular texture
1743
+ * @param tex - The texture to set
1744
+ * @param sampler - Sampler of the texture
1745
+ * @param texCoordIndex - Texture coordinate index of the texture
1746
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1747
+ */ setSpecularMap(tex, sampler, texCoordIndex, texTransform) {
1748
+ this.setTextureOptions(TEX_NAME_SPECULAR, tex, sampler, texCoordIndex, texTransform);
1749
+ }
1750
+ /**
1751
+ * {@inheritDoc LightModel.applyUniforms}
1752
+ * @override
1753
+ */ applyUniforms(bindGroup, ctx) {
1754
+ super.applyUniforms(bindGroup, ctx);
1755
+ bindGroup.setValue('lm_specularFactor', this._specularFactor);
1756
+ bindGroup.setValue('lm_glossinessFactor', this._glossinessFactor);
1757
+ }
1758
+ /**
1759
+ * {@inheritDoc LightModel.calculateHash}
1760
+ * @override
1761
+ */ calculateHash() {
1762
+ return `${super.calculateHash()}_${this.specularMap ? `${this.specularMapTexCoord + 1}` : 0}`;
1763
+ }
1764
+ /**
1765
+ * {@inheritDoc LightModel.setupUniforms}
1766
+ * @override
1767
+ */ setupUniforms(scope, ctx) {
1768
+ super.setupUniforms(scope, ctx);
1769
+ if (scope.$builder.shaderKind === 'fragment') {
1770
+ scope.lm_specularFactor = scope.$builder.vec4().uniform(2).tag(PBRLightModelSG.uniformSpecularFactor);
1771
+ scope.lm_glossinessFactor = scope.$builder.float().uniform(2).tag(PBRLightModelSG.uniformGlossinessFactor);
1772
+ }
1773
+ }
1774
+ /**
1775
+ * {@inheritDoc LightModel.fillSurfaceData}
1776
+ * @override
1777
+ */ fillSurfaceData(scope, envLight) {
1778
+ const funcNameFillSurfaceDataSG = 'lib_fillSurfaceDataSG';
1779
+ const that = this;
1780
+ const pb = scope.$builder;
1781
+ super.fillSurfaceData(scope, envLight);
1782
+ // surface data contains F0, metallic, roughness
1783
+ pb.func(funcNameFillSurfaceDataSG, [], function() {
1784
+ this.surfaceData.f0 = pb.vec4(this.$query(PBRLightModelSG.uniformSpecularFactor).rgb, this.surfaceData.f0.a);
1785
+ this.surfaceData.roughness = this.$query(PBRLightModelSG.uniformGlossinessFactor);
1786
+ if (that.specularMap) {
1787
+ const texCoord = this.$inputs[`texcoord${that.specularMapTexCoord ?? that.albedoMapTexCoord}`];
1788
+ this.$l.t = pb.textureSample(this[that.getTextureUniformName(TEX_NAME_SPECULAR)], texCoord);
1789
+ this.surfaceData.roughness = pb.mul(this.surfaceData.roughness, this.t.a);
1790
+ this.surfaceData.f0 = pb.mul(this.surfaceData.f0, pb.vec4(this.t.rgb, 1));
1791
+ }
1792
+ this.surfaceData.roughness = pb.sub(1, this.surfaceData.roughness);
1793
+ this.surfaceData.metallic = pb.max(pb.max(this.surfaceData.f0.r, this.surfaceData.f0.g), this.surfaceData.f0.b);
1794
+ if (envLight) {
1795
+ this.surfaceData.ggxLutSample = pb.clamp(pb.textureSample(this.ggxLut, pb.vec2(this.surfaceData.NdotV, this.surfaceData.roughness)), pb.vec4(0), pb.vec4(1));
1796
+ }
1797
+ this.surfaceData.diffuse = pb.vec4(pb.mul(this.surfaceData.diffuse.rgb, pb.sub(1, this.surfaceData.metallic)), this.surfaceData.diffuse.a);
1798
+ this.surfaceData.specularWeight = pb.float(1);
1799
+ if (that._clearcoat) {
1800
+ this.surfaceData.clearcoatFresnel = fresnelSchlick(this, this.surfaceData.clearcoatNdotV, this.surfaceData.f0.rgb, this.surfaceData.f90);
1801
+ }
1802
+ if (envLight?.hasIrradiance()) {
1803
+ this.surfaceData.irradiance = envLight.getIrradiance(this, this.surfaceData.normal);
1804
+ } else {
1805
+ this.surfaceData.irradiance = pb.vec3(0);
1806
+ }
1807
+ if (envLight?.hasRadiance()) {
1808
+ this.$l.refl = pb.reflect(pb.neg(this.surfaceData.viewVec), this.surfaceData.normal);
1809
+ this.surfaceData.radiance = envLight.getRadiance(this, this.refl, this.surfaceData.roughness);
1810
+ if (that.useClearcoat) {
1811
+ this.$l.ccRefl = pb.reflect(pb.neg(this.surfaceData.viewVec), this.surfaceData.clearcoatNormal);
1812
+ this.surfaceData.radianceClearcoat = envLight.getRadiance(this, this.ccRefl, this.surfaceData.clearcoatFactor.y);
1813
+ }
1814
+ } else {
1815
+ this.surfaceData.radiance = pb.vec3(0);
1816
+ if (that.useClearcoat) {
1817
+ this.surfaceData.radianceClearcoat = pb.vec3(0);
1818
+ }
1819
+ }
1820
+ });
1821
+ pb.getGlobalScope()[funcNameFillSurfaceDataSG]();
1822
+ }
1823
+ /**
1824
+ * {@inheritDoc LightModel.createSurfaceDataType}
1825
+ * @override
1826
+ */ createSurfaceDataType(env) {
1827
+ return super.createSurfaceDataType(env).extends('', [
1828
+ {
1829
+ name: 'specularWeight',
1830
+ type: typeF32
1831
+ }
1832
+ ]);
1833
+ }
1834
+ }
1835
+ /**
1836
+ * Metallic-Roughness PBR light model
1837
+ * @public
1838
+ */ class PBRLightModelMR extends PBRLightModelBase {
1839
+ /** @internal */ static uniformMetallic = 'lib_PBRLM_metallic';
1840
+ /** @internal */ static uniformRoughness = 'lib_PBRLM_roughness';
1841
+ /** @internal */ static uniformSpecularFactor = 'lib_PBRLM_specularFactor';
1842
+ /** @internal */ _metallic;
1843
+ /** @internal */ _roughness;
1844
+ /** @internal */ _metallicIndex;
1845
+ /** @internal */ _roughnessIndex;
1846
+ /** @internal */ _specularFactor;
1847
+ /**
1848
+ * Creates an instance of PBRLightModelMR
1849
+ */ constructor(){
1850
+ super();
1851
+ this._metallic = 1;
1852
+ this._roughness = 1;
1853
+ this._metallicIndex = 2;
1854
+ this._roughnessIndex = 1;
1855
+ this._specularFactor = Vector4.one();
1856
+ }
1857
+ /** The metallic factor */ get metallic() {
1858
+ return this._metallic;
1859
+ }
1860
+ set metallic(val) {
1861
+ if (val !== this._metallic) {
1862
+ this._metallic = val;
1863
+ this.optionChanged(false);
1864
+ }
1865
+ }
1866
+ /** The roughness factor */ get roughness() {
1867
+ return this._roughness;
1868
+ }
1869
+ set roughness(val) {
1870
+ if (val !== this._roughness) {
1871
+ this._roughness = val;
1872
+ this.optionChanged(false);
1873
+ }
1874
+ }
1875
+ /** index of the metallic channel in the metallic-roughness texture */ get metallicIndex() {
1876
+ return this._metallicIndex;
1877
+ }
1878
+ set metallicIndex(val) {
1879
+ if (this._metallicIndex !== val) {
1880
+ this._metallicIndex = val;
1881
+ this.optionChanged(true);
1882
+ }
1883
+ }
1884
+ /** index of the roughness channel in the metallic-roughness texture */ get roughnessIndex() {
1885
+ return this._roughnessIndex;
1886
+ }
1887
+ set roughnessIndex(val) {
1888
+ if (this._roughnessIndex !== val) {
1889
+ this._roughnessIndex = val;
1890
+ this.optionChanged(true);
1891
+ }
1892
+ }
1893
+ /** The metallic-roughness texture */ get metallicMap() {
1894
+ return (this._textureOptions[TEX_NAME_METALLIC]?.texture) ?? null;
1895
+ }
1896
+ /** Sampler of the metallic-roughness texture */ get metallicSampler() {
1897
+ return this._textureOptions[TEX_NAME_METALLIC]?.sampler ?? null;
1898
+ }
1899
+ /** Texture coordinate index of the metallic-roughness texture */ get metallicMapTexCoord() {
1900
+ return this._textureOptions[TEX_NAME_METALLIC]?.texCoordIndex ?? null;
1901
+ }
1902
+ /**
1903
+ * Sets the metallic-roughness texture
1904
+ * @param tex - The texture to set
1905
+ * @param sampler - Sampler of the texture
1906
+ * @param texCoordIndex - Texture coordinate index of the texture
1907
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1908
+ */ setMetallicMap(tex, sampler, texCoordIndex, texTransform) {
1909
+ this.setTextureOptions(TEX_NAME_METALLIC, tex, sampler, texCoordIndex, texTransform);
1910
+ }
1911
+ /** The specular factor */ get specularFactor() {
1912
+ return this._specularFactor;
1913
+ }
1914
+ set specularFactor(val) {
1915
+ if (!val.equalsTo(this._specularFactor)) {
1916
+ this._specularFactor.set(val);
1917
+ this.optionChanged(true);
1918
+ }
1919
+ }
1920
+ /** The specular texture */ get specularMap() {
1921
+ return (this._textureOptions[TEX_NAME_SPECULAR]?.texture) ?? null;
1922
+ }
1923
+ /** Sampler of the specular texture */ get specularSampler() {
1924
+ return this._textureOptions[TEX_NAME_SPECULAR]?.sampler ?? null;
1925
+ }
1926
+ /** Texture coordinate index of the specular texture */ get specularMapTexCoord() {
1927
+ return this._textureOptions[TEX_NAME_SPECULAR]?.texCoordIndex ?? null;
1928
+ }
1929
+ /**
1930
+ * Sets the specular texture
1931
+ * @param tex - The texture to set
1932
+ * @param sampler - Sampler of the texture
1933
+ * @param texCoordIndex - Texture coordinate index of the texture
1934
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1935
+ */ setSpecularMap(tex, sampler, texCoordIndex, texTransform) {
1936
+ this.setTextureOptions(TEX_NAME_SPECULAR, tex, sampler, texCoordIndex, texTransform);
1937
+ }
1938
+ /** The specular color texture */ get specularColorMap() {
1939
+ return (this._textureOptions[TEX_NAME_SPECULAR_COLOR]?.texture) ?? null;
1940
+ }
1941
+ /** Sampler of the specular color texture */ get specularColorSampler() {
1942
+ return this._textureOptions[TEX_NAME_SPECULAR_COLOR]?.sampler ?? null;
1943
+ }
1944
+ /** Texture coordinate index of the specular color texture */ get specularColorMapTexCoord() {
1945
+ return this._textureOptions[TEX_NAME_SPECULAR_COLOR]?.texCoordIndex ?? null;
1946
+ }
1947
+ /**
1948
+ * Sets the specular color texture
1949
+ * @param tex - The texture to set
1950
+ * @param sampler - Sampler of the texture
1951
+ * @param texCoordIndex - Texture coordinate index of the texture
1952
+ * @param texTransform - Transformation matrix for texture coordinates of the texture
1953
+ */ setSpecularColorMap(tex, sampler, texCoordIndex, texTransform) {
1954
+ this.setTextureOptions(TEX_NAME_SPECULAR_COLOR, tex, sampler, texCoordIndex, texTransform);
1955
+ }
1956
+ /**
1957
+ * {@inheritDoc LightModel.applyUniforms}
1958
+ * @override
1959
+ */ applyUniforms(bindGroup, ctx) {
1960
+ super.applyUniforms(bindGroup, ctx);
1961
+ bindGroup.setValue('lm_pbrMetallic', this._metallic);
1962
+ bindGroup.setValue('lm_pbrRoughness', this._roughness);
1963
+ bindGroup.setValue('lm_pbrSpecularFactor', this._specularFactor);
1964
+ }
1965
+ /**
1966
+ * {@inheritDoc LightModel.calculateHash}
1967
+ * @override
1968
+ */ calculateHash() {
1969
+ const metallicMapHash = this.metallicMap ? `${this.metallicMapTexCoord + 1}_${this._metallicIndex}_${this._roughnessIndex}` : '0';
1970
+ const specularMapHash = this.specularMap ? `${this.specularMapTexCoord + 1}` : '0';
1971
+ const specularColorMapHash = this.specularColorMap ? `${this.specularColorMapTexCoord + 1}` : '0';
1972
+ return `${super.calculateHash()}_${metallicMapHash}_${specularMapHash}_${specularColorMapHash}`;
1973
+ }
1974
+ /**
1975
+ * {@inheritDoc LightModel.setupUniforms}
1976
+ * @override
1977
+ */ setupUniforms(scope, ctx) {
1978
+ super.setupUniforms(scope, ctx);
1979
+ if (scope.$builder.shaderKind === 'fragment') {
1980
+ scope.lm_pbrMetallic = scope.$builder.float().uniform(2).tag(PBRLightModelMR.uniformMetallic);
1981
+ scope.lm_pbrRoughness = scope.$builder.float().uniform(2).tag(PBRLightModelMR.uniformRoughness);
1982
+ scope.lm_pbrSpecularFactor = scope.$builder.vec4().uniform(2).tag(PBRLightModelMR.uniformSpecularFactor);
1983
+ }
1984
+ }
1985
+ /**
1986
+ * {@inheritDoc LightModel.fillSurfaceData}
1987
+ * @override
1988
+ */ fillSurfaceData(scope, envLight) {
1989
+ const funcNameFillSurfaceDataMR = 'lib_fillSurfaceDataMR';
1990
+ const that = this;
1991
+ const pb = scope.$builder;
1992
+ super.fillSurfaceData(scope, envLight);
1993
+ // surface data contains F0, metallic, roughness
1994
+ pb.func(funcNameFillSurfaceDataMR, [], function() {
1995
+ const metallicMap = that.metallicMap ? this[that.getTextureUniformName(TEX_NAME_METALLIC)] : null;
1996
+ const specularMap = that.specularMap ? this[that.getTextureUniformName(TEX_NAME_SPECULAR)] : null;
1997
+ const specularColorMap = that.specularColorMap ? this[that.getTextureUniformName(TEX_NAME_SPECULAR_COLOR)] : null;
1998
+ const metallicFactor = this.$query(PBRLightModelMR.uniformMetallic);
1999
+ const roughnessFactor = this.$query(PBRLightModelMR.uniformRoughness);
2000
+ if (metallicMap) {
2001
+ const texCoord = this.$inputs[`texcoord${that.metallicMapTexCoord ?? that.albedoMapTexCoord}`];
2002
+ this.$l.t = pb.textureSample(metallicMap, texCoord);
2003
+ const metallic = this.t['xyzw'[that._metallicIndex] || 'z'];
2004
+ const roughness = this.t['xyzw'[that._roughnessIndex] || 'y'];
2005
+ this.surfaceData.metallic = metallicFactor ? pb.mul(metallic, metallicFactor) : metallic;
2006
+ this.surfaceData.roughness = roughnessFactor ? pb.mul(roughness, roughnessFactor) : roughness;
2007
+ } else {
2008
+ this.surfaceData.metallic = metallicFactor;
2009
+ this.surfaceData.roughness = roughnessFactor;
2010
+ }
2011
+ if (envLight) {
2012
+ this.surfaceData.ggxLutSample = pb.textureSample(this.ggxLut, pb.vec2(this.surfaceData.NdotV, this.surfaceData.roughness));
2013
+ }
2014
+ const specularFactor = this.$query(PBRLightModelMR.uniformSpecularFactor);
2015
+ this.$l.specularColorFactor = specularFactor.rgb;
2016
+ this.surfaceData.specularWeight = specularFactor.a;
2017
+ if (specularColorMap) {
2018
+ const texCoord = this.$inputs[`texcoord${that.specularColorMapTexCoord ?? that.albedoMapTexCoord}`];
2019
+ this.specularColorFactor = pb.mul(this.specularColorFactor, pb.textureSample(specularColorMap, texCoord).rgb);
2020
+ }
2021
+ if (specularMap) {
2022
+ const texCoord = this.$inputs[`texcoord${that.specularMapTexCoord ?? that.albedoMapTexCoord}`];
2023
+ this.surfaceData.specularWeight = pb.mul(this.surfaceData.specularWeight, pb.textureSample(specularMap, texCoord).a);
2024
+ }
2025
+ this.surfaceData.f0 = pb.vec4(pb.mix(pb.min(pb.mul(this.surfaceData.f0.rgb, this.specularColorFactor), pb.vec3(1)), this.surfaceData.diffuse.rgb, this.surfaceData.metallic), this.surfaceData.f0.a);
2026
+ this.surfaceData.diffuse = pb.vec4(pb.mix(this.surfaceData.diffuse.rgb, pb.vec3(0), this.surfaceData.metallic), this.surfaceData.diffuse.a);
2027
+ if (that._clearcoat) {
2028
+ this.surfaceData.clearcoatFresnel = fresnelSchlick(this, this.surfaceData.clearcoatNdotV, this.surfaceData.f0.rgb, this.surfaceData.f90);
2029
+ }
2030
+ if (envLight?.hasIrradiance()) {
2031
+ this.surfaceData.irradiance = envLight.getIrradiance(this, this.surfaceData.normal);
2032
+ } else {
2033
+ this.surfaceData.irradiance = pb.vec3(0);
2034
+ }
2035
+ if (envLight?.hasRadiance()) {
2036
+ this.$l.refl = pb.reflect(pb.neg(this.surfaceData.viewVec), this.surfaceData.normal);
2037
+ this.surfaceData.radiance = envLight.getRadiance(this, this.refl, this.surfaceData.roughness);
2038
+ if (that.useClearcoat) {
2039
+ this.$l.ccRefl = pb.reflect(pb.neg(this.surfaceData.viewVec), this.surfaceData.clearcoatNormal);
2040
+ this.surfaceData.radianceClearcoat = envLight.getRadiance(this, this.ccRefl, this.surfaceData.clearcoatFactor.y);
2041
+ }
2042
+ } else {
2043
+ this.surfaceData.radiance = pb.vec3(0);
2044
+ if (that.useClearcoat) {
2045
+ this.surfaceData.radianceClearcoat = pb.vec3(0);
2046
+ }
2047
+ }
2048
+ });
2049
+ pb.getGlobalScope()[funcNameFillSurfaceDataMR]();
2050
+ }
2051
+ /**
2052
+ * {@inheritDoc LightModel.createSurfaceDataType}
2053
+ * @override
2054
+ */ createSurfaceDataType(env) {
2055
+ return super.createSurfaceDataType(env).extends('', [
2056
+ {
2057
+ name: 'specularWeight',
2058
+ type: typeF32
2059
+ }
2060
+ ]);
2061
+ }
2062
+ /**
2063
+ * {@inheritDoc LightModel.isTextureUsed}
2064
+ * @override
2065
+ */ isTextureUsed(name) {
2066
+ if (!this._sheen && (name === TEX_NAME_SHEEN_COLOR || name === TEX_NAME_SHEEN_ROUGHNESS)) {
2067
+ return false;
2068
+ }
2069
+ return super.isTextureUsed(name);
2070
+ }
2071
+ }
2072
+
2073
+ export { BlinnLightModel, LambertLightModel, LightModel, PBRLightModelBase, PBRLightModelMR, PBRLightModelSG, UnlitLightModel };
2074
+ //# sourceMappingURL=lightmodel.js.map