1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsRenderBeast.h"
4#include "BsCoreApplication.h"
5#include "CoreThread/BsCoreThread.h"
6#include "CoreThread/BsCoreObjectManager.h"
7#include "Material/BsMaterial.h"
8#include "Material/BsShader.h"
9#include "Material/BsPass.h"
10#include "RenderAPI/BsViewport.h"
11#include "RenderAPI/BsRenderTarget.h"
12#include "RenderAPI/BsGpuParamBlockBuffer.h"
13#include "Profiling/BsProfilerCPU.h"
14#include "Profiling/BsProfilerGPU.h"
15#include "Utility/BsTime.h"
16#include "Animation/BsAnimationManager.h"
17#include "Animation/BsSkeleton.h"
18#include "Renderer/BsLight.h"
19#include "Renderer/BsRendererExtension.h"
20#include "Renderer/BsReflectionProbe.h"
21#include "Renderer/BsRenderSettings.h"
22#include "Renderer/BsIBLUtility.h"
23#include "Renderer/BsSkybox.h"
24#include "Renderer/BsCamera.h"
25#include "Renderer/BsRendererUtility.h"
26#include "Utility/BsRendererTextures.h"
27#include "Renderer/BsGpuResourcePool.h"
28#include "Renderer/BsRendererManager.h"
29#include "Shading/BsShadowRendering.h"
30#include "Shading/BsStandardDeferred.h"
31#include "Shading/BsTiledDeferred.h"
32#include "BsRenderBeastOptions.h"
33#include "BsRenderBeastIBLUtility.h"
34#include "BsRenderCompositor.h"
35#include "Shading/BsGpuParticleSimulation.h"
36
37using namespace std::placeholders;
38
39namespace bs { namespace ct
40{
41 RenderBeast::RenderBeast()
42 {
43 mOptions = bs_shared_ptr_new<RenderBeastOptions>();
44 }
45
46 const StringID& RenderBeast::getName() const
47 {
48 static StringID name = "RenderBeast";
49 return name;
50 }
51
52 void RenderBeast::initialize()
53 {
54 Renderer::initialize();
55
56 gCoreThread().queueCommand(std::bind(&RenderBeast::initializeCore, this), CTQF_InternalQueue);
57 }
58
59 void RenderBeast::destroy()
60 {
61 Renderer::destroy();
62
63 gCoreThread().queueCommand(std::bind(&RenderBeast::destroyCore, this));
64 gCoreThread().submit(true);
65 }
66
67 void RenderBeast::initializeCore()
68 {
69 const RenderAPICapabilities& caps = gCaps();
70
71 if(
72 !caps.hasCapability(RSC_COMPUTE_PROGRAM) ||
73 !caps.hasCapability(RSC_LOAD_STORE) ||
74 !caps.hasCapability(RSC_TEXTURE_VIEWS))
75 {
76 mFeatureSet = RenderBeastFeatureSet::DesktopMacOS;
77 }
78
79 // Ensure profiler methods can be called from start-up methods
80 gProfilerGPU().beginFrame();
81
82 RendererUtility::startUp();
83 GpuSort::startUp();
84 GpuResourcePool::startUp();
85 IBLUtility::startUp<RenderBeastIBLUtility>();
86 RendererTextures::startUp();
87
88 mCoreOptions = bs_shared_ptr_new<RenderBeastOptions>();
89 mScene = bs_shared_ptr_new<RendererScene>(mCoreOptions);
90
91 mMainViewGroup = bs_new<RendererViewGroup>(nullptr, 0, true);
92
93 StandardDeferred::startUp();
94 ParticleRenderer::startUp();
95 GpuParticleSimulation::startUp();
96
97 gProfilerGPU().endFrame();
98
99 RenderCompositor::registerNodeType<RCNodeSceneDepth>();
100 RenderCompositor::registerNodeType<RCNodeBasePass>();
101 RenderCompositor::registerNodeType<RCNodeLightAccumulation>();
102 RenderCompositor::registerNodeType<RCNodeSceneColor>();
103 RenderCompositor::registerNodeType<RCNodeDeferredDirectLighting>();
104 RenderCompositor::registerNodeType<RCNodeIndirectDiffuseLighting>();
105 RenderCompositor::registerNodeType<RCNodeDeferredIndirectSpecularLighting>();
106 RenderCompositor::registerNodeType<RCNodeFinalResolve>();
107 RenderCompositor::registerNodeType<RCNodeSkybox>();
108 RenderCompositor::registerNodeType<RCNodePostProcess>();
109 RenderCompositor::registerNodeType<RCNodeTonemapping>();
110 RenderCompositor::registerNodeType<RCNodeGaussianDOF>();
111 RenderCompositor::registerNodeType<RCNodeFXAA>();
112 RenderCompositor::registerNodeType<RCNodeResolvedSceneDepth>();
113 RenderCompositor::registerNodeType<RCNodeHiZ>();
114 RenderCompositor::registerNodeType<RCNodeSSAO>();
115 RenderCompositor::registerNodeType<RCNodeClusteredForward>();
116 RenderCompositor::registerNodeType<RCNodeSSR>();
117 RenderCompositor::registerNodeType<RCNodeMSAACoverage>();
118 RenderCompositor::registerNodeType<RCNodeParticleSimulate>();
119 RenderCompositor::registerNodeType<RCNodeParticleSort>();
120 RenderCompositor::registerNodeType<RCNodeHalfSceneColor>();
121 RenderCompositor::registerNodeType<RCNodeBloom>();
122 RenderCompositor::registerNodeType<RCNodeEyeAdaptation>();
123 }
124
125 void RenderBeast::destroyCore()
126 {
127 // Make sure all tasks finish first
128 processTasks(true);
129
130 mScene = nullptr;
131
132 RenderCompositor::cleanUp();
133
134 GpuParticleSimulation::shutDown();
135 ParticleRenderer::shutDown();
136 StandardDeferred::shutDown();
137
138 bs_delete(mMainViewGroup);
139
140 RendererTextures::shutDown();
141 IBLUtility::shutDown();
142 GpuResourcePool::shutDown();
143 GpuSort::shutDown();
144 RendererUtility::shutDown();
145 }
146
147 void RenderBeast::notifyRenderableAdded(Renderable* renderable)
148 {
149 mScene->registerRenderable(renderable);
150 }
151
152 void RenderBeast::notifyRenderableRemoved(Renderable* renderable)
153 {
154 mScene->unregisterRenderable(renderable);
155 }
156
157 void RenderBeast::notifyRenderableUpdated(Renderable* renderable)
158 {
159 mScene->updateRenderable(renderable);
160 }
161
162 void RenderBeast::notifyLightAdded(Light* light)
163 {
164 mScene->registerLight(light);
165 }
166
167 void RenderBeast::notifyLightUpdated(Light* light)
168 {
169 mScene->updateLight(light);
170 }
171
172 void RenderBeast::notifyLightRemoved(Light* light)
173 {
174 mScene->unregisterLight(light);
175 }
176
177 void RenderBeast::notifyCameraAdded(Camera* camera)
178 {
179 mScene->registerCamera(camera);
180 }
181
182 void RenderBeast::notifyCameraUpdated(Camera* camera, UINT32 updateFlag)
183 {
184 mScene->updateCamera(camera, updateFlag);
185 }
186
187 void RenderBeast::notifyCameraRemoved(Camera* camera)
188 {
189 mScene->unregisterCamera(camera);
190 }
191
192 void RenderBeast::notifyReflectionProbeAdded(ReflectionProbe* probe)
193 {
194 mScene->registerReflectionProbe(probe);
195 }
196
197 void RenderBeast::notifyReflectionProbeUpdated(ReflectionProbe* probe, bool texture)
198 {
199 mScene->updateReflectionProbe(probe, texture);
200 }
201
202 void RenderBeast::notifyReflectionProbeRemoved(ReflectionProbe* probe)
203 {
204 mScene->unregisterReflectionProbe(probe);
205 }
206
207 void RenderBeast::notifyLightProbeVolumeAdded(LightProbeVolume* volume)
208 {
209 mScene->registerLightProbeVolume(volume);
210 }
211
212 void RenderBeast::notifyLightProbeVolumeUpdated(LightProbeVolume* volume)
213 {
214 mScene->updateLightProbeVolume(volume);
215 }
216
217 void RenderBeast::notifyLightProbeVolumeRemoved(LightProbeVolume* volume)
218 {
219 mScene->unregisterLightProbeVolume(volume);
220 }
221
222 void RenderBeast::notifySkyboxAdded(Skybox* skybox)
223 {
224 mScene->registerSkybox(skybox);
225 }
226
227 void RenderBeast::notifySkyboxRemoved(Skybox* skybox)
228 {
229 mScene->unregisterSkybox(skybox);
230 }
231
232 void RenderBeast::notifyParticleSystemAdded(ParticleSystem* particleSystem)
233 {
234 mScene->registerParticleSystem(particleSystem);
235 }
236
237 void RenderBeast::notifyParticleSystemUpdated(ParticleSystem* particleSystem, bool tfrmOnly)
238 {
239 mScene->updateParticleSystem(particleSystem, tfrmOnly);
240 }
241
242 void RenderBeast::notifyParticleSystemRemoved(ParticleSystem* particleSystem)
243 {
244 mScene->unregisterParticleSystem(particleSystem);
245 }
246
247 void RenderBeast::notifyDecalAdded(Decal* decal)
248 {
249 mScene->registerDecal(decal);
250 }
251
252 void RenderBeast::notifyDecalRemoved(Decal* decal)
253 {
254 mScene->unregisterDecal(decal);
255 }
256
257 void RenderBeast::notifyDecalUpdated(Decal* decal)
258 {
259 mScene->updateDecal(decal);
260 }
261 void RenderBeast::setOptions(const SPtr<RendererOptions>& options)
262 {
263 mOptions = std::static_pointer_cast<RenderBeastOptions>(options);
264 mOptionsDirty = true;
265 }
266
267 SPtr<RendererOptions> RenderBeast::getOptions() const
268 {
269 return mOptions;
270 }
271
272 void RenderBeast::syncOptions(const RenderBeastOptions& options)
273 {
274 bool filteringChanged = mCoreOptions->filtering != options.filtering;
275 if (options.filtering == RenderBeastFiltering::Anisotropic)
276 filteringChanged |= mCoreOptions->anisotropyMax != options.anisotropyMax;
277
278 if (filteringChanged)
279 mScene->refreshSamplerOverrides(true);
280
281 *mCoreOptions = options;
282
283 mScene->setOptions(mCoreOptions);
284
285 ShadowRendering& shadowRenderer = mMainViewGroup->getShadowRenderer();
286 shadowRenderer.setShadowMapSize(mCoreOptions->shadowMapSize);
287 }
288
289 ShaderExtensionPointInfo RenderBeast::getShaderExtensionPointInfo(const String& name)
290 {
291 if(name == "DeferredDirectLighting")
292 {
293 ShaderExtensionPointInfo info;
294
295 ExtensionShaderInfo tiledDeferredInfo;
296 tiledDeferredInfo.name = "TiledDeferredDirectLighting";
297 tiledDeferredInfo.path = TiledDeferredLightingMat::getShaderPath();
298 tiledDeferredInfo.defines = TiledDeferredLightingMat::getShaderDefines();
299 info.shaders.push_back(tiledDeferredInfo);
300
301 ExtensionShaderInfo standardDeferredPointInfo;
302 standardDeferredPointInfo.name = "StandardDeferredPointDirectLighting";
303 standardDeferredPointInfo.path = DeferredPointLightMat::getShaderPath();
304 standardDeferredPointInfo.defines = DeferredPointLightMat::getShaderDefines();
305 info.shaders.push_back(standardDeferredPointInfo);
306
307 ExtensionShaderInfo standardDeferredDirInfo;
308 standardDeferredDirInfo.name = "StandardDeferredDirDirectLighting";
309 standardDeferredDirInfo.path = DeferredDirectionalLightMat::getShaderPath();
310 standardDeferredDirInfo.defines = DeferredDirectionalLightMat::getShaderDefines();
311 info.shaders.push_back(standardDeferredPointInfo);
312
313 return info;
314 }
315
316 return ShaderExtensionPointInfo();
317 }
318
319 void RenderBeast::setGlobalShaderOverride(const String& name, const SPtr<bs::Shader>& shader)
320 {
321 SPtr<ct::Shader> shaderCore;
322 if(shader)
323 shaderCore = shader->getCore();
324
325 auto setShaderOverride = [name, shaderCore]()
326 {
327 if (name == "TiledDeferredDirectLighting")
328 TiledDeferredLightingMat::setOverride(shaderCore);
329 else if(name == "StandardDeferredPointDirectLighting")
330 DeferredPointLightMat::setOverride(shaderCore);
331 else if(name == "StandardDeferredDirDirectLighting")
332 DeferredDirectionalLightMat::setOverride(shaderCore);
333 };
334
335 gCoreThread().queueCommand(setShaderOverride);
336 }
337
338 void RenderBeast::renderAll(PerFrameData perFrameData)
339 {
340 // Sync all dirty sim thread CoreObject data to core thread
341 PROFILE_CALL(CoreObjectManager::instance().syncToCore(), "Sync to core")
342
343 if (mOptionsDirty)
344 {
345 gCoreThread().queueCommand(std::bind(&RenderBeast::syncOptions, this, *mOptions));
346 mOptionsDirty = false;
347 }
348
349 FrameTimings timings;
350 timings.time = gTime().getTime();
351 timings.timeDelta = gTime().getFrameDelta();
352 timings.frameIdx = gTime().getFrameIdx();
353
354 gCoreThread().queueCommand(std::bind(&RenderBeast::renderAllCore, this, timings, perFrameData));
355 }
356
357 void RenderBeast::renderAllCore(FrameTimings timings, PerFrameData perFrameData)
358 {
359 THROW_IF_NOT_CORE_THREAD;
360
361 gProfilerGPU().beginFrame();
362 gProfilerCPU().beginSample("Render");
363
364 const SceneInfo& sceneInfo = mScene->getSceneInfo();
365
366 // Note: I'm iterating over all sampler states every frame. If this ends up being a performance
367 // issue consider handling this internally in ct::Material which can only do it when sampler states
368 // are actually modified after sync
369 mScene->refreshSamplerOverrides();
370
371 // Update global per-frame hardware buffers
372 mScene->setParamFrameParams(timings.time);
373
374 // Update bounds for all particle systems
375 if(perFrameData.particles)
376 PROFILE_CALL(mScene->updateParticleSystemBounds(perFrameData.particles), "Particle bounds")
377
378 sceneInfo.renderableReady.resize(sceneInfo.renderables.size(), false);
379 sceneInfo.renderableReady.assign(sceneInfo.renderables.size(), false);
380
381 FrameInfo frameInfo(timings, perFrameData);
382
383 // Make sure any renderer tasks finish first, as rendering might depend on them
384 processTasks(false, timings.frameIdx);
385
386 // If any reflection probes were updated or added, we need to copy them over in the global reflection probe array
387 updateReflProbeArray();
388
389 // Update material parameters & animation times for all renderables
390 for (UINT32 i = 0; i < sceneInfo.renderables.size(); i++)
391 {
392 RendererRenderable* renderable = sceneInfo.renderables[i];
393 for (auto& element : renderable->elements)
394 element.materialAnimationTime += timings.timeDelta;
395 }
396
397 for (UINT32 i = 0; i < sceneInfo.particleSystems.size(); i++)
398 mScene->prepareParticleSystem(i, frameInfo);
399
400 for (UINT32 i = 0; i < sceneInfo.decals.size(); i++)
401 {
402 const RendererDecal& decal = sceneInfo.decals[i];
403 decal.renderElement.materialAnimationTime += timings.timeDelta;
404
405 mScene->prepareDecal(i, frameInfo);
406 }
407
408 // Gather all views
409 for (auto& rtInfo : sceneInfo.renderTargets)
410 {
411 Vector<RendererView*> views;
412 SPtr<RenderTarget> target = rtInfo.target;
413 const Vector<Camera*>& cameras = rtInfo.cameras;
414
415 UINT32 numCameras = (UINT32)cameras.size();
416 for (UINT32 i = 0; i < numCameras; i++)
417 {
418 UINT32 viewIdx = sceneInfo.cameraToView.at(cameras[i]);
419 RendererView* viewInfo = sceneInfo.views[viewIdx];
420 views.push_back(viewInfo);
421 }
422
423 mMainViewGroup->setViews(views.data(), (UINT32)views.size());
424 PROFILE_CALL(mMainViewGroup->determineVisibility(sceneInfo), "Determine visibility")
425
426 // Render everything
427 renderViews(*mMainViewGroup, frameInfo);
428
429 if(rtInfo.target->getProperties().isWindow)
430 PROFILE_CALL(RenderAPI::instance().swapBuffers(rtInfo.target), "Swap buffers");
431 }
432
433 gProfilerGPU().endFrame();
434 gProfilerCPU().endSample("Render");
435 }
436
437 void RenderBeast::renderViews(RendererViewGroup& viewGroup, const FrameInfo& frameInfo)
438 {
439 const SceneInfo& sceneInfo = mScene->getSceneInfo();
440 const VisibilityInfo& visibility = viewGroup.getVisibilityInfo();
441
442 // Render shadow maps
443 ShadowRendering& shadowRenderer = viewGroup.getShadowRenderer();
444 shadowRenderer.renderShadowMaps(*mScene, viewGroup, frameInfo);
445
446 // Update various buffers required by each renderable
447 UINT32 numRenderables = (UINT32)sceneInfo.renderables.size();
448 for (UINT32 i = 0; i < numRenderables; i++)
449 {
450 if (!visibility.renderables[i])
451 continue;
452
453 mScene->prepareRenderable(i, frameInfo);
454 }
455
456 UINT32 numViews = viewGroup.getNumViews();
457 for (UINT32 i = 0; i < numViews; i++)
458 {
459 RendererView* view = viewGroup.getView(i);
460 const RenderSettings& settings = view->getRenderSettings();
461
462 if (settings.overlayOnly)
463 renderOverlay(*view);
464 else
465 renderView(viewGroup, *view, frameInfo);
466 }
467 }
468
469 void RenderBeast::renderView(const RendererViewGroup& viewGroup, RendererView& view, const FrameInfo& frameInfo)
470 {
471 gProfilerCPU().beginSample("Render view");
472
473 const SceneInfo& sceneInfo = mScene->getSceneInfo();
474 auto& viewProps = view.getProperties();
475
476 SPtr<GpuParamBlockBuffer> perCameraBuffer = view.getPerViewBuffer();
477 perCameraBuffer->flushToGPU();
478
479 // Make sure light probe data is up to date
480 if(view.getRenderSettings().enableIndirectLighting)
481 mScene->updateLightProbes();
482
483 view.beginFrame();
484
485 RenderCompositorNodeInputs inputs(viewGroup, view, sceneInfo, *mCoreOptions, frameInfo, mFeatureSet);
486
487 // Register callbacks
488 if (viewProps.triggerCallbacks)
489 {
490 for(auto& extension : mCallbacks)
491 {
492 RenderLocation location = extension->getLocation();
493 switch(location)
494 {
495 case RenderLocation::Prepare:
496 inputs.extPrepare.add(extension);
497 break;
498 case RenderLocation::PreBasePass:
499 inputs.extPreBasePass.add(extension);
500 break;
501 case RenderLocation::PostBasePass:
502 inputs.extPostBasePass.add(extension);
503 break;
504 case RenderLocation::PostLightPass:
505 inputs.extPostLighting.add(extension);
506 break;
507 case RenderLocation::Overlay:
508 inputs.extOverlay.add(extension);
509 break;
510 }
511 }
512 }
513
514 const RenderCompositor& compositor = view.getCompositor();
515 PROFILE_CALL(compositor.execute(inputs), "Compositor")
516
517 view.endFrame();
518
519 gProfilerCPU().endSample("Render view");
520 }
521
522 void RenderBeast::renderOverlay(RendererView& view)
523 {
524 gProfilerCPU().beginSample("Render overlay");
525
526 view.getPerViewBuffer()->flushToGPU();
527 view.beginFrame();
528
529 auto& viewProps = view.getProperties();
530 const Camera* camera = view.getSceneCamera();
531 SPtr<RenderTarget> target = viewProps.target.target;
532 SPtr<Viewport> viewport = camera->getViewport();
533
534 ClearFlags clearFlags = viewport->getClearFlags();
535 UINT32 clearBuffers = 0;
536 if (clearFlags.isSet(ClearFlagBits::Color))
537 clearBuffers |= FBT_COLOR;
538
539 if (clearFlags.isSet(ClearFlagBits::Depth))
540 clearBuffers |= FBT_DEPTH;
541
542 if (clearFlags.isSet(ClearFlagBits::Stencil))
543 clearBuffers |= FBT_STENCIL;
544
545 if (clearBuffers != 0)
546 {
547 RenderAPI::instance().setRenderTarget(target);
548 RenderAPI::instance().clearViewport(clearBuffers, viewport->getClearColorValue(),
549 viewport->getClearDepthValue(), viewport->getClearStencilValue());
550 }
551 else
552 RenderAPI::instance().setRenderTarget(target, 0, RT_COLOR0);
553
554 RenderAPI::instance().setViewport(viewport->getArea());
555
556 // Trigger overlay callbacks
557 auto iterRenderCallback = mCallbacks.begin();
558 while (iterRenderCallback != mCallbacks.end())
559 {
560 RendererExtension* extension = *iterRenderCallback;
561 if (extension->getLocation() != RenderLocation::Overlay)
562 {
563 ++iterRenderCallback;
564 continue;
565 }
566
567 if (extension->check(*camera))
568 extension->render(*camera);
569
570 ++iterRenderCallback;
571 }
572
573 view.endFrame();
574
575 gProfilerCPU().endSample("Render overlay");
576 }
577
578 void RenderBeast::updateReflProbeArray()
579 {
580 SceneInfo& sceneInfo = mScene->_getSceneInfo();
581 UINT32 numProbes = (UINT32)sceneInfo.reflProbes.size();
582
583 bs_frame_mark();
584 {
585 UINT32 currentCubeArraySize = 0;
586
587 if(sceneInfo.reflProbeCubemapsTex != nullptr)
588 currentCubeArraySize = sceneInfo.reflProbeCubemapsTex->getProperties().getNumArraySlices();
589
590 bool forceArrayUpdate = false;
591 if(sceneInfo.reflProbeCubemapsTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))
592 {
593 TEXTURE_DESC cubeMapDesc;
594 cubeMapDesc.type = TEX_TYPE_CUBE_MAP;
595 cubeMapDesc.format = PF_RG11B10F;
596 cubeMapDesc.width = IBLUtility::REFLECTION_CUBEMAP_SIZE;
597 cubeMapDesc.height = IBLUtility::REFLECTION_CUBEMAP_SIZE;
598 cubeMapDesc.numMips = PixelUtil::getMaxMipmaps(cubeMapDesc.width, cubeMapDesc.height, 1, cubeMapDesc.format);
599 cubeMapDesc.numArraySlices = std::min(MaxReflectionCubemaps, numProbes + 4); // Keep a few empty entries
600
601 sceneInfo.reflProbeCubemapsTex = Texture::create(cubeMapDesc);
602
603 forceArrayUpdate = true;
604 }
605
606 auto& cubemapArrayProps = sceneInfo.reflProbeCubemapsTex->getProperties();
607
608 FrameQueue<UINT32> emptySlots;
609 for (UINT32 i = 0; i < numProbes; i++)
610 {
611 const RendererReflectionProbe& probeInfo = sceneInfo.reflProbes[i];
612
613 if (probeInfo.arrayIdx > MaxReflectionCubemaps)
614 continue;
615
616 if(probeInfo.arrayDirty || forceArrayUpdate)
617 {
618 SPtr<Texture> texture = probeInfo.probe->getFilteredTexture();
619 if (texture == nullptr)
620 continue;
621
622 auto& srcProps = texture->getProperties();
623 bool isValid = srcProps.getWidth() == IBLUtility::REFLECTION_CUBEMAP_SIZE &&
624 srcProps.getHeight() == IBLUtility::REFLECTION_CUBEMAP_SIZE &&
625 srcProps.getNumMipmaps() == cubemapArrayProps.getNumMipmaps() &&
626 srcProps.getTextureType() == TEX_TYPE_CUBE_MAP;
627
628 if(!isValid)
629 {
630 if (!probeInfo.errorFlagged)
631 {
632 String errMsg = StringUtil::format("Cubemap texture invalid to use as a reflection cubemap. "
633 "Check texture size (must be {0}x{0}) and mip-map count",
634 IBLUtility::REFLECTION_CUBEMAP_SIZE);
635
636 LOGERR(errMsg);
637 probeInfo.errorFlagged = true;
638 }
639 }
640 else
641 {
642 for(UINT32 face = 0; face < 6; face++)
643 {
644 for(UINT32 mip = 0; mip <= srcProps.getNumMipmaps(); mip++)
645 {
646 TEXTURE_COPY_DESC copyDesc;
647 copyDesc.srcFace = face;
648 copyDesc.srcMip = mip;
649 copyDesc.dstFace = probeInfo.arrayIdx * 6 + face;
650 copyDesc.dstMip = mip;
651
652 texture->copy(sceneInfo.reflProbeCubemapsTex, copyDesc);
653 }
654 }
655 }
656
657 mScene->setReflectionProbeArrayIndex(i, probeInfo.arrayIdx, true);
658 }
659
660 // Note: Consider pruning the reflection cubemap array if empty slot count becomes too high
661 }
662 }
663 bs_frame_clear();
664 }
665
666 void RenderBeast::captureSceneCubeMap(const SPtr<Texture>& cubemap, const Vector3& position,
667 const CaptureSettings& settings)
668 {
669 const SceneInfo& sceneInfo = mScene->getSceneInfo();
670 auto& texProps = cubemap->getProperties();
671
672 Matrix4 projTransform = Matrix4::projectionPerspective(Degree(90.0f), 1.0f, 0.05f, 1000.0f);
673 ConvexVolume localFrustum(projTransform);
674 RenderAPI::instance().convertProjectionMatrix(projTransform, projTransform);
675
676 RENDERER_VIEW_DESC viewDesc;
677 viewDesc.target.clearFlags = FBT_COLOR | FBT_DEPTH;
678 viewDesc.target.clearColor = Color::Black;
679 viewDesc.target.clearDepthValue = 1.0f;
680 viewDesc.target.clearStencilValue = 0;
681
682 viewDesc.target.nrmViewRect = Rect2(0, 0, 1.0f, 1.0f);
683 viewDesc.target.viewRect = Rect2I(0, 0, texProps.getWidth(), texProps.getHeight());
684 viewDesc.target.targetWidth = texProps.getWidth();
685 viewDesc.target.targetHeight = texProps.getHeight();
686 viewDesc.target.numSamples = 1;
687
688 viewDesc.mainView = false;
689 viewDesc.triggerCallbacks = false;
690 viewDesc.runPostProcessing = false;
691 viewDesc.capturingReflections = true;
692 viewDesc.encodeDepth = settings.encodeDepth;
693 viewDesc.depthEncodeNear = settings.depthEncodeNear;
694 viewDesc.depthEncodeFar = settings.depthEncodeFar;
695
696 viewDesc.visibleLayers = 0xFFFFFFFFFFFFFFFF;
697 viewDesc.nearPlane = 0.5f;
698 viewDesc.farPlane = 1000.0f;
699 viewDesc.flipView = gCaps().conventions.uvYAxis != Conventions::Axis::Up;
700
701 viewDesc.viewOrigin = position;
702 viewDesc.projTransform = projTransform;
703 viewDesc.projType = PT_PERSPECTIVE;
704
705 viewDesc.stateReduction = mCoreOptions->stateReductionMode;
706 viewDesc.sceneCamera = nullptr;
707
708 SPtr<RenderSettings> renderSettings = bs_shared_ptr_new<RenderSettings>();
709 renderSettings->enableHDR = settings.hdr;
710 renderSettings->enableShadows = true;
711 renderSettings->enableIndirectLighting = false;
712 renderSettings->screenSpaceReflections.enabled = false;
713 renderSettings->ambientOcclusion.enabled = false;
714
715 Matrix4 viewOffsetMat = Matrix4::translation(-position);
716
717 // Note: We render upside down, then flip the image vertically, which results in a horizontal flip. The horizontal
718 // flip is required due to the fact how cubemap faces are defined. Another option would be to change the view
719 // orientation matrix, but that also requires a culling mode flip which is inconvenient to do globally.
720 RendererView views[6];
721 for(UINT32 i = 0; i < 6; i++)
722 {
723 // Calculate view matrix
724 Vector3 forward;
725 Vector3 up = Vector3::UNIT_Y;
726
727 switch (i)
728 {
729 case CF_PositiveX:
730 forward = -Vector3::UNIT_X;
731 up = -Vector3::UNIT_Y;
732 break;
733 case CF_NegativeX:
734 forward = Vector3::UNIT_X;
735 up = -Vector3::UNIT_Y;
736 break;
737 case CF_PositiveY:
738 forward = Vector3::UNIT_Y;
739 up = -Vector3::UNIT_Z;
740 break;
741 case CF_NegativeY:
742 forward = -Vector3::UNIT_Y;
743 up = Vector3::UNIT_Z;
744 break;
745 case CF_PositiveZ:
746 forward = -Vector3::UNIT_Z;
747 up = -Vector3::UNIT_Y;
748 break;
749 case CF_NegativeZ:
750 forward = Vector3::UNIT_Z;
751 up = -Vector3::UNIT_Y;
752 break;
753 }
754
755 Vector3 right = Vector3::cross(up, forward);
756 Matrix3 viewRotationMat = Matrix3(right, up, forward);
757
758 viewDesc.viewDirection = -forward;
759 viewDesc.viewTransform = Matrix4(viewRotationMat) * viewOffsetMat;
760
761 // Calculate world frustum for culling
762 const Vector<Plane>& frustumPlanes = localFrustum.getPlanes();
763 Matrix4 worldMatrix = viewDesc.viewTransform.transpose();
764
765 Vector<Plane> worldPlanes(frustumPlanes.size());
766 UINT32 j = 0;
767 for (auto& plane : frustumPlanes)
768 {
769 worldPlanes[j] = worldMatrix.multiplyAffine(plane);
770 j++;
771 }
772
773 viewDesc.cullFrustum = ConvexVolume(worldPlanes);
774
775 // Set up face render target
776 RENDER_TEXTURE_DESC cubeFaceRTDesc;
777 cubeFaceRTDesc.colorSurfaces[0].texture = cubemap;
778 cubeFaceRTDesc.colorSurfaces[0].face = i;
779 cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
780
781 viewDesc.target.target = RenderTexture::create(cubeFaceRTDesc);
782
783 views[i].setView(viewDesc);
784 views[i].setRenderSettings(renderSettings);
785 views[i].updatePerViewBuffer();
786 }
787
788 RendererView* viewPtrs[] = { &views[0], &views[1], &views[2], &views[3], &views[4], &views[5] };
789
790 RendererViewGroup viewGroup(viewPtrs, 6, false, mCoreOptions->shadowMapSize);
791 viewGroup.determineVisibility(sceneInfo);
792
793 FrameInfo frameInfo({ 0.0f, 1.0f / 60.0f, 0 }, PerFrameData());
794 renderViews(viewGroup, frameInfo);
795
796 // Make sure the render texture is available for reads
797 RenderAPI::instance().setRenderTarget(nullptr);
798 }
799
800 SPtr<RenderBeast> gRenderBeast()
801 {
802 return std::static_pointer_cast<RenderBeast>(RendererManager::instance().getActive());
803 }
804}}