@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,881 @@
1
+ import { Application } from '../app.js';
2
+ import { decodeNormalizedFloatFromRGBA, linearToGamma } from '../shaders/misc.js';
3
+ import { ShaderFramework } from '../shaders/framework.js';
4
+ import { smoothNoise3D } from '../shaders/noise.js';
5
+ import { Matrix4x4, Vector3, Vector4, Vector2, CubeFace } from '@zephyr3d/base';
6
+ import { BoxShape } from '../shapes/box.js';
7
+ import { ScatteringLut } from './scatteringlut.js';
8
+ import { Camera } from '../camera/camera.js';
9
+ import { prefilterCubemap } from '../utility/pmrem.js';
10
+ import '../utility/panorama.js';
11
+ import '../utility/shprojection.js';
12
+
13
+ const fogTypeMap = {
14
+ linear: ShaderFramework.FOG_TYPE_LINEAR,
15
+ exp: ShaderFramework.FOG_TYPE_EXP,
16
+ exp2: ShaderFramework.FOG_TYPE_EXP2,
17
+ scatter: ShaderFramework.FOG_TYPE_SCATTER,
18
+ none: ShaderFramework.FOG_TYPE_NONE
19
+ };
20
+ const defaultSkyWorldMatrix = Matrix4x4.identity();
21
+ /**
22
+ * The sky renderer
23
+ * @public
24
+ */ class SkyRenderer {
25
+ static _defaultSunDir = Vector3.one().inplaceNormalize();
26
+ _skyType;
27
+ _skyColor;
28
+ _skyboxTexture;
29
+ _updateRadianceMaps;
30
+ _radianceMapDirty;
31
+ _scatterSkyboxFramebuffer;
32
+ _scatterSkyboxTextureWidth;
33
+ _radianceMap;
34
+ _radianceMapWidth;
35
+ _irradianceMap;
36
+ _irradianceMapWidth;
37
+ _fogType;
38
+ _fogColor;
39
+ _fogParams;
40
+ _cloudy;
41
+ _cloudIntensity;
42
+ _wind;
43
+ _nearestSampler;
44
+ _programSky;
45
+ _bindgroupSky;
46
+ _programFog;
47
+ _bindgroupFog;
48
+ _programFogScatter;
49
+ _bindgroupFogScatter;
50
+ _vertexLayout;
51
+ _primitiveSky;
52
+ _skyWorldMatrix;
53
+ _renderStatesSky;
54
+ _renderStatesSkyNoDepthTest;
55
+ _renderStatesFog;
56
+ _renderStatesFogScatter;
57
+ _lastSunDir;
58
+ /**
59
+ * Creates an instance of SkyRenderer
60
+ */ constructor(){
61
+ this._skyType = 'scatter';
62
+ this._updateRadianceMaps = true;
63
+ this._radianceMapDirty = true;
64
+ this._skyColor = Vector4.zero();
65
+ this._skyboxTexture = null;
66
+ this._scatterSkyboxFramebuffer = null;
67
+ this._scatterSkyboxTextureWidth = 256;
68
+ this._radianceMap = null;
69
+ this._radianceMapWidth = 128;
70
+ this._irradianceMap = null;
71
+ this._irradianceMapWidth = 64;
72
+ this._fogType = 'none';
73
+ this._fogColor = Vector4.one();
74
+ this._fogParams = new Vector4(1, 100, 50, 0.002);
75
+ this._cloudy = 0.6;
76
+ this._cloudIntensity = 40;
77
+ this._wind = Vector2.zero();
78
+ this._nearestSampler = null;
79
+ this._programSky = {};
80
+ this._bindgroupSky = {};
81
+ this._programFog = null;
82
+ this._bindgroupFog = null;
83
+ this._programFogScatter = null;
84
+ this._bindgroupFogScatter = null;
85
+ this._vertexLayout = null;
86
+ this._primitiveSky = null;
87
+ this._renderStatesSky = null;
88
+ this._renderStatesSkyNoDepthTest = null;
89
+ this._renderStatesFog = null;
90
+ this._renderStatesFogScatter = null;
91
+ this._skyWorldMatrix = defaultSkyWorldMatrix;
92
+ this._lastSunDir = Vector3.zero();
93
+ }
94
+ /** @internal */ getHash(ctx) {
95
+ return ctx.applyFog ? this._fogType === 'none' ? '0' : this.drawScatteredFog(ctx) ? '1' : '2' : '';
96
+ }
97
+ /** Which type of the sky should be rendered */ get skyType() {
98
+ return this._skyType;
99
+ }
100
+ set skyType(val) {
101
+ if (val !== this._skyType) {
102
+ this._skyType = val;
103
+ this.invalidateIBLMaps();
104
+ }
105
+ }
106
+ /**
107
+ * Wether the IBL maps should be updated automatically.
108
+ *
109
+ * @remarks
110
+ * If use use the sky for image-based lighting, the value shoud be set to true. default is false
111
+ *
112
+ */ get autoUpdateIBLMaps() {
113
+ return this._updateRadianceMaps;
114
+ }
115
+ set autoUpdateIBLMaps(val) {
116
+ if (this._updateRadianceMaps !== !!val) {
117
+ this._updateRadianceMaps = !!val;
118
+ if (this._updateRadianceMaps) {
119
+ this.invalidateIBLMaps();
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * The solid sky color
125
+ */ get skyColor() {
126
+ return this._skyColor;
127
+ }
128
+ set skyColor(val) {
129
+ if (!val.equalsTo(this._skyColor)) {
130
+ this._skyColor.set(val);
131
+ this.invalidateIBLMaps();
132
+ }
133
+ }
134
+ /**
135
+ * Light density of the sky.
136
+ *
137
+ * @remarks
138
+ * This value controls how much cloud should be rendered when the sky type is scatter.
139
+ * Typically, the value should be in the range of 0 to 1.
140
+ */ get cloudy() {
141
+ return this._cloudy;
142
+ }
143
+ set cloudy(val) {
144
+ if (val !== this._cloudy && this._skyType === 'scatter') {
145
+ this._cloudy = val;
146
+ this.invalidateIBLMaps();
147
+ }
148
+ }
149
+ /**
150
+ * Intensity of the sky color
151
+ */ get cloudIntensity() {
152
+ return this._cloudIntensity;
153
+ }
154
+ set cloudIntensity(val) {
155
+ if (val !== this._cloudIntensity && this._skyType === 'scatter') {
156
+ this._cloudIntensity = val;
157
+ this.invalidateIBLMaps();
158
+ }
159
+ }
160
+ /**
161
+ * Wind velocity
162
+ *
163
+ * @remarks
164
+ * This value affects the movement of the cloud
165
+ */ get wind() {
166
+ return this._wind;
167
+ }
168
+ set wind(val) {
169
+ this._wind.set(val);
170
+ }
171
+ /**
172
+ * Radiance map of the sky.
173
+ */ get radianceMap() {
174
+ if (!this._radianceMap) {
175
+ this._radianceMap = Application.instance.device.createCubeTexture('rgba16f', this._radianceMapWidth);
176
+ this._radianceMap.name = 'SkyRadianceMap';
177
+ }
178
+ return this._radianceMap;
179
+ }
180
+ /**
181
+ * Irradiance map of the sky.
182
+ */ get irradianceMap() {
183
+ if (!this._irradianceMap) {
184
+ this._irradianceMap = Application.instance.device.createCubeTexture('rgba16f', this._irradianceMapWidth, {
185
+ samplerOptions: {
186
+ mipFilter: 'none'
187
+ }
188
+ });
189
+ this._irradianceMap.name = 'SkyIrradianceMap';
190
+ }
191
+ return this._irradianceMap;
192
+ }
193
+ /**
194
+ * Cube texture for skybox.
195
+ */ get skyboxTexture() {
196
+ return this._skyboxTexture;
197
+ }
198
+ set skyboxTexture(tex) {
199
+ if (tex !== this._skyboxTexture) {
200
+ this._skyboxTexture = tex;
201
+ if (this._skyType === 'skybox') {
202
+ this.invalidateIBLMaps();
203
+ }
204
+ }
205
+ }
206
+ /** @internal */ get skyWorldMatrix() {
207
+ return this._skyWorldMatrix;
208
+ }
209
+ set skyWorldMatrix(val) {
210
+ val = val ?? defaultSkyWorldMatrix;
211
+ if (val !== this._skyWorldMatrix) {
212
+ this._skyWorldMatrix = val;
213
+ this.invalidateIBLMaps();
214
+ }
215
+ }
216
+ /** @internal */ get mappedFogType() {
217
+ return fogTypeMap[this._fogType];
218
+ }
219
+ /** Current fog type */ get fogType() {
220
+ return this._fogType;
221
+ }
222
+ set fogType(val) {
223
+ this._fogType = val;
224
+ }
225
+ /** Start distance of linear fog */ get fogStart() {
226
+ return this._fogParams.x;
227
+ }
228
+ set fogStart(val) {
229
+ this._fogParams.x = val;
230
+ }
231
+ /** End distance of linear fog */ get fogEnd() {
232
+ return this._fogParams.y;
233
+ }
234
+ set fogEnd(val) {
235
+ this._fogParams.y = val;
236
+ }
237
+ /** Top distance of fog if fog type is not scatter */ get fogTop() {
238
+ return this._fogParams.z;
239
+ }
240
+ set fogTop(val) {
241
+ this._fogParams.z = val;
242
+ }
243
+ /** Density of exp/exp2 fog */ get fogDensity() {
244
+ return this._fogParams.w;
245
+ }
246
+ set fogDensity(val) {
247
+ this._fogParams.w = val;
248
+ }
249
+ /** The fog color if fog type is not scatter */ get fogColor() {
250
+ return this._fogColor;
251
+ }
252
+ set fogColor(val) {
253
+ this._fogColor.set(val);
254
+ }
255
+ /** @internal */ get fogParams() {
256
+ return this._fogParams;
257
+ }
258
+ set fogParams(val) {
259
+ this._fogParams.set(val);
260
+ }
261
+ /**
262
+ * Force the radiance map and irradiance map to be regenerated.
263
+ */ invalidateIBLMaps() {
264
+ this._radianceMapDirty = true;
265
+ }
266
+ /** @internal */ drawScatteredFog(ctx) {
267
+ return ctx.sunLight && this._fogType === 'scatter';
268
+ }
269
+ /** @internal */ getAerialPerspectiveLUT(ctx) {
270
+ if (this.drawScatteredFog(ctx)) {
271
+ const sunDir = SkyRenderer._getSunDir(ctx.sunLight);
272
+ const alpha = Math.PI / 2 - Math.acos(Math.max(-1, Math.min(1, sunDir.y)));
273
+ const farPlane = ctx.camera.getFarPlane() * ctx.scene.worldUnit;
274
+ return ScatteringLut.getAerialPerspectiveLut(alpha, farPlane);
275
+ } else {
276
+ return null;
277
+ }
278
+ }
279
+ /**
280
+ * Regenerate the radiance map and irradiance map
281
+ *
282
+ * @param sunLight - The sun light
283
+ */ updateIBLMaps(sunDir) {
284
+ const device = Application.instance.device;
285
+ let bakedSkyboxTexture = null;
286
+ if (this._skyType === 'skybox' && this._skyboxTexture) {
287
+ bakedSkyboxTexture = this._skyboxTexture;
288
+ } else {
289
+ if (!this._scatterSkyboxFramebuffer) {
290
+ const texCaps = device.getDeviceCaps().textureCaps;
291
+ const format = texCaps.supportHalfFloatColorBuffer && texCaps.supportLinearHalfFloatTexture ? 'rgba16f' : texCaps.supportFloatColorBuffer && texCaps.supportLinearFloatTexture ? 'rgba32f' : 'rgba8unorm';
292
+ const tex = device.createCubeTexture(format, this._scatterSkyboxTextureWidth);
293
+ tex.name = 'BakedSkyboxTexture';
294
+ this._scatterSkyboxFramebuffer = device.createFrameBuffer([
295
+ tex
296
+ ], null);
297
+ this._radianceMapDirty = true;
298
+ }
299
+ const camera = new Camera(null);
300
+ camera.setPerspective(Math.PI / 2, 1, 1, 20);
301
+ const saveRenderStates = device.getRenderStates();
302
+ device.pushDeviceStates();
303
+ device.setFramebuffer(this._scatterSkyboxFramebuffer);
304
+ for (const face of [
305
+ CubeFace.PX,
306
+ CubeFace.NX,
307
+ CubeFace.PY,
308
+ CubeFace.NY,
309
+ CubeFace.PZ,
310
+ CubeFace.NZ
311
+ ]){
312
+ camera.lookAtCubeFace(face);
313
+ this._scatterSkyboxFramebuffer.setColorAttachmentCubeFace(0, face);
314
+ this._renderSky(camera, false, sunDir, true, false);
315
+ }
316
+ device.popDeviceStates();
317
+ device.setRenderStates(saveRenderStates);
318
+ bakedSkyboxTexture = this._scatterSkyboxFramebuffer.getColorAttachments()[0];
319
+ }
320
+ prefilterCubemap(bakedSkyboxTexture, 'ggx', this.radianceMap);
321
+ prefilterCubemap(bakedSkyboxTexture, 'lambertian', this.irradianceMap);
322
+ }
323
+ /** @internal */ renderFog(ctx) {
324
+ const camera = ctx.camera;
325
+ const sceneDepthTexture = ctx.linearDepthTexture;
326
+ const device = Application.instance.device;
327
+ const savedRenderStates = device.getRenderStates();
328
+ this._prepareSkyBox(device);
329
+ const sunLight = ctx.sunLight;
330
+ if (this._fogType === 'scatter' && !sunLight) {
331
+ console.error('Cannot render scattering fog without sun light');
332
+ return;
333
+ }
334
+ const fogProgram = this._fogType === 'scatter' ? this._programFogScatter : this._programFog;
335
+ const renderStates = this._fogType === 'scatter' ? this._renderStatesFogScatter : this._renderStatesFog;
336
+ if (fogProgram && sceneDepthTexture) {
337
+ const bindgroup = this._fogType === 'scatter' ? this._bindgroupFogScatter : this._bindgroupFog;
338
+ bindgroup.setTexture('depthTex', sceneDepthTexture, this._nearestSampler);
339
+ bindgroup.setValue('rt', device.getFramebuffer() ? 1 : 0);
340
+ bindgroup.setValue('invProjViewMatrix', camera.invViewProjectionMatrix);
341
+ bindgroup.setValue('cameraNearFar', new Vector2(camera.getNearPlane(), camera.getFarPlane()));
342
+ bindgroup.setValue('cameraPosition', camera.getWorldPosition());
343
+ bindgroup.setValue('srgbOut', device.getFramebuffer() ? 0 : 1);
344
+ if (this._fogType === 'scatter') {
345
+ const sunDir = sunLight ? sunLight.directionAndCutoff.xyz().scaleBy(-1) : SkyRenderer._defaultSunDir;
346
+ const alpha = Math.PI / 2 - Math.acos(Math.max(-1, Math.min(1, sunDir.y)));
347
+ const farPlane = ctx.camera.getFarPlane() * ctx.scene.worldUnit;
348
+ bindgroup.setTexture('apLut', ScatteringLut.getAerialPerspectiveLut(alpha, farPlane));
349
+ bindgroup.setValue('sliceDist', farPlane / ScatteringLut.aerialPerspectiveSliceZ);
350
+ bindgroup.setValue('sunDir', sunDir);
351
+ bindgroup.setValue('worldScale', ctx.scene.worldUnit);
352
+ } else {
353
+ bindgroup.setValue('fogType', this.mappedFogType);
354
+ bindgroup.setValue('fogColor', this._fogColor);
355
+ bindgroup.setValue('fogParams', this._fogParams);
356
+ }
357
+ device.setProgram(fogProgram);
358
+ device.setBindGroup(0, bindgroup);
359
+ device.setVertexLayout(this._vertexLayout);
360
+ device.setRenderStates(renderStates);
361
+ device.draw('triangle-strip', 0, 4);
362
+ device.setRenderStates(savedRenderStates);
363
+ }
364
+ }
365
+ /** @internal */ renderSky(ctx) {
366
+ const sunDir = SkyRenderer._getSunDir(ctx.sunLight);
367
+ if (!sunDir.equalsTo(this._lastSunDir)) {
368
+ this._radianceMapDirty = true;
369
+ }
370
+ this._renderSky(ctx.camera, true, sunDir, false, this._skyType === 'scatter' && this._cloudy > 0);
371
+ if (this._radianceMapDirty && ctx.env.light.type === 'ibl') {
372
+ if (ctx.env.light.radianceMap && (ctx.env.light.radianceMap === this._radianceMap || ctx.env.light.irradianceMap === this._irradianceMap)) {
373
+ this._radianceMapDirty = false;
374
+ this._lastSunDir.set(sunDir);
375
+ this.updateIBLMaps(sunDir);
376
+ }
377
+ }
378
+ }
379
+ /** @internal */ _renderSky(camera, depthTest, sunDir, drawGround, drawCloud) {
380
+ const device = Application.instance.device;
381
+ const savedRenderStates = device.getRenderStates();
382
+ this._prepareSkyBox(device);
383
+ if (this._skyType === 'scatter') {
384
+ this._drawScattering(camera, sunDir, depthTest, drawGround, drawCloud);
385
+ } else if (this._skyType === 'skybox' && this._skyboxTexture) {
386
+ this._drawSkybox(camera, depthTest);
387
+ } else {
388
+ this._drawSkyColor(camera, depthTest);
389
+ }
390
+ device.setRenderStates(savedRenderStates);
391
+ }
392
+ /** @internal */ _drawSkyColor(camera, depthTest) {
393
+ const device = Application.instance.device;
394
+ const bindgroup = this._bindgroupSky.color;
395
+ bindgroup.setValue('viewProjMatrix', camera.viewProjectionMatrix);
396
+ bindgroup.setValue('worldMatrix', this._skyWorldMatrix);
397
+ bindgroup.setValue('cameraPos', camera.getWorldPosition());
398
+ bindgroup.setValue('color', this._skyColor);
399
+ bindgroup.setValue('srgbOut', device.getFramebuffer() ? 0 : 1);
400
+ device.setProgram(this._programSky.color);
401
+ device.setBindGroup(0, bindgroup);
402
+ device.setRenderStates(depthTest ? this._renderStatesSky : this._renderStatesSkyNoDepthTest);
403
+ this._primitiveSky.draw();
404
+ }
405
+ /** @internal */ _drawSkybox(camera, depthTest) {
406
+ const device = Application.instance.device;
407
+ const bindgroup = this._bindgroupSky.skybox;
408
+ bindgroup.setTexture('skyCubeMap', this._skyboxTexture);
409
+ bindgroup.setValue('flip', device.getFramebuffer() && device.type === 'webgpu' ? new Vector4(1, -1, 1, 1) : new Vector4(1, 1, 1, 1));
410
+ bindgroup.setValue('viewProjMatrix', camera.viewProjectionMatrix);
411
+ bindgroup.setValue('worldMatrix', this._skyWorldMatrix);
412
+ bindgroup.setValue('cameraPos', camera.getWorldPosition());
413
+ bindgroup.setValue('srgbOut', device.getFramebuffer() ? 0 : 1);
414
+ device.setProgram(this._programSky.skybox);
415
+ device.setBindGroup(0, bindgroup);
416
+ device.setRenderStates(depthTest ? this._renderStatesSky : this._renderStatesSkyNoDepthTest);
417
+ this._primitiveSky.draw();
418
+ }
419
+ /** @internal */ _drawScattering(camera, sunDir, depthTest, drawGround, drawCloud) {
420
+ const device = Application.instance.device;
421
+ const alpha = Math.PI / 2 - Math.acos(Math.max(-1, Math.min(1, sunDir.y)));
422
+ const tLut = ScatteringLut.getTransmittanceLut();
423
+ const skyLut = ScatteringLut.getSkyViewLut(alpha);
424
+ //const apLut = ScatteringLut.getAerialPerspectiveLut(alpha, 8000);
425
+ const program = drawCloud ? this._programSky.scatter : this._programSky['scatter-nocloud'];
426
+ const bindgroup = drawCloud ? this._bindgroupSky.scatter : this._bindgroupSky['scatter-nocloud'];
427
+ bindgroup.setValue('sunDir', sunDir);
428
+ bindgroup.setValue('flip', device.getFramebuffer() && device.type === 'webgpu' ? new Vector4(1, -1, 1, 1) : new Vector4(1, 1, 1, 1));
429
+ bindgroup.setValue('viewProjMatrix', camera.viewProjectionMatrix);
430
+ bindgroup.setValue('worldMatrix', this._skyWorldMatrix);
431
+ bindgroup.setValue('cameraPos', camera.getWorldPosition());
432
+ bindgroup.setValue('srgbOut', device.getFramebuffer() ? 0 : 1);
433
+ bindgroup.setTexture('tLut', tLut);
434
+ bindgroup.setTexture('skyLut', skyLut);
435
+ if (drawCloud) {
436
+ bindgroup.setValue('cloudy', this._cloudy);
437
+ bindgroup.setValue('cloudIntensity', this._cloudIntensity);
438
+ bindgroup.setValue('time', device.frameInfo.elapsedOverall * 0.001);
439
+ bindgroup.setValue('velocity', this._wind);
440
+ }
441
+ bindgroup.setValue('drawGround', drawGround ? 1 : 0);
442
+ device.setProgram(program);
443
+ device.setBindGroup(0, bindgroup);
444
+ device.setRenderStates(depthTest ? this._renderStatesSky : this._renderStatesSkyNoDepthTest);
445
+ this._primitiveSky.draw();
446
+ }
447
+ /** @internal */ _prepareSkyBox(device) {
448
+ if (!this._programFogScatter) {
449
+ this._programFogScatter = device.buildRenderProgram({
450
+ label: 'FogScatter',
451
+ vertex (pb) {
452
+ this.rt = pb.int().uniform(0);
453
+ this.$inputs.pos = pb.vec2().attrib('position');
454
+ this.$outputs.uv = pb.vec2();
455
+ pb.main(function() {
456
+ this.$builtins.position = pb.vec4(this.$inputs.pos, 1, 1);
457
+ this.$outputs.uv = pb.add(pb.mul(this.$inputs.pos.xy, 0.5), pb.vec2(0.5));
458
+ if (device.type === 'webgpu') {
459
+ this.$if(pb.notEqual(this.rt, 0), function() {
460
+ this.$builtins.position.y = pb.neg(this.$builtins.position.y);
461
+ });
462
+ }
463
+ });
464
+ },
465
+ fragment (pb) {
466
+ this.depthTex = pb.tex2D().sampleType('unfilterable-float').uniform(0);
467
+ this.invProjViewMatrix = pb.mat4().uniform(0);
468
+ this.cameraNearFar = pb.vec2().uniform(0);
469
+ this.cameraPosition = pb.vec3().uniform(0);
470
+ this.apLut = pb.tex2D().uniform(0);
471
+ this.worldScale = pb.float().uniform(0);
472
+ this.sliceDist = pb.float().uniform(0);
473
+ this.sunDir = pb.vec3().uniform(0);
474
+ this.srgbOut = pb.int().uniform(0);
475
+ this.$outputs.outColor = pb.vec4();
476
+ pb.main(function() {
477
+ this.$l.depthValue = pb.textureSample(this.depthTex, this.$inputs.uv);
478
+ if (device.type === 'webgl') {
479
+ this.$l.linearDepth = decodeNormalizedFloatFromRGBA(this, this.depthValue);
480
+ } else {
481
+ this.$l.linearDepth = this.depthValue.r;
482
+ }
483
+ this.$l.nonLinearDepth = pb.div(pb.sub(pb.div(this.cameraNearFar.x, this.linearDepth), this.cameraNearFar.y), pb.sub(this.cameraNearFar.x, this.cameraNearFar.y));
484
+ //this.$l.clipSpacePos = pb.vec4(pb.sub(pb.mul(this.$inputs.uv, 2), pb.vec2(1)), this.nonLinearDepth, 1);
485
+ this.$l.clipSpacePos = pb.vec4(pb.sub(pb.mul(this.$inputs.uv, 2), pb.vec2(1)), pb.sub(pb.mul(this.nonLinearDepth, 2), 1), 1);
486
+ this.$l.hPos = pb.mul(this.invProjViewMatrix, this.clipSpacePos);
487
+ this.$l.hPos = pb.div(this.$l.hPos, this.$l.hPos.w);
488
+ this.$l.viewDir = pb.sub(this.hPos.xyz, this.cameraPosition);
489
+ // Assume object is above the sea level
490
+ this.viewDir.y = pb.max(0, this.viewDir.y);
491
+ this.$l.distance = pb.mul(pb.length(this.viewDir), this.worldScale);
492
+ this.$l.slice0 = pb.floor(pb.div(this.distance, this.sliceDist));
493
+ this.$l.slice1 = pb.add(this.slice0, 1);
494
+ this.$l.factor = pb.sub(pb.div(this.distance, this.sliceDist), this.slice0);
495
+ this.$l.viewNormal = pb.normalize(this.viewDir);
496
+ this.$l.horizonAngle = pb.acos(pb.clamp(pb.dot(pb.normalize(this.sunDir.xz), pb.normalize(this.viewNormal.xz)), 0, 1));
497
+ this.$l.zenithAngle = pb.asin(this.viewNormal.y);
498
+ this.$l.sliceU = pb.max(pb.div(this.horizonAngle, Math.PI * 2), 0.5 / ScatteringLut.aerialPerspectiveSliceZ);
499
+ this.$l.u0 = pb.div(pb.add(this.slice0, this.sliceU), ScatteringLut.aerialPerspectiveSliceZ);
500
+ this.$l.u1 = pb.add(this.u0, 1 / ScatteringLut.aerialPerspectiveSliceZ);
501
+ this.$l.v = pb.div(this.zenithAngle, Math.PI / 2);
502
+ this.$l.t0 = pb.textureSampleLevel(this.apLut, pb.vec2(this.u0, this.v), 0);
503
+ this.$l.t1 = pb.textureSampleLevel(this.apLut, pb.vec2(this.u1, this.v), 0);
504
+ this.$l.t = pb.mix(this.t0, this.t1, this.factor);
505
+ this.$outputs.outColor = pb.vec4(this.t.rgb, pb.sub(1, this.t.a));
506
+ });
507
+ }
508
+ });
509
+ this._bindgroupFogScatter = device.createBindGroup(this._programFogScatter.bindGroupLayouts[0]);
510
+ }
511
+ if (!this._programFog) {
512
+ this._programFog = device.buildRenderProgram({
513
+ label: 'Fog',
514
+ vertex (pb) {
515
+ this.rt = pb.int().uniform(0);
516
+ this.$inputs.pos = pb.vec2().attrib('position');
517
+ this.$outputs.uv = pb.vec2();
518
+ pb.main(function() {
519
+ this.$builtins.position = pb.vec4(this.$inputs.pos, 1, 1);
520
+ this.$outputs.uv = pb.add(pb.mul(this.$inputs.pos.xy, 0.5), pb.vec2(0.5));
521
+ if (device.type === 'webgpu') {
522
+ this.$if(pb.notEqual(this.rt, 0), function() {
523
+ this.$builtins.position.y = pb.neg(this.$builtins.position.y);
524
+ });
525
+ }
526
+ });
527
+ },
528
+ fragment (pb) {
529
+ this.depthTex = pb.tex2D().sampleType('unfilterable-float').uniform(0);
530
+ this.invProjViewMatrix = pb.mat4().uniform(0);
531
+ this.cameraNearFar = pb.vec2().uniform(0);
532
+ this.cameraPosition = pb.vec3().uniform(0);
533
+ this.fogType = pb.int().uniform(0);
534
+ this.fogColor = pb.vec4().uniform(0);
535
+ this.fogParams = pb.vec4().uniform(0);
536
+ this.srgbOut = pb.int().uniform(0);
537
+ this.$outputs.outColor = pb.vec4();
538
+ pb.main(function() {
539
+ this.$l.depthValue = pb.textureSample(this.depthTex, this.$inputs.uv);
540
+ if (device.type === 'webgl') {
541
+ this.$l.linearDepth = decodeNormalizedFloatFromRGBA(this, this.depthValue);
542
+ } else {
543
+ this.$l.linearDepth = this.depthValue.r;
544
+ }
545
+ this.$l.nonLinearDepth = pb.div(pb.sub(pb.div(this.cameraNearFar.x, this.linearDepth), this.cameraNearFar.y), pb.sub(this.cameraNearFar.x, this.cameraNearFar.y));
546
+ //this.$l.clipSpacePos = pb.vec4(pb.sub(pb.mul(this.$inputs.uv, 2), pb.vec2(1)), this.nonLinearDepth, 1);
547
+ this.$l.clipSpacePos = pb.vec4(pb.sub(pb.mul(this.$inputs.uv, 2), pb.vec2(1)), pb.sub(pb.mul(this.nonLinearDepth, 2), 1), 1);
548
+ this.$l.hPos = pb.mul(this.invProjViewMatrix, this.clipSpacePos);
549
+ this.$l.hPos = pb.div(this.$l.hPos, this.$l.hPos.w);
550
+ this.$l.viewDir = pb.sub(this.hPos.xyz, this.cameraPosition);
551
+ this.$l.fogFactor = ShaderFramework.computeFogFactor(this, this.viewDir, this.fogType, this.fogParams);
552
+ this.$l.color = pb.mul(this.fogColor.rgb, this.fogFactor);
553
+ this.$if(pb.equal(this.srgbOut, 0), function() {
554
+ this.$outputs.outColor = pb.vec4(this.color, this.fogFactor);
555
+ }).$else(function() {
556
+ this.$outputs.outColor = pb.vec4(linearToGamma(this, this.color), this.fogFactor);
557
+ });
558
+ });
559
+ }
560
+ });
561
+ this._bindgroupFog = device.createBindGroup(this._programFog.bindGroupLayouts[0]);
562
+ }
563
+ if (!this._programSky.color) {
564
+ this._programSky.color = device.buildRenderProgram({
565
+ label: 'SolidColorSky',
566
+ vertex (pb) {
567
+ this.$inputs.pos = pb.vec3().attrib('position');
568
+ this.worldMatrix = pb.mat4().uniform(0);
569
+ this.viewProjMatrix = pb.mat4().uniform(0);
570
+ this.cameraPos = pb.vec3().uniform(0);
571
+ pb.main(function() {
572
+ this.$l.worldDirection = pb.mul(this.worldMatrix, pb.vec4(this.$inputs.pos, 0)).xyz;
573
+ this.$builtins.position = pb.mul(this.viewProjMatrix, pb.vec4(pb.add(this.worldDirection, this.cameraPos), 1));
574
+ this.$builtins.position.z = this.$builtins.position.w;
575
+ });
576
+ },
577
+ fragment (pb) {
578
+ this.$outputs.outColor = pb.vec4();
579
+ this.color = pb.vec4().uniform(0);
580
+ this.srgbOut = pb.int().uniform(0);
581
+ pb.main(function() {
582
+ this.$if(pb.equal(this.srgbOut, 0), function() {
583
+ this.$outputs.outColor = pb.vec4(this.color.rgb, 1);
584
+ }).$else(function() {
585
+ this.$outputs.outColor = pb.vec4(linearToGamma(this, this.color.rgb), 1);
586
+ });
587
+ });
588
+ }
589
+ });
590
+ this._bindgroupSky.color = device.createBindGroup(this._programSky.color.bindGroupLayouts[0]);
591
+ }
592
+ if (!this._programSky.scatter) {
593
+ this._programSky.scatter = SkyRenderer._createScatterProgram(device, true);
594
+ this._bindgroupSky.scatter = device.createBindGroup(this._programSky.scatter.bindGroupLayouts[0]);
595
+ }
596
+ if (!this._programSky['scatter-nocloud']) {
597
+ this._programSky['scatter-nocloud'] = SkyRenderer._createScatterProgram(device, false);
598
+ this._bindgroupSky['scatter-nocloud'] = device.createBindGroup(this._programSky['scatter-nocloud'].bindGroupLayouts[0]);
599
+ }
600
+ if (!this._programSky.skybox) {
601
+ this._programSky.skybox = device.buildRenderProgram({
602
+ label: 'SkyBoxSky',
603
+ vertex (pb) {
604
+ this.$inputs.pos = pb.vec3().attrib('position');
605
+ this.$outputs.texCoord = pb.vec3();
606
+ this.worldMatrix = pb.mat4().uniform(0);
607
+ this.viewProjMatrix = pb.mat4().uniform(0);
608
+ this.cameraPos = pb.vec3().uniform(0);
609
+ this.flip = pb.vec4().uniform(0);
610
+ pb.main(function() {
611
+ this.$outputs.texCoord = this.$inputs.pos;
612
+ this.$l.worldPos = pb.add(this.cameraPos, pb.mul(this.worldMatrix, pb.vec4(this.$inputs.pos, 0)).xyz);
613
+ this.$builtins.position = pb.mul(this.viewProjMatrix, pb.vec4(this.worldPos, 1), this.flip);
614
+ this.$builtins.position.z = this.$builtins.position.w;
615
+ });
616
+ },
617
+ fragment (pb) {
618
+ this.$outputs.outColor = pb.vec4();
619
+ this.skyCubeMap = pb.texCube().uniform(0);
620
+ this.srgbOut = pb.int().uniform(0);
621
+ pb.main(function() {
622
+ this.$l.texCoord = pb.normalize(this.$inputs.texCoord);
623
+ this.$l.color = pb.textureSampleLevel(this.skyCubeMap, this.texCoord, 0).rgb;
624
+ this.$if(pb.equal(this.srgbOut, 0), function() {
625
+ this.$outputs.outColor = pb.vec4(this.color, 1);
626
+ }).$else(function() {
627
+ this.$outputs.outColor = pb.vec4(linearToGamma(this, this.color), 1);
628
+ });
629
+ });
630
+ }
631
+ });
632
+ this._bindgroupSky.skybox = device.createBindGroup(this._programSky.skybox.bindGroupLayouts[0]);
633
+ }
634
+ if (!this._renderStatesSky) {
635
+ this._renderStatesSky = device.createRenderStateSet();
636
+ this._renderStatesSky.useDepthState().enableTest(true).enableWrite(false).setCompareFunc('le');
637
+ this._renderStatesSky.useRasterizerState().setCullMode('none');
638
+ }
639
+ if (!this._renderStatesSkyNoDepthTest) {
640
+ this._renderStatesSkyNoDepthTest = device.createRenderStateSet();
641
+ this._renderStatesSkyNoDepthTest.useDepthState().enableTest(false).enableWrite(false);
642
+ this._renderStatesSkyNoDepthTest.useRasterizerState().setCullMode('none');
643
+ }
644
+ if (!this._renderStatesFog) {
645
+ this._renderStatesFog = device.createRenderStateSet();
646
+ this._renderStatesFog.useRasterizerState().setCullMode('none');
647
+ this._renderStatesFog.useBlendingState().enable(true).setBlendFunc('one', 'inv-src-alpha');
648
+ this._renderStatesFog.useDepthState().enableTest(false).enableWrite(false);
649
+ }
650
+ if (!this._renderStatesFogScatter) {
651
+ this._renderStatesFogScatter = device.createRenderStateSet();
652
+ this._renderStatesFogScatter.useRasterizerState().setCullMode('none');
653
+ this._renderStatesFogScatter.useBlendingState().enable(true).setBlendFunc('one', 'inv-src-alpha');
654
+ this._renderStatesFogScatter.useDepthState().enableTest(true).enableWrite(false).setCompareFunc('gt');
655
+ }
656
+ if (!this._nearestSampler) {
657
+ this._nearestSampler = device.createSampler({
658
+ magFilter: 'nearest',
659
+ minFilter: 'nearest',
660
+ mipFilter: 'none',
661
+ addressU: 'clamp',
662
+ addressV: 'clamp'
663
+ });
664
+ }
665
+ if (!this._vertexLayout) {
666
+ this._vertexLayout = device.createVertexLayout({
667
+ vertexBuffers: [
668
+ {
669
+ buffer: device.createVertexBuffer('position_f32x2', new Float32Array([
670
+ -1,
671
+ -1,
672
+ 1,
673
+ -1,
674
+ -1,
675
+ 1,
676
+ 1,
677
+ 1
678
+ ]))
679
+ }
680
+ ]
681
+ });
682
+ }
683
+ if (!this._primitiveSky) {
684
+ this._primitiveSky = new BoxShape({
685
+ size: 8,
686
+ anchorX: 0.5,
687
+ anchorY: 0.5,
688
+ anchorZ: 0.5
689
+ });
690
+ }
691
+ }
692
+ /** @internal */ static _getSunDir(sunLight) {
693
+ // TODO: reduce GC
694
+ return sunLight?.directionAndCutoff.xyz().scaleBy(-1) ?? SkyRenderer._defaultSunDir;
695
+ }
696
+ static _createScatterProgram(device, cloud) {
697
+ return device.buildRenderProgram({
698
+ vertex (pb) {
699
+ this.$inputs.pos = pb.vec3().attrib('position');
700
+ this.worldMatrix = pb.mat4().uniform(0);
701
+ this.viewProjMatrix = pb.mat4().uniform(0);
702
+ this.cameraPos = pb.vec3().uniform(0);
703
+ this.flip = pb.vec4().uniform(0);
704
+ pb.main(function() {
705
+ this.$outputs.worldDirection = pb.mul(this.worldMatrix, pb.vec4(this.$inputs.pos, 0)).xyz;
706
+ this.$builtins.position = pb.mul(this.viewProjMatrix, pb.vec4(pb.add(this.$outputs.worldDirection, this.cameraPos), 1), this.flip);
707
+ this.$builtins.position.z = this.$builtins.position.w;
708
+ });
709
+ },
710
+ fragment (pb) {
711
+ this.$outputs.outColor = pb.vec4();
712
+ this.tLut = pb.tex2D().uniform(0);
713
+ this.skyLut = pb.tex2D().uniform(0);
714
+ this.sunDir = pb.vec3().uniform(0);
715
+ if (cloud) {
716
+ this.cloudy = pb.float().uniform(0);
717
+ this.cloudIntensity = pb.float().uniform(0);
718
+ this.time = pb.float().uniform(0);
719
+ this.velocity = pb.vec2().uniform(0);
720
+ }
721
+ this.drawGround = pb.int().uniform(0);
722
+ this.srgbOut = pb.int().uniform(0);
723
+ this.viewPos = pb.vec3(ScatteringLut.viewPosition.x, ScatteringLut.viewPosition.y, ScatteringLut.viewPosition.z);
724
+ pb.func('getMiePhase', [
725
+ pb.float('cosTheta')
726
+ ], function() {
727
+ this.$l.g = pb.float(0.8);
728
+ this.$l.scale = pb.float(3 / (Math.PI * 8));
729
+ this.$l.gg = pb.mul(this.g, this.g);
730
+ this.$l.num = pb.mul(pb.sub(1, this.gg), pb.add(pb.mul(this.cosTheta, this.cosTheta), 1));
731
+ this.$l.denom = pb.mul(pb.add(2, this.gg), pb.pow(pb.sub(pb.add(1, this.gg), pb.mul(this.g, this.cosTheta, 2)), 1.5));
732
+ this.$return(pb.div(pb.mul(this.scale, this.num), this.denom));
733
+ });
734
+ pb.func('noise', [
735
+ pb.vec3('p'),
736
+ pb.float('t')
737
+ ], function() {
738
+ this.p2 = pb.mul(this.p, 0.25);
739
+ this.f = pb.mul(smoothNoise3D(this, this.p2), 0.5);
740
+ this.p2 = pb.mul(this.p2, 3.02);
741
+ this.p2.y = pb.sub(this.p2.y, pb.mul(this.t, 0.02));
742
+ this.f = pb.add(this.f, pb.mul(smoothNoise3D(this, this.p2), 0.25));
743
+ this.p2 = pb.mul(this.p2, 3.03);
744
+ this.p2.y = pb.add(this.p2.y, pb.mul(this.t, 0.01));
745
+ this.f = pb.add(this.f, pb.mul(smoothNoise3D(this, this.p2), 0.125));
746
+ this.p2 = pb.mul(this.p2, 3.02);
747
+ this.f = pb.add(this.f, pb.mul(smoothNoise3D(this, this.p2), 0.0625));
748
+ this.p2 = pb.mul(this.p2, 3.01);
749
+ this.f = pb.add(this.f, pb.mul(smoothNoise3D(this, this.p2), 0.03125));
750
+ this.p2 = pb.mul(this.p2, 3.01);
751
+ this.f = pb.add(this.f, pb.mul(smoothNoise3D(this, this.p2), 0.015625));
752
+ this.$return(this.f);
753
+ });
754
+ pb.func('getValFromSkyLUT', [
755
+ pb.vec3('rayDir'),
756
+ pb.vec3('sunDir')
757
+ ], function() {
758
+ this.$l.height = pb.length(this.viewPos);
759
+ this.$l.up = pb.div(this.viewPos, this.height);
760
+ this.$l.c = pb.div(pb.sqrt(pb.sub(pb.mul(this.height, this.height), pb.mul(ScatteringLut.groundRadius, ScatteringLut.groundRadius))), this.height);
761
+ this.$l.horizonAngle = pb.acos(pb.clamp(this.c, -1, 1));
762
+ this.$l.altitudeAngle = pb.sub(this.horizonAngle, pb.acos(pb.dot(this.rayDir, this.up)));
763
+ this.$l.azimuthAngle = pb.float();
764
+ this.$if(pb.greaterThan(pb.abs(this.altitudeAngle), Math.PI * 0.5 - 0.0001), function() {
765
+ this.azimuthAngle = 0;
766
+ }).$else(function() {
767
+ this.$l.right = pb.cross(this.sunDir, this.up);
768
+ this.$l.forward = pb.cross(this.up, this.right);
769
+ this.$l.projectedDir = pb.normalize(pb.sub(this.rayDir, pb.mul(this.up, pb.dot(this.rayDir, this.up))));
770
+ this.$l.sinTheta = pb.dot(this.projectedDir, this.right);
771
+ this.$l.cosTheta = pb.dot(this.projectedDir, this.forward);
772
+ this.azimuthAngle = pb.add(pb.atan2(this.sinTheta, this.cosTheta), Math.PI);
773
+ });
774
+ this.$l.v = pb.add(0.5, pb.mul(0.5, pb.sign(this.altitudeAngle), pb.sqrt(pb.mul(pb.abs(this.altitudeAngle), 2 / Math.PI))));
775
+ this.$l.uv = pb.vec2(pb.div(this.azimuthAngle, Math.PI * 2), this.v);
776
+ this.$return(pb.textureSampleLevel(this.skyLut, this.uv, 0).rgb);
777
+ });
778
+ pb.func('sunWithBloom', [
779
+ pb.vec3('rayDir'),
780
+ pb.vec3('sunDir')
781
+ ], function() {
782
+ this.$l.sunSolidAngle = 0.53 * Math.PI / 180;
783
+ this.$l.minSunCosTheta = pb.cos(this.sunSolidAngle);
784
+ this.$l.cosTheta = pb.dot(this.rayDir, this.sunDir);
785
+ this.$if(pb.greaterThanEqual(this.cosTheta, this.minSunCosTheta), function() {
786
+ this.$return(pb.vec3(1));
787
+ });
788
+ this.$l.offset = pb.sub(this.minSunCosTheta, this.cosTheta);
789
+ this.$l.gaussianBloom = pb.mul(pb.exp(pb.mul(this.offset, -50000)), 0.5);
790
+ this.$l.invBloom = pb.mul(pb.div(1, pb.add(0.02, pb.mul(this.offset, 300))), 0.01);
791
+ this.$return(pb.vec3(pb.add(this.gaussianBloom, this.invBloom)));
792
+ });
793
+ pb.func('rayIntersectSphere', [
794
+ pb.vec3('ro'),
795
+ pb.vec3('rd'),
796
+ pb.float('rad')
797
+ ], function() {
798
+ this.$l.b = pb.dot(this.ro, this.rd);
799
+ this.$l.c = pb.sub(pb.dot(this.ro, this.ro), pb.mul(this.rad, this.rad));
800
+ this.$if(pb.and(pb.greaterThan(this.c, 0), pb.greaterThan(this.b, 0)), function() {
801
+ this.$return(pb.float(-1));
802
+ });
803
+ this.$l.bb = pb.mul(this.b, this.b);
804
+ this.$l.discr = pb.sub(this.bb, this.c);
805
+ this.$if(pb.lessThan(this.discr, 0), function() {
806
+ this.$return(pb.float(-1));
807
+ });
808
+ this.$if(pb.greaterThan(this.discr, this.bb), function() {
809
+ this.$return(pb.sub(pb.sqrt(this.discr), this.b));
810
+ });
811
+ this.$return(pb.sub(pb.neg(pb.sqrt(this.discr)), this.b));
812
+ });
813
+ pb.func('getValFromTLUT', [
814
+ pb.vec3('pos'),
815
+ pb.vec3('sunDir')
816
+ ], function() {
817
+ this.$l.height = pb.length(this.pos);
818
+ this.$l.up = pb.div(this.pos, this.height);
819
+ this.$l.sunCosZenithAngle = pb.dot(this.sunDir, this.up);
820
+ this.$l.uv = pb.vec2(pb.clamp(pb.add(0.5, pb.mul(this.sunCosZenithAngle, 0.5)), 0, 1), pb.max(0, pb.min(1, pb.div(pb.sub(this.height, ScatteringLut.groundRadius), pb.sub(ScatteringLut.atmosphereRadius, ScatteringLut.groundRadius)))));
821
+ this.$return(pb.textureSampleLevel(this.tLut, this.uv, 0).rgb);
822
+ });
823
+ pb.main(function() {
824
+ this.$l.rayDir = pb.normalize(this.$inputs.worldDirection);
825
+ // ad-hoc
826
+ this.$l.sunIntensity = pb.sqrt(pb.max(0, pb.mul(this.sunDir.y, this.rayDir.y)));
827
+ // compute cloud
828
+ if (cloud) {
829
+ this.$l.noiseValue = pb.float();
830
+ this.$if(pb.lessThanEqual(this.rayDir.y, 0), function() {
831
+ this.noiseValue = 0;
832
+ }).$else(function() {
833
+ this.$l.tMin = pb.div(3000, this.rayDir.y);
834
+ this.$l.cloudPoint = pb.mul(this.rayDir, this.tMin);
835
+ this.speed = pb.mul(pb.vec3(this.velocity.x, 0, this.velocity.y), this.time);
836
+ this.$l.noiseScale = pb.float(4e-4);
837
+ this.noiseValue = this.noise(pb.mul(pb.add(this.cloudPoint, this.speed), this.noiseScale), this.time);
838
+ this.noiseValue = pb.add(this.noiseValue, this.cloudy);
839
+ this.noiseValue = pb.smoothStep(1, pb.add(1, this.cloudy), this.noiseValue);
840
+ });
841
+ // use sun color as cloud color
842
+ this.$l.sunColor = pb.mul(this.getValFromSkyLUT(this.sunDir, this.sunDir), this.sunIntensity);
843
+ this.$l.cloudColor = pb.mul(this.sunColor.rgb, pb.mul(this.noiseValue, this.cloudIntensity));
844
+ }
845
+ // Compute sky color
846
+ this.$l.skyRayDir = this.$choice(pb.equal(this.drawGround, 0), pb.normalize(pb.vec3(this.rayDir.x, pb.max(0, this.rayDir.y), this.rayDir.z)), this.rayDir);
847
+ this.$l.lum = this.getValFromSkyLUT(this.skyRayDir, this.sunDir);
848
+ this.$l.sunLum = this.sunWithBloom(this.rayDir, this.sunDir);
849
+ this.sunLum = pb.smoothStep(pb.vec3(0.002), pb.vec3(1), this.sunLum);
850
+ this.$if(pb.greaterThan(pb.length(this.sunLum), 0), function() {
851
+ this.$if(pb.greaterThanEqual(this.rayIntersectSphere(this.viewPos, this.rayDir, ScatteringLut.groundRadius), 0), function() {
852
+ this.sunLum = pb.vec3(0);
853
+ }).$else(function() {
854
+ this.sunLum = pb.mul(this.sunLum, this.getValFromTLUT(this.viewPos, this.sunDir));
855
+ });
856
+ });
857
+ if (cloud) {
858
+ this.lum = pb.add(this.lum, this.sunLum);
859
+ // blend
860
+ this.$l.vfactor = pb.clamp(pb.div(pb.sub(this.rayDir.y, 0.01), pb.sub(0.03, 0.01)), 0, 1);
861
+ this.$l.factor = pb.clamp(pb.mul(this.noiseValue, this.vfactor), 0, 1);
862
+ this.$l.color = pb.mix(this.lum, this.cloudColor, this.factor);
863
+ } else {
864
+ this.$l.color = this.lum;
865
+ }
866
+ this.color = pb.mul(this.color, 8);
867
+ this.color = pb.pow(this.color, pb.vec3(1.3));
868
+ this.color = pb.div(this.color, pb.add(pb.mul(pb.smoothStep(0, 0.2, pb.clamp(this.sunDir.y, 0, 1)), 2), 0.15));
869
+ this.$if(pb.equal(this.srgbOut, 0), function() {
870
+ this.$outputs.outColor = pb.vec4(this.color, 1);
871
+ }).$else(function() {
872
+ this.$outputs.outColor = pb.vec4(linearToGamma(this, this.color), 1);
873
+ });
874
+ });
875
+ }
876
+ });
877
+ }
878
+ }
879
+
880
+ export { SkyRenderer };
881
+ //# sourceMappingURL=sky.js.map