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 "BsPostProcessing.h"
4#include "RenderAPI/BsRenderTexture.h"
5#include "Renderer/BsRendererUtility.h"
6#include "Renderer/BsCamera.h"
7#include "Material/BsGpuParamsSet.h"
8#include "Image/BsPixelUtil.h"
9#include "Utility/BsBitwise.h"
10#include "Renderer/BsGpuResourcePool.h"
11#include "BsRendererView.h"
12#include "BsRenderBeast.h"
13
14namespace bs { namespace ct
15{
16 void setSamplerState(const SPtr<GpuParams>& params, GpuProgramType gpType, const String& name,
17 const String& secondaryName, const SPtr<SamplerState>& samplerState, bool optional = false)
18 {
19 if (params->hasSamplerState(gpType, name))
20 params->setSamplerState(gpType, name, samplerState);
21 else
22 {
23 if(optional)
24 {
25 if (params->hasSamplerState(gpType, secondaryName))
26 params->setSamplerState(gpType, secondaryName, samplerState);
27 }
28 else
29 params->setSamplerState(gpType, secondaryName, samplerState);
30 }
31 }
32
33 DownsampleParamDef gDownsampleParamDef;
34
35 DownsampleMat::DownsampleMat()
36 {
37 mParamBuffer = gDownsampleParamDef.createBuffer();
38
39 if(mParams->hasParamBlock(GPT_FRAGMENT_PROGRAM, "Input"))
40 mParams->setParamBlockBuffer("Input", mParamBuffer);
41
42 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
43 }
44
45 void DownsampleMat::execute(const SPtr<Texture>& input, const SPtr<RenderTarget>& output)
46 {
47 BS_RENMAT_PROFILE_BLOCK
48
49 // Set parameters
50 mInputTexture.set(input);
51
52 const TextureProperties& rtProps = input->getProperties();
53
54 bool MSAA = mVariation.getInt("MSAA") > 0;
55 if(MSAA)
56 {
57 gDownsampleParamDef.gOffsets.set(mParamBuffer, Vector2(-1.0f, -1.0f));
58 gDownsampleParamDef.gOffsets.set(mParamBuffer, Vector2(1.0f, -1.0f));
59 gDownsampleParamDef.gOffsets.set(mParamBuffer, Vector2(-1.0f, 1.0f));
60 gDownsampleParamDef.gOffsets.set(mParamBuffer, Vector2(1.0f, 1.0f));
61 }
62 else
63 {
64 Vector2 invTextureSize(1.0f / rtProps.getWidth(), 1.0f / rtProps.getHeight());
65
66 gDownsampleParamDef.gOffsets.set(mParamBuffer, invTextureSize * Vector2(-1.0f, -1.0f));
67 gDownsampleParamDef.gOffsets.set(mParamBuffer, invTextureSize * Vector2(1.0f, -1.0f));
68 gDownsampleParamDef.gOffsets.set(mParamBuffer, invTextureSize * Vector2(-1.0f, 1.0f));
69 gDownsampleParamDef.gOffsets.set(mParamBuffer, invTextureSize * Vector2(1.0f, 1.0f));
70 }
71
72 RenderAPI& rapi = RenderAPI::instance();
73 rapi.setRenderTarget(output, FBT_DEPTH | FBT_STENCIL);
74
75 bind();
76
77 if (MSAA)
78 gRendererUtility().drawScreenQuad(Rect2(0.0f, 0.0f, (float)rtProps.getWidth(), (float)rtProps.getHeight()));
79 else
80 gRendererUtility().drawScreenQuad();
81
82 rapi.setRenderTarget(nullptr);
83 }
84
85 POOLED_RENDER_TEXTURE_DESC DownsampleMat::getOutputDesc(const SPtr<Texture>& target)
86 {
87 const TextureProperties& rtProps = target->getProperties();
88
89 UINT32 width = std::max(1, Math::ceilToInt(rtProps.getWidth() * 0.5f));
90 UINT32 height = std::max(1, Math::ceilToInt(rtProps.getHeight() * 0.5f));
91
92 return POOLED_RENDER_TEXTURE_DESC::create2D(rtProps.getFormat(), width, height, TU_RENDERTARGET);
93 }
94
95 DownsampleMat* DownsampleMat::getVariation(UINT32 quality, bool msaa)
96 {
97 if(quality == 0)
98 {
99 if (msaa)
100 return get(getVariation<0, true>());
101 else
102 return get(getVariation<0, false>());
103 }
104 else
105 {
106 if (msaa)
107 return get(getVariation<1, true>());
108 else
109 return get(getVariation<1, false>());
110 }
111 }
112
113 EyeAdaptHistogramParamDef gEyeAdaptHistogramParamDef;
114
115 EyeAdaptHistogramMat::EyeAdaptHistogramMat()
116 {
117 mParamBuffer = gEyeAdaptHistogramParamDef.createBuffer();
118
119 mParams->setParamBlockBuffer("Input", mParamBuffer);
120 mParams->getTextureParam(GPT_COMPUTE_PROGRAM, "gSceneColorTex", mSceneColor);
121 mParams->getLoadStoreTextureParam(GPT_COMPUTE_PROGRAM, "gOutputTex", mOutputTex);
122 }
123
124 void EyeAdaptHistogramMat::_initDefines(ShaderDefines& defines)
125 {
126 defines.set("THREADGROUP_SIZE_X", THREAD_GROUP_SIZE_X);
127 defines.set("THREADGROUP_SIZE_Y", THREAD_GROUP_SIZE_Y);
128 defines.set("LOOP_COUNT_X", LOOP_COUNT_X);
129 defines.set("LOOP_COUNT_Y", LOOP_COUNT_Y);
130 }
131
132 void EyeAdaptHistogramMat::execute(const SPtr<Texture>& input, const SPtr<Texture>& output,
133 const AutoExposureSettings& settings)
134 {
135 BS_RENMAT_PROFILE_BLOCK
136
137 // Set parameters
138 mSceneColor.set(input);
139
140 const TextureProperties& props = input->getProperties();
141 Vector4I offsetAndSize(0, 0, (INT32)props.getWidth(), (INT32)props.getHeight());
142
143 gEyeAdaptHistogramParamDef.gHistogramParams.set(mParamBuffer, getHistogramScaleOffset(settings));
144 gEyeAdaptHistogramParamDef.gPixelOffsetAndSize.set(mParamBuffer, offsetAndSize);
145
146 Vector2I threadGroupCount = getThreadGroupCount(input);
147 gEyeAdaptHistogramParamDef.gThreadGroupCount.set(mParamBuffer, threadGroupCount);
148
149 // Dispatch
150 mOutputTex.set(output);
151
152 bind();
153
154 RenderAPI& rapi = RenderAPI::instance();
155 rapi.dispatchCompute(threadGroupCount.x, threadGroupCount.y);
156 }
157
158 POOLED_RENDER_TEXTURE_DESC EyeAdaptHistogramMat::getOutputDesc(const SPtr<Texture>& target)
159 {
160 Vector2I threadGroupCount = getThreadGroupCount(target);
161 UINT32 numHistograms = threadGroupCount.x * threadGroupCount.y;
162
163 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA16F, HISTOGRAM_NUM_TEXELS, numHistograms,
164 TU_LOADSTORE);
165 }
166
167 Vector2I EyeAdaptHistogramMat::getThreadGroupCount(const SPtr<Texture>& target)
168 {
169 const UINT32 texelsPerThreadGroupX = THREAD_GROUP_SIZE_X * LOOP_COUNT_X;
170 const UINT32 texelsPerThreadGroupY = THREAD_GROUP_SIZE_Y * LOOP_COUNT_Y;
171
172 const TextureProperties& props = target->getProperties();
173
174 Vector2I threadGroupCount;
175 threadGroupCount.x = ((INT32)props.getWidth() + texelsPerThreadGroupX - 1) / texelsPerThreadGroupX;
176 threadGroupCount.y = ((INT32)props.getHeight() + texelsPerThreadGroupY - 1) / texelsPerThreadGroupY;
177
178 return threadGroupCount;
179 }
180
181 Vector2 EyeAdaptHistogramMat::getHistogramScaleOffset(const AutoExposureSettings& settings)
182 {
183 float diff = settings.histogramLog2Max - settings.histogramLog2Min;
184 float scale = 1.0f / diff;
185 float offset = -settings.histogramLog2Min * scale;
186
187 return Vector2(scale, offset);
188 }
189
190 EyeAdaptHistogramReduceParamDef gEyeAdaptHistogramReduceParamDef;
191
192 EyeAdaptHistogramReduceMat::EyeAdaptHistogramReduceMat()
193 {
194 mParamBuffer = gEyeAdaptHistogramReduceParamDef.createBuffer();
195
196 mParams->setParamBlockBuffer("Input", mParamBuffer);
197 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gHistogramTex", mHistogramTex);
198 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gEyeAdaptationTex", mEyeAdaptationTex);
199 }
200
201 void EyeAdaptHistogramReduceMat::execute(const SPtr<Texture>& sceneColor, const SPtr<Texture>& histogram,
202 const SPtr<Texture>& prevFrame, const SPtr<RenderTarget>& output)
203 {
204 BS_RENMAT_PROFILE_BLOCK
205
206 // Set parameters
207 mHistogramTex.set(histogram);
208
209 SPtr<Texture> eyeAdaptationTex;
210 if (prevFrame == nullptr) // Could be that this is the first run
211 eyeAdaptationTex = Texture::WHITE;
212 else
213 eyeAdaptationTex = prevFrame;
214
215 mEyeAdaptationTex.set(eyeAdaptationTex);
216
217 Vector2I threadGroupCount = EyeAdaptHistogramMat::getThreadGroupCount(sceneColor);
218 UINT32 numHistograms = threadGroupCount.x * threadGroupCount.y;
219
220 gEyeAdaptHistogramReduceParamDef.gThreadGroupCount.set(mParamBuffer, numHistograms);
221
222 RenderAPI& rapi = RenderAPI::instance();
223 rapi.setRenderTarget(output, FBT_DEPTH | FBT_STENCIL);
224
225 bind();
226
227 Rect2 drawUV(0.0f, 0.0f, (float)EyeAdaptHistogramMat::HISTOGRAM_NUM_TEXELS, 2.0f);
228 gRendererUtility().drawScreenQuad(drawUV);
229
230 rapi.setRenderTarget(nullptr);
231 }
232
233 POOLED_RENDER_TEXTURE_DESC EyeAdaptHistogramReduceMat::getOutputDesc()
234 {
235 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA16F, EyeAdaptHistogramMat::HISTOGRAM_NUM_TEXELS, 2,
236 TU_RENDERTARGET);
237 }
238
239 EyeAdaptationParamDef gEyeAdaptationParamDef;
240
241 EyeAdaptationMat::EyeAdaptationMat()
242 {
243 mParamBuffer = gEyeAdaptationParamDef.createBuffer();
244
245 mParams->setParamBlockBuffer("EyeAdaptationParams", mParamBuffer);
246 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gHistogramTex", mReducedHistogramTex);
247 }
248
249 void EyeAdaptationMat::_initDefines(ShaderDefines& defines)
250 {
251 defines.set("THREADGROUP_SIZE_X", EyeAdaptHistogramMat::THREAD_GROUP_SIZE_X);
252 defines.set("THREADGROUP_SIZE_Y", EyeAdaptHistogramMat::THREAD_GROUP_SIZE_Y);
253 }
254
255 void EyeAdaptationMat::execute(const SPtr<Texture>& reducedHistogram, const SPtr<RenderTarget>& output,
256 float frameDelta, const AutoExposureSettings& settings, float exposureScale)
257 {
258 BS_RENMAT_PROFILE_BLOCK
259
260 // Set parameters
261 mReducedHistogramTex.set(reducedHistogram);
262
263 populateParams(mParamBuffer, frameDelta, settings, exposureScale);
264
265 // Render
266 RenderAPI& rapi = RenderAPI::instance();
267 rapi.setRenderTarget(output, FBT_DEPTH | FBT_STENCIL);
268
269 bind();
270 gRendererUtility().drawScreenQuad();
271
272 rapi.setRenderTarget(nullptr);
273 }
274
275 POOLED_RENDER_TEXTURE_DESC EyeAdaptationMat::getOutputDesc()
276 {
277 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_R32F, 1, 1, TU_RENDERTARGET);
278 }
279
280 void EyeAdaptationMat::populateParams(const SPtr<GpuParamBlockBuffer>& paramBuffer, float frameDelta,
281 const AutoExposureSettings& settings, float exposureScale)
282 {
283 Vector2 histogramScaleAndOffset = EyeAdaptHistogramMat::getHistogramScaleOffset(settings);
284
285 Vector4 eyeAdaptationParams[3];
286 eyeAdaptationParams[0].x = histogramScaleAndOffset.x;
287 eyeAdaptationParams[0].y = histogramScaleAndOffset.y;
288
289 float histogramPctHigh = Math::clamp01(settings.histogramPctHigh);
290
291 eyeAdaptationParams[0].z = std::min(Math::clamp01(settings.histogramPctLow), histogramPctHigh);
292 eyeAdaptationParams[0].w = histogramPctHigh;
293
294 eyeAdaptationParams[1].x = std::min(settings.minEyeAdaptation, settings.maxEyeAdaptation);
295 eyeAdaptationParams[1].y = settings.maxEyeAdaptation;
296
297 eyeAdaptationParams[1].z = settings.eyeAdaptationSpeedUp;
298 eyeAdaptationParams[1].w = settings.eyeAdaptationSpeedDown;
299
300 eyeAdaptationParams[2].x = Math::pow(2.0f, exposureScale);
301 eyeAdaptationParams[2].y = frameDelta;
302
303 eyeAdaptationParams[2].z = Math::pow(2.0f, settings.histogramLog2Min);
304 eyeAdaptationParams[2].w = 0.0f; // Unused
305
306 gEyeAdaptationParamDef.gEyeAdaptationParams.set(paramBuffer, eyeAdaptationParams[0], 0);
307 gEyeAdaptationParamDef.gEyeAdaptationParams.set(paramBuffer, eyeAdaptationParams[1], 1);
308 gEyeAdaptationParamDef.gEyeAdaptationParams.set(paramBuffer, eyeAdaptationParams[2], 2);
309 }
310
311 EyeAdaptationBasicSetupMat::EyeAdaptationBasicSetupMat()
312 {
313 mParamBuffer = gEyeAdaptationParamDef.createBuffer();
314
315 mParams->setParamBlockBuffer("EyeAdaptationParams", mParamBuffer);
316 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTex);
317
318 SAMPLER_STATE_DESC desc;
319 desc.minFilter = FO_POINT;
320 desc.magFilter = FO_POINT;
321 desc.mipFilter = FO_POINT;
322
323 SPtr<SamplerState> samplerState = SamplerState::create(desc);
324 setSamplerState(mParams, GPT_FRAGMENT_PROGRAM, "gInputSamp", "gInputTex", samplerState);
325 }
326
327 void EyeAdaptationBasicSetupMat::execute(const SPtr<Texture>& input, const SPtr<RenderTarget>& output,
328 float frameDelta, const AutoExposureSettings& settings, float exposureScale)
329 {
330 BS_RENMAT_PROFILE_BLOCK
331
332 // Set parameters
333 mInputTex.set(input);
334
335 EyeAdaptationMat::populateParams(mParamBuffer, frameDelta, settings, exposureScale);
336
337 // Render
338 RenderAPI& rapi = RenderAPI::instance();
339 rapi.setRenderTarget(output);
340
341 bind();
342 gRendererUtility().drawScreenQuad();
343
344 rapi.setRenderTarget(nullptr);
345 }
346
347 POOLED_RENDER_TEXTURE_DESC EyeAdaptationBasicSetupMat::getOutputDesc(const SPtr<Texture>& input)
348 {
349 auto& props = input->getProperties();
350 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA16F, props.getWidth(), props.getHeight(), TU_RENDERTARGET);
351 }
352
353 EyeAdaptationBasicParamsMatDef gEyeAdaptationBasicParamsMatDef;
354
355 EyeAdaptationBasicMat::EyeAdaptationBasicMat()
356 {
357 mEyeAdaptationParamsBuffer = gEyeAdaptationParamDef.createBuffer();
358 mParamsBuffer = gEyeAdaptationBasicParamsMatDef.createBuffer();
359
360 mParams->setParamBlockBuffer("EyeAdaptationParams", mEyeAdaptationParamsBuffer);
361 mParams->setParamBlockBuffer("Input", mParamsBuffer);
362 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gCurFrameTex", mCurFrameTexParam);
363 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gPrevFrameTex", mPrevFrameTexParam);
364 }
365
366 void EyeAdaptationBasicMat::execute(const SPtr<Texture>& curFrame, const SPtr<Texture>& prevFrame,
367 const SPtr<RenderTarget>& output, float frameDelta, const AutoExposureSettings& settings, float exposureScale)
368 {
369 BS_RENMAT_PROFILE_BLOCK
370
371 // Set parameters
372 mCurFrameTexParam.set(curFrame);
373
374 if (prevFrame == nullptr) // Could be that this is the first run
375 mPrevFrameTexParam.set(Texture::WHITE);
376 else
377 mPrevFrameTexParam.set(prevFrame);
378
379 EyeAdaptationMat::populateParams(mEyeAdaptationParamsBuffer, frameDelta, settings, exposureScale);
380
381 auto& texProps = curFrame->getProperties();
382 Vector2I texSize = { (INT32)texProps.getWidth(), (INT32)texProps.getHeight() };
383
384 gEyeAdaptationBasicParamsMatDef.gInputTexSize.set(mParamsBuffer, texSize);
385
386 // Render
387 RenderAPI& rapi = RenderAPI::instance();
388 rapi.setRenderTarget(output);
389
390 bind();
391 gRendererUtility().drawScreenQuad();
392
393 rapi.setRenderTarget(nullptr);
394 }
395
396 POOLED_RENDER_TEXTURE_DESC EyeAdaptationBasicMat::getOutputDesc()
397 {
398 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_R32F, 1, 1, TU_RENDERTARGET);
399 }
400
401 CreateTonemapLUTParamDef gCreateTonemapLUTParamDef;
402 WhiteBalanceParamDef gWhiteBalanceParamDef;
403
404 CreateTonemapLUTMat::CreateTonemapLUTMat()
405 {
406 mIs3D = mVariation.getBool("VOLUME_LUT");
407
408 mParamBuffer = gCreateTonemapLUTParamDef.createBuffer();
409 mWhiteBalanceParamBuffer = gWhiteBalanceParamDef.createBuffer();
410
411 mParams->setParamBlockBuffer("Input", mParamBuffer);
412 mParams->setParamBlockBuffer("WhiteBalanceInput", mWhiteBalanceParamBuffer);
413
414 if(mIs3D)
415 mParams->getLoadStoreTextureParam(GPT_COMPUTE_PROGRAM, "gOutputTex", mOutputTex);
416 }
417
418 void CreateTonemapLUTMat::_initDefines(ShaderDefines& defines)
419 {
420 defines.set("LUT_SIZE", LUT_SIZE);
421 }
422
423 void CreateTonemapLUTMat::execute3D(const SPtr<Texture>& output, const RenderSettings& settings)
424 {
425 assert(mIs3D);
426 BS_RENMAT_PROFILE_BLOCK
427
428 populateParamBuffers(settings);
429
430 // Dispatch
431 mOutputTex.set(output);
432
433 bind();
434
435 RenderAPI& rapi = RenderAPI::instance();
436 rapi.dispatchCompute(LUT_SIZE / 8, LUT_SIZE / 8, LUT_SIZE);
437 }
438
439 void CreateTonemapLUTMat::execute2D(const SPtr<RenderTexture>& output, const RenderSettings& settings)
440 {
441 assert(!mIs3D);
442 BS_RENMAT_PROFILE_BLOCK
443
444 populateParamBuffers(settings);
445
446 // Render
447 RenderAPI& rapi = RenderAPI::instance();
448 rapi.setRenderTarget(output);
449
450 bind();
451 gRendererUtility().drawScreenQuad();
452
453 rapi.setRenderTarget(nullptr);
454 }
455
456 void CreateTonemapLUTMat::populateParamBuffers(const RenderSettings& settings)
457 {
458 // Set parameters
459 gCreateTonemapLUTParamDef.gGammaAdjustment.set(mParamBuffer, 2.2f / settings.gamma);
460
461 // Note: Assuming sRGB (PC monitor) for now, change to Rec.709 when running on console (value 1), or to raw 2.2
462 // gamma when running on Mac (value 2)
463 gCreateTonemapLUTParamDef.gGammaCorrectionType.set(mParamBuffer, 0);
464
465 Vector4 tonemapParams[2];
466 tonemapParams[0].x = settings.tonemapping.filmicCurveShoulderStrength;
467 tonemapParams[0].y = settings.tonemapping.filmicCurveLinearStrength;
468 tonemapParams[0].z = settings.tonemapping.filmicCurveLinearAngle;
469 tonemapParams[0].w = settings.tonemapping.filmicCurveToeStrength;
470
471 tonemapParams[1].x = settings.tonemapping.filmicCurveToeNumerator;
472 tonemapParams[1].y = settings.tonemapping.filmicCurveToeDenominator;
473 tonemapParams[1].z = settings.tonemapping.filmicCurveLinearWhitePoint;
474 tonemapParams[1].w = 0.0f; // Unused
475
476 gCreateTonemapLUTParamDef.gTonemapParams.set(mParamBuffer, tonemapParams[0], 0);
477 gCreateTonemapLUTParamDef.gTonemapParams.set(mParamBuffer, tonemapParams[1], 1);
478
479 // Set color grading params
480 gCreateTonemapLUTParamDef.gSaturation.set(mParamBuffer, settings.colorGrading.saturation);
481 gCreateTonemapLUTParamDef.gContrast.set(mParamBuffer, settings.colorGrading.contrast);
482 gCreateTonemapLUTParamDef.gGain.set(mParamBuffer, settings.colorGrading.gain);
483 gCreateTonemapLUTParamDef.gOffset.set(mParamBuffer, settings.colorGrading.offset);
484
485 // Set white balance params
486 gWhiteBalanceParamDef.gWhiteTemp.set(mWhiteBalanceParamBuffer, settings.whiteBalance.temperature);
487 gWhiteBalanceParamDef.gWhiteOffset.set(mWhiteBalanceParamBuffer, settings.whiteBalance.tint);
488 }
489
490 POOLED_RENDER_TEXTURE_DESC CreateTonemapLUTMat::getOutputDesc() const
491 {
492 if(mIs3D)
493 return POOLED_RENDER_TEXTURE_DESC::create3D(PF_RGBA8, LUT_SIZE, LUT_SIZE, LUT_SIZE, TU_LOADSTORE);
494
495 return POOLED_RENDER_TEXTURE_DESC::create2D(PF_RGBA8, LUT_SIZE * LUT_SIZE, LUT_SIZE, TU_RENDERTARGET);
496 }
497
498 CreateTonemapLUTMat* CreateTonemapLUTMat::getVariation(bool is3D)
499 {
500 if(is3D)
501 return get(getVariation<true>());
502
503 return get(getVariation<false>());
504 }
505
506 TonemappingParamDef gTonemappingParamDef;
507
508 TonemappingMat::TonemappingMat()
509 {
510 mParamBuffer = gTonemappingParamDef.createBuffer();
511
512 mParams->setParamBlockBuffer("Input", mParamBuffer);
513 mParams->getTextureParam(GPT_VERTEX_PROGRAM, "gEyeAdaptationTex", mEyeAdaptationTex);
514 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTex);
515 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gBloomTex", mBloomTex);
516
517 if(!mVariation.getBool("GAMMA_ONLY"))
518 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gColorLUT", mColorLUT);
519 }
520
521 void TonemappingMat::_initDefines(ShaderDefines& defines)
522 {
523 defines.set("LUT_SIZE", CreateTonemapLUTMat::LUT_SIZE);
524 }
525
526 void TonemappingMat::execute(const SPtr<Texture>& sceneColor, const SPtr<Texture>& eyeAdaptation,
527 const SPtr<Texture>& bloom, const SPtr<Texture>& colorLUT, const SPtr<RenderTarget>& output,
528 const RenderSettings& settings)
529 {
530 BS_RENMAT_PROFILE_BLOCK
531
532 const TextureProperties& texProps = sceneColor->getProperties();
533
534 gTonemappingParamDef.gRawGamma.set(mParamBuffer, 1.0f / settings.gamma);
535 gTonemappingParamDef.gManualExposureScale.set(mParamBuffer, Math::pow(2.0f, settings.exposureScale));
536 gTonemappingParamDef.gTexSize.set(mParamBuffer, Vector2((float)texProps.getWidth(), (float)texProps.getHeight()));
537 gTonemappingParamDef.gBloomTint.set(mParamBuffer, settings.bloom.tint);
538 gTonemappingParamDef.gNumSamples.set(mParamBuffer, texProps.getNumSamples());
539
540 // Set parameters
541 mInputTex.set(sceneColor);
542 mColorLUT.set(colorLUT);
543 mEyeAdaptationTex.set(eyeAdaptation);
544 mBloomTex.set(bloom != nullptr ? bloom : Texture::BLACK);
545
546 // Render
547 RenderAPI& rapi = RenderAPI::instance();
548 rapi.setRenderTarget(output);
549
550 bind();
551 gRendererUtility().drawScreenQuad();
552 }
553
554 TonemappingMat* TonemappingMat::getVariation(bool volumeLUT, bool gammaOnly, bool autoExposure, bool MSAA)
555 {
556 if(volumeLUT)
557 {
558 if (gammaOnly)
559 {
560 if (autoExposure)
561 {
562 if (MSAA)
563 return get(getVariation<true, true, true, true>());
564 else
565 return get(getVariation<true, true, true, false>());
566 }
567 else
568 {
569 if (MSAA)
570 return get(getVariation<true, true, false, true>());
571 else
572 return get(getVariation<true, true, false, false>());
573 }
574 }
575 else
576 {
577 if (autoExposure)
578 {
579 if (MSAA)
580 return get(getVariation<true, false, true, true>());
581 else
582 return get(getVariation<true, false, true, false>());
583 }
584 else
585 {
586 if (MSAA)
587 return get(getVariation<true, false, false, true>());
588 else
589 return get(getVariation<true, false, false, false>());
590 }
591 }
592 }
593 else
594 {
595 if (gammaOnly)
596 {
597 if (autoExposure)
598 {
599 if (MSAA)
600 return get(getVariation<false, true, true, true>());
601 else
602 return get(getVariation<false, true, true, false>());
603 }
604 else
605 {
606 if (MSAA)
607 return get(getVariation<false, true, false, true>());
608 else
609 return get(getVariation<false, true, false, false>());
610 }
611 }
612 else
613 {
614 if (autoExposure)
615 {
616 if (MSAA)
617 return get(getVariation<false, false, true, true>());
618 else
619 return get(getVariation<false, false, true, false>());
620 }
621 else
622 {
623 if (MSAA)
624 return get(getVariation<false, false, false, true>());
625 else
626 return get(getVariation<false, false, false, false>());
627 }
628 }
629 }
630 }
631
632 BloomClipParamDef gBloomClipParamDef;
633
634 BloomClipMat::BloomClipMat()
635 {
636 mParamBuffer = gBloomClipParamDef.createBuffer();
637
638 mParams->setParamBlockBuffer("Input", mParamBuffer);
639 mParams->getTextureParam(GPT_VERTEX_PROGRAM, "gEyeAdaptationTex", mEyeAdaptationTex);
640 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTex);
641 }
642
643 void BloomClipMat::execute(const SPtr<Texture>& input, float threshold, const SPtr<Texture>& eyeAdaptation,
644 const RenderSettings& settings, const SPtr<RenderTarget>& output)
645 {
646 BS_RENMAT_PROFILE_BLOCK
647
648 gBloomClipParamDef.gThreshold.set(mParamBuffer, threshold);
649 gBloomClipParamDef.gManualExposureScale.set(mParamBuffer, Math::pow(2.0f, settings.exposureScale));
650
651 // Set parameters
652 mInputTex.set(input);
653 mEyeAdaptationTex.set(eyeAdaptation);
654
655 // Render
656 RenderAPI& rapi = RenderAPI::instance();
657
658 rapi.setRenderTarget(output);
659
660 bind();
661 gRendererUtility().drawScreenQuad();
662 }
663
664 BloomClipMat* BloomClipMat::getVariation(bool autoExposure)
665 {
666 if (autoExposure)
667 return get(getVariation<true>());
668
669 return get(getVariation<false>());
670 }
671
672 GaussianBlurParamDef gGaussianBlurParamDef;
673
674 GaussianBlurMat::GaussianBlurMat()
675 {
676 mParamBuffer = gGaussianBlurParamDef.createBuffer();
677 mIsAdditive = mVariation.getBool("ADDITIVE");
678
679 mParams->setParamBlockBuffer("Input", mParamBuffer);
680 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
681
682 if(mIsAdditive)
683 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gAdditiveTex", mAdditiveTexture);
684 }
685
686 void GaussianBlurMat::_initDefines(ShaderDefines& defines)
687 {
688 defines.set("MAX_NUM_SAMPLES", MAX_BLUR_SAMPLES);
689 }
690
691 void GaussianBlurMat::execute(const SPtr<Texture>& source, float filterSize, const SPtr<RenderTexture>& destination,
692 const Color& tint, const SPtr<Texture>& additive)
693 {
694 BS_RENMAT_PROFILE_BLOCK
695
696 const TextureProperties& srcProps = source->getProperties();
697 const RenderTextureProperties& dstProps = destination->getProperties();
698
699 Vector2 invTexSize(1.0f / srcProps.getWidth(), 1.0f / srcProps.getHeight());
700
701 std::array<float, MAX_BLUR_SAMPLES> sampleOffsets;
702 std::array<float, MAX_BLUR_SAMPLES> sampleWeights;
703
704 POOLED_RENDER_TEXTURE_DESC tempTextureDesc = POOLED_RENDER_TEXTURE_DESC::create2D(srcProps.getFormat(),
705 dstProps.width, dstProps.height, TU_RENDERTARGET);
706 SPtr<PooledRenderTexture> tempTexture = GpuResourcePool::instance().get(tempTextureDesc);
707
708 const auto updateParamBuffer =
709 [&source, &filterSize, &sampleWeights, &sampleOffsets, &invTexSize, &paramBuffer = mParamBuffer]
710 (Direction direction, const Color& tint)
711 {
712 const float kernelRadius = calcKernelRadius(source, filterSize, direction);
713 const UINT32 numSamples = calcStdDistribution(kernelRadius, sampleWeights, sampleOffsets);
714
715 for(UINT32 i = 0; i < numSamples; ++i)
716 {
717 Vector4 weight(tint.r, tint.g, tint.b, tint.a);
718 weight *= sampleWeights[i];
719
720 gGaussianBlurParamDef.gSampleWeights.set(paramBuffer, weight, i);
721 }
722
723 UINT32 axis0 = direction == DirHorizontal ? 0 : 1;
724 UINT32 axis1 = (axis0 + 1) % 2;
725
726 for(UINT32 i = 0; i < (numSamples + 1) / 2; ++i)
727 {
728 UINT32 remainder = std::min(2U, numSamples - i * 2);
729
730 Vector4 offset;
731 offset[axis0] = sampleOffsets[i * 2 + 0] * invTexSize[axis0];
732 offset[axis1] = 0.0f;
733
734 if(remainder == 2)
735 {
736 offset[axis0 + 2] = sampleOffsets[i * 2 + 1] * invTexSize[axis0];
737 offset[axis1 + 2] = 0.0f;
738 }
739 else
740 {
741 offset[axis0 + 2] = 0.0f;
742 offset[axis1 + 2] = 0.0f;
743 }
744
745 gGaussianBlurParamDef.gSampleOffsets.set(paramBuffer, offset, i);
746 }
747
748 gGaussianBlurParamDef.gNumSamples.set(paramBuffer, numSamples);
749 };
750
751 // Horizontal pass
752 {
753 updateParamBuffer(DirHorizontal, Color::White);
754 mInputTexture.set(source);
755
756 if(mIsAdditive)
757 mAdditiveTexture.set(Texture::BLACK);
758
759 RenderAPI& rapi = RenderAPI::instance();
760 rapi.setRenderTarget(tempTexture->renderTexture);
761
762 bind();
763 gRendererUtility().drawScreenQuad();
764 }
765
766 // Vertical pass
767 {
768 updateParamBuffer(DirVertical, tint);
769 mInputTexture.set(tempTexture->texture);
770
771 if(mIsAdditive)
772 {
773 if(additive)
774 mAdditiveTexture.set(additive);
775 else
776 mAdditiveTexture.set(Texture::BLACK);
777 }
778
779 RenderAPI& rapi = RenderAPI::instance();
780 rapi.setRenderTarget(destination);
781
782 bind();
783 gRendererUtility().drawScreenQuad();
784 }
785
786 GpuResourcePool::instance().release(tempTexture);
787 }
788
789 UINT32 GaussianBlurMat::calcStdDistribution(float filterRadius, std::array<float, MAX_BLUR_SAMPLES>& weights,
790 std::array<float, MAX_BLUR_SAMPLES>& offsets)
791 {
792 filterRadius = Math::clamp(filterRadius, 0.00001f, (float)(MAX_BLUR_SAMPLES - 1));
793 INT32 intFilterRadius = std::min(Math::ceilToInt(filterRadius), MAX_BLUR_SAMPLES - 1);
794
795 // Note: Does not include the scaling factor since we normalize later anyway
796 auto normalDistribution = [](int i, float scale)
797 {
798 // Higher value gives more weight to samples near the center
799 constexpr float CENTER_BIAS = 30;
800
801 // Mathematica visualization: Manipulate[Plot[E^(-0.5*centerBias*(Abs[x]*(1/radius))^2), {x, -radius, radius}],
802 // {centerBias, 1, 30}, {radius, 1, 72}]
803 float samplePos = fabs((float)i) * scale;
804 return exp(-0.5f * CENTER_BIAS * samplePos * samplePos);
805 };
806
807 // We make use of the hardware linear filtering, and therefore only generate half the number of samples.
808 // The weights and the sampling location needs to be adjusted in order to get the same results as if we
809 // perform two samples separately:
810 //
811 // Original formula is: t1*w1 + t2*w2
812 // With hardware filtering it's: (t1 + (t2 - t1) * o) * w3
813 // Or expanded: t1*w3 - t1*o*w3 + t2*o*w3 = t1 * (w3 - o*w3) + t2 * (o*w3)
814 //
815 // These two need to equal, which means this follows:
816 // w1 = w3 - o*w3
817 // w2 = o*w3
818 //
819 // From the second equation get the offset o:
820 // o = w2/w3
821 //
822 // From the first equation and o, get w3:
823 // w1 = w3 - w2
824 // w3 = w1 + w2
825
826 float scale = 1.0f / filterRadius;
827 UINT32 numSamples = 0;
828 float totalWeight = 0.0f;
829 for(int i = -intFilterRadius; i < intFilterRadius; i += 2)
830 {
831 float w1 = normalDistribution(i, scale);
832 float w2 = normalDistribution(i + 1, scale);
833
834 float w3 = w1 + w2;
835 float o = (float)i + w2/w3; // Relative to first sample
836
837 weights[numSamples] = w3;
838 offsets[numSamples] = o;
839
840 numSamples++;
841 totalWeight += w3;
842 }
843
844 // Special case for last weight, as it doesn't have a matching pair
845 float w = normalDistribution(intFilterRadius, scale);
846 weights[numSamples] = w;
847 offsets[numSamples] = (float)(intFilterRadius - 1);
848
849 numSamples++;
850 totalWeight += w;
851
852 // Normalize weights
853 float invTotalWeight = 1.0f / totalWeight;
854 for(UINT32 i = 0; i < numSamples; i++)
855 weights[i] *= invTotalWeight;
856
857 return numSamples;
858 }
859
860 float GaussianBlurMat::calcKernelRadius(const SPtr<Texture>& source, float scale, Direction filterDir)
861 {
862 scale = Math::clamp01(scale);
863
864 UINT32 length;
865 if (filterDir == DirHorizontal)
866 length = source->getProperties().getWidth();
867 else
868 length = source->getProperties().getHeight();
869
870 // Divide by two because we need the radius
871 return std::min(length * scale / 2, (float)MAX_BLUR_SAMPLES - 1);
872 }
873
874 GaussianBlurMat* GaussianBlurMat::getVariation(bool additive)
875 {
876 if(additive)
877 return get(getVariation<true>());
878
879 return get(getVariation<false>());
880 }
881
882 GaussianDOFParamDef gGaussianDOFParamDef;
883
884 GaussianDOFSeparateMat::GaussianDOFSeparateMat()
885 {
886 mParamBuffer = gGaussianDOFParamDef.createBuffer();
887
888 mParams->setParamBlockBuffer("Input", mParamBuffer);
889 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gColorTex", mColorTexture);
890 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthTex", mDepthTexture);
891
892 SAMPLER_STATE_DESC desc;
893 desc.minFilter = FO_POINT;
894 desc.magFilter = FO_POINT;
895 desc.mipFilter = FO_POINT;
896 desc.addressMode.u = TAM_CLAMP;
897 desc.addressMode.v = TAM_CLAMP;
898 desc.addressMode.w = TAM_CLAMP;
899
900 SPtr<SamplerState> samplerState = SamplerState::create(desc);
901 setSamplerState(mParams, GPT_FRAGMENT_PROGRAM, "gColorSamp", "gColorTex", samplerState);
902 }
903
904 void GaussianDOFSeparateMat::execute(const SPtr<Texture>& color, const SPtr<Texture>& depth,
905 const RendererView& view, const DepthOfFieldSettings& settings)
906 {
907 BS_RENMAT_PROFILE_BLOCK
908
909 const TextureProperties& srcProps = color->getProperties();
910
911 UINT32 outputWidth = std::max(1U, srcProps.getWidth() / 2);
912 UINT32 outputHeight = std::max(1U, srcProps.getHeight() / 2);
913
914 POOLED_RENDER_TEXTURE_DESC outputTexDesc = POOLED_RENDER_TEXTURE_DESC::create2D(srcProps.getFormat(),
915 outputWidth, outputHeight, TU_RENDERTARGET);
916 mOutput0 = GpuResourcePool::instance().get(outputTexDesc);
917
918 bool near = mVariation.getBool("NEAR");
919 bool far = mVariation.getBool("FAR");
920
921 SPtr<RenderTexture> rt;
922 if (near && far)
923 {
924 mOutput1 = GpuResourcePool::instance().get(outputTexDesc);
925
926 RENDER_TEXTURE_DESC rtDesc;
927 rtDesc.colorSurfaces[0].texture = mOutput0->texture;
928 rtDesc.colorSurfaces[1].texture = mOutput1->texture;
929
930 rt = RenderTexture::create(rtDesc);
931 }
932 else
933 rt = mOutput0->renderTexture;
934
935 Vector2 invTexSize(1.0f / srcProps.getWidth(), 1.0f / srcProps.getHeight());
936
937 gGaussianDOFParamDef.gHalfPixelOffset.set(mParamBuffer, invTexSize * 0.5f);
938 gGaussianDOFParamDef.gNearBlurPlane.set(mParamBuffer, settings.focalDistance - settings.focalRange * 0.5f);
939 gGaussianDOFParamDef.gFarBlurPlane.set(mParamBuffer, settings.focalDistance + settings.focalRange * 0.5f);
940 gGaussianDOFParamDef.gInvNearBlurRange.set(mParamBuffer, 1.0f / settings.nearTransitionRange);
941 gGaussianDOFParamDef.gInvFarBlurRange.set(mParamBuffer, 1.0f / settings.farTransitionRange);
942
943 mColorTexture.set(color);
944 mDepthTexture.set(depth);
945
946 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
947 mParams->setParamBlockBuffer("PerCamera", perView);
948
949 RenderAPI& rapi = RenderAPI::instance();
950 rapi.setRenderTarget(rt);
951
952 bind();
953 gRendererUtility().drawScreenQuad();
954 }
955
956 SPtr<PooledRenderTexture> GaussianDOFSeparateMat::getOutput(UINT32 idx)
957 {
958 if (idx == 0)
959 return mOutput0;
960 else if (idx == 1)
961 return mOutput1;
962
963 return nullptr;
964 }
965
966 void GaussianDOFSeparateMat::release()
967 {
968 if (mOutput0 != nullptr)
969 GpuResourcePool::instance().release(mOutput0);
970
971 if (mOutput1 != nullptr)
972 GpuResourcePool::instance().release(mOutput1);
973 }
974
975 GaussianDOFSeparateMat* GaussianDOFSeparateMat::getVariation(bool near, bool far)
976 {
977 if (near)
978 {
979 if (far)
980 return get(getVariation<true, true>());
981 else
982 return get(getVariation<true, false>());
983 }
984 else
985 return get(getVariation<false, true>());
986 }
987
988 GaussianDOFCombineMat::GaussianDOFCombineMat()
989 {
990 mParamBuffer = gGaussianDOFParamDef.createBuffer();
991
992 mParams->setParamBlockBuffer("Input", mParamBuffer);
993
994 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gFocusedTex", mFocusedTexture);
995 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthTex", mDepthTexture);
996
997 if(mParams->hasTexture(GPT_FRAGMENT_PROGRAM, "gNearTex"))
998 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gNearTex", mNearTexture);
999
1000 if(mParams->hasTexture(GPT_FRAGMENT_PROGRAM, "gFarTex"))
1001 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gFarTex", mFarTexture);
1002 }
1003
1004 void GaussianDOFCombineMat::execute(const SPtr<Texture>& focused, const SPtr<Texture>& near,
1005 const SPtr<Texture>& far, const SPtr<Texture>& depth, const SPtr<RenderTarget>& output,
1006 const RendererView& view, const DepthOfFieldSettings& settings)
1007 {
1008 BS_RENMAT_PROFILE_BLOCK
1009
1010 const TextureProperties& srcProps = focused->getProperties();
1011
1012 Vector2 invTexSize(1.0f / srcProps.getWidth(), 1.0f / srcProps.getHeight());
1013
1014 gGaussianDOFParamDef.gHalfPixelOffset.set(mParamBuffer, invTexSize * 0.5f);
1015 gGaussianDOFParamDef.gNearBlurPlane.set(mParamBuffer, settings.focalDistance - settings.focalRange * 0.5f);
1016 gGaussianDOFParamDef.gFarBlurPlane.set(mParamBuffer, settings.focalDistance + settings.focalRange * 0.5f);
1017 gGaussianDOFParamDef.gInvNearBlurRange.set(mParamBuffer, 1.0f / settings.nearTransitionRange);
1018 gGaussianDOFParamDef.gInvFarBlurRange.set(mParamBuffer, 1.0f / settings.farTransitionRange);
1019
1020 mFocusedTexture.set(focused);
1021 mNearTexture.set(near);
1022 mFarTexture.set(far);
1023 mDepthTexture.set(depth);
1024
1025 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1026 mParams->setParamBlockBuffer("PerCamera", perView);
1027
1028 RenderAPI& rapi = RenderAPI::instance();
1029 rapi.setRenderTarget(output);
1030
1031 bind();
1032 gRendererUtility().drawScreenQuad();
1033 }
1034
1035 GaussianDOFCombineMat* GaussianDOFCombineMat::getVariation(bool near, bool far)
1036 {
1037 if (near)
1038 {
1039 if (far)
1040 return get(getVariation<true, true>());
1041 else
1042 return get(getVariation<true, false>());
1043 }
1044 else
1045 return get(getVariation<false, true>());
1046 }
1047
1048 BuildHiZFParamDef gBuildHiZParamDef;
1049
1050 BuildHiZMat::BuildHiZMat()
1051 {
1052 mNoTextureViews = mVariation.getBool("NO_TEXTURE_VIEWS");
1053
1054 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthTex", mInputTexture);
1055
1056 // If no texture view support, we must manually pick a valid mip level in the shader
1057 if(mNoTextureViews)
1058 {
1059 mParamBuffer = gBuildHiZParamDef.createBuffer();
1060 mParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "Input", mParamBuffer);
1061
1062 SAMPLER_STATE_DESC inputSampDesc;
1063 inputSampDesc.minFilter = FO_POINT;
1064 inputSampDesc.magFilter = FO_POINT;
1065 inputSampDesc.mipFilter = FO_POINT;
1066
1067 SPtr<SamplerState> inputSampState = SamplerState::create(inputSampDesc);
1068 setSamplerState(mParams, GPT_FRAGMENT_PROGRAM, "gDepthSamp", "gDepthTex", inputSampState);
1069 }
1070 }
1071
1072 void BuildHiZMat::execute(const SPtr<Texture>& source, UINT32 srcMip, const Rect2& srcRect, const Rect2& dstRect,
1073 const SPtr<RenderTexture>& output)
1074 {
1075 BS_RENMAT_PROFILE_BLOCK
1076
1077 RenderAPI& rapi = RenderAPI::instance();
1078
1079 // If no texture view support, we must manually pick a valid mip level in the shader
1080 if(mNoTextureViews)
1081 {
1082 mInputTexture.set(source);
1083
1084 auto& props = source->getProperties();
1085 float pixelWidth = (float)props.getWidth();
1086 float pixelHeight = (float)props.getHeight();
1087
1088 Vector2 halfPixelOffset(0.5f / pixelWidth, 0.5f / pixelHeight);
1089
1090 gBuildHiZParamDef.gHalfPixelOffset.set(mParamBuffer, halfPixelOffset);
1091 gBuildHiZParamDef.gMipLevel.set(mParamBuffer, srcMip);
1092 }
1093 else
1094 mInputTexture.set(source, TextureSurface(srcMip));
1095
1096 rapi.setRenderTarget(output);
1097 rapi.setViewport(dstRect);
1098
1099 bind();
1100 gRendererUtility().drawScreenQuad(srcRect);
1101
1102 rapi.setViewport(Rect2(0, 0, 1, 1));
1103 }
1104
1105 BuildHiZMat* BuildHiZMat::getVariation(bool noTextureViews)
1106 {
1107 if (noTextureViews)
1108 return get(getVariation<true>());
1109
1110 return get(getVariation<false>());
1111 }
1112
1113 FXAAParamDef gFXAAParamDef;
1114
1115 FXAAMat::FXAAMat()
1116 {
1117 mParamBuffer = gFXAAParamDef.createBuffer();
1118
1119 mParams->setParamBlockBuffer("Input", mParamBuffer);
1120 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
1121 }
1122
1123 void FXAAMat::execute(const SPtr<Texture>& source, const SPtr<RenderTarget>& destination)
1124 {
1125 BS_RENMAT_PROFILE_BLOCK
1126
1127 const TextureProperties& srcProps = source->getProperties();
1128
1129 Vector2 invTexSize(1.0f / srcProps.getWidth(), 1.0f / srcProps.getHeight());
1130 gFXAAParamDef.gInvTexSize.set(mParamBuffer, invTexSize);
1131
1132 mInputTexture.set(source);
1133
1134 RenderAPI& rapi = RenderAPI::instance();
1135 rapi.setRenderTarget(destination);
1136
1137 bind();
1138 gRendererUtility().drawScreenQuad();
1139 }
1140
1141 SSAOParamDef gSSAOParamDef;
1142
1143 SSAOMat::SSAOMat()
1144 {
1145 bool isFinal = mVariation.getBool("FINAL_AO");
1146 bool mixWithUpsampled = mVariation.getBool("MIX_WITH_UPSAMPLED");
1147
1148 mParamBuffer = gSSAOParamDef.createBuffer();
1149
1150 mParams->setParamBlockBuffer("Input", mParamBuffer);
1151
1152 if (isFinal)
1153 {
1154 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthTex", mDepthTexture);
1155 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gNormalsTex", mNormalsTexture);
1156 }
1157
1158 if(!isFinal || mixWithUpsampled)
1159 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSetupAO", mSetupAOTexture);
1160
1161 if(mixWithUpsampled)
1162 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDownsampledAO", mDownsampledAOTexture);
1163
1164 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gRandomTex", mRandomTexture);
1165
1166 SAMPLER_STATE_DESC inputSampDesc;
1167 inputSampDesc.minFilter = FO_POINT;
1168 inputSampDesc.magFilter = FO_POINT;
1169 inputSampDesc.mipFilter = FO_POINT;
1170 inputSampDesc.addressMode.u = TAM_CLAMP;
1171 inputSampDesc.addressMode.v = TAM_CLAMP;
1172 inputSampDesc.addressMode.w = TAM_CLAMP;
1173
1174 SPtr<SamplerState> inputSampState = SamplerState::create(inputSampDesc);
1175 if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gInputSamp"))
1176 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gInputSamp", inputSampState);
1177 else
1178 {
1179 if (isFinal)
1180 {
1181 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthTex", inputSampState);
1182 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gNormalsTex", inputSampState);
1183 }
1184
1185 if(!isFinal || mixWithUpsampled)
1186 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gSetupAO", inputSampState);
1187
1188 if(mixWithUpsampled)
1189 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDownsampledAO", inputSampState);
1190 }
1191
1192 SAMPLER_STATE_DESC randomSampDesc;
1193 randomSampDesc.minFilter = FO_POINT;
1194 randomSampDesc.magFilter = FO_POINT;
1195 randomSampDesc.mipFilter = FO_POINT;
1196 randomSampDesc.addressMode.u = TAM_WRAP;
1197 randomSampDesc.addressMode.v = TAM_WRAP;
1198 randomSampDesc.addressMode.w = TAM_WRAP;
1199
1200 SPtr<SamplerState> randomSampState = SamplerState::create(randomSampDesc);
1201 setSamplerState(mParams, GPT_FRAGMENT_PROGRAM, "gRandomSamp", "gRandomTex", randomSampState);
1202 }
1203
1204 void SSAOMat::execute(const RendererView& view, const SSAOTextureInputs& textures,
1205 const SPtr<RenderTexture>& destination, const AmbientOcclusionSettings& settings)
1206 {
1207 BS_RENMAT_PROFILE_BLOCK
1208
1209 // Scale that can be used to adjust how quickly does AO radius increase with downsampled AO. This yields a very
1210 // small AO radius at highest level, and very large radius at lowest level
1211 static const float DOWNSAMPLE_SCALE = 4.0f;
1212
1213 const RendererViewProperties& viewProps = view.getProperties();
1214 const RenderTargetProperties& rtProps = destination->getProperties();
1215
1216 Vector2 tanHalfFOV;
1217 tanHalfFOV.x = 1.0f / viewProps.projTransform[0][0];
1218 tanHalfFOV.y = 1.0f / viewProps.projTransform[1][1];
1219
1220 float cotHalfFOV = viewProps.projTransform[0][0];
1221
1222 // Downsampled AO uses a larger AO radius (in higher resolutions this would cause too much cache trashing). This
1223 // means if only full res AO is used, then only AO from nearby geometry will be calculated.
1224 float viewScale = viewProps.target.viewRect.width / (float)rtProps.width;
1225
1226 // Ramp up the radius exponentially. c^log2(x) function chosen arbitrarily, as it ramps up the radius in a nice way
1227 float scale = pow(DOWNSAMPLE_SCALE, Math::log2(viewScale));
1228
1229 // Determine maximum radius scale (division by 4 because we don't downsample more than quarter-size)
1230 float maxScale = pow(DOWNSAMPLE_SCALE, Math::log2(4.0f));
1231
1232 // Normalize the scale in [0, 1] range
1233 scale /= maxScale;
1234
1235 float radius = settings.radius * scale;
1236
1237 // Factors used for scaling the AO contribution with range
1238 Vector2 fadeMultiplyAdd;
1239 fadeMultiplyAdd.x = 1.0f / settings.fadeRange;
1240 fadeMultiplyAdd.y = -settings.fadeDistance / settings.fadeRange;
1241
1242 gSSAOParamDef.gSampleRadius.set(mParamBuffer, radius);
1243 gSSAOParamDef.gCotHalfFOV.set(mParamBuffer, cotHalfFOV);
1244 gSSAOParamDef.gTanHalfFOV.set(mParamBuffer, tanHalfFOV);
1245 gSSAOParamDef.gWorldSpaceRadiusMask.set(mParamBuffer, 1.0f);
1246 gSSAOParamDef.gBias.set(mParamBuffer, (settings.bias * viewScale) / 1000.0f);
1247 gSSAOParamDef.gFadeMultiplyAdd.set(mParamBuffer, fadeMultiplyAdd);
1248 gSSAOParamDef.gPower.set(mParamBuffer, settings.power);
1249 gSSAOParamDef.gIntensity.set(mParamBuffer, settings.intensity);
1250
1251 bool upsample = mVariation.getBool("MIX_WITH_UPSAMPLED");
1252 if(upsample)
1253 {
1254 const TextureProperties& props = textures.aoDownsampled->getProperties();
1255
1256 Vector2 downsampledPixelSize;
1257 downsampledPixelSize.x = 1.0f / props.getWidth();
1258 downsampledPixelSize.y = 1.0f / props.getHeight();
1259
1260 gSSAOParamDef.gDownsampledPixelSize.set(mParamBuffer, downsampledPixelSize);
1261 }
1262
1263 // Generate a scale which we need to use in order to achieve tiling
1264 const TextureProperties& rndProps = textures.randomRotations->getProperties();
1265 UINT32 rndWidth = rndProps.getWidth();
1266 UINT32 rndHeight = rndProps.getHeight();
1267
1268 //// Multiple of random texture size, rounded up
1269 UINT32 scaleWidth = (rtProps.width + rndWidth - 1) / rndWidth;
1270 UINT32 scaleHeight = (rtProps.height + rndHeight - 1) / rndHeight;
1271
1272 Vector2 randomTileScale((float)scaleWidth, (float)scaleHeight);
1273 gSSAOParamDef.gRandomTileScale.set(mParamBuffer, randomTileScale);
1274
1275 mSetupAOTexture.set(textures.aoSetup);
1276
1277 bool finalPass = mVariation.getBool("FINAL_AO");
1278 if (finalPass)
1279 {
1280 mDepthTexture.set(textures.sceneDepth);
1281 mNormalsTexture.set(textures.sceneNormals);
1282 }
1283
1284 if (upsample)
1285 mDownsampledAOTexture.set(textures.aoDownsampled);
1286
1287 mRandomTexture.set(textures.randomRotations);
1288
1289 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1290 mParams->setParamBlockBuffer("PerCamera", perView);
1291
1292 RenderAPI& rapi = RenderAPI::instance();
1293 rapi.setRenderTarget(destination);
1294
1295 bind();
1296 gRendererUtility().drawScreenQuad();
1297 }
1298
1299 SSAOMat* SSAOMat::getVariation(bool upsample, bool finalPass, int quality)
1300 {
1301#define PICK_MATERIAL(QUALITY) \
1302 if(upsample) \
1303 if(finalPass) \
1304 return get(getVariation<true, true, QUALITY>()); \
1305 else \
1306 return get(getVariation<true, false, QUALITY>()); \
1307 else \
1308 if(finalPass) \
1309 return get(getVariation<false, true, QUALITY>()); \
1310 else \
1311 return get(getVariation<false, false, QUALITY>()); \
1312
1313 switch(quality)
1314 {
1315 case 0:
1316 PICK_MATERIAL(0)
1317 case 1:
1318 PICK_MATERIAL(1)
1319 case 2:
1320 PICK_MATERIAL(2)
1321 case 3:
1322 PICK_MATERIAL(3)
1323 default:
1324 case 4:
1325 PICK_MATERIAL(4)
1326 }
1327
1328#undef PICK_MATERIAL
1329 }
1330
1331 SSAODownsampleParamDef gSSAODownsampleParamDef;
1332
1333 SSAODownsampleMat::SSAODownsampleMat()
1334 {
1335 mParamBuffer = gSSAODownsampleParamDef.createBuffer();
1336
1337 mParams->setParamBlockBuffer("Input", mParamBuffer);
1338 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthTex", mDepthTexture);
1339 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gNormalsTex", mNormalsTexture);
1340
1341 SAMPLER_STATE_DESC inputSampDesc;
1342 inputSampDesc.minFilter = FO_LINEAR;
1343 inputSampDesc.magFilter = FO_LINEAR;
1344 inputSampDesc.mipFilter = FO_LINEAR;
1345 inputSampDesc.addressMode.u = TAM_CLAMP;
1346 inputSampDesc.addressMode.v = TAM_CLAMP;
1347 inputSampDesc.addressMode.w = TAM_CLAMP;
1348
1349 SPtr<SamplerState> inputSampState = SamplerState::create(inputSampDesc);
1350
1351 if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gInputSamp"))
1352 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gInputSamp", inputSampState);
1353 else
1354 {
1355 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthTex", inputSampState);
1356 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gNormalsTex", inputSampState);
1357 }
1358 }
1359
1360 void SSAODownsampleMat::execute(const RendererView& view, const SPtr<Texture>& depth, const SPtr<Texture>& normals,
1361 const SPtr<RenderTexture>& destination, float depthRange)
1362 {
1363 BS_RENMAT_PROFILE_BLOCK
1364
1365 const RendererViewProperties& viewProps = view.getProperties();
1366 const RenderTargetProperties& rtProps = destination->getProperties();
1367
1368 Vector2 pixelSize;
1369 pixelSize.x = 1.0f / rtProps.width;
1370 pixelSize.y = 1.0f / rtProps.height;
1371
1372 float scale = viewProps.target.viewRect.width / (float)rtProps.width;
1373
1374 gSSAODownsampleParamDef.gPixelSize.set(mParamBuffer, pixelSize);
1375 gSSAODownsampleParamDef.gInvDepthThreshold.set(mParamBuffer, (1.0f / depthRange) / scale);
1376
1377 mDepthTexture.set(depth);
1378 mNormalsTexture.set(normals);
1379
1380 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1381 mParams->setParamBlockBuffer("PerCamera", perView);
1382
1383 RenderAPI& rapi = RenderAPI::instance();
1384 rapi.setRenderTarget(destination);
1385
1386 bind();
1387 gRendererUtility().drawScreenQuad();
1388 }
1389
1390 SSAOBlurParamDef gSSAOBlurParamDef;
1391
1392 SSAOBlurMat::SSAOBlurMat()
1393 {
1394 mParamBuffer = gSSAOBlurParamDef.createBuffer();
1395
1396 mParams->setParamBlockBuffer("Input", mParamBuffer);
1397 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mAOTexture);
1398 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthTex", mDepthTexture);
1399
1400 SAMPLER_STATE_DESC inputSampDesc;
1401 inputSampDesc.minFilter = FO_POINT;
1402 inputSampDesc.magFilter = FO_POINT;
1403 inputSampDesc.mipFilter = FO_POINT;
1404 inputSampDesc.addressMode.u = TAM_CLAMP;
1405 inputSampDesc.addressMode.v = TAM_CLAMP;
1406 inputSampDesc.addressMode.w = TAM_CLAMP;
1407
1408 SPtr<SamplerState> inputSampState = SamplerState::create(inputSampDesc);
1409 if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gInputSamp"))
1410 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gInputSamp", inputSampState);
1411 else
1412 {
1413 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gInputTex", inputSampState);
1414 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gDepthTex", inputSampState);
1415 }
1416 }
1417
1418 void SSAOBlurMat::execute(const RendererView& view, const SPtr<Texture>& ao, const SPtr<Texture>& depth,
1419 const SPtr<RenderTexture>& destination, float depthRange)
1420 {
1421 BS_RENMAT_PROFILE_BLOCK
1422
1423 const RendererViewProperties& viewProps = view.getProperties();
1424 const TextureProperties& texProps = ao->getProperties();
1425
1426 Vector2 pixelSize;
1427 pixelSize.x = 1.0f / texProps.getWidth();
1428 pixelSize.y = 1.0f / texProps.getHeight();
1429
1430 Vector2 pixelOffset(BsZero);
1431 if (mVariation.getBool("DIR_HORZ"))
1432 pixelOffset.x = pixelSize.x;
1433 else
1434 pixelOffset.y = pixelSize.y;
1435
1436 float scale = viewProps.target.viewRect.width / (float)texProps.getWidth();
1437
1438 gSSAOBlurParamDef.gPixelSize.set(mParamBuffer, pixelSize);
1439 gSSAOBlurParamDef.gPixelOffset.set(mParamBuffer, pixelOffset);
1440 gSSAOBlurParamDef.gInvDepthThreshold.set(mParamBuffer, (1.0f / depthRange) / scale);
1441
1442 mAOTexture.set(ao);
1443 mDepthTexture.set(depth);
1444
1445 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1446 mParams->setParamBlockBuffer("PerCamera", perView);
1447
1448 RenderAPI& rapi = RenderAPI::instance();
1449 rapi.setRenderTarget(destination);
1450
1451 bind();
1452 gRendererUtility().drawScreenQuad();
1453 }
1454
1455 SSAOBlurMat* SSAOBlurMat::getVariation(bool horizontal)
1456 {
1457 if (horizontal)
1458 return get(getVariation<true>());
1459
1460 return get(getVariation<false>());
1461 }
1462
1463 SSRStencilParamDef gSSRStencilParamDef;
1464
1465 SSRStencilMat::SSRStencilMat()
1466 :mGBufferParams(GPT_FRAGMENT_PROGRAM, mParams)
1467 {
1468 mParamBuffer = gSSRStencilParamDef.createBuffer();
1469 mParams->setParamBlockBuffer("Input", mParamBuffer);
1470 }
1471
1472 void SSRStencilMat::execute(const RendererView& view, GBufferTextures gbuffer,
1473 const ScreenSpaceReflectionsSettings& settings)
1474 {
1475 BS_RENMAT_PROFILE_BLOCK
1476
1477 mGBufferParams.bind(gbuffer);
1478
1479 Vector2 roughnessScaleBias = SSRTraceMat::calcRoughnessFadeScaleBias(settings.maxRoughness);
1480 gSSRStencilParamDef.gRoughnessScaleBias.set(mParamBuffer, roughnessScaleBias);
1481
1482 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1483 mParams->setParamBlockBuffer("PerCamera", perView);
1484
1485 const RendererViewProperties& viewProps = view.getProperties();
1486 const Rect2I& viewRect = viewProps.target.viewRect;
1487 bind();
1488
1489 if(viewProps.target.numSamples > 1)
1490 gRendererUtility().drawScreenQuad(Rect2(0.0f, 0.0f, (float)viewRect.width, (float)viewRect.height));
1491 else
1492 gRendererUtility().drawScreenQuad();
1493 }
1494
1495 SSRStencilMat* SSRStencilMat::getVariation(bool msaa, bool singleSampleMSAA)
1496 {
1497 if (msaa)
1498 {
1499 if (singleSampleMSAA)
1500 return get(getVariation<true, true>());
1501
1502 return get(getVariation<true, false>());
1503 }
1504 else
1505 return get(getVariation<false, false>());
1506 }
1507
1508 SSRTraceParamDef gSSRTraceParamDef;
1509
1510 SSRTraceMat::SSRTraceMat()
1511 :mGBufferParams(GPT_FRAGMENT_PROGRAM, mParams)
1512 {
1513 mParamBuffer = gSSRTraceParamDef.createBuffer();
1514
1515 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSceneColor", mSceneColorTexture);
1516 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gHiZ", mHiZTexture);
1517
1518 if(mParams->hasParamBlock(GPT_FRAGMENT_PROGRAM, "Input"))
1519 mParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "Input", mParamBuffer);
1520
1521 SAMPLER_STATE_DESC desc;
1522 desc.minFilter = FO_POINT;
1523 desc.magFilter = FO_POINT;
1524 desc.mipFilter = FO_POINT;
1525 desc.addressMode.u = TAM_CLAMP;
1526 desc.addressMode.v = TAM_CLAMP;
1527 desc.addressMode.w = TAM_CLAMP;
1528
1529 SPtr<SamplerState> hiZSamplerState = SamplerState::create(desc);
1530 if (mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gHiZSamp"))
1531 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gHiZSamp", hiZSamplerState);
1532 else if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gHiZ"))
1533 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gHiZ", hiZSamplerState);
1534 }
1535
1536 void SSRTraceMat::execute(const RendererView& view, GBufferTextures gbuffer, const SPtr<Texture>& sceneColor,
1537 const SPtr<Texture>& hiZ, const ScreenSpaceReflectionsSettings& settings,
1538 const SPtr<RenderTarget>& destination)
1539 {
1540 BS_RENMAT_PROFILE_BLOCK
1541
1542 const RendererViewProperties& viewProps = view.getProperties();
1543
1544 const TextureProperties& hiZProps = hiZ->getProperties();
1545
1546 mGBufferParams.bind(gbuffer);
1547 mSceneColorTexture.set(sceneColor);
1548 mHiZTexture.set(hiZ);
1549
1550 Rect2I viewRect = viewProps.target.viewRect;
1551
1552 // Maps from NDC to UV [0, 1]
1553 Vector4 ndcToHiZUV;
1554 ndcToHiZUV.x = 0.5f;
1555 ndcToHiZUV.y = -0.5f;
1556 ndcToHiZUV.z = 0.5f;
1557 ndcToHiZUV.w = 0.5f;
1558
1559 // Either of these flips the Y axis, but if they're both true they cancel out
1560 const Conventions& rapiConventions = gCaps().conventions;
1561
1562 if ((rapiConventions.uvYAxis == Conventions::Axis::Up) ^ (rapiConventions.ndcYAxis == Conventions::Axis::Down))
1563 ndcToHiZUV.y = -ndcToHiZUV.y;
1564
1565 // Maps from [0, 1] to area of HiZ where depth is stored in
1566 ndcToHiZUV.x *= (float)viewRect.width / hiZProps.getWidth();
1567 ndcToHiZUV.y *= (float)viewRect.height / hiZProps.getHeight();
1568 ndcToHiZUV.z *= (float)viewRect.width / hiZProps.getWidth();
1569 ndcToHiZUV.w *= (float)viewRect.height / hiZProps.getHeight();
1570
1571 // Maps from HiZ UV to [0, 1] UV
1572 Vector2 HiZUVToScreenUV;
1573 HiZUVToScreenUV.x = hiZProps.getWidth() / (float)viewRect.width;
1574 HiZUVToScreenUV.y = hiZProps.getHeight() / (float)viewRect.height;
1575
1576 // Used for roughness fading
1577 Vector2 roughnessScaleBias = calcRoughnessFadeScaleBias(settings.maxRoughness);
1578
1579 UINT32 temporalJitter = (viewProps.frameIdx % 8) * 1503;
1580
1581 Vector2I bufferSize(viewRect.width, viewRect.height);
1582 gSSRTraceParamDef.gHiZSize.set(mParamBuffer, bufferSize);
1583 gSSRTraceParamDef.gHiZNumMips.set(mParamBuffer, hiZProps.getNumMipmaps());
1584 gSSRTraceParamDef.gNDCToHiZUV.set(mParamBuffer, ndcToHiZUV);
1585 gSSRTraceParamDef.gHiZUVToScreenUV.set(mParamBuffer, HiZUVToScreenUV);
1586 gSSRTraceParamDef.gIntensity.set(mParamBuffer, settings.intensity);
1587 gSSRTraceParamDef.gRoughnessScaleBias.set(mParamBuffer, roughnessScaleBias);
1588 gSSRTraceParamDef.gTemporalJitter.set(mParamBuffer, temporalJitter);
1589
1590 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1591 mParams->setParamBlockBuffer("PerCamera", perView);
1592
1593 RenderAPI& rapi = RenderAPI::instance();
1594 rapi.setRenderTarget(destination, FBT_DEPTH | FBT_STENCIL, RT_DEPTH_STENCIL);
1595
1596 bind();
1597
1598 if(viewProps.target.numSamples > 1)
1599 gRendererUtility().drawScreenQuad(Rect2(0.0f, 0.0f, (float)viewRect.width, (float)viewRect.height));
1600 else
1601 gRendererUtility().drawScreenQuad();
1602 }
1603
1604 Vector2 SSRTraceMat::calcRoughnessFadeScaleBias(float maxRoughness)
1605 {
1606 const static float RANGE_SCALE = 2.0f;
1607
1608 Vector2 scaleBias;
1609 scaleBias.x = -RANGE_SCALE / (-1.0f + maxRoughness);
1610 scaleBias.y = (RANGE_SCALE * maxRoughness) / (-1.0f + maxRoughness);
1611
1612 return scaleBias;
1613 }
1614
1615 SSRTraceMat* SSRTraceMat::getVariation(UINT32 quality, bool msaa, bool singleSampleMSAA)
1616 {
1617#define PICK_MATERIAL(QUALITY) \
1618 if(msaa) \
1619 if(singleSampleMSAA) \
1620 return get(getVariation<QUALITY, true, true>()); \
1621 else \
1622 return get(getVariation<QUALITY, true, false>()); \
1623 else \
1624 return get(getVariation<QUALITY, false, false>()); \
1625
1626 switch(quality)
1627 {
1628 case 0:
1629 PICK_MATERIAL(0)
1630 case 1:
1631 PICK_MATERIAL(1)
1632 case 2:
1633 PICK_MATERIAL(2)
1634 case 3:
1635 PICK_MATERIAL(3)
1636 default:
1637 case 4:
1638 PICK_MATERIAL(4)
1639 }
1640
1641#undef PICK_MATERIAL
1642 }
1643
1644 TemporalResolveParamDef gTemporalResolveParamDef;
1645 SSRResolveParamDef gSSRResolveParamDef;
1646
1647 SSRResolveMat::SSRResolveMat()
1648 {
1649 mSSRParamBuffer = gSSRResolveParamDef.createBuffer();
1650 mTemporalParamBuffer = gTemporalResolveParamDef.createBuffer();
1651
1652 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSceneDepth", mSceneDepthTexture);
1653 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSceneColor", mSceneColorTexture);
1654 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gPrevColor", mPrevColorTexture);
1655
1656 mParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "Input", mSSRParamBuffer);
1657 mParams->setParamBlockBuffer(GPT_FRAGMENT_PROGRAM, "TemporalInput", mTemporalParamBuffer);
1658
1659 SAMPLER_STATE_DESC pointSampDesc;
1660 pointSampDesc.minFilter = FO_POINT;
1661 pointSampDesc.magFilter = FO_POINT;
1662 pointSampDesc.mipFilter = FO_POINT;
1663 pointSampDesc.addressMode.u = TAM_CLAMP;
1664 pointSampDesc.addressMode.v = TAM_CLAMP;
1665 pointSampDesc.addressMode.w = TAM_CLAMP;
1666
1667 SPtr<SamplerState> pointSampState = SamplerState::create(pointSampDesc);
1668
1669 if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gPointSampler"))
1670 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gPointSampler", pointSampState);
1671 else
1672 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gSceneDepth", pointSampState);
1673
1674 SAMPLER_STATE_DESC linearSampDesc;
1675 linearSampDesc.minFilter = FO_POINT;
1676 linearSampDesc.magFilter = FO_POINT;
1677 linearSampDesc.mipFilter = FO_POINT;
1678 linearSampDesc.addressMode.u = TAM_CLAMP;
1679 linearSampDesc.addressMode.v = TAM_CLAMP;
1680 linearSampDesc.addressMode.w = TAM_CLAMP;
1681
1682 SPtr<SamplerState> linearSampState = SamplerState::create(linearSampDesc);
1683 if(mParams->hasSamplerState(GPT_FRAGMENT_PROGRAM, "gLinearSampler"))
1684 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gLinearSampler", linearSampState);
1685 else
1686 {
1687 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gSceneColor", linearSampState);
1688 mParams->setSamplerState(GPT_FRAGMENT_PROGRAM, "gPrevColor", linearSampState);
1689 }
1690 }
1691
1692 void SSRResolveMat::execute(const RendererView& view, const SPtr<Texture>& prevFrame,
1693 const SPtr<Texture>& curFrame, const SPtr<Texture>& sceneDepth, const SPtr<RenderTarget>& destination)
1694 {
1695 BS_RENMAT_PROFILE_BLOCK
1696
1697 // Note: This shader should not be called when temporal AA is turned on
1698 // Note: This shader doesn't have velocity texture enabled and will only account for camera movement (can be easily
1699 // enabled when velocity texture is added)
1700 // - WHen added, velocity should use a 16-bit SNORM format
1701
1702 mPrevColorTexture.set(prevFrame);
1703 mSceneColorTexture.set(curFrame);
1704 mSceneDepthTexture.set(sceneDepth);
1705
1706 auto& colorProps = curFrame->getProperties(); // Assuming prev and current frame are the same size
1707 auto& depthProps = sceneDepth->getProperties();
1708
1709 Vector2 colorPixelSize(1.0f / colorProps.getWidth(), 1.0f / colorProps.getHeight());
1710 Vector2 depthPixelSize(1.0f / depthProps.getWidth(), 1.0f / depthProps.getHeight());
1711
1712 gSSRResolveParamDef.gSceneColorTexelSize.set(mSSRParamBuffer, colorPixelSize);
1713 gSSRResolveParamDef.gSceneDepthTexelSize.set(mSSRParamBuffer, depthPixelSize);
1714 gSSRResolveParamDef.gManualExposure.set(mSSRParamBuffer, 1.0f);
1715
1716 // Generate samples
1717 // Note: Move this code to a more general spot where it can be used by other temporal shaders.
1718
1719 float sampleWeights[9];
1720 float sampleWeightsLowPass[9];
1721
1722 float totalWeights = 0.0f;
1723 float totalWeightsLowPass = 0.0f;
1724
1725 Vector2 jitter(BsZero); // Only relevant for general case, not using this type of jitter for SSR
1726
1727 // Weights are generated using an exponential fit to Blackman-Harris 3.3
1728 bool useYCoCg = false; // Only relevant for general case, not using it for SSR
1729 float sharpness = 1.0f; // Make this a customizable parameter eventually
1730 if(useYCoCg)
1731 {
1732 static const Vector2 sampleOffsets[] =
1733 {
1734 { 0.0f, -1.0f },
1735 { -1.0f, 0.0f },
1736 { 0.0f, 0.0f },
1737 { 1.0f, 0.0f },
1738 { 0.0f, 1.0f },
1739 };
1740
1741 for (UINT32 i = 0; i < 5; ++i)
1742 {
1743 // Get rid of jitter introduced by the projection matrix
1744 Vector2 offset = sampleOffsets[i] - jitter;
1745
1746 offset *= 1.0f + sharpness * 0.5f;
1747 sampleWeights[i] = exp(-2.29f * offset.dot(offset));
1748 totalWeights += sampleWeights[i];
1749 }
1750
1751 for (UINT32 i = 5; i < 9; ++i)
1752 sampleWeights[i] = 0.0f;
1753
1754 memset(sampleWeightsLowPass, 0, sizeof(sampleWeightsLowPass));
1755 totalWeightsLowPass = 1.0f;
1756 }
1757 else
1758 {
1759 static const Vector2 sampleOffsets[] =
1760 {
1761 { -1.0f, -1.0f },
1762 { 0.0f, -1.0f },
1763 { 1.0f, -1.0f },
1764 { -1.0f, 0.0f },
1765 { 0.0f, 0.0f },
1766 { 1.0f, 0.0f },
1767 { -1.0f, 1.0f },
1768 { 0.0f, 1.0f },
1769 { 1.0f, 1.0f },
1770 };
1771
1772 for (UINT32 i = 0; i < 9; ++i)
1773 {
1774 // Get rid of jitter introduced by the projection matrix
1775 Vector2 offset = sampleOffsets[i] - jitter;
1776
1777 offset *= 1.0f + sharpness * 0.5f;
1778 sampleWeights[i] = exp(-2.29f * offset.dot(offset));
1779 totalWeights += sampleWeights[i];
1780
1781 // Low pass
1782 offset *= 0.25f;
1783 sampleWeightsLowPass[i] = exp(-2.29f * offset.dot(offset));
1784 totalWeightsLowPass += sampleWeightsLowPass[i];
1785 }
1786 }
1787
1788 for (UINT32 i = 0; i < 9; ++i)
1789 {
1790 gTemporalResolveParamDef.gSampleWeights.set(mTemporalParamBuffer, sampleWeights[i] / totalWeights, i);
1791 gTemporalResolveParamDef.gSampleWeightsLowpass.set(mTemporalParamBuffer, sampleWeightsLowPass[i] / totalWeightsLowPass, i);
1792 }
1793
1794 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1795 mParams->setParamBlockBuffer("PerCamera", perView);
1796
1797 RenderAPI& rapi = RenderAPI::instance();
1798 rapi.setRenderTarget(destination);
1799
1800 const RendererViewProperties& viewProps = view.getProperties();
1801 const Rect2I& viewRect = viewProps.target.viewRect;
1802
1803 bind();
1804
1805 if(viewProps.target.numSamples > 1)
1806 gRendererUtility().drawScreenQuad(Rect2(0.0f, 0.0f, (float)viewRect.width, (float)viewRect.height));
1807 else
1808 gRendererUtility().drawScreenQuad();
1809 }
1810
1811 SSRResolveMat* SSRResolveMat::getVariation(bool msaa)
1812 {
1813 if (msaa)
1814 return get(getVariation<true>());
1815 else
1816 return get(getVariation<false>());
1817 }
1818
1819 EncodeDepthParamDef gEncodeDepthParamDef;
1820
1821 EncodeDepthMat::EncodeDepthMat()
1822 {
1823 mParamBuffer = gEncodeDepthParamDef.createBuffer();
1824
1825 mParams->setParamBlockBuffer("Params", mParamBuffer);
1826 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gInputTex", mInputTexture);
1827
1828 SAMPLER_STATE_DESC sampDesc;
1829 sampDesc.minFilter = FO_POINT;
1830 sampDesc.magFilter = FO_POINT;
1831 sampDesc.mipFilter = FO_POINT;
1832 sampDesc.addressMode.u = TAM_CLAMP;
1833 sampDesc.addressMode.v = TAM_CLAMP;
1834 sampDesc.addressMode.w = TAM_CLAMP;
1835
1836 SPtr<SamplerState> samplerState = SamplerState::create(sampDesc);
1837 setSamplerState(mParams, GPT_FRAGMENT_PROGRAM, "gInputSamp", "gInputTex", samplerState);
1838 }
1839
1840 void EncodeDepthMat::execute(const SPtr<Texture>& depth, float near, float far, const SPtr<RenderTarget>& output)
1841 {
1842 BS_RENMAT_PROFILE_BLOCK
1843
1844 mInputTexture.set(depth);
1845
1846 gEncodeDepthParamDef.gNear.set(mParamBuffer, near);
1847 gEncodeDepthParamDef.gFar.set(mParamBuffer, far);
1848
1849 RenderAPI& rapi = RenderAPI::instance();
1850 rapi.setRenderTarget(output, 0, RT_COLOR0);
1851
1852 bind();
1853 gRendererUtility().drawScreenQuad();
1854 }
1855
1856 MSAACoverageMat::MSAACoverageMat()
1857 :mGBufferParams(GPT_FRAGMENT_PROGRAM, mParams)
1858 { }
1859
1860 void MSAACoverageMat::execute(const RendererView& view, GBufferTextures gbuffer)
1861 {
1862 BS_RENMAT_PROFILE_BLOCK
1863
1864 mGBufferParams.bind(gbuffer);
1865
1866 const Rect2I& viewRect = view.getProperties().target.viewRect;
1867 SPtr<GpuParamBlockBuffer> perView = view.getPerViewBuffer();
1868 mParams->setParamBlockBuffer("PerCamera", perView);
1869
1870 bind();
1871 gRendererUtility().drawScreenQuad(Rect2(0, 0, (float)viewRect.width, (float)viewRect.height));
1872 }
1873
1874 MSAACoverageMat* MSAACoverageMat::getVariation(UINT32 msaaCount)
1875 {
1876 switch(msaaCount)
1877 {
1878 case 2:
1879 return get(getVariation<2>());
1880 case 4:
1881 return get(getVariation<4>());
1882 case 8:
1883 default:
1884 return get(getVariation<8>());
1885 }
1886 }
1887
1888 MSAACoverageStencilMat::MSAACoverageStencilMat()
1889 {
1890 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gMSAACoverage", mCoverageTexParam);
1891 }
1892
1893 void MSAACoverageStencilMat::execute(const RendererView& view, const SPtr<Texture>& coverage)
1894 {
1895 BS_RENMAT_PROFILE_BLOCK
1896
1897 const Rect2I& viewRect = view.getProperties().target.viewRect;
1898 mCoverageTexParam.set(coverage);
1899
1900 bind();
1901 gRendererUtility().drawScreenQuad(Rect2(0, 0, (float)viewRect.width, (float)viewRect.height));
1902 }
1903}}
1904