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 "BsRenderBeastIBLUtility.h"
4#include "Image/BsTexture.h"
5#include "Material/BsGpuParamsSet.h"
6#include "Renderer/BsRendererUtility.h"
7#include "RenderAPI/BsGpuBuffer.h"
8#include "BsRenderBeast.h"
9
10namespace bs { namespace ct
11{
12 ReflectionCubeDownsampleParamDef gReflectionCubeDownsampleParamDef;
13
14 ReflectionCubeDownsampleMat::ReflectionCubeDownsampleMat()
15 {
16 mParamBuffer = gReflectionCubeDownsampleParamDef.createBuffer();
17
18 mParams->setParamBlockBuffer("Input", mParamBuffer);
19 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
20 }
21
22 void ReflectionCubeDownsampleMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 mip,
23 const SPtr<RenderTarget>& target)
24 {
25 BS_RENMAT_PROFILE_BLOCK
26
27 gReflectionCubeDownsampleParamDef.gCubeFace.set(mParamBuffer, face);
28
29 const RenderAPICapabilities& caps = gCaps();
30 if(caps.hasCapability(RSC_TEXTURE_VIEWS))
31 {
32 mInputTexture.set(source, TextureSurface(mip, 1, 0, 6));
33 gReflectionCubeDownsampleParamDef.gMipLevel.set(mParamBuffer, 0);
34 }
35 else
36 {
37 mInputTexture.set(source);
38 gReflectionCubeDownsampleParamDef.gMipLevel.set(mParamBuffer, mip);
39 }
40
41 RenderAPI& rapi = RenderAPI::instance();
42 rapi.setRenderTarget(target);
43
44 bind();
45 gRendererUtility().drawScreenQuad();
46 }
47
48 const UINT32 ReflectionCubeImportanceSampleMat::NUM_SAMPLES = 1024;
49 ReflectionCubeImportanceSampleParamDef gReflectionCubeImportanceSampleParamDef;
50
51 ReflectionCubeImportanceSampleMat::ReflectionCubeImportanceSampleMat()
52 {
53 mParamBuffer = gReflectionCubeImportanceSampleParamDef.createBuffer();
54
55 mParams->setParamBlockBuffer("Input", mParamBuffer);
56 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
57 }
58
59 void ReflectionCubeImportanceSampleMat::_initDefines(ShaderDefines& defines)
60 {
61 defines.set("NUM_SAMPLES", NUM_SAMPLES);
62 }
63
64 void ReflectionCubeImportanceSampleMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 mip,
65 const SPtr<RenderTarget>& target)
66 {
67 BS_RENMAT_PROFILE_BLOCK
68
69 mInputTexture.set(source);
70 gReflectionCubeImportanceSampleParamDef.gCubeFace.set(mParamBuffer, face);
71 gReflectionCubeImportanceSampleParamDef.gMipLevel.set(mParamBuffer, mip);
72 gReflectionCubeImportanceSampleParamDef.gNumMips.set(mParamBuffer, source->getProperties().getNumMipmaps() + 1);
73
74 float width = (float)source->getProperties().getWidth();
75 float height = (float)source->getProperties().getHeight();
76
77 // First part of the equation for determining mip level to sample from.
78 // See http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html
79 float mipFactor = 0.5f * std::log2(width * height / NUM_SAMPLES);
80 gReflectionCubeImportanceSampleParamDef.gPrecomputedMipFactor.set(mParamBuffer, mipFactor);
81
82 RenderAPI& rapi = RenderAPI::instance();
83 rapi.setRenderTarget(target);
84
85 bind();
86 gRendererUtility().drawScreenQuad();
87 }
88
89 IrradianceComputeSHParamDef gIrradianceComputeSHParamDef;
90
91 // TILE_WIDTH * TILE_HEIGHT must be pow2 because of parallel reduction algorithm
92 const static UINT32 TILE_WIDTH = 8;
93 const static UINT32 TILE_HEIGHT = 8;
94
95 // For very small textures this should be reduced so number of launched threads can properly utilize GPU cores
96 const static UINT32 PIXELS_PER_THREAD = 4;
97
98 IrradianceComputeSHMat::IrradianceComputeSHMat()
99 {
100 mParamBuffer = gIrradianceComputeSHParamDef.createBuffer();
101
102 mParams->setParamBlockBuffer("Params", mParamBuffer);
103 mParams->getTextureParam(GPT_COMPUTE_PROGRAM, "gInputTex", mInputTexture);
104 mParams->getBufferParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputBuffer);
105 }
106
107 void IrradianceComputeSHMat::_initDefines(ShaderDefines& defines)
108 {
109 defines.set("TILE_WIDTH", TILE_WIDTH);
110 defines.set("TILE_HEIGHT", TILE_HEIGHT);
111 defines.set("PIXELS_PER_THREAD", PIXELS_PER_THREAD);
112 }
113
114 void IrradianceComputeSHMat::execute(const SPtr<Texture>& source, UINT32 face, const SPtr<GpuBuffer>& output)
115 {
116 BS_RENMAT_PROFILE_BLOCK
117
118 auto& props = source->getProperties();
119 UINT32 faceSize = props.getWidth();
120 assert(faceSize == props.getHeight());
121
122 Vector2I dispatchSize;
123 dispatchSize.x = Math::divideAndRoundUp(faceSize, TILE_WIDTH * PIXELS_PER_THREAD);
124 dispatchSize.y = Math::divideAndRoundUp(faceSize, TILE_HEIGHT * PIXELS_PER_THREAD);
125
126 mInputTexture.set(source);
127 gIrradianceComputeSHParamDef.gCubeFace.set(mParamBuffer, face);
128 gIrradianceComputeSHParamDef.gFaceSize.set(mParamBuffer, source->getProperties().getWidth());
129 gIrradianceComputeSHParamDef.gDispatchSize.set(mParamBuffer, dispatchSize);
130
131 mOutputBuffer.set(output);
132
133 RenderAPI& rapi = RenderAPI::instance();
134
135 bind();
136 rapi.dispatchCompute(dispatchSize.x, dispatchSize.y);
137 }
138
139 SPtr<GpuBuffer> IrradianceComputeSHMat::createOutputBuffer(const SPtr<Texture>& source, UINT32& numCoeffSets)
140 {
141 auto& props = source->getProperties();
142 UINT32 faceSize = props.getWidth();
143 assert(faceSize == props.getHeight());
144
145 Vector2I dispatchSize;
146 dispatchSize.x = Math::divideAndRoundUp(faceSize, TILE_WIDTH * PIXELS_PER_THREAD);
147 dispatchSize.y = Math::divideAndRoundUp(faceSize, TILE_HEIGHT * PIXELS_PER_THREAD);
148
149 numCoeffSets = dispatchSize.x * dispatchSize.y * 6;
150
151 GPU_BUFFER_DESC bufferDesc;
152 bufferDesc.type = GBT_STRUCTURED;
153 bufferDesc.elementCount = numCoeffSets;
154 bufferDesc.format = BF_UNKNOWN;
155 bufferDesc.usage = GBU_LOADSTORE;
156
157 if(mVariation.getInt("SH_ORDER") == 3)
158 bufferDesc.elementSize = sizeof(SHCoeffsAndWeight3);
159 else
160 bufferDesc.elementSize = sizeof(SHCoeffsAndWeight5);
161
162 return GpuBuffer::create(bufferDesc);
163 }
164
165 IrradianceComputeSHMat* IrradianceComputeSHMat::getVariation(int order)
166 {
167 if (order == 3)
168 return get(getVariation<3>());
169
170 return get(getVariation<5>());
171 }
172
173 IrradianceComputeSHFragParamDef gIrradianceComputeSHFragParamDef;
174
175 IrradianceComputeSHFragMat::IrradianceComputeSHFragMat()
176 {
177 mParamBuffer = gIrradianceComputeSHFragParamDef.createBuffer();
178
179 mParams->setParamBlockBuffer("Params", mParamBuffer);
180 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
181 }
182
183 void IrradianceComputeSHFragMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 coefficientIdx,
184 const SPtr<RenderTarget>& output)
185 {
186 BS_RENMAT_PROFILE_BLOCK
187
188 // Set parameters
189 mInputTexture.set(source);
190
191 gIrradianceComputeSHFragParamDef.gCubeFace.set(mParamBuffer, face);
192 gIrradianceComputeSHFragParamDef.gFaceSize.set(mParamBuffer, source->getProperties().getWidth());
193 gIrradianceComputeSHFragParamDef.gCoeffEntryIdx.set(mParamBuffer, coefficientIdx / 4);
194 gIrradianceComputeSHFragParamDef.gCoeffComponentIdx.set(mParamBuffer, coefficientIdx % 4);
195
196 // Render
197 RenderAPI& rapi = RenderAPI::instance();
198 rapi.setRenderTarget(output);
199
200 bind();
201 gRendererUtility().drawScreenQuad();
202
203 rapi.setRenderTarget(nullptr);
204 }
205
206 POOLED_RENDER_TEXTURE_DESC IrradianceComputeSHFragMat::getOutputDesc(const SPtr<Texture>& input)
207 {
208 auto& props = input->getProperties();
209 return POOLED_RENDER_TEXTURE_DESC::createCube(PF_RGBA16F, props.getWidth(), props.getHeight(), TU_RENDERTARGET);
210 }
211
212 IrradianceAccumulateSHParamDef gIrradianceAccumulateSHParamDef;
213
214 IrradianceAccumulateSHMat::IrradianceAccumulateSHMat()
215 {
216 mParamBuffer = gIrradianceAccumulateSHParamDef.createBuffer();
217
218 mParams->setParamBlockBuffer("Params", mParamBuffer);
219 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
220 }
221
222 void IrradianceAccumulateSHMat::execute(const SPtr<Texture>& source, UINT32 face, UINT32 sourceMip,
223 const SPtr<RenderTarget>& output)
224 {
225 BS_RENMAT_PROFILE_BLOCK
226
227 // Set parameters
228 mInputTexture.set(source);
229
230 auto& props = source->getProperties();
231 Vector2 halfPixel(0.5f / props.getWidth(), 0.5f / props.getHeight());
232
233 gIrradianceAccumulateSHParamDef.gCubeFace.set(mParamBuffer, face);
234 gIrradianceAccumulateSHParamDef.gCubeMip.set(mParamBuffer, sourceMip);
235 gIrradianceAccumulateSHParamDef.gHalfPixel.set(mParamBuffer, halfPixel);
236
237 // Render
238 RenderAPI& rapi = RenderAPI::instance();
239 rapi.setRenderTarget(output);
240
241 bind();
242 gRendererUtility().drawScreenQuad();
243
244 rapi.setRenderTarget(nullptr);
245 }
246
247 POOLED_RENDER_TEXTURE_DESC IrradianceAccumulateSHMat::getOutputDesc(const SPtr<Texture>& input)
248 {
249 auto& props = input->getProperties();
250
251 // Assuming it's a cubemap
252 UINT32 size = std::max(1U, (UINT32)(props.getWidth() * 0.5f));
253
254 return POOLED_RENDER_TEXTURE_DESC::createCube(PF_RGBA32F, size, size, TU_RENDERTARGET);
255 }
256
257 IrradianceAccumulateCubeSHMat::IrradianceAccumulateCubeSHMat()
258 {
259 mParamBuffer = gIrradianceAccumulateSHParamDef.createBuffer();
260
261 mParams->setParamBlockBuffer("Params", mParamBuffer);
262 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
263 }
264
265 void IrradianceAccumulateCubeSHMat::execute(const SPtr<Texture>& source, UINT32 sourceMip, const Vector2I& outputOffset,
266 UINT32 coefficientIdx, const SPtr<RenderTarget>& output)
267 {
268 BS_RENMAT_PROFILE_BLOCK
269
270 // Set parameters
271 mInputTexture.set(source);
272
273 auto& props = source->getProperties();
274 Vector2 halfPixel(0.5f / props.getWidth(), 0.5f / props.getHeight());
275
276 gIrradianceAccumulateSHParamDef.gCubeFace.set(mParamBuffer, 0);
277 gIrradianceAccumulateSHParamDef.gCubeMip.set(mParamBuffer, sourceMip);
278 gIrradianceAccumulateSHParamDef.gHalfPixel.set(mParamBuffer, halfPixel);
279
280 auto& rtProps = output->getProperties();
281
282 // Render to just one pixel corresponding to the coefficient
283 Rect2 viewRect;
284 viewRect.x = (outputOffset.x + coefficientIdx) / (float)rtProps.width;
285 viewRect.y = outputOffset.y / (float)rtProps.height;
286
287 viewRect.width = 1.0f / rtProps.width;
288 viewRect.height = 1.0f / rtProps.height;
289
290 // Render
291 RenderAPI& rapi = RenderAPI::instance();
292 rapi.setRenderTarget(output);
293 rapi.setViewport(viewRect);
294
295 bind();
296 gRendererUtility().drawScreenQuad();
297
298 rapi.setRenderTarget(nullptr);
299 rapi.setViewport(Rect2(0, 0, 1, 1));
300 }
301
302 POOLED_RENDER_TEXTURE_DESC IrradianceAccumulateCubeSHMat::getOutputDesc()
303 {
304 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA32F, 9, 1, TU_RENDERTARGET);
305 }
306
307 IrradianceReduceSHParamDef gIrradianceReduceSHParamDef;
308
309 IrradianceReduceSHMat::IrradianceReduceSHMat()
310 {
311 mParamBuffer = gIrradianceReduceSHParamDef.createBuffer();
312
313 mParams->setParamBlockBuffer("Params", mParamBuffer);
314 mParams->getBufferParam(GPT_COMPUTE_PROGRAM, "gInput", mInputBuffer);
315 mParams->getLoadStoreTextureParam(GPT_COMPUTE_PROGRAM, "gOutput", mOutputTexture);
316 }
317
318 void IrradianceReduceSHMat::execute(const SPtr<GpuBuffer>& source, UINT32 numCoeffSets,
319 const SPtr<Texture>& output, UINT32 outputIdx)
320 {
321 BS_RENMAT_PROFILE_BLOCK
322
323 UINT32 shOrder = (UINT32)mVariation.getInt("SH_ORDER");
324
325 Vector2I outputCoords = IBLUtility::getSHCoeffXYFromIdx(outputIdx, shOrder);
326 gIrradianceReduceSHParamDef.gOutputIdx.set(mParamBuffer, outputCoords);
327 gIrradianceReduceSHParamDef.gNumEntries.set(mParamBuffer, numCoeffSets);
328
329 mInputBuffer.set(source);
330 mOutputTexture.set(output);
331
332 bind();
333
334 RenderAPI& rapi = RenderAPI::instance();
335 rapi.dispatchCompute(1);
336 }
337
338 SPtr<Texture> IrradianceReduceSHMat::createOutputTexture(UINT32 numCoeffSets)
339 {
340 UINT32 shOrder = (UINT32)mVariation.getInt("SH_ORDER");
341 Vector2I size = IBLUtility::getSHCoeffTextureSize(numCoeffSets, shOrder);
342
343 TEXTURE_DESC textureDesc;
344 textureDesc.width = (UINT32)size.x;
345 textureDesc.height = (UINT32)size.y;
346 textureDesc.format = PF_RGBA32F;
347 textureDesc.usage = TU_STATIC | TU_LOADSTORE;
348
349 return Texture::create(textureDesc);
350 }
351
352 IrradianceReduceSHMat* IrradianceReduceSHMat::getVariation(int order)
353 {
354 if (order == 3)
355 return get(getVariation<3>());
356
357 return get(getVariation<5>());
358 }
359
360 IrradianceProjectSHParamDef gIrradianceProjectSHParamDef;
361
362 IrradianceProjectSHMat::IrradianceProjectSHMat()
363 {
364 mParamBuffer = gIrradianceProjectSHParamDef.createBuffer();
365
366 mParams->setParamBlockBuffer("Params", mParamBuffer);
367 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSHCoeffs", mInputTexture);
368 }
369
370 void IrradianceProjectSHMat::execute(const SPtr<Texture>& shCoeffs, UINT32 face, const SPtr<RenderTarget>& target)
371 {
372 BS_RENMAT_PROFILE_BLOCK
373
374 gIrradianceProjectSHParamDef.gCubeFace.set(mParamBuffer, face);
375
376 mInputTexture.set(shCoeffs);
377
378 RenderAPI& rapi = RenderAPI::instance();
379 rapi.setRenderTarget(target);
380
381 bind();
382 gRendererUtility().drawScreenQuad();
383 }
384
385 void RenderBeastIBLUtility::filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch) const
386 {
387 auto& props = cubemap->getProperties();
388
389 SPtr<Texture> scratchCubemap = scratch;
390 if (scratchCubemap == nullptr)
391 {
392 TEXTURE_DESC cubemapDesc;
393 cubemapDesc.type = TEX_TYPE_CUBE_MAP;
394 cubemapDesc.format = props.getFormat();
395 cubemapDesc.width = props.getWidth();
396 cubemapDesc.height = props.getHeight();
397 cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
398 cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
399
400 scratchCubemap = Texture::create(cubemapDesc);
401 }
402
403 // We sample the cubemaps using importance sampling to generate roughness
404 UINT32 numMips = props.getNumMipmaps() + 1;
405
406 // Before importance sampling the cubemaps we first create box filtered versions for each mip level. This helps fix
407 // the aliasing artifacts that would otherwise be noticeable on importance sampled cubemaps. The aliasing happens
408 // because:
409 // 1. We use the same random samples for all pixels, which appears to duplicate reflections instead of creating
410 // noise, which is usually more acceptable
411 // 2. Even if we were to use fully random samples we would need a lot to avoid noticeable noise, which isn't
412 // practical
413
414 // Copy base mip level to scratch cubemap
415 for (UINT32 face = 0; face < 6; face++)
416 {
417 TEXTURE_COPY_DESC copyDesc;
418 copyDesc.srcFace = face;
419 copyDesc.dstFace = face;
420
421 cubemap->copy(scratchCubemap, copyDesc);
422 }
423
424 // Fill out remaining scratch mip levels by downsampling
425 for (UINT32 mip = 1; mip < numMips; mip++)
426 {
427 UINT32 sourceMip = mip - 1;
428 downsampleCubemap(scratchCubemap, sourceMip, scratchCubemap, mip);
429 }
430
431 // Importance sample
432 for (UINT32 mip = 1; mip < numMips; mip++)
433 {
434 for (UINT32 face = 0; face < 6; face++)
435 {
436 RENDER_TEXTURE_DESC cubeFaceRTDesc;
437 cubeFaceRTDesc.colorSurfaces[0].texture = cubemap;
438 cubeFaceRTDesc.colorSurfaces[0].face = face;
439 cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
440 cubeFaceRTDesc.colorSurfaces[0].mipLevel = mip;
441
442 SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
443
444 ReflectionCubeImportanceSampleMat* material = ReflectionCubeImportanceSampleMat::get();
445 material->execute(scratchCubemap, face, mip, target);
446 }
447 }
448
449 RenderAPI& rapi = RenderAPI::instance();
450 rapi.setRenderTarget(nullptr);
451 }
452
453 bool supportsComputeSH()
454 {
455 return gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop;
456 }
457
458 void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output) const
459 {
460 SPtr<Texture> coeffTexture;
461 if(supportsComputeSH())
462 {
463 IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(5);
464 IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(5);
465
466 UINT32 numCoeffSets;
467 SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
468 for (UINT32 face = 0; face < 6; face++)
469 shCompute->execute(cubemap, face, coeffSetBuffer);
470
471 coeffTexture = shReduce->createOutputTexture(1);
472 shReduce->execute(coeffSetBuffer, numCoeffSets, coeffTexture, 0);
473 }
474 else
475 {
476 GpuResourcePool& resPool = GpuResourcePool::instance();
477 SPtr<PooledRenderTexture> finalCoeffs = resPool.get(IrradianceAccumulateCubeSHMat::getOutputDesc());
478
479 filterCubemapForIrradianceNonCompute(cubemap, 0, finalCoeffs->renderTexture);
480 coeffTexture = finalCoeffs->texture;
481 }
482
483 IrradianceProjectSHMat* shProject = IrradianceProjectSHMat::get();
484 for (UINT32 face = 0; face < 6; face++)
485 {
486 RENDER_TEXTURE_DESC cubeFaceRTDesc;
487 cubeFaceRTDesc.colorSurfaces[0].texture = output;
488 cubeFaceRTDesc.colorSurfaces[0].face = face;
489 cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
490 cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
491
492 SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
493 shProject->execute(coeffTexture, face, target);
494 }
495 }
496
497 void RenderBeastIBLUtility::filterCubemapForIrradiance(const SPtr<Texture>& cubemap, const SPtr<Texture>& output,
498 UINT32 outputIdx) const
499 {
500 if(supportsComputeSH())
501 {
502 IrradianceComputeSHMat* shCompute = IrradianceComputeSHMat::getVariation(3);
503 IrradianceReduceSHMat* shReduce = IrradianceReduceSHMat::getVariation(3);
504
505 UINT32 numCoeffSets;
506 SPtr<GpuBuffer> coeffSetBuffer = shCompute->createOutputBuffer(cubemap, numCoeffSets);
507 for (UINT32 face = 0; face < 6; face++)
508 shCompute->execute(cubemap, face, coeffSetBuffer);
509
510 shReduce->execute(coeffSetBuffer, numCoeffSets, output, outputIdx);
511 }
512 else
513 {
514 RENDER_TEXTURE_DESC rtDesc;
515 rtDesc.colorSurfaces[0].texture = output;
516
517 SPtr<RenderTexture> target = RenderTexture::create(rtDesc);
518 filterCubemapForIrradianceNonCompute(cubemap, outputIdx, target);
519 }
520 }
521
522 void RenderBeastIBLUtility::scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
523 UINT32 dstMip) const
524 {
525 auto& srcProps = src->getProperties();
526 auto& dstProps = dst->getProperties();
527
528 SPtr<Texture> scratchTex = src;
529 int sizeSrcLog2 = (int)log2((float)srcProps.getWidth());
530 int sizeDstLog2 = (int)log2((float)dstProps.getWidth());
531
532 int sizeLog2Diff = sizeSrcLog2 - sizeDstLog2;
533
534 // If size difference is greater than one mip-level and we're downscaling, we need to generate intermediate mip
535 // levels
536 if(sizeLog2Diff > 1)
537 {
538 UINT32 mipSize = (UINT32)exp2((float)(sizeSrcLog2 - 1));
539 UINT32 numDownsamples = sizeLog2Diff - 1;
540
541 TEXTURE_DESC cubemapDesc;
542 cubemapDesc.type = TEX_TYPE_CUBE_MAP;
543 cubemapDesc.format = srcProps.getFormat();
544 cubemapDesc.width = mipSize;
545 cubemapDesc.height = mipSize;
546 cubemapDesc.numMips = numDownsamples - 1;
547 cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
548
549 scratchTex = Texture::create(cubemapDesc);
550
551 downsampleCubemap(src, srcMip, scratchTex, 0);
552 for(UINT32 i = 0; i < cubemapDesc.numMips; i++)
553 downsampleCubemap(scratchTex, i, scratchTex, i + 1);
554
555 srcMip = cubemapDesc.numMips;
556 }
557
558 // Same size so just copy
559 if(sizeSrcLog2 == sizeDstLog2)
560 {
561 for (UINT32 face = 0; face < 6; face++)
562 {
563 TEXTURE_COPY_DESC copyDesc;
564 copyDesc.srcFace = face;
565 copyDesc.srcMip = srcMip;
566 copyDesc.dstFace = face;
567 copyDesc.dstMip = dstMip;
568
569 src->copy(dst, copyDesc);
570 }
571 }
572 else
573 downsampleCubemap(scratchTex, srcMip, dst, dstMip);
574 }
575
576 void RenderBeastIBLUtility::downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst,
577 UINT32 dstMip)
578 {
579 for (UINT32 face = 0; face < 6; face++)
580 {
581 RENDER_TEXTURE_DESC cubeFaceRTDesc;
582 cubeFaceRTDesc.colorSurfaces[0].texture = dst;
583 cubeFaceRTDesc.colorSurfaces[0].face = face;
584 cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
585 cubeFaceRTDesc.colorSurfaces[0].mipLevel = dstMip;
586
587 SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
588
589 ReflectionCubeDownsampleMat* material = ReflectionCubeDownsampleMat::get();
590 material->execute(src, face, srcMip, target);
591 }
592 }
593
594 void RenderBeastIBLUtility::filterCubemapForIrradianceNonCompute(const SPtr<Texture>& cubemap, UINT32 outputIdx,
595 const SPtr<RenderTexture>& output)
596 {
597 static const UINT32 NUM_COEFFS = 9;
598
599 GpuResourcePool& resPool = GpuResourcePool::instance();
600 IrradianceComputeSHFragMat* shCompute = IrradianceComputeSHFragMat::get();
601 IrradianceAccumulateSHMat* shAccum = IrradianceAccumulateSHMat::get();
602 IrradianceAccumulateCubeSHMat* shAccumCube = IrradianceAccumulateCubeSHMat::get();
603
604 for(UINT32 coeff = 0; coeff < NUM_COEFFS; ++coeff)
605 {
606 SPtr<PooledRenderTexture> coeffsTex = resPool.get(shCompute->getOutputDesc(cubemap));
607
608 // Generate SH coefficients and weights per-texel
609 for(UINT32 face = 0; face < 6; face++)
610 {
611 RENDER_TEXTURE_DESC cubeFaceRTDesc;
612 cubeFaceRTDesc.colorSurfaces[0].texture = coeffsTex->texture;
613 cubeFaceRTDesc.colorSurfaces[0].face = face;
614 cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
615 cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
616
617 SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
618 shCompute->execute(cubemap, face, coeff, target);
619 }
620
621 // Downsample, summing up coefficients and weights all the way down to 1x1
622 auto& sourceProps = cubemap->getProperties();
623 UINT32 numMips = PixelUtil::getMaxMipmaps(sourceProps.getWidth(), sourceProps.getHeight(), 1,
624 sourceProps.getFormat());
625
626 SPtr<PooledRenderTexture> downsampleInput = coeffsTex;
627 coeffsTex = nullptr;
628
629 for(UINT32 mip = 0; mip < numMips; mip++)
630 {
631 SPtr<PooledRenderTexture> accumCoeffsTex = resPool.get(shAccum->getOutputDesc(downsampleInput->texture));
632
633 for(UINT32 face = 0; face < 6; face++)
634 {
635 RENDER_TEXTURE_DESC cubeFaceRTDesc;
636 cubeFaceRTDesc.colorSurfaces[0].texture = accumCoeffsTex->texture;
637 cubeFaceRTDesc.colorSurfaces[0].face = face;
638 cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
639 cubeFaceRTDesc.colorSurfaces[0].mipLevel = 0;
640
641 SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
642 shAccum->execute(downsampleInput->texture, face, 0, target);
643 }
644
645 downsampleInput = accumCoeffsTex;
646 }
647
648 // Sum up all the faces and write the coefficient to the final texture
649 Vector2I outputOffset = getSHCoeffXYFromIdx(outputIdx, 3);
650 shAccumCube->execute(downsampleInput->texture, 0, outputOffset, coeff, output);
651 }
652 }
653}}
654