Unravel Engine C++ Reference
Loading...
Searching...
No Matches
shadow.cpp
Go to the documentation of this file.
1#include "shadow.h"
2#include "bgfx/bgfx.h"
3
8#include <engine/engine.h>
9#include <engine/events.h>
17
21#include <graphics/texture.h>
23
24namespace unravel
25{
26namespace shadow
27{
28
29namespace
30{
31
32// clang-format off
33RenderState render_states[RenderState::Count] =
34 {
35 { // Default
36 0
37 | BGFX_STATE_WRITE_RGB
38 | BGFX_STATE_WRITE_A
39 | BGFX_STATE_DEPTH_TEST_LESS
40 | BGFX_STATE_WRITE_Z
41 | BGFX_STATE_CULL_CCW
42 | BGFX_STATE_MSAA
43 , UINT32_MAX
44 , BGFX_STENCIL_NONE
45 , BGFX_STENCIL_NONE
46 },
47 { // ShadowMap_PackDepth
48 0
49 | BGFX_STATE_WRITE_RGB
50 | BGFX_STATE_WRITE_A
51 | BGFX_STATE_WRITE_Z
52 | BGFX_STATE_DEPTH_TEST_LESS
53 | BGFX_STATE_CULL_CCW
54 | BGFX_STATE_MSAA
55 , UINT32_MAX
56 , BGFX_STENCIL_NONE
57 , BGFX_STENCIL_NONE
58 },
59 { // ShadowMap_PackDepthHoriz
60 0
61 | BGFX_STATE_WRITE_RGB
62 | BGFX_STATE_WRITE_A
63 | BGFX_STATE_WRITE_Z
64 | BGFX_STATE_DEPTH_TEST_LESS
65 | BGFX_STATE_CULL_CCW
66 | BGFX_STATE_MSAA
67 , UINT32_MAX
68 , BGFX_STENCIL_TEST_EQUAL
69 | BGFX_STENCIL_FUNC_REF(1)
70 | BGFX_STENCIL_FUNC_RMASK(0xff)
71 | BGFX_STENCIL_OP_FAIL_S_KEEP
72 | BGFX_STENCIL_OP_FAIL_Z_KEEP
73 | BGFX_STENCIL_OP_PASS_Z_KEEP
74 , BGFX_STENCIL_NONE
75 },
76 { // ShadowMap_PackDepthVert
77 0
78 | BGFX_STATE_WRITE_RGB
79 | BGFX_STATE_WRITE_A
80 | BGFX_STATE_WRITE_Z
81 | BGFX_STATE_DEPTH_TEST_LESS
82 | BGFX_STATE_CULL_CCW
83 | BGFX_STATE_MSAA
84 , UINT32_MAX
85 , BGFX_STENCIL_TEST_EQUAL
86 | BGFX_STENCIL_FUNC_REF(0)
87 | BGFX_STENCIL_FUNC_RMASK(0xff)
88 | BGFX_STENCIL_OP_FAIL_S_KEEP
89 | BGFX_STENCIL_OP_FAIL_Z_KEEP
90 | BGFX_STENCIL_OP_PASS_Z_KEEP
91 , BGFX_STENCIL_NONE
92 },
93 { // Custom_BlendLightTexture
94 BGFX_STATE_WRITE_RGB
95 | BGFX_STATE_WRITE_A
96 | BGFX_STATE_WRITE_Z
97 | BGFX_STATE_DEPTH_TEST_LESS
98 | BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_COLOR, BGFX_STATE_BLEND_INV_SRC_COLOR)
99 | BGFX_STATE_CULL_CCW
100 | BGFX_STATE_MSAA
101 , UINT32_MAX
102 , BGFX_STENCIL_NONE
103 , BGFX_STENCIL_NONE
104 },
105 { // Custom_DrawPlaneBottom
106 BGFX_STATE_WRITE_RGB
107 | BGFX_STATE_CULL_CW
108 | BGFX_STATE_MSAA
109 , UINT32_MAX
110 , BGFX_STENCIL_NONE
111 , BGFX_STENCIL_NONE
112 },
113 };
114// clang-format on
115
116auto apply_shadow_cull(uint64_t base_state, cull_type cull) -> uint64_t
117{
118 uint64_t state = base_state & ~(BGFX_STATE_CULL_CCW | BGFX_STATE_CULL_CW);
120 {
121 state |= BGFX_STATE_CULL_CCW;
122 }
123 else if(cull == cull_type::clockwise)
124 {
125 state |= BGFX_STATE_CULL_CW;
126 }
127 return state;
128}
129
130auto resolve_cutout_pack_program(const Programs& programs, gpu_program* opaque_prog) -> gpu_program*
131{
132 if(!opaque_prog)
133 {
134 return opaque_prog;
135 }
136
137 for(uint32_t depth = 0; depth < DepthImpl::Count; ++depth)
138 {
139 for(uint32_t pack = 0; pack < PackDepth::Count; ++pack)
140 {
141 if(programs.m_packDepth[depth][pack].get() == opaque_prog)
142 {
143 return programs.m_packDepthCutout[depth][pack].get();
144 }
145 if(programs.m_packDepthInstanced[depth][pack].get() == opaque_prog)
146 {
147 return programs.m_packDepthInstancedCutout[depth][pack].get();
148 }
149 if(programs.m_packDepthSkinned[depth][pack].get() == opaque_prog)
150 {
151 return programs.m_packDepthSkinnedCutout[depth][pack].get();
152 }
153 }
154 }
155
156 return opaque_prog;
157}
158
159auto convert(light_type t) -> LightType::Enum
160{
161 static_assert(std::uint8_t(light_type::count) == std::uint8_t(LightType::Count), "Missing impl");
162
163 switch(t)
164 {
165 case light_type::spot:
169 default:
171 }
172}
173
174auto convert(sm_impl t) -> SmImpl::Enum
175{
176 static_assert(std::uint8_t(sm_impl::count) == std::uint8_t(SmImpl::Count), "Missing impl");
177
178 switch(t)
179 {
180 case sm_impl::hard:
181 return SmImpl::Hard;
182
183 case sm_impl::pcf:
184 return SmImpl::PCF;
185
186 case sm_impl::pcss:
187 return SmImpl::PCSS;
188
189 case sm_impl::esm:
190 return SmImpl::ESM;
191
192 case sm_impl::vsm:
193 return SmImpl::VSM;
194 default:
195 return SmImpl::Count;
196 }
197}
198
199auto convert(sm_depth t) -> DepthImpl::Enum
200{
201 static_assert(std::uint8_t(sm_depth::count) == std::uint8_t(DepthImpl::Count), "Missing impl");
202 switch(t)
203 {
204 case sm_depth::invz:
205 return DepthImpl::InvZ;
206
207 case sm_depth::linear:
208 return DepthImpl::Linear;
209
210 default:
211 return DepthImpl::Count;
212 }
213}
214
215auto convert(sm_resolution t) -> float
216{
217 switch(t)
218 {
220 return 8;
221
223 return 9;
224
226 return 10;
227
229 return 11;
230
231 default:
232 return 10;
233 }
234}
235
236void mtxYawPitchRoll(float* _result, float _yaw, float _pitch, float _roll)
237{
238 float sroll = bx::sin(_roll);
239 float croll = bx::cos(_roll);
240 float spitch = bx::sin(_pitch);
241 float cpitch = bx::cos(_pitch);
242 float syaw = bx::sin(_yaw);
243 float cyaw = bx::cos(_yaw);
244
245 _result[0] = sroll * spitch * syaw + croll * cyaw;
246 _result[1] = sroll * cpitch;
247 _result[2] = sroll * spitch * cyaw - croll * syaw;
248 _result[3] = 0.0f;
249 _result[4] = croll * spitch * syaw - sroll * cyaw;
250 _result[5] = croll * cpitch;
251 _result[6] = croll * spitch * cyaw + sroll * syaw;
252 _result[7] = 0.0f;
253 _result[8] = cpitch * syaw;
254 _result[9] = -spitch;
255 _result[10] = cpitch * cyaw;
256 _result[11] = 0.0f;
257 _result[12] = 0.0f;
258 _result[13] = 0.0f;
259 _result[14] = 0.0f;
260 _result[15] = 1.0f;
261}
262
263void screenSpaceQuad(bool _originBottomLeft = true, float _width = 1.0f, float _height = 1.0f)
264{
265 if(3 == bgfx::getAvailTransientVertexBuffer(3, PosColorTexCoord0Vertex::get_layout()))
266 {
267 bgfx::TransientVertexBuffer vb;
268 bgfx::allocTransientVertexBuffer(&vb, 3, PosColorTexCoord0Vertex::get_layout());
270
271 const float zz = 0.0f;
272
273 const float minx = -_width;
274 const float maxx = _width;
275 const float miny = 0.0f;
276 const float maxy = _height * 2.0f;
277
278 const float minu = -1.0f;
279 const float maxu = 1.0f;
280
281 float minv = 0.0f;
282 float maxv = 2.0f;
283
284 if(_originBottomLeft)
285 {
286 std::swap(minv, maxv);
287 minv -= 1.0f;
288 maxv -= 1.0f;
289 }
290
291 vertex[0].m_x = minx;
292 vertex[0].m_y = miny;
293 vertex[0].m_z = zz;
294 vertex[0].m_rgba = 0xffffffff;
295 vertex[0].m_u = minu;
296 vertex[0].m_v = minv;
297
298 vertex[1].m_x = maxx;
299 vertex[1].m_y = miny;
300 vertex[1].m_z = zz;
301 vertex[1].m_rgba = 0xffffffff;
302 vertex[1].m_u = maxu;
303 vertex[1].m_v = minv;
304
305 vertex[2].m_x = maxx;
306 vertex[2].m_y = maxy;
307 vertex[2].m_z = zz;
308 vertex[2].m_rgba = 0xffffffff;
309 vertex[2].m_u = maxu;
310 vertex[2].m_v = maxv;
311
312 bgfx::setVertexBuffer(0, &vb);
313 }
314}
315
316void worldSpaceFrustumCorners(
317 float* _corners24f
318 , float _near
319 , float _far
320 , float _projWidth
321 , float _projHeight
322 , const float* _invViewMtx
323 )
324{
325 // Define frustum corners in view space.
326 const float nw = _near * _projWidth;
327 const float nh = _near * _projHeight;
328 const float fw = _far * _projWidth;
329 const float fh = _far * _projHeight;
330
331 const uint8_t numCorners = 8;
332 const bx::Vec3 corners[numCorners] =
333 {
334 { -nw, nh, _near },
335 { nw, nh, _near },
336 { nw, -nh, _near },
337 { -nw, -nh, _near },
338 { -fw, fh, _far },
339 { fw, fh, _far },
340 { fw, -fh, _far },
341 { -fw, -fh, _far },
342 };
343
344 // Convert them to world space.
345 float (*out)[3] = (float(*)[3])_corners24f;
346 for (uint8_t ii = 0; ii < numCorners; ++ii)
347 {
348 bx::store(&out[ii], bx::mul(corners[ii], _invViewMtx) );
349 }
350}
351
362void calculateCascadeSplits(float* cascadeSplits, uint8_t numSplits, float nearClip, float farClip, float splitLambda)
363{
364 const float clipRange = farClip - nearClip;
365 const float minZ = nearClip;
366 const float maxZ = nearClip + clipRange;
367 const float range = maxZ - minZ;
368 const float ratio = maxZ / minZ;
369
370 // Calculate split depths based on view camera frustum
371 // Use a modified indexing similar to the original implementation for better distribution feel
372 // The original used odd indices (1,3,5,7) out of (num_splits*2), giving more weight to near cascades
373 const float numSlicesF = float(numSplits) * 2.5f;
374
375 for(uint32_t i = 0; i < numSplits; i++)
376 {
377 // Use odd indices like the original: 1, 3, 5, 7 for 4 splits
378 const float si = float(i * 2 + 1) / numSlicesF;
379 const float logSplit = minZ * bx::pow(ratio, si);
380 const float uniformSplit = minZ + range * si;
381 const float d = splitLambda * (logSplit - uniformSplit) + uniformSplit;
382 cascadeSplits[i] = (d - nearClip) / clipRange;
383 }
384}
385
386} // namespace
387
392
397
399{
402
403 uniforms_.destroy();
404 programs_.destroy();
405}
406
408{
409 if(!valid_)
410 {
411 return;
412 }
413
414 valid_ = false;
415
416 for(int i = 0; i < ShadowMapRenderTargets::Count; ++i)
417 {
418 if(bgfx::isValid(rt_shadow_map_[i]))
419 {
420 bgfx::destroy(rt_shadow_map_[i]);
421 rt_shadow_map_[i] = {bgfx::kInvalidHandle};
422 }
423 }
424
425 if(bgfx::isValid(rt_blur_))
426 {
427 bgfx::destroy(rt_blur_);
428 rt_blur_ = {bgfx::kInvalidHandle};
429 }
430}
431
433{
434 if(bgfx::isValid(tex_color_))
435 {
436 bgfx::destroy(tex_color_);
437 }
438
439 for(int i = 0; i < ShadowMapRenderTargets::Count; ++i)
440 {
441 if(bgfx::isValid(shadow_map_[i]))
442 {
443 bgfx::destroy(shadow_map_[i]);
444 }
445 }
446}
447
449{
450 if(bgfx::isValid(tex_color_))
451 {
452 return;
453 }
454 // Uniforms.
455 uniforms_.init();
456 tex_color_ = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
457 shadow_map_[0] = bgfx::createUniform("s_shadowMap0", bgfx::UniformType::Sampler);
458 shadow_map_[1] = bgfx::createUniform("s_shadowMap1", bgfx::UniformType::Sampler);
459 shadow_map_[2] = bgfx::createUniform("s_shadowMap2", bgfx::UniformType::Sampler);
460 shadow_map_[3] = bgfx::createUniform("s_shadowMap3", bgfx::UniformType::Sampler);
461
462 for(int i = 0; i < ShadowMapRenderTargets::Count; ++i)
463 {
464 rt_shadow_map_[i] = {bgfx::kInvalidHandle};
465 }
466
467 // Programs.
468 programs_.init(ctx);
469
470 // Lights.
471 // clang-format off
472 point_light_ =
473 {
474 { { 0.0f, 0.0f, 0.0f, 1.0f } }, //position
475 { { 0.0f,-0.4f,-0.6f, 0.0f } }, //spotdirection, spotexponent
476 };
477
478 directional_light_ =
479 {
480 { { 0.5f,-1.0f, 0.1f, 0.0f } }, //position
481 { { 0.0f, 0.0f, 0.0f, 1.0f } }, //spotdirection, spotexponent
482 };
483
484 // clang-format on
485
486 // Setup uniforms.
487 color_[0] = color_[1] = color_[2] = color_[3] = 1.0f;
488 uniforms_.setPtrs(&point_light_,
489 color_,
490 light_mtx_,
491 &shadow_map_mtx_[ShadowMapRenderTargets::First][0],
492 &shadow_map_mtx_[ShadowMapRenderTargets::Second][0],
493 &shadow_map_mtx_[ShadowMapRenderTargets::Third][0],
494 &shadow_map_mtx_[ShadowMapRenderTargets::Fourth][0]);
495 // uniforms_.submitConstUniforms();
496
497 // clang-format off
498 // Settings.
500 {
501 { //LightType::Spot
502
503 { //DepthImpl::InvZ
504
505 { //SmImpl::Hard
506 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
507 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
508 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
509 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
510 , 0.0035f, 0.0f, 0.01f, 0.00001f // m_bias
511 , 0.0012f, 0.0f, 0.05f, 0.00001f // m_normalOffset
512 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
513 , 500.0f, 1.0f, 1000.0f, 1.0f // m_customParam1
514 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
515 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
516 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
517 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
518 , true // m_doBlur
519 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
520 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackSkinned
521 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
522 },
523 { //SmImpl::PCF
524 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
525 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
526 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
527 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
528 , 0.007f, 0.0f, 0.01f, 0.00001f // m_bias
529 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
530 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
531 , 500.0f, 1.0f, 1000.0f, 1.0f // m_customParam1
532 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
533 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
534 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
535 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
536 , true // m_doBlur
537 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
538 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackSkinned
539 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
540 },
541 { //SmImpl::PCSS
542 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
543 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
544 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
545 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
546 , 0.007f, 0.0f, 0.01f, 0.00001f // m_bias
547 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
548 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
549 , 500.0f, 1.0f, 1000.0f, 1.0f // m_customParam1
550 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
551 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
552 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
553 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
554 , true // m_doBlur
555 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
556 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackSkinned
557 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
558 },
559 { //SmImpl::VSM
560 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
561 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
562 , 8.0f, 1.0f, 10.0f, 1.0f // m_near
563 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
564 , 0.045f, 0.0f, 0.1f, 0.00001f // m_bias
565 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
566 , 0.02f, 0.0f, 0.04f, 0.00001f // m_customParam0
567 , 450.0f, 1.0f, 1000.0f, 1.0f // m_customParam1
568 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
569 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
570 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
571 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
572 , true // m_doBlur
573 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPack
574 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPackSkinned
575 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPackInstanced
576 },
577 { //SmImpl::ESM
578 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
579 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
580 , 3.0f, 1.0f, 10.0f, 0.01f // m_near
581 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
582 , 0.02f, 0.0f, 0.3f, 0.00001f // m_bias
583 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
584 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
585 , 9000.0f, 1.0f, 15000.0f, 1.0f // m_customParam1
586 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
587 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
588 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
589 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
590 , true // m_doBlur
591 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
592 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
593 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
594 }
595
596 },
597 { //DepthImpl::Linear
598
599 { //SmImpl::Hard
600 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
601 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
602 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
603 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
604 , 0.0025f, 0.0f, 0.01f, 0.00001f // m_bias
605 , 0.0012f, 0.0f, 0.05f, 0.00001f // m_normalOffset
606 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
607 , 500.0f, 1.0f, 1000.0f, 1.0f // m_customParam1
608 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
609 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
610 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
611 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
612 , true // m_doBlur
613 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
614 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_packDepthSkinned
615 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
616 },
617 { //SmImpl::PCF
618 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
619 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
620 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
621 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
622 , 0.0025f, 0.0f, 0.01f, 0.00001f // m_bias
623 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
624 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
625 , 2000.0f, 1.0f, 2000.0f, 1.0f // m_customParam1
626 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
627 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
628 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
629 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
630 , true // m_doBlur
631 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
632 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackSkinned
633 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
634 },
635 { //SmImpl::PCSS
636 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
637 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
638 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
639 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
640 , 0.0025f, 0.0f, 0.01f, 0.00001f // m_bias
641 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
642 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
643 , 2000.0f, 1.0f, 2000.0f, 1.0f // m_customParam1
644 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
645 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
646 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
647 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
648 , true // m_doBlur
649 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
650 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackSkinned
651 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
652 },
653 { //SmImpl::VSM
654 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
655 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
656 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
657 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
658 , 0.006f, 0.0f, 0.01f, 0.00001f // m_bias
659 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
660 , 0.02f, 0.0f, 0.1f, 0.00001f // m_customParam0
661 , 300.0f, 1.0f, 1500.0f, 1.0f // m_customParam1
662 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
663 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
664 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
665 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
666 , true // m_doBlur
667 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::VSM].get() //m_progPack
668 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::VSM].get() //m_progPackSkinned
669 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::VSM].get() //m_progPackInstanced
670 },
671 { //SmImpl::ESM
672 10.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
673 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
674 , 1.0f, 1.0f, 10.0f, 0.01f // m_near
675 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
676 , 0.0055f, 0.0f, 0.01f, 0.00001f // m_bias
677 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
678 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
679 , 2500.0f, 1.0f, 5000.0f, 1.0f // m_customParam1
680 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
681 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
682 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
683 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
684 , true // m_doBlur
685 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
686 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_packDepthSkinned
687 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
688 }
689
690 }
691
692 },
693 { //LightType::Point
694
695 { //DepthImpl::InvZ
696
697 { //SmImpl::Hard
698 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
699 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
700 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
701 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
702 , 0.006f, 0.0f, 0.01f, 0.00001f // m_bias
703 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
704 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
705 , 50.0f, 1.0f, 300.0f, 1.0f // m_customParam1
706 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
707 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
708 , 0.25f, 0.0f, 2.0f, 0.001f // m_xOffset
709 , 0.25f, 0.0f, 2.0f, 0.001f // m_yOffset
710 , true // m_doBlur
711 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
712 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
713 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
714 },
715 { //SmImpl::PCF
716 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
717 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
718 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
719 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
720 , 0.004f, 0.0f, 0.01f, 0.00001f // m_bias
721 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
722 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
723 , 50.0f, 1.0f, 300.0f, 1.0f // m_customParam1
724 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
725 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
726 , 1.0f, 0.0f, 3.0f, 0.001f // m_xOffset
727 , 1.0f, 0.0f, 3.0f, 0.001f // m_yOffset
728 , true // m_doBlur
729 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
730 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
731 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
732 },
733 { //SmImpl::PCSS
734 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
735 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
736 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
737 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
738 , 0.004f, 0.0f, 0.01f, 0.00001f // m_bias
739 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
740 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
741 , 50.0f, 1.0f, 300.0f, 1.0f // m_customParam1
742 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
743 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
744 , 1.0f, 0.0f, 3.0f, 0.001f // m_xOffset
745 , 1.0f, 0.0f, 3.0f, 0.001f // m_yOffset
746 , true // m_doBlur
747 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
748 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
749 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
750 },
751 { //SmImpl::VSM
752 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
753 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
754 , 8.0f, 1.0f, 10.0f, 1.0f // m_near
755 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
756 , 0.055f, 0.0f, 0.1f, 0.00001f // m_bias
757 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
758 , 0.02f, 0.0f, 0.04f, 0.00001f // m_customParam0
759 , 450.0f, 1.0f, 900.0f, 1.0f // m_customParam1
760 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
761 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
762 , 0.25f, 0.0f, 2.0f, 0.001f // m_xOffset
763 , 0.25f, 0.0f, 2.0f, 0.001f // m_yOffset
764 , true // m_doBlur
765 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPack
766 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::VSM].get() //m_packDepthSkinned
767 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPackInstanced
768 },
769 { //SmImpl::ESM
770 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
771 , 10.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
772 , 3.0f, 1.0f, 10.0f, 0.01f // m_near
773 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
774 , 0.035f, 0.0f, 0.1f, 0.00001f // m_bias
775 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
776 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
777 , 9000.0f, 1.0f, 15000.0f, 1.0f // m_customParam1
778 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
779 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
780 , 0.25f, 0.0f, 2.0f, 0.001f // m_xOffset
781 , 0.25f, 0.0f, 2.0f, 0.001f // m_yOffset
782 , true // m_doBlur
783 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
784 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
785 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
786 }
787
788 },
789 { //DepthImpl::Linear
790
791 { //SmImpl::Hard
792 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
793 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
794 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
795 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
796 , 0.003f, 0.0f, 0.01f, 0.00001f // m_bias
797 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
798 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
799 , 120.0f, 1.0f, 300.0f, 1.0f // m_customParam1
800 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
801 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
802 , 0.25f, 0.0f, 2.0f, 0.001f // m_xOffset
803 , 0.25f, 0.0f, 2.0f, 0.001f // m_yOffset
804 , true // m_doBlur
805 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
806 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_packDepthSkinned
807 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
808 },
809 { //SmImpl::PCF
810 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
811 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
812 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
813 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
814 , 0.0035f, 0.0f, 0.01f, 0.00001f // m_bias
815 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
816 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
817 , 120.0f, 1.0f, 300.0f, 1.0f // m_customParam1
818 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
819 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
820 , 1.0f, 0.0f, 3.0f, 0.001f // m_xOffset
821 , 1.0f, 0.0f, 3.0f, 0.001f // m_yOffset
822 , true // m_doBlur
823 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
824 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackSkinned
825 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
826 },
827 { //SmImpl::PCSS
828 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
829 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
830 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
831 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
832 , 0.0035f, 0.0f, 0.01f, 0.00001f // m_bias
833 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
834 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
835 , 120.0f, 1.0f, 300.0f, 1.0f // m_customParam1
836 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
837 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
838 , 1.0f, 0.0f, 3.0f, 0.001f // m_xOffset
839 , 1.0f, 0.0f, 3.0f, 0.001f // m_yOffset
840 , true // m_doBlur
841 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
842 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackSkinned
843 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
844 },
845 { //SmImpl::VSM
846 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
847 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
848 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
849 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
850 , 0.006f, 0.0f, 0.1f, 0.00001f // m_bias
851 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
852 , 0.02f, 0.0f, 0.1f, 0.00001f // m_customParam0
853 , 400.0f, 1.0f, 900.0f, 1.0f // m_customParam1
854 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
855 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
856 , 0.25f, 0.0f, 2.0f, 0.001f // m_xOffset
857 , 0.25f, 0.0f, 2.0f, 0.001f // m_yOffset
858 , true // m_doBlur
859 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::VSM].get() //m_progPack
860 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::VSM].get() //m_progPackSkinned
861 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::VSM].get() //m_progPackInstanced
862 },
863 { //SmImpl::ESM
864 12.0f, 9.0f, 12.0f, 1.0f // m_sizePwrTwo
865 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
866 , 1.0f, 1.0f, 10.0f, 0.01f // m_near
867 , 250.0f, 100.0f, 2000.0f, 50.0f // m_far
868 , 0.007f, 0.0f, 0.01f, 0.00001f // m_bias
869 , 0.001f, 0.0f, 0.05f, 0.00001f // m_normalOffset
870 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
871 , 8000.0f, 1.0f, 15000.0f, 1.0f // m_customParam1
872 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
873 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
874 , 0.25f, 0.0f, 2.0f, 0.001f // m_xOffset
875 , 0.25f, 0.0f, 2.0f, 0.001f // m_yOffset
876 , true // m_doBlur
877 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
878 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_packDepthSkinned
879 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
880 }
881
882 }
883
884 },
885 { //LightType::Directional
886
887 { //DepthImpl::InvZ
888
889 { //SmImpl::Hard
890 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
891 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
892 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
893 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
894 , 0.0012f, 0.0f, 0.01f, 0.00001f // m_bias
895 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
896 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
897 , 200.0f, 1.0f, 400.0f, 1.0f // m_customParam1
898 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
899 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
900 , 0.2f, 0.0f, 1.0f, 0.01f // m_xOffset
901 , 0.2f, 0.0f, 1.0f, 0.01f // m_yOffset
902 , true // m_doBlur
903 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
904 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
905 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
906 },
907 { //SmImpl::PCF
908 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
909 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
910 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
911 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
912 , 0.0012f, 0.0f, 0.01f, 0.00001f // m_bias
913 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
914 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
915 , 200.0f, 1.0f, 400.0f, 1.0f // m_customParam1
916 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
917 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
918 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
919 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
920 , true // m_doBlur
921 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
922 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
923 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
924 },
925 { //SmImpl::PCSS
926 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
927 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
928 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
929 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
930 , 0.0012f, 0.0f, 0.01f, 0.00001f // m_bias
931 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
932 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
933 , 200.0f, 1.0f, 400.0f, 1.0f // m_customParam1
934 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
935 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
936 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
937 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
938 , true // m_doBlur
939 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
940 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
941 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
942 },
943 { //SmImpl::VSM
944 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
945 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
946 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
947 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
948 , 0.004f, 0.0f, 0.01f, 0.00001f // m_bias
949 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
950 , 0.02f, 0.0f, 0.04f, 0.00001f // m_customParam0
951 , 2500.0f, 1.0f, 5000.0f, 1.0f // m_customParam1
952 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
953 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
954 , 0.2f, 0.0f, 1.0f, 0.01f // m_xOffset
955 , 0.2f, 0.0f, 1.0f, 0.01f // m_yOffset
956 , true // m_doBlur
957 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPack
958 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::VSM].get() //m_packDepthSkinned
959 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::VSM].get() //m_progPackInstanced
960 },
961 { //SmImpl::ESM
962 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
963 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
964 , 1.0f, 1.0f, 10.0f, 0.01f // m_near
965 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
966 , 0.004f, 0.0f, 0.01f, 0.00001f // m_bias
967 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
968 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
969 , 9500.0f, 1.0f, 15000.0f, 1.0f // m_customParam1
970 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
971 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
972 , 0.2f, 0.0f, 1.0f, 0.01f // m_xOffset
973 , 0.2f, 0.0f, 1.0f, 0.01f // m_yOffset
974 , true // m_doBlur
975 , programs_.m_packDepth[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPack
976 , programs_.m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA].get() //m_packDepthSkinned
977 , programs_.m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA].get() //m_progPackInstanced
978 }
979
980 },
981 { //DepthImpl::Linear
982
983 { //SmImpl::Hard
984 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
985 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
986 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
987 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
988 , 0.0012f, 0.0f, 0.01f, 0.00001f // m_bias
989 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
990 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
991 , 500.0f, 1.0f, 1000.0f, 1.0f // m_customParam1
992 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
993 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
994 , 0.2f, 0.0f, 1.0f, 0.01f // m_xOffset
995 , 0.2f, 0.0f, 1.0f, 0.01f // m_yOffset
996 , true // m_doBlur
997 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
998 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_packDepthSkinned
999 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
1000 },
1001 { //SmImpl::PCF
1002 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
1003 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
1004 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
1005 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
1006 , 0.0012f, 0.0f, 0.01f, 0.00001f // m_bias
1007 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
1008 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
1009 , 200.0f, 1.0f, 400.0f, 1.0f // m_customParam1
1010 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
1011 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
1012 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
1013 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
1014 , true // m_doBlur
1015 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
1016 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackSkinned
1017 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
1018 },
1019 { //SmImpl::PCSS
1020 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
1021 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
1022 , 1.0f, 1.0f, 99.0f, 1.0f // m_near
1023 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
1024 , 0.0012f, 0.0f, 0.01f, 0.00001f // m_bias
1025 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
1026 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
1027 , 200.0f, 1.0f, 400.0f, 1.0f // m_customParam1
1028 , 2.0f, 0.0f, 8.0f, 1.0f // m_xNum
1029 , 2.0f, 0.0f, 8.0f, 1.0f // m_yNum
1030 , 1.0f, 0.0f, 3.0f, 0.01f // m_xOffset
1031 , 1.0f, 0.0f, 3.0f, 0.01f // m_yOffset
1032 , true // m_doBlur
1033 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
1034 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackSkinned
1035 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
1036 },
1037 { //SmImpl::VSM
1038 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
1039 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
1040 , 1.0f, 1.0f, 10.0f, 1.0f // m_near
1041 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
1042 , 0.004f, 0.0f, 0.01f, 0.00001f // m_bias
1043 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
1044 , 0.02f, 0.0f, 0.04f, 0.00001f // m_customParam0
1045 , 2500.0f, 1.0f, 5000.0f, 1.0f // m_customParam1
1046 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
1047 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
1048 , 0.2f, 0.0f, 1.0f, 0.01f // m_xOffset
1049 , 0.2f, 0.0f, 1.0f, 0.01f // m_yOffset
1050 , true // m_doBlur
1051 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::VSM].get() //m_progPack
1052 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::VSM].get() //m_progPackSkinned
1053 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::VSM].get() //m_progPackInstanced
1054 },
1055 { //SmImpl::ESM
1056 11.0f, 7.0f, 12.0f, 1.0f // m_sizePwrTwo
1057 , 1.0f, 1.0f, 20.0f, 1.0f // m_depthValuePow
1058 , 1.0f, 1.0f, 10.0f, 0.01f // m_near
1059 , 550.0f, 100.0f, 2000.0f, 50.0f // m_far
1060 , 0.004f, 0.0f, 0.01f, 0.00001f // m_bias
1061 , 0.001f, 0.0f, 0.04f, 0.00001f // m_normalOffset
1062 , 0.7f, 0.0f, 1.0f, 0.01f // m_customParam0
1063 , 9500.0f, 1.0f, 15000.0f, 1.0f // m_customParam1
1064 , 2.0f, 0.0f, 4.0f, 1.0f // m_xNum
1065 , 2.0f, 0.0f, 4.0f, 1.0f // m_yNum
1066 , 0.2f, 0.0f, 1.0f, 0.01f // m_xOffset
1067 , 0.2f, 0.0f, 1.0f, 0.01f // m_yOffset
1068 , true // m_doBlur
1069 , programs_.m_packDepth[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPack
1070 , programs_.m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA].get() //m_packDepthSkinned
1071 , programs_.m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA].get() //m_progPackInstanced
1072 }
1073
1074 }
1075 }
1076 };
1077 // clang-format on
1078 bx::memCopy(sm_settings_, smSettings, sizeof(smSettings));
1079
1081 settings_.m_depthImpl = DepthImpl::InvZ;
1082 settings_.m_smImpl = SmImpl::Hard;
1083 settings_.m_spotOuterAngle = 45.0f;
1084 settings_.m_spotInnerAngle = 30.0f;
1085 settings_.m_fovXAdjust = 0.0f;
1086 settings_.m_fovYAdjust = 0.0f;
1087 settings_.m_coverageSpotL = 90.0f;
1088 settings_.m_splitDistribution = 0.6f;
1089 settings_.m_numSplits = 4;
1090 settings_.m_updateLights = true;
1091 settings_.m_updateScene = true;
1092 settings_.m_drawDepthBuffer = false;
1093 settings_.m_showSmCoverage = false;
1094 settings_.m_stencilPack = true;
1095 settings_.m_stabilize = true;
1096}
1097
1099{
1100 PackDepth::Enum depthType = (SmImpl::VSM == settings_.m_smImpl) ? PackDepth::VSM : PackDepth::RGBA;
1101 return depthType;
1102}
1103
1104auto shadowmap_generator::get_rt_texture(uint8_t split) const -> bgfx::TextureHandle
1105{
1106 if(!bgfx::isValid(rt_shadow_map_[split]))
1107 {
1108 return {bgfx::kInvalidHandle};
1109 }
1110
1111 return bgfx::getTexture(rt_shadow_map_[split]);
1112}
1113
1115{
1116 return programs_.m_drawDepth[depth];
1117}
1118
1120{
1121 if(!bgfx::isValid(tex_color_))
1122 {
1123 return;
1124 }
1125 // uniforms_.submitConstUniforms();
1126 // uniforms_.submitPerFrameUniforms();
1127 uniforms_.submitPerDrawUniforms();
1128
1129 for(uint8_t ii = 0; ii < ShadowMapRenderTargets::Count; ++ii)
1130 {
1131 if(!bgfx::isValid(rt_shadow_map_[ii]))
1132 {
1133 continue;
1134 }
1135
1136 bgfx::setTexture(stage + ii, shadow_map_[ii], bgfx::getTexture(rt_shadow_map_[ii]));
1137 }
1138}
1139
1141{
1142 return last_update_ == gfx::get_render_frame();
1143}
1144
1145void shadowmap_generator::update(const camera& cam, const light& l, const math::transform& ltrans, bool is_active)
1146{
1147 last_update_ = gfx::get_render_frame();
1148
1149 if(!l.casts_shadows)
1150 {
1152 return;
1153 }
1154
1155 bool recreateTextures = false;
1156 recreateTextures |= !valid_;
1157
1158 valid_ = true;
1159
1160 const auto& pos = ltrans.get_position();
1161 const auto& dir = ltrans.z_unit_axis();
1162 point_light_.m_position.m_x = pos.x;
1163 point_light_.m_position.m_y = pos.y;
1164 point_light_.m_position.m_z = pos.z;
1165
1166 point_light_.m_spotDirectionInner.m_x = dir.x;
1167 point_light_.m_spotDirectionInner.m_y = dir.y;
1168 point_light_.m_spotDirectionInner.m_z = dir.z;
1169
1170 directional_light_.m_position.m_x = dir.x;
1171 directional_light_.m_position.m_y = dir.y;
1172 directional_light_.m_position.m_z = dir.z;
1173
1174 auto last_settings = settings_;
1175
1176 settings_.m_lightType = convert(l.type);
1177 settings_.m_smImpl = convert(l.shadow_params.type);
1178 settings_.m_depthImpl = convert(l.shadow_params.depth);
1179
1180 settings_.m_showSmCoverage = l.shadow_params.show_coverage;
1181
1182 switch(l.type)
1183 {
1184 case light_type::spot:
1185 settings_.m_spotOuterAngle = l.spot_data.get_outer_angle();
1186 settings_.m_spotInnerAngle = l.spot_data.get_inner_angle();
1187 settings_.m_coverageSpotL = settings_.m_spotOuterAngle;
1188 break;
1189 case light_type::point:
1190 settings_.m_stencilPack = l.point_shadow_params.stencil_pack;
1191 settings_.m_fovXAdjust = l.point_shadow_params.fov_x_adjust;
1192 settings_.m_fovYAdjust = l.point_shadow_params.fov_y_adjust;
1193
1194 break;
1195 default:
1196 settings_.m_splitDistribution = l.directional_shadow_params.split_distribution;
1197 settings_.m_numSplits = l.directional_shadow_params.num_splits;
1198 settings_.m_stabilize = l.directional_shadow_params.stabilize;
1199
1200 break;
1201 }
1202
1203#define SET_CLAMPED_VAL(x, val) x = val // math::clamp(val, x##Min, x##Max)
1204
1205 ShadowMapSettings* currentSmSettings =
1206 &sm_settings_[settings_.m_lightType][settings_.m_depthImpl][settings_.m_smImpl];
1207
1208 SET_CLAMPED_VAL(currentSmSettings->m_sizePwrTwo, convert(l.shadow_params.resolution));
1209 SET_CLAMPED_VAL(currentSmSettings->m_near, l.shadow_params.near_plane);
1210 SET_CLAMPED_VAL(currentSmSettings->m_bias, l.shadow_params.bias);
1211 SET_CLAMPED_VAL(currentSmSettings->m_normalOffset, l.shadow_params.normal_bias);
1212
1213 switch(l.shadow_params.type)
1214 {
1215 case sm_impl::hard:
1216 currentSmSettings->m_doBlur = false;
1217
1218 SET_CLAMPED_VAL(currentSmSettings->m_xOffset, 1);
1219 SET_CLAMPED_VAL(currentSmSettings->m_yOffset, 1);
1220 break;
1221 case sm_impl::pcf:
1222 currentSmSettings->m_doBlur = false;
1223 SET_CLAMPED_VAL(currentSmSettings->m_xOffset, l.shadow_params.pcf.x_offset);
1224 SET_CLAMPED_VAL(currentSmSettings->m_yOffset, l.shadow_params.pcf.y_offset);
1225 break;
1226 case sm_impl::pcss:
1227 currentSmSettings->m_doBlur = false;
1228 SET_CLAMPED_VAL(currentSmSettings->m_xOffset, l.shadow_params.pcss.penumbra_x_offset);
1229 SET_CLAMPED_VAL(currentSmSettings->m_yOffset, l.shadow_params.pcss.penumbra_y_offset);
1230 break;
1231 case sm_impl::vsm:
1232 currentSmSettings->m_doBlur = l.shadow_params.vsm.do_blur;
1233 SET_CLAMPED_VAL(currentSmSettings->m_customParam0, l.shadow_params.vsm.min_variance);
1234 SET_CLAMPED_VAL(currentSmSettings->m_customParam1, l.shadow_params.vsm.depth_multiplier);
1235 SET_CLAMPED_VAL(currentSmSettings->m_xOffset, l.shadow_params.vsm.blur_x_offset);
1236 SET_CLAMPED_VAL(currentSmSettings->m_yOffset, l.shadow_params.vsm.blur_y_offset);
1237 break;
1238 case sm_impl::esm:
1239 currentSmSettings->m_doBlur = l.shadow_params.esm.do_blur;
1240 SET_CLAMPED_VAL(currentSmSettings->m_customParam0, l.shadow_params.esm.hardness);
1241 SET_CLAMPED_VAL(currentSmSettings->m_customParam1, l.shadow_params.esm.depth_multiplier);
1242 SET_CLAMPED_VAL(currentSmSettings->m_xOffset, l.shadow_params.esm.blur_x_offset);
1243 SET_CLAMPED_VAL(currentSmSettings->m_yOffset, l.shadow_params.esm.blur_y_offset);
1244 break;
1245 default:
1246 break;
1247 }
1248
1249 switch(l.type)
1250 {
1251 case light_type::spot:
1252 SET_CLAMPED_VAL(currentSmSettings->m_far, l.spot_data.range);
1253 break;
1254 case light_type::point:
1255 SET_CLAMPED_VAL(currentSmSettings->m_far, l.point_data.range);
1256 break;
1257 default:
1258 SET_CLAMPED_VAL(currentSmSettings->m_far, l.shadow_params.far_plane);
1259 break;
1260 }
1261
1262 if(LightType::SpotLight == settings_.m_lightType)
1263 {
1264 point_light_.m_spotDirectionInner.m_inner = settings_.m_spotInnerAngle;
1265 }
1266
1267 // Update render target size.
1268 uint16_t shadowMapSize = 1 << uint32_t(currentSmSettings->m_sizePwrTwo);
1269 recreateTextures |= current_shadow_map_size_ != shadowMapSize;
1270 recreateTextures |= last_settings.m_smImpl != settings_.m_smImpl;
1271 recreateTextures |= last_settings.m_numSplits != settings_.m_numSplits;
1272 recreateTextures |= last_settings.m_lightType != settings_.m_lightType;
1273
1274 if(recreateTextures)
1275 {
1276 current_shadow_map_size_ = shadowMapSize;
1277
1278 if(bgfx::isValid(rt_shadow_map_[0]))
1279 {
1280 bgfx::destroy(rt_shadow_map_[0]);
1281 rt_shadow_map_[0] = {bgfx::kInvalidHandle};
1282 }
1283
1284 {
1285 bgfx::TextureHandle fbtextures[] = {
1286 bgfx::createTexture2D(current_shadow_map_size_,
1287 current_shadow_map_size_,
1288 false,
1289 1,
1290 bgfx::TextureFormat::BGRA8,
1291 BGFX_TEXTURE_RT),
1292 bgfx::createTexture2D(current_shadow_map_size_,
1293 current_shadow_map_size_,
1294 false,
1295 1,
1296 bgfx::TextureFormat::D24S8,
1297 BGFX_TEXTURE_RT),
1298 };
1299 bgfx::Attachment attachments[2];
1300 attachments[0].init(fbtextures[0], bgfx::Access::Write, 0, 1, 0, BGFX_RESOLVE_AUTO_GEN_MIPS);
1301 attachments[1].init(fbtextures[1], bgfx::Access::Write, 0, 1, 0, BGFX_RESOLVE_NONE);
1302
1303 rt_shadow_map_[0] = bgfx::createFrameBuffer(BX_COUNTOF(attachments), attachments, true);
1304 }
1305
1306 // if(LightType::DirectionalLight == settings_.m_lightType)
1307 {
1308 for(uint8_t ii = 1; ii < ShadowMapRenderTargets::Count; ++ii)
1309 {
1310 if(bgfx::isValid(rt_shadow_map_[ii]))
1311 {
1312 bgfx::destroy(rt_shadow_map_[ii]);
1313 rt_shadow_map_[ii] = {bgfx::kInvalidHandle};
1314 }
1315
1316 if(ii < settings_.m_numSplits)
1317 {
1318 bgfx::TextureHandle fbtextures[] = {
1319 bgfx::createTexture2D(current_shadow_map_size_,
1320 current_shadow_map_size_,
1321 false,
1322 1,
1323 bgfx::TextureFormat::BGRA8,
1324 BGFX_TEXTURE_RT),
1325 bgfx::createTexture2D(current_shadow_map_size_,
1326 current_shadow_map_size_,
1327 false,
1328 1,
1329 bgfx::TextureFormat::D24S8,
1330 BGFX_TEXTURE_RT),
1331 };
1332
1333 bgfx::Attachment attachments[2];
1334 attachments[0].init(fbtextures[0], bgfx::Access::Write, 0, 1, 0, BGFX_RESOLVE_AUTO_GEN_MIPS);
1335 attachments[1].init(fbtextures[1], bgfx::Access::Write, 0, 1, 0, BGFX_RESOLVE_NONE);
1336
1337 rt_shadow_map_[ii] = bgfx::createFrameBuffer(BX_COUNTOF(attachments), attachments, true);
1338 }
1339 }
1340 }
1341
1342 if(bgfx::isValid(rt_blur_))
1343 {
1344 bgfx::destroy(rt_blur_);
1345 rt_blur_ = {bgfx::kInvalidHandle};
1346 }
1347
1348 bool bVsmOrEsm = (SmImpl::VSM == settings_.m_smImpl) || (SmImpl::ESM == settings_.m_smImpl);
1349
1350 // Blur shadow map.
1351 if(bVsmOrEsm && currentSmSettings->m_doBlur)
1352 {
1353 rt_blur_ = bgfx::createFrameBuffer(current_shadow_map_size_, current_shadow_map_size_, bgfx::TextureFormat::BGRA8);
1354 }
1355 }
1356
1357 float currentShadowMapSizef = float(int16_t(current_shadow_map_size_));
1358
1359 // Update uniforms.
1360
1361 uniforms_.m_shadowMapTexelSize = 1.0f / currentShadowMapSizef;
1362 uniforms_.m_shadowMapBias = currentSmSettings->m_bias;
1363 uniforms_.m_shadowMapOffset = currentSmSettings->m_normalOffset;
1364 uniforms_.m_shadowMapParam0 = currentSmSettings->m_customParam0;
1365 uniforms_.m_shadowMapParam1 = currentSmSettings->m_customParam1;
1366 uniforms_.m_depthValuePow = currentSmSettings->m_depthValuePow;
1367 uniforms_.m_XNum = currentSmSettings->m_xNum;
1368 uniforms_.m_YNum = currentSmSettings->m_yNum;
1369 uniforms_.m_XOffset = currentSmSettings->m_xOffset;
1370 uniforms_.m_YOffset = currentSmSettings->m_yOffset;
1371 uniforms_.m_showSmCoverage = float(settings_.m_showSmCoverage);
1372 uniforms_.m_numSplits = float(settings_.m_numSplits);
1373 uniforms_.m_lightPtr = (LightType::DirectionalLight == settings_.m_lightType) ? &directional_light_ : &point_light_;
1374
1376 bool homogeneousDepth = gfx::is_homogeneous_depth();
1377 bool originBottomLeft = gfx::is_origin_bottom_left();
1378
1379 // Compute transform matrices.
1380 auto& lightView = light_view_;
1381 auto& lightProj = light_proj_;
1382 auto& lightFrustums = light_frustums_;
1383
1384 float mtxYpr[TetrahedronFaces::Count][16];
1385
1386 if(LightType::SpotLight == settings_.m_lightType)
1387 {
1388 const float fovy = settings_.m_coverageSpotL;
1389 const float aspect = 1.0f;
1390 bx::mtxProj(lightProj[ProjType::Horizontal],
1391 fovy,
1392 aspect,
1393 currentSmSettings->m_near,
1394 currentSmSettings->m_far,
1395 false);
1396
1397 // For linear depth, prevent depth division by variable w-component in shaders and divide here by far plane
1398 if(DepthImpl::Linear == settings_.m_depthImpl)
1399 {
1400 lightProj[ProjType::Horizontal][10] /= currentSmSettings->m_far;
1401 lightProj[ProjType::Horizontal][14] /= currentSmSettings->m_far;
1402 }
1403
1404 const bx::Vec3 at = bx::add(bx::load<bx::Vec3>(point_light_.m_position.m_v),
1405 bx::load<bx::Vec3>(point_light_.m_spotDirectionInner.m_v));
1406 bx::mtxLookAt(lightView[TetrahedronFaces::Green], bx::load<bx::Vec3>(point_light_.m_position.m_v), at);
1407 }
1408 else if(LightType::PointLight == settings_.m_lightType)
1409 {
1410 float ypr[TetrahedronFaces::Count][3] = {
1411 {bx::toRad(0.0f), bx::toRad(27.36780516f), bx::toRad(0.0f)},
1412 {bx::toRad(180.0f), bx::toRad(27.36780516f), bx::toRad(0.0f)},
1413 {bx::toRad(-90.0f), bx::toRad(-27.36780516f), bx::toRad(0.0f)},
1414 {bx::toRad(90.0f), bx::toRad(-27.36780516f), bx::toRad(0.0f)},
1415 };
1416
1417 if(settings_.m_stencilPack)
1418 {
1419 const float fovx = 143.98570868f + 3.51f + settings_.m_fovXAdjust;
1420 const float fovy = 125.26438968f + 9.85f + settings_.m_fovYAdjust;
1421 const float aspect = bx::tan(bx::toRad(fovx * 0.5f)) / bx::tan(bx::toRad(fovy * 0.5f));
1422
1423 bx::mtxProj(lightProj[ProjType::Vertical],
1424 fovx,
1425 aspect,
1426 currentSmSettings->m_near,
1427 currentSmSettings->m_far,
1428 false);
1429
1430 // For linear depth, prevent depth division by variable w-component in shaders and divide here by far plane
1431 if(DepthImpl::Linear == settings_.m_depthImpl)
1432 {
1433 lightProj[ProjType::Vertical][10] /= currentSmSettings->m_far;
1434 lightProj[ProjType::Vertical][14] /= currentSmSettings->m_far;
1435 }
1436
1437 ypr[TetrahedronFaces::Green][2] = bx::toRad(180.0f);
1438 ypr[TetrahedronFaces::Yellow][2] = bx::toRad(0.0f);
1439 ypr[TetrahedronFaces::Blue][2] = bx::toRad(90.0f);
1440 ypr[TetrahedronFaces::Red][2] = bx::toRad(-90.0f);
1441 }
1442
1443 const float fovx = 143.98570868f + 7.8f + settings_.m_fovXAdjust;
1444 const float fovy = 125.26438968f + 3.0f + settings_.m_fovYAdjust;
1445 const float aspect = bx::tan(bx::toRad(fovx * 0.5f)) / bx::tan(bx::toRad(fovy * 0.5f));
1446
1447 bx::mtxProj(lightProj[ProjType::Horizontal],
1448 fovy,
1449 aspect,
1450 currentSmSettings->m_near,
1451 currentSmSettings->m_far,
1452 homogeneousDepth);
1453
1454 // For linear depth, prevent depth division by variable w component in shaders and divide here by far plane
1455 if(DepthImpl::Linear == settings_.m_depthImpl)
1456 {
1457 lightProj[ProjType::Horizontal][10] /= currentSmSettings->m_far;
1458 lightProj[ProjType::Horizontal][14] /= currentSmSettings->m_far;
1459 }
1460
1461 for(uint8_t ii = 0; ii < TetrahedronFaces::Count; ++ii)
1462 {
1463 float mtxTmp[16];
1464 mtxYawPitchRoll(mtxTmp, ypr[ii][0], ypr[ii][1], ypr[ii][2]);
1465
1466 float tmp[3] = {
1467 -bx::dot(bx::load<bx::Vec3>(point_light_.m_position.m_v), bx::load<bx::Vec3>(&mtxTmp[0])),
1468 -bx::dot(bx::load<bx::Vec3>(point_light_.m_position.m_v), bx::load<bx::Vec3>(&mtxTmp[4])),
1469 -bx::dot(bx::load<bx::Vec3>(point_light_.m_position.m_v), bx::load<bx::Vec3>(&mtxTmp[8])),
1470 };
1471
1472 bx::mtxTranspose(mtxYpr[ii], mtxTmp);
1473
1474 bx::memCopy(lightView[ii], mtxYpr[ii], 12 * sizeof(float));
1475 lightView[ii][12] = tmp[0];
1476 lightView[ii][13] = tmp[1];
1477 lightView[ii][14] = tmp[2];
1478 lightView[ii][15] = 1.0f;
1479 }
1480 }
1481 else // LightType::DirectionalLight == m_settings.m_lightType
1482 {
1483 // ============================================================================
1484 // Cascaded Shadow Map calculation based on GPU Gems 3, Chapter 10
1485 // ============================================================================
1486
1487 const uint8_t maxNumSplits = 4;
1488 BX_ASSERT(maxNumSplits >= settings_.m_numSplits, "Error! Max num splits.");
1489
1490 // Get camera parameters
1491 const float nearClip = currentSmSettings->m_near;
1492 const float farClip = currentSmSettings->m_far;
1493 const float clipRange = farClip - nearClip;
1494
1495 // Calculate cascade split depths using GPU Gems 3 logarithmic/uniform blend
1496 float cascadeSplits[maxNumSplits];
1497 calculateCascadeSplits(cascadeSplits,
1498 uint8_t(settings_.m_numSplits),
1499 nearClip,
1500 farClip,
1501 settings_.m_splitDistribution);
1502
1503 // Light direction (normalized) - this is the direction the light travels
1504 const bx::Vec3 lightDir = bx::normalize(bx::Vec3{
1505 directional_light_.m_position.m_x,
1506 directional_light_.m_position.m_y,
1507 directional_light_.m_position.m_z
1508 });
1509
1510 const bx::Vec3 lightEye = {0.0f, 0.0f, 0.0f};
1511 const bx::Vec3 lightAt = lightDir; // Look along light direction
1512
1513 // Use +Y as default up vector, falling back to +Z if light is nearly vertical
1514 bx::Vec3 upVec = {0.0f, 1.0f, 0.0f};
1515 if(bx::abs(bx::dot(lightDir, upVec)) > 0.99f)
1516 {
1517 upVec = {0.0f, 0.0f, 1.0f};
1518 }
1519
1520 // All cascades share the same light view matrix (camera position independent)
1521 bx::mtxLookAt(lightView[0], lightEye, lightAt, upVec);
1522
1523 // Create base orthographic projection (will be adjusted by crop matrices)
1524 float mtxProj[16];
1525 bx::mtxOrtho(mtxProj,
1526 -1.0f, 1.0f, // left, right
1527 -1.0f, 1.0f, // bottom, top
1528 -currentSmSettings->m_far,
1529 currentSmSettings->m_far,
1530 0.0f,
1531 homogeneousDepth);
1532
1533 // Get camera inverse view matrix for frustum corner calculation
1534 const auto& mtxViewInv = cam.get_view_inverse();
1535
1536 // Process each cascade
1537 const uint8_t numCorners = 8;
1538 float frustumCorners[maxNumSplits][numCorners][3];
1539 float lastSplitDist = 0.0f;
1540
1541 for(uint8_t ii = 0; ii < settings_.m_numSplits; ++ii)
1542 {
1543 const float splitDist = cascadeSplits[ii];
1544
1545 const float cascadeNear = nearClip + lastSplitDist * clipRange;
1546 const float cascadeFar = nearClip + splitDist * clipRange;
1547
1548 // Update cascade far distance uniform (use original split distance for shader blend)
1549 uniforms_.m_csmFarDistances[ii] = cascadeFar;
1550
1551 // Compute frustum corners in view space then transform to world space
1552 const float camFovy = cam.get_fov();
1553 const float camAspect = cam.get_aspect_ratio();
1554 const float projHeight = bx::tan(bx::toRad(camFovy) * 0.5f);
1555 const float projWidth = projHeight * camAspect;
1556
1557 // Compute frustum corners for one split in world space.
1558 worldSpaceFrustumCorners((float*)frustumCorners[ii], cascadeNear, cascadeFar, projWidth, projHeight, mtxViewInv);
1559
1560 bx::Vec3 min = {9000.0f, 9000.0f, 9000.0f};
1561 bx::Vec3 max = {-9000.0f, -9000.0f, -9000.0f};
1562 float frustum_radius = 0.0f;
1563
1564 // Calculate frustum center in world space
1565 bx::Vec3 frustumCenter = {0.0f, 0.0f, 0.0f};
1566 for(uint8_t jj = 0; jj < numCorners; ++jj)
1567 {
1568 // Transform from view space to world space
1569 const bx::Vec3 worldCorner = bx::load<bx::Vec3>(&frustumCorners[ii][jj]);
1570 frustumCenter = bx::add(frustumCenter, worldCorner);
1571 }
1572 frustumCenter = bx::mul(frustumCenter, 1.0f / float(numCorners));
1573
1574
1575 // Transform center to light space for radius calculation
1576 const bx::Vec3 lightSpaceCenter = bx::mul(frustumCenter, lightView[0]);
1577
1578 // Transform corners to light space and compute bounds
1579 for(uint8_t jj = 0; jj < numCorners; ++jj)
1580 {
1581 const bx::Vec3 xyz = bx::mul(bx::load<bx::Vec3>(frustumCorners[ii][jj]), lightView[0]);
1582
1583 // Calculate distance from center for radius
1584 const float dx = xyz.x - lightSpaceCenter.x;
1585 const float dy = xyz.y - lightSpaceCenter.y;
1586 const float dz = xyz.z - lightSpaceCenter.z;
1587 const float distance = bx::sqrt(dx*dx + dy*dy + dz*dz);
1588 frustum_radius = bx::max(frustum_radius, distance);
1589
1590 // Update bounding box in light space
1591 min = bx::min(min, xyz);
1592 max = bx::max(max, xyz);
1593 }
1594
1595 // Round radius to reduce flickering
1596 frustum_radius = bx::ceil(frustum_radius * 16.0f) / 16.0f;
1597
1598 // Project bounds to the base ortho projection space
1599 const bx::Vec3 minproj = bx::mulH(min, mtxProj);
1600 const bx::Vec3 maxproj = bx::mulH(max, mtxProj);
1601
1602 // Calculate scale using radius-based approach for stability
1603 float scalex = 1.0f / frustum_radius;
1604 float scaley = 1.0f / frustum_radius;
1605
1606 if(settings_.m_stabilize)
1607 {
1608 // Quantize scale for stability
1609 const float quantizer = 64.0f;
1610 scalex = quantizer / bx::ceil(quantizer / scalex);
1611 scaley = quantizer / bx::ceil(quantizer / scaley);
1612 }
1613
1614 // Calculate offset to center the cascade in the projection
1615 float offsetx = -scalex * (minproj.x + maxproj.x) * 0.5f;
1616 float offsety = -scaley * (minproj.y + maxproj.y) * 0.5f;
1617
1618 // Apply texel snapping for stability
1619 if(settings_.m_stabilize)
1620 {
1621 float currentShadowMapSizef = float(int16_t(current_shadow_map_size_));
1622 const float halfSize = currentShadowMapSizef * 0.5f;
1623
1624 // Snap to texel grid
1625 offsetx = bx::round(offsetx * halfSize) / halfSize;
1626 offsety = bx::round(offsety * halfSize) / halfSize;
1627 }
1628
1629 // Build crop matrix to adjust the base projection for this cascade
1630 float mtxCrop[16];
1631 bx::mtxIdentity(mtxCrop);
1632 mtxCrop[0] = scalex; // x-scale
1633 mtxCrop[5] = scaley; // y-scale
1634 mtxCrop[12] = offsetx; // x-offset
1635 mtxCrop[13] = offsety; // y-offset
1636 mtxCrop[14] = -lightSpaceCenter.z; // z-offset: center depth range on cascade frustum
1637
1638 // Final projection = crop * base projection
1639 bx::mtxMul(lightProj[ii], mtxCrop, mtxProj);
1640
1641 lastSplitDist = splitDist;
1642 }
1643 }
1644
1645 if(LightType::SpotLight == settings_.m_lightType)
1646 {
1647 lightFrustums[0].update(math::make_mat4(lightView[0]),
1648 math::make_mat4(lightProj[ProjType::Horizontal]),
1649 homogeneousDepth);
1650 }
1651 else if(LightType::PointLight == settings_.m_lightType)
1652 {
1653 lightFrustums[TetrahedronFaces::Green].update(math::make_mat4(lightView[TetrahedronFaces::Green]),
1654 math::make_mat4(lightProj[ProjType::Horizontal]),
1655 homogeneousDepth);
1656
1657 lightFrustums[TetrahedronFaces::Yellow].update(math::make_mat4(lightView[TetrahedronFaces::Yellow]),
1658 math::make_mat4(lightProj[ProjType::Horizontal]),
1659 homogeneousDepth);
1660
1661 if(settings_.m_stencilPack)
1662 {
1663 lightFrustums[TetrahedronFaces::Blue].update(math::make_mat4(lightView[TetrahedronFaces::Blue]),
1664 math::make_mat4(lightProj[ProjType::Vertical]),
1665 homogeneousDepth);
1666
1667 lightFrustums[TetrahedronFaces::Red].update(math::make_mat4(lightView[TetrahedronFaces::Red]),
1668 math::make_mat4(lightProj[ProjType::Vertical]),
1669 homogeneousDepth);
1670 }
1671 else
1672 {
1673 lightFrustums[TetrahedronFaces::Blue].update(math::make_mat4(lightView[TetrahedronFaces::Blue]),
1674 math::make_mat4(lightProj[ProjType::Horizontal]),
1675 homogeneousDepth);
1676
1677 lightFrustums[TetrahedronFaces::Red].update(math::make_mat4(lightView[TetrahedronFaces::Red]),
1678 math::make_mat4(lightProj[ProjType::Horizontal]),
1679 homogeneousDepth);
1680 }
1681 }
1682 else // LightType::DirectionalLight == settings.m_lightType
1683 {
1684 lightFrustums[0].update(math::make_mat4(lightView[0]), math::make_mat4(lightProj[0]), homogeneousDepth);
1685 lightFrustums[1].update(math::make_mat4(lightView[0]), math::make_mat4(lightProj[1]), homogeneousDepth);
1686 lightFrustums[2].update(math::make_mat4(lightView[0]), math::make_mat4(lightProj[2]), homogeneousDepth);
1687 lightFrustums[3].update(math::make_mat4(lightView[0]), math::make_mat4(lightProj[3]), homogeneousDepth);
1688 }
1689
1690 // Prepare for scene.
1691 {
1692 // Setup shadow mtx.
1693 float mtxShadow[16];
1694
1695 const float ymul = (originBottomLeft) ? 0.5f : -0.5f;
1696 float zadd = (DepthImpl::Linear == settings_.m_depthImpl) ? 0.0f : 0.5f;
1697
1698 // clang-format off
1699 const float mtxBias[16] =
1700 {
1701 0.5f, 0.0f, 0.0f, 0.0f,
1702 0.0f, ymul, 0.0f, 0.0f,
1703 0.0f, 0.0f, 0.5f, 0.0f,
1704 0.5f, 0.5f, zadd, 1.0f,
1705 };
1706 // clang-format on
1707
1708 if(LightType::SpotLight == settings_.m_lightType)
1709 {
1710 float mtxTmp[16];
1711 bx::mtxMul(mtxTmp, lightProj[ProjType::Horizontal], mtxBias);
1712 bx::mtxMul(mtxShadow, lightView[0], mtxTmp); // lightViewProjBias
1713 }
1714 else if(LightType::PointLight == settings_.m_lightType)
1715 {
1716 const float s = (originBottomLeft) ? 1.0f : -1.0f; // sign
1717 zadd = (DepthImpl::Linear == settings_.m_depthImpl) ? 0.0f : 0.5f;
1718
1719 // clang-format off
1720 const float mtxCropBias[2][TetrahedronFaces::Count][16] =
1721 {
1722 { // settings.m_stencilPack == false
1723
1724 { // D3D: Green, OGL: Blue
1725 0.25f, 0.0f, 0.0f, 0.0f,
1726 0.0f, s*0.25f, 0.0f, 0.0f,
1727 0.0f, 0.0f, 0.5f, 0.0f,
1728 0.25f, 0.25f, zadd, 1.0f,
1729 },
1730 { // D3D: Yellow, OGL: Red
1731 0.25f, 0.0f, 0.0f, 0.0f,
1732 0.0f, s*0.25f, 0.0f, 0.0f,
1733 0.0f, 0.0f, 0.5f, 0.0f,
1734 0.75f, 0.25f, zadd, 1.0f,
1735 },
1736 { // D3D: Blue, OGL: Green
1737 0.25f, 0.0f, 0.0f, 0.0f,
1738 0.0f, s*0.25f, 0.0f, 0.0f,
1739 0.0f, 0.0f, 0.5f, 0.0f,
1740 0.25f, 0.75f, zadd, 1.0f,
1741 },
1742 { // D3D: Red, OGL: Yellow
1743 0.25f, 0.0f, 0.0f, 0.0f,
1744 0.0f, s*0.25f, 0.0f, 0.0f,
1745 0.0f, 0.0f, 0.5f, 0.0f,
1746 0.75f, 0.75f, zadd, 1.0f,
1747 },
1748 },
1749 { // settings.m_stencilPack == true
1750
1751 { // D3D: Red, OGL: Blue
1752 0.25f, 0.0f, 0.0f, 0.0f,
1753 0.0f, s*0.5f, 0.0f, 0.0f,
1754 0.0f, 0.0f, 0.5f, 0.0f,
1755 0.25f, 0.5f, zadd, 1.0f,
1756 },
1757 { // D3D: Blue, OGL: Red
1758 0.25f, 0.0f, 0.0f, 0.0f,
1759 0.0f, s*0.5f, 0.0f, 0.0f,
1760 0.0f, 0.0f, 0.5f, 0.0f,
1761 0.75f, 0.5f, zadd, 1.0f,
1762 },
1763 { // D3D: Green, OGL: Green
1764 0.5f, 0.0f, 0.0f, 0.0f,
1765 0.0f, s*0.25f, 0.0f, 0.0f,
1766 0.0f, 0.0f, 0.5f, 0.0f,
1767 0.5f, 0.75f, zadd, 1.0f,
1768 },
1769 { // D3D: Yellow, OGL: Yellow
1770 0.5f, 0.0f, 0.0f, 0.0f,
1771 0.0f, s*0.25f, 0.0f, 0.0f,
1772 0.0f, 0.0f, 0.5f, 0.0f,
1773 0.5f, 0.25f, zadd, 1.0f,
1774 },
1775 }
1776 };
1777 // clang-format on
1778
1779 // clang-format off
1780 // Use as: [stencilPack][flipV][tetrahedronFace]
1781 static const uint8_t cropBiasIndices[2][2][4] =
1782 {
1783 { // settings.m_stencilPack == false
1784 { 0, 1, 2, 3 }, //flipV == false
1785 { 2, 3, 0, 1 }, //flipV == true
1786 },
1787 { // settings.m_stencilPack == true
1788 { 3, 2, 0, 1 }, //flipV == false
1789 { 2, 3, 0, 1 }, //flipV == true
1790 },
1791 };
1792 // clang-format on
1793
1794 for(uint8_t ii = 0; ii < TetrahedronFaces::Count; ++ii)
1795 {
1796 ProjType::Enum projType = (settings_.m_stencilPack) ? ProjType::Enum(ii > 1) : ProjType::Horizontal;
1797 uint8_t biasIndex = cropBiasIndices[settings_.m_stencilPack][uint8_t(originBottomLeft)][ii];
1798
1799 float mtxTmp[16];
1800 bx::mtxMul(mtxTmp, mtxYpr[ii], lightProj[projType]);
1801 bx::mtxMul(shadow_map_mtx_[ii],
1802 mtxTmp,
1803 mtxCropBias[settings_.m_stencilPack][biasIndex]); // mtxYprProjBias
1804 }
1805
1806 bx::mtxTranslate(mtxShadow // lightInvTranslate
1807 ,
1808 -point_light_.m_position.m_v[0],
1809 -point_light_.m_position.m_v[1],
1810 -point_light_.m_position.m_v[2]);
1811 }
1812 else // LightType::DirectionalLight == settings.m_lightType
1813 {
1814 // Use per-cascade light view matrices for improved cascade stability
1815 for(uint8_t ii = 0; ii < settings_.m_numSplits; ++ii)
1816 {
1817 float mtxTmp[16];
1818
1819 bx::mtxMul(mtxTmp, lightProj[ii], mtxBias);
1820 bx::mtxMul(shadow_map_mtx_[ii], lightView[0], mtxTmp); // lViewProjCropBias
1821 }
1822 }
1823
1825 {
1826 float tmp[16];
1827 bx::mtxIdentity(tmp);
1828
1829 bx::mtxMul(light_mtx_, tmp, mtxShadow);
1830 }
1831 }
1832}
1833
1835{
1836 auto& lightView = light_view_;
1837 auto& lightProj = light_proj_;
1838 auto& lightFrustums = light_frustums_;
1839
1840 bool homogeneousDepth = gfx::is_homogeneous_depth();
1841 bool originBottomLeft = gfx::is_origin_bottom_left();
1842
1843 float screenProj[16];
1844 float screenView[16];
1845 bx::mtxIdentity(screenView);
1846
1847 bx::mtxOrtho(screenProj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f, 0.0f, gfx::get_caps()->homogeneousDepth);
1848
1849
1851 gfx::render_pass shadowmap_pass_0("ShadowmapPass 0");
1852 gfx::render_pass shadowmap_pass_1("Shadowmap Pass 1");
1853 gfx::render_pass shadowmap_pass_2("Shadowmap Pass 2");
1854 gfx::render_pass shadowmap_pass_3("Shadowmap Pass 3");
1855 gfx::render_pass shadowmap_pass_4("Shadowmap Pass 4");
1856 gfx::render_pass shadowmap_vblur_pass_0("Shadowmap VBlur Pass 0");
1857 gfx::render_pass shadowmap_hblur_pass_0("Shadowmap HBlur Pass 0");
1858 gfx::render_pass shadowmap_vblur_pass_1("Shadowmap VBlur Pass 1");
1859 gfx::render_pass shadowmap_hblur_pass_1("Shadowmap HBlur Pass 1");
1860 gfx::render_pass shadowmap_vblur_pass_2("Shadowmap VBlur Pass 2");
1861 gfx::render_pass shadowmap_hblur_pass_2("Shadowmap HBlur Pass 2");
1862 gfx::render_pass shadowmap_vblur_pass_3("Shadowmap VBlur Pass 3");
1863 gfx::render_pass shadowmap_hblur_pass_3("Shadowmap HBlur Pass 3");
1864
1865 auto RENDERVIEW_SHADOWMAP_0_ID = shadowmap_pass_0.id;
1866 auto RENDERVIEW_SHADOWMAP_1_ID = shadowmap_pass_1.id;
1867 auto RENDERVIEW_SHADOWMAP_2_ID = shadowmap_pass_2.id;
1868 auto RENDERVIEW_SHADOWMAP_3_ID = shadowmap_pass_3.id;
1869 auto RENDERVIEW_SHADOWMAP_4_ID = shadowmap_pass_4.id;
1870 auto RENDERVIEW_VBLUR_0_ID = shadowmap_vblur_pass_0.id;
1871 auto RENDERVIEW_HBLUR_0_ID = shadowmap_hblur_pass_0.id;
1872 auto RENDERVIEW_VBLUR_1_ID = shadowmap_vblur_pass_1.id;
1873 auto RENDERVIEW_HBLUR_1_ID = shadowmap_hblur_pass_1.id;
1874 auto RENDERVIEW_VBLUR_2_ID = shadowmap_vblur_pass_2.id;
1875 auto RENDERVIEW_HBLUR_2_ID = shadowmap_hblur_pass_2.id;
1876 auto RENDERVIEW_VBLUR_3_ID = shadowmap_vblur_pass_3.id;
1877 auto RENDERVIEW_HBLUR_3_ID = shadowmap_hblur_pass_3.id;
1878
1879 if(LightType::SpotLight == settings_.m_lightType)
1880 {
1888 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1889 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_1_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1890 bgfx::setViewRect(RENDERVIEW_VBLUR_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1891 bgfx::setViewRect(RENDERVIEW_HBLUR_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1892
1893 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_0_ID, screenView, screenProj);
1894 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_1_ID, lightView[0], lightProj[ProjType::Horizontal]);
1895 bgfx::setViewTransform(RENDERVIEW_VBLUR_0_ID, screenView, screenProj);
1896 bgfx::setViewTransform(RENDERVIEW_HBLUR_0_ID, screenView, screenProj);
1897
1898 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_0_ID, rt_shadow_map_[0]);
1899 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_1_ID, rt_shadow_map_[0]);
1900 bgfx::setViewFrameBuffer(RENDERVIEW_VBLUR_0_ID, rt_blur_);
1901 bgfx::setViewFrameBuffer(RENDERVIEW_HBLUR_0_ID, rt_shadow_map_[0]);
1902 }
1903 else if(LightType::PointLight == settings_.m_lightType)
1904 {
1915 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1916 if(settings_.m_stencilPack)
1917 {
1918 const uint16_t f = current_shadow_map_size_; // full size
1919 const uint16_t h = current_shadow_map_size_ / 2; // half size
1920 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_1_ID, 0, 0, f, h);
1921 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_2_ID, 0, h, f, h);
1922 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_3_ID, 0, 0, h, f);
1923 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_4_ID, h, 0, h, f);
1924 }
1925 else
1926 {
1927 const uint16_t h = current_shadow_map_size_ / 2; // half size
1928 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_1_ID, 0, 0, h, h);
1929 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_2_ID, h, 0, h, h);
1930 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_3_ID, 0, h, h, h);
1931 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_4_ID, h, h, h, h);
1932 }
1933 bgfx::setViewRect(RENDERVIEW_VBLUR_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1934 bgfx::setViewRect(RENDERVIEW_HBLUR_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1935
1936 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_0_ID, screenView, screenProj);
1937 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_1_ID,
1938 lightView[TetrahedronFaces::Green],
1939 lightProj[ProjType::Horizontal]);
1940
1941 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_2_ID,
1942 lightView[TetrahedronFaces::Yellow],
1943 lightProj[ProjType::Horizontal]);
1944
1945 if(settings_.m_stencilPack)
1946 {
1947 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_3_ID,
1948 lightView[TetrahedronFaces::Blue],
1949 lightProj[ProjType::Vertical]);
1950
1951 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_4_ID,
1952 lightView[TetrahedronFaces::Red],
1953 lightProj[ProjType::Vertical]);
1954 }
1955 else
1956 {
1957 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_3_ID,
1958 lightView[TetrahedronFaces::Blue],
1959 lightProj[ProjType::Horizontal]);
1960
1961 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_4_ID,
1962 lightView[TetrahedronFaces::Red],
1963 lightProj[ProjType::Horizontal]);
1964 }
1965 bgfx::setViewTransform(RENDERVIEW_VBLUR_0_ID, screenView, screenProj);
1966 bgfx::setViewTransform(RENDERVIEW_HBLUR_0_ID, screenView, screenProj);
1967
1968 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_0_ID, rt_shadow_map_[0]);
1969 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_1_ID, rt_shadow_map_[0]);
1970 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_2_ID, rt_shadow_map_[0]);
1971 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_3_ID, rt_shadow_map_[0]);
1972 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_4_ID, rt_shadow_map_[0]);
1973 bgfx::setViewFrameBuffer(RENDERVIEW_VBLUR_0_ID, rt_blur_);
1974 bgfx::setViewFrameBuffer(RENDERVIEW_HBLUR_0_ID, rt_shadow_map_[0]);
1975 }
1976 else // LightType::DirectionalLight == settings.m_lightType
1977 {
1993 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_1_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1994 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_2_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1995 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_3_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1996 bgfx::setViewRect(RENDERVIEW_SHADOWMAP_4_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1997 bgfx::setViewRect(RENDERVIEW_VBLUR_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1998 bgfx::setViewRect(RENDERVIEW_HBLUR_0_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
1999 bgfx::setViewRect(RENDERVIEW_VBLUR_1_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
2000 bgfx::setViewRect(RENDERVIEW_HBLUR_1_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
2001 bgfx::setViewRect(RENDERVIEW_VBLUR_2_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
2002 bgfx::setViewRect(RENDERVIEW_HBLUR_2_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
2003 bgfx::setViewRect(RENDERVIEW_VBLUR_3_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
2004 bgfx::setViewRect(RENDERVIEW_HBLUR_3_ID, 0, 0, current_shadow_map_size_, current_shadow_map_size_);
2005
2006 // Each cascade now has its own light view matrix for improved stability
2007 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_1_ID, lightView[0], lightProj[0]);
2008 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_2_ID, lightView[0], lightProj[1]);
2009 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_3_ID, lightView[0], lightProj[2]);
2010 bgfx::setViewTransform(RENDERVIEW_SHADOWMAP_4_ID, lightView[0], lightProj[3]);
2011
2012 bgfx::setViewTransform(RENDERVIEW_VBLUR_0_ID, screenView, screenProj);
2013 bgfx::setViewTransform(RENDERVIEW_HBLUR_0_ID, screenView, screenProj);
2014 bgfx::setViewTransform(RENDERVIEW_VBLUR_1_ID, screenView, screenProj);
2015 bgfx::setViewTransform(RENDERVIEW_HBLUR_1_ID, screenView, screenProj);
2016 bgfx::setViewTransform(RENDERVIEW_VBLUR_2_ID, screenView, screenProj);
2017 bgfx::setViewTransform(RENDERVIEW_HBLUR_2_ID, screenView, screenProj);
2018 bgfx::setViewTransform(RENDERVIEW_VBLUR_3_ID, screenView, screenProj);
2019 bgfx::setViewTransform(RENDERVIEW_HBLUR_3_ID, screenView, screenProj);
2020
2021 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_1_ID, rt_shadow_map_[0]);
2022 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_2_ID, rt_shadow_map_[1]);
2023 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_3_ID, rt_shadow_map_[2]);
2024 bgfx::setViewFrameBuffer(RENDERVIEW_SHADOWMAP_4_ID, rt_shadow_map_[3]);
2025 bgfx::setViewFrameBuffer(RENDERVIEW_VBLUR_0_ID, rt_blur_); // vblur
2026 bgfx::setViewFrameBuffer(RENDERVIEW_HBLUR_0_ID, rt_shadow_map_[0]); // hblur
2027 bgfx::setViewFrameBuffer(RENDERVIEW_VBLUR_1_ID, rt_blur_); // vblur
2028 bgfx::setViewFrameBuffer(RENDERVIEW_HBLUR_1_ID, rt_shadow_map_[1]); // hblur
2029 bgfx::setViewFrameBuffer(RENDERVIEW_VBLUR_2_ID, rt_blur_); // vblur
2030 bgfx::setViewFrameBuffer(RENDERVIEW_HBLUR_2_ID, rt_shadow_map_[2]); // hblur
2031 bgfx::setViewFrameBuffer(RENDERVIEW_VBLUR_3_ID, rt_blur_); // vblur
2032 bgfx::setViewFrameBuffer(RENDERVIEW_HBLUR_3_ID, rt_shadow_map_[3]); // hblur
2033 }
2034
2035 // Clear shadowmap rendertarget at beginning.
2036 const uint8_t flags0 = (LightType::DirectionalLight == settings_.m_lightType)
2037 ? 0
2038 : BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL;
2039
2040 bgfx::setViewClear(RENDERVIEW_SHADOWMAP_0_ID,
2041 flags0,
2042 0xfefefefe // blur fails on completely white regions
2043 ,
2044 clear_values_.clear_depth,
2045 clear_values_.clear_stencil);
2046 bgfx::touch(RENDERVIEW_SHADOWMAP_0_ID);
2047
2048 const uint8_t flags1 =
2049 (LightType::DirectionalLight == settings_.m_lightType) ? BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH : 0;
2050
2051 for(uint8_t ii = 0; ii < 4; ++ii)
2052 {
2053 bgfx::setViewClear(RENDERVIEW_SHADOWMAP_1_ID + ii,
2054 flags1,
2055 0xfefefefe // blur fails on completely white regions
2056 ,
2057 clear_values_.clear_depth,
2058 clear_values_.clear_stencil);
2059 bgfx::touch(RENDERVIEW_SHADOWMAP_1_ID + ii);
2060 }
2061
2062 // Render.
2063
2064 ShadowMapSettings* currentSmSettings =
2065 &sm_settings_[settings_.m_lightType][settings_.m_depthImpl][settings_.m_smImpl];
2066
2067 // uniforms_.submitConstUniforms();
2068 // uniforms_.submitPerFrameUniforms();
2069
2070 bool anythingDrawn = false;
2071 // Craft shadow map.
2072 {
2073 // Craft stencil mask for point light shadow map packing.
2074 if(LightType::PointLight == settings_.m_lightType && settings_.m_stencilPack)
2075 {
2076 if(6 == bgfx::getAvailTransientVertexBuffer(6, PosVertex::get_layout()))
2077 {
2078 bgfx::TransientVertexBuffer vb;
2079 bgfx::allocTransientVertexBuffer(&vb, 6, PosVertex::get_layout());
2080 PosVertex* vertex = (PosVertex*)vb.data;
2081
2082 const float min = 0.0f;
2083 const float max = 1.0f;
2084 const float center = 0.5f;
2085 const float zz = 0.0f;
2086
2087 vertex[0].m_x = min;
2088 vertex[0].m_y = min;
2089 vertex[0].m_z = zz;
2090
2091 vertex[1].m_x = max;
2092 vertex[1].m_y = min;
2093 vertex[1].m_z = zz;
2094
2095 vertex[2].m_x = center;
2096 vertex[2].m_y = center;
2097 vertex[2].m_z = zz;
2098
2099 vertex[3].m_x = center;
2100 vertex[3].m_y = center;
2101 vertex[3].m_z = zz;
2102
2103 vertex[4].m_x = max;
2104 vertex[4].m_y = max;
2105 vertex[4].m_z = zz;
2106
2107 vertex[5].m_x = min;
2108 vertex[5].m_y = max;
2109 vertex[5].m_z = zz;
2110
2111 bgfx::setState(0);
2112 bgfx::setStencil(BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(1) | BGFX_STENCIL_FUNC_RMASK(0xff) |
2113 BGFX_STENCIL_OP_FAIL_S_REPLACE | BGFX_STENCIL_OP_FAIL_Z_REPLACE |
2114 BGFX_STENCIL_OP_PASS_Z_REPLACE);
2115 bgfx::setVertexBuffer(0, &vb);
2116
2117 programs_.m_black->begin();
2118 bgfx::submit(RENDERVIEW_SHADOWMAP_0_ID, programs_.m_black->native_handle());
2119 programs_.m_black->end();
2120 }
2121 }
2122
2123 anythingDrawn =
2124 render_scene_into_shadowmap(RENDERVIEW_SHADOWMAP_1_ID, models, lightFrustums, currentSmSettings, &cam, stats);
2125 }
2126
2127 if(anythingDrawn)
2128 {
2129 PackDepth::Enum depthType = (SmImpl::VSM == settings_.m_smImpl) ? PackDepth::VSM : PackDepth::RGBA;
2130 bool bVsmOrEsm = (SmImpl::VSM == settings_.m_smImpl) || (SmImpl::ESM == settings_.m_smImpl);
2131
2132 // Blur shadow map.
2133 if(bVsmOrEsm && currentSmSettings->m_doBlur)
2134 {
2135 bgfx::setTexture(4, shadow_map_[0], bgfx::getTexture(rt_shadow_map_[0]));
2136 bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
2137 screenSpaceQuad(originBottomLeft);
2138 programs_.m_vBlur[depthType]->begin();
2139 bgfx::submit(RENDERVIEW_VBLUR_0_ID, programs_.m_vBlur[depthType]->native_handle());
2140 programs_.m_vBlur[depthType]->end();
2141
2142 bgfx::setTexture(4, shadow_map_[0], bgfx::getTexture(rt_blur_));
2143 bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
2144 screenSpaceQuad(originBottomLeft);
2145 programs_.m_hBlur[depthType]->begin();
2146 bgfx::submit(RENDERVIEW_HBLUR_0_ID, programs_.m_hBlur[depthType]->native_handle());
2147 programs_.m_hBlur[depthType]->end();
2148
2150 {
2151 for(uint8_t ii = 1, jj = 2; ii < settings_.m_numSplits; ++ii, jj += 2)
2152 {
2153 const uint8_t viewId = RENDERVIEW_VBLUR_0_ID + jj;
2154
2155 bgfx::setTexture(4, shadow_map_[0], bgfx::getTexture(rt_shadow_map_[ii]));
2156 bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
2157 screenSpaceQuad(originBottomLeft);
2158 bgfx::submit(viewId, programs_.m_vBlur[depthType]->native_handle());
2159
2160 bgfx::setTexture(4, shadow_map_[0], bgfx::getTexture(rt_blur_));
2161 bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
2162 screenSpaceQuad(originBottomLeft);
2163 bgfx::submit(viewId + 1, programs_.m_hBlur[depthType]->native_handle());
2164 }
2165 }
2166 }
2167 }
2168}
2169
2170auto shadowmap_generator::render_scene_into_shadowmap(uint8_t shadowmap_1_id,
2171 const shadow_map_models_t& models,
2172 const math::frustum lightFrustums[ShadowMapRenderTargets::Count],
2173 ShadowMapSettings* currentSmSettings,
2174 const camera* cam,
2176{
2177 bool any_rendered = false;
2178 // Draw scene into shadowmap.
2179 uint8_t drawNum;
2180 if(LightType::SpotLight == settings_.m_lightType)
2181 {
2182 drawNum = 1;
2183 }
2184 else if(LightType::PointLight == settings_.m_lightType)
2185 {
2186 drawNum = 4;
2187 }
2188 else // LightType::DirectionalLight == settings.m_lightType)
2189 {
2190 drawNum = uint8_t(settings_.m_numSplits);
2191 }
2192
2193 // Resize and clear batch collectors for each cascade/view
2194 cascade_batch_collectors_.resize(drawNum);
2195 for (auto& collector : cascade_batch_collectors_)
2196 {
2197 collector.clear();
2198 }
2199
2200 bool static_batching_enabled = batch_collector::is_static_mesh_batching_enabled();
2201
2202
2203 for(const auto& element : models)
2204 {
2205 const auto& entity = element.entity;
2206 const auto& lod_data = element.lod_data;
2207 const auto& transform_comp = entity.get<transform_component>();
2208 auto& model_comp = entity.get<model_component>();
2209
2210
2211 const auto& world_transform = transform_comp.get_transform_global();
2212 const auto& world_bounds = model_comp.get_world_bounds();
2213
2214 // For directional lights, perform additional swept AABB test using camera frustum
2215 bool should_render = true;
2216 if(LightType::DirectionalLight == settings_.m_lightType && cam != nullptr)
2217 {
2218 const auto& camera_frustum = cam->get_frustum();
2219
2220 const auto& bounds_extents = world_bounds.get_extents();
2221 const float bounds_radius = math::length(bounds_extents);
2222
2223 const math::vec3 light_direction(
2224 directional_light_.m_position.m_x,
2225 directional_light_.m_position.m_y,
2226 directional_light_.m_position.m_z
2227 );
2228 // Conservative sweep distance: allow the caster to project into the camera frustum
2229 // across the full shadow range, including its bounding radius.
2230 const float max_distance = currentSmSettings->m_far + bounds_radius;
2231
2232 should_render = camera_frustum.test_swept_aabb(world_bounds, light_direction, max_distance);
2233 }
2234
2235 if(!should_render)
2236 {
2237 continue;
2238 }
2239
2240 const auto& submesh_transforms = model_comp.get_submesh_transforms();
2241 const auto& bone_transforms = model_comp.get_bone_transforms();
2242 const auto& skinning_matrices = model_comp.get_skinning_transforms();
2243
2244 const bool is_skinned = !skinning_matrices.empty();
2245
2246
2247 const auto& model = model_comp.get_model();
2248 if(!model.is_valid())
2249 continue;
2250
2251
2252 // Check if this model can be batched (static mesh, no skinning)
2253 const bool can_batch = static_batching_enabled && !is_skinned;
2254
2255 const auto extras = model_comp.get_submit_extras(true);
2256
2257 // For CSM (directional lights), cascades are nested by distance, so anything
2258 // fully inside a nearer cascade does not need to be rendered into the farther
2259 // (larger) cascades. For point/spot lights, faces cover different directions, so
2260 // an object must be rendered to ALL faces where it is visible.
2261 const bool nested_cascades = (LightType::DirectionalLight == settings_.m_lightType);
2262
2263 if(can_batch)
2264 {
2265 // Collect into all cascades in one pass so the nested-cascade trick can be
2266 // applied at submesh granularity (a submesh fully inside a nearer cascade is
2267 // not collected into farther cascades).
2268 const bool collected = model.submit_for_shadow_batching_cascaded(cascade_batch_collectors_,
2269 drawNum,
2270 world_transform,
2271 submesh_transforms,
2272 lod_data.current_lod_index,
2273 0.0f,
2274 lightFrustums,
2275 nested_cascades,
2276 extras);
2277 if(collected)
2278 {
2279 model_comp.set_last_render_frame(gfx::get_render_frame());
2280 any_rendered = true;
2281 if(stats != nullptr)
2282 {
2283 stats->drawn_models_for_shadows++;
2284 }
2285 }
2286
2287 continue;
2288 }
2289
2290 bool counted_static_model_for_shadows = false;
2291 bool counted_skinned_model_for_shadows = false;
2292 for(uint8_t ii = 0; ii < drawNum; ++ii)
2293 {
2294 // Standard frustum culling against the pose-aware world AABB. Bind-pose local
2295 // bounds are never used for culling - they don't track node/bone animation.
2296 auto query = lightFrustums[ii].classify_aabb(world_bounds);
2297 if(query == math::volume_query::outside)
2298 {
2299 continue;
2300 }
2301
2302 const uint8_t viewId = shadowmap_1_id + ii;
2303
2304 uint8_t renderStateIndex = RenderState::ShadowMap_PackDepth;
2305 if(LightType::PointLight == settings_.m_lightType && settings_.m_stencilPack)
2306 {
2307 renderStateIndex =
2309 }
2310
2311 const auto& _renderState = render_states[renderStateIndex];
2312
2313 // Always set the last render frame for tracking
2314 model_comp.set_last_render_frame(gfx::get_render_frame());
2315
2316 // Render skinned models individually (original behavior)
2317 model::submit_callbacks callbacks;
2318 callbacks.setup_begin = [&](const model::submit_callbacks::params& submit_params)
2319 {
2320 if(stats != nullptr)
2321 {
2322 if(submit_params.skinned)
2323 {
2324 if(!counted_skinned_model_for_shadows)
2325 {
2326 stats->drawn_skinned_models_for_shadows++;
2327 counted_skinned_model_for_shadows = true;
2328 }
2329 }
2330 else if(!counted_static_model_for_shadows)
2331 {
2332 stats->drawn_models_for_shadows++;
2333 counted_static_model_for_shadows = true;
2334 }
2335 }
2336 };
2337 callbacks.setup_params_per_instance = [&](const model::submit_callbacks::params& submit_params)
2338 {
2339 uniforms_.submitPerDrawUniforms();
2340 };
2341 callbacks.setup_params_per_submesh =
2342 [&](const model::submit_callbacks::params& submit_params, const material& mat)
2343 {
2344 if(mat.is<pbr_material>() && !static_cast<const pbr_material&>(mat).casts_shadow())
2345 {
2346 return;
2347 }
2348
2349 if(stats != nullptr)
2350 {
2351 if(submit_params.skinned)
2352 {
2353 stats->drawn_skinned_submeshes_for_shadows++;
2354 }
2355 else
2356 {
2357 stats->drawn_submeshes_for_shadows++;
2358 }
2359 }
2360
2361 const bool uses_cutout =
2362 mat.is<pbr_material>() && static_cast<const pbr_material&>(mat).uses_alpha_cutout();
2363 gpu_program* opaque_prog =
2364 submit_params.skinned ? currentSmSettings->m_progPackSkinned : currentSmSettings->m_progPack;
2365 gpu_program* prog =
2366 uses_cutout ? resolve_cutout_pack_program(programs_, opaque_prog) : opaque_prog;
2367 if(!prog)
2368 {
2369 return;
2370 }
2371
2372 prog->begin();
2373
2374 if(uses_cutout)
2375 {
2376 uniforms_.submitShadowCutoutState(static_cast<const pbr_material&>(mat).make_shadow_cutout_state());
2377 }
2378
2379 const uint64_t draw_state = apply_shadow_cull(_renderState.m_state, mat.get_cull_type());
2380 gfx::set_stencil(_renderState.m_fstencil, _renderState.m_bstencil);
2381 gfx::set_state(draw_state, _renderState.m_blendFactorRgba);
2382
2383 gfx::submit(viewId, prog->native_handle(), 0, submit_params.preserve_state);
2384 prog->end();
2385 };
2386 callbacks.setup_end = [&](const model::submit_callbacks::params& submit_params)
2387 {
2388 };
2389
2390 model.submit(world_transform,
2391 submesh_transforms,
2392 bone_transforms,
2393 skinning_matrices,
2394 lod_data.current_lod_index,
2395 callbacks,
2396 &lightFrustums[ii],
2397 nullptr,
2398 extras);
2399
2400 any_rendered = true;
2401
2402 if(nested_cascades && query == math::volume_query::inside)
2403 {
2404 break;
2405 }
2406 }
2407 }
2408
2409 // Submit batched geometry for each cascade
2410 if (static_batching_enabled)
2411 {
2412 for(uint8_t ii = 0; ii < drawNum; ++ii)
2413 {
2414 const uint8_t viewId = shadowmap_1_id + ii;
2415
2416 uint8_t renderStateIndex = RenderState::ShadowMap_PackDepth;
2417 if(LightType::PointLight == settings_.m_lightType && settings_.m_stencilPack)
2418 {
2419 renderStateIndex =
2421 }
2422
2423 const auto& renderState = render_states[renderStateIndex];
2424 submit_batched_shadow_geometry_cascade(cascade_batch_collectors_[ii], viewId, currentSmSettings, renderState, stats);
2425 }
2426 }
2427
2428 return any_rendered;
2429}
2430
2431void shadowmap_generator::submit_batched_shadow_geometry_cascade(shadow_batch_collector& collector,
2432 uint8_t viewId,
2433 ShadowMapSettings* currentSmSettings,
2434 const RenderState& renderState,
2436{
2437 // Prepare batches for rendering
2438 submit_context context;
2439 context.view_id = viewId;
2440 context.enable_distance_sorting = false; // Shadow maps don't need distance sorting
2441 context.max_instances_per_batch = 1024;
2442 context.enable_profiling = false;
2443
2444 collector.prepare_batches(context);
2445 const auto& prepared_batches = collector.get_prepared_batches();
2446
2447 if (prepared_batches.empty())
2448 {
2449 return;
2450 }
2451
2452 // Submit each batch (opaque and cutout may use different programs)
2453 for (const auto* batch : prepared_batches)
2454 {
2455 if (!batch->is_valid() || batch->instances.empty())
2456 {
2457 continue;
2458 }
2459
2460 const auto instance_count = static_cast<uint32_t>(batch->instances.size());
2461 if(stats != nullptr)
2462 {
2463 stats->drawn_submeshes_for_shadows += instance_count;
2464 }
2465
2466 const auto mesh_ptr = batch->key.mesh_ptr;
2467 const auto lod_index = batch->key.lod_index;
2468 const auto submesh_index = batch->key.submesh_index;
2469
2470 if (!mesh_ptr)
2471 {
2472 continue;
2473 }
2474
2475 const auto submesh = mesh_ptr->get_submesh(submesh_index, lod_index);
2476 if(!submesh)
2477 {
2478 continue;
2479 }
2480
2481 const bool uses_cutout = batch->key.uses_alpha_cutout();
2482 gpu_program* opaque_prog = currentSmSettings->m_progPackInstanced;
2483 gpu_program* prog = uses_cutout ? resolve_cutout_pack_program(programs_, opaque_prog) : opaque_prog;
2484 if(!prog)
2485 {
2486 continue;
2487 }
2488
2489 prog->begin();
2490
2491 const auto instance_data_size = static_cast<uint16_t>(instance_vertex_data::packed_size());
2492
2493 bgfx::InstanceDataBuffer instance_buffer;
2494 bgfx::allocInstanceDataBuffer(&instance_buffer, instance_count, instance_data_size);
2495 if (!instance_buffer.data)
2496 {
2497 prog->end();
2498 continue;
2499 }
2500
2501 auto* buffer_data = reinterpret_cast<instance_vertex_data*>(instance_buffer.data);
2502 for (size_t i = 0; i < batch->instances.size(); ++i)
2503 {
2504 buffer_data[i] = instance_vertex_data(batch->instances[i]);
2505 }
2506
2507 bgfx::setInstanceDataBuffer(&instance_buffer);
2508 mesh_ptr->bind_render_buffers_for_submesh(submesh, lod_index);
2509
2510 uniforms_.submitPerDrawUniforms();
2511 if(uses_cutout && batch->key.cutout.has_value())
2512 {
2513 uniforms_.submitShadowCutoutState(*batch->key.cutout);
2514 }
2515
2516 const uint64_t draw_state = apply_shadow_cull(renderState.m_state, batch->key.cull);
2517 gfx::set_stencil(renderState.m_fstencil, renderState.m_bstencil);
2518 gfx::set_state(draw_state, renderState.m_blendFactorRgba);
2519
2520 gfx::submit(viewId, prog->native_handle(), 0, false);
2521 prog->end();
2522 }
2523
2524 // Update statistics
2525 if(stats != nullptr)
2526 {
2527 const auto& batch_stats = collector.get_stats();
2528 stats->add_batch_stats(batch_stats);
2529 }
2530
2531 collector.clear();
2532}
2533
2535{
2536 // clang-format off
2537 auto& am = ctx.get_cached<asset_manager>();
2538 // clang-format on
2539
2540 auto loadProgram = [&](const std::string& vs, const std::string& fs)
2541 {
2542 auto vs_shader = am.get_asset<gfx::shader>("engine:/data/shaders/shadowmaps/" + vs + ".sc");
2543 auto fs_shadfer = am.get_asset<gfx::shader>("engine:/data/shaders/shadowmaps/" + fs + ".sc");
2544
2545 return std::make_shared<gpu_program>(vs_shader, fs_shadfer);
2546 };
2547
2548 // clang-format off
2549 // Misc.
2550 m_black = loadProgram("vs_shadowmaps_color", "fs_shadowmaps_color_black");
2551
2552 // Blur.
2553 m_vBlur[PackDepth::RGBA] = loadProgram("vs_shadowmaps_vblur", "fs_shadowmaps_vblur");
2554 m_hBlur[PackDepth::RGBA] = loadProgram("vs_shadowmaps_hblur", "fs_shadowmaps_hblur");
2555 m_vBlur[PackDepth::VSM] = loadProgram("vs_shadowmaps_vblur", "fs_shadowmaps_vblur_vsm");
2556 m_hBlur[PackDepth::VSM] = loadProgram("vs_shadowmaps_hblur", "fs_shadowmaps_hblur_vsm");
2557
2558 // Draw depth.
2559 m_drawDepth[PackDepth::RGBA] = loadProgram("vs_shadowmaps_unpackdepth", "fs_shadowmaps_unpackdepth");
2560 m_drawDepth[PackDepth::VSM] = loadProgram("vs_shadowmaps_unpackdepth", "fs_shadowmaps_unpackdepth_vsm");
2561
2562 // Pack depth.
2563 m_packDepth[DepthImpl::InvZ][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth", "packdepth/fs_shadowmaps_packdepth");
2564 m_packDepth[DepthImpl::InvZ][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth", "packdepth/fs_shadowmaps_packdepth_vsm");
2565
2566 m_packDepth[DepthImpl::Linear][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear", "packdepth/fs_shadowmaps_packdepth_linear");
2567 m_packDepth[DepthImpl::Linear][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear", "packdepth/fs_shadowmaps_packdepth_vsm_linear");
2568
2569 m_packDepthSkinned[DepthImpl::InvZ][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_skinned", "packdepth/fs_shadowmaps_packdepth");
2570 m_packDepthSkinned[DepthImpl::InvZ][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_skinned", "packdepth/fs_shadowmaps_packdepth_vsm");
2571
2572 m_packDepthSkinned[DepthImpl::Linear][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_skinned", "packdepth/fs_shadowmaps_packdepth_linear");
2573 m_packDepthSkinned[DepthImpl::Linear][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_skinned", "packdepth/fs_shadowmaps_packdepth_vsm_linear");
2574
2575 // Pack depth instanced (for static mesh batching).
2576 m_packDepthInstanced[DepthImpl::InvZ][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_instanced", "packdepth/fs_shadowmaps_packdepth");
2577 m_packDepthInstanced[DepthImpl::InvZ][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_instanced", "packdepth/fs_shadowmaps_packdepth_vsm");
2578
2579 m_packDepthInstanced[DepthImpl::Linear][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_instanced", "packdepth/fs_shadowmaps_packdepth_linear");
2580 m_packDepthInstanced[DepthImpl::Linear][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_instanced", "packdepth/fs_shadowmaps_packdepth_vsm_linear");
2581
2582 // Pack depth cutout (alpha test — matches lit material clip semantics).
2583 m_packDepthCutout[DepthImpl::InvZ][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_cutout", "packdepth/fs_shadowmaps_packdepth_cutout");
2584 m_packDepthCutout[DepthImpl::InvZ][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_cutout", "packdepth/fs_shadowmaps_packdepth_vsm_cutout");
2585
2586 m_packDepthCutout[DepthImpl::Linear][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_cutout", "packdepth/fs_shadowmaps_packdepth_linear_cutout");
2587 m_packDepthCutout[DepthImpl::Linear][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_cutout", "packdepth/fs_shadowmaps_packdepth_vsm_linear_cutout");
2588
2589 m_packDepthSkinnedCutout[DepthImpl::InvZ][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_skinned_cutout", "packdepth/fs_shadowmaps_packdepth_cutout");
2590 m_packDepthSkinnedCutout[DepthImpl::InvZ][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_skinned_cutout", "packdepth/fs_shadowmaps_packdepth_vsm_cutout");
2591
2592 m_packDepthSkinnedCutout[DepthImpl::Linear][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_skinned_cutout", "packdepth/fs_shadowmaps_packdepth_linear_cutout");
2593 m_packDepthSkinnedCutout[DepthImpl::Linear][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_skinned_cutout", "packdepth/fs_shadowmaps_packdepth_vsm_linear_cutout");
2594
2595 m_packDepthInstancedCutout[DepthImpl::InvZ][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_instanced_cutout", "packdepth/fs_shadowmaps_packdepth_cutout");
2596 m_packDepthInstancedCutout[DepthImpl::InvZ][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_instanced_cutout", "packdepth/fs_shadowmaps_packdepth_vsm_cutout");
2597
2598 m_packDepthInstancedCutout[DepthImpl::Linear][PackDepth::RGBA] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_instanced_cutout", "packdepth/fs_shadowmaps_packdepth_linear_cutout");
2599 m_packDepthInstancedCutout[DepthImpl::Linear][PackDepth::VSM] = loadProgram("packdepth/vs_shadowmaps_packdepth_linear_instanced_cutout", "packdepth/fs_shadowmaps_packdepth_vsm_linear_cutout");
2600
2601}
2602
2603}
2604} // namespace unravel
bgfx::ProgramHandle loadProgram(bx::FileReaderI *_reader, const bx::StringView &_vsName, const bx::StringView &_fsName)
Storage for frustum planes / values and wraps up common functionality.
Definition frustum.h:18
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
auto get_position() const noexcept -> const vec3_t &
Get the position component.
auto z_unit_axis() const noexcept -> vec3_t
Get the unit Z axis of the transform.
Manages assets, including loading, unloading, and storage.
static auto is_static_mesh_batching_enabled() -> bool
Class representing a camera. Contains functionality for manipulating and updating a camera....
Definition camera.h:62
auto get_fov() const -> float
Retrieves the current field of view angle in degrees.
Definition camera.cpp:59
auto get_view_inverse() const -> const math::transform &
Definition camera.cpp:298
auto get_aspect_ratio() const -> float
Retrieves the aspect ratio used to generate the horizontal FOV angle.
Definition camera.cpp:185
Class representing a GPU program.
Definition gpu_program.h:17
std::shared_ptr< gpu_program > ptr
Definition gpu_program.h:19
auto get_depth_type() const -> PackDepth::Enum
Definition shadow.cpp:1098
void submit_uniforms(uint8_t stage) const
Definition shadow.cpp:1119
auto get_rt_texture(uint8_t split) const -> bgfx::TextureHandle
Definition shadow.cpp:1104
void update(const camera &cam, const light &l, const math::transform &ltrans, bool is_active)
Definition shadow.cpp:1145
void generate_shadowmaps(const shadow_map_models_t &model, const camera &cam, ::unravel::rendering::pipeline_stats *stats=nullptr)
Definition shadow.cpp:1834
void init(rtti::context &ctx)
Definition shadow.cpp:448
auto already_updated() const -> bool
Definition shadow.cpp:1140
auto get_depth_render_program(PackDepth::Enum depth) const -> gpu_program::ptr
Definition shadow.cpp:1114
Definition cache.hpp:11
void submit(view_id _id, program_handle _handle, int32_t _depth, bool _preserveState)
bgfx::Stats stats
Definition graphics.h:31
void set_state(uint64_t _state, uint32_t _rgba)
Definition graphics.cpp:937
void set_stencil(uint32_t _fstencil, uint32_t _bstencil)
Definition graphics.cpp:947
const caps * get_caps()
Definition graphics.cpp:445
auto is_homogeneous_depth() -> bool
auto is_origin_bottom_left() -> bool
uint32_t get_render_frame()
hpp::small_vector< shadow_visibility_data > shadow_map_models_t
Definition shadow.h:624
sm_resolution
Enum representing the resolution of shadow maps.
Definition light.h:74
sm_impl
Enum representing the implementation type for shadow mapping.
Definition light.h:48
sm_depth
Enum representing the depth method for shadow mapping.
Definition light.h:26
cull_type
Enum representing the type of culling to be used.
Definition material.h:25
@ counter_clockwise
Cull counter-clockwise faces.
@ clockwise
Cull clockwise faces.
light_type
Enum representing the type of light.
Definition light.h:14
batch_collector_t< shadow_batch_key > shadow_batch_collector
entt::handle entity
#define SET_CLAMPED_VAL(x, val)
gfx::view_id id
static auto get_layout() -> const vertex_layout &
Definition vertex_decl.h:15
auto get_cached() -> T &
Definition context.hpp:49
static auto context() -> rtti::context &
Definition engine.cpp:111
static auto packed_size() -> size_t
Struct representing a light.
Definition light.h:87
SpotDirectionInner m_spotDirectionInner
Definition shadow.h:160
gpu_program::ptr m_packDepthSkinned[DepthImpl::Count][PackDepth::Count]
Definition shadow.h:518
gpu_program::ptr m_hBlur[PackDepth::Count]
Definition shadow.h:515
gpu_program::ptr m_black
Definition shadow.h:513
gpu_program::ptr m_packDepth[DepthImpl::Count][PackDepth::Count]
Definition shadow.h:517
gpu_program::ptr m_packDepthInstanced[DepthImpl::Count][PackDepth::Count]
Definition shadow.h:519
void init(rtti::context &ctx)
Definition shadow.cpp:2534
gpu_program::ptr m_drawDepth[PackDepth::Count]
Definition shadow.h:516
gpu_program::ptr m_packDepthSkinnedCutout[DepthImpl::Count][PackDepth::Count]
Definition shadow.h:521
gpu_program::ptr m_packDepthInstancedCutout[DepthImpl::Count][PackDepth::Count]
Definition shadow.h:522
gpu_program::ptr m_packDepthCutout[DepthImpl::Count][PackDepth::Count]
Definition shadow.h:520
gpu_program::ptr m_vBlur[PackDepth::Count]
Definition shadow.h:514
DepthImpl::Enum m_depthImpl
Definition shadow.h:553
LightType::Enum m_lightType
Definition shadow.h:552
void setPtrs(Light *_lightPtr, float *_colorPtr, float *_lightMtxPtr, float *_shadowMapMtx0, float *_shadowMapMtx1, float *_shadowMapMtx2, float *_shadowMapMtx3)
Definition shadow.h:228
void submitPerDrawUniforms() const
Definition shadow.h:265
void submitShadowCutoutState(const shadow_cutout_state &cutout) const
Definition shadow.h:279