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 "BsRendererLight.h"
4#include "Material/BsMaterial.h"
5#include "Material/BsGpuParamsSet.h"
6#include "RenderAPI/BsGpuBuffer.h"
7#include "RenderAPI/BsGpuParams.h"
8#include "Renderer/BsLight.h"
9#include "Renderer/BsRendererUtility.h"
10#include "BsRenderBeast.h"
11#include "Shading/BsStandardDeferred.h"
12
13namespace bs { namespace ct
14{
15 static const UINT32 LIGHT_DATA_BUFFER_INCREMENT = 16 * sizeof(LightData);
16
17 RendererLight::RendererLight(Light* light)
18 :internal(light)
19 { }
20
21 void RendererLight::getParameters(LightData& output) const
22 {
23 Radian spotAngle = Math::clamp(internal->getSpotAngle() * 0.5f, Degree(0), Degree(89));
24 Radian spotFalloffAngle = Math::clamp(internal->getSpotFalloffAngle() * 0.5f, Degree(0), (Degree)spotAngle);
25 Color color = internal->getColor();
26
27 const Transform& tfrm = internal->getTransform();
28 output.position = tfrm.getPosition();
29 output.boundsRadius = internal->getBounds().getRadius();
30 output.srcRadius = internal->getSourceRadius();
31 output.direction = -tfrm.getRotation().zAxis();
32 output.luminance = internal->getLuminance();
33 output.spotAngles.x = spotAngle.valueRadians();
34 output.spotAngles.y = Math::cos(output.spotAngles.x);
35 output.spotAngles.z = 1.0f / std::max(Math::cos(spotFalloffAngle) - output.spotAngles.y, 0.001f);
36 output.attRadiusSqrdInv = 1.0f / (internal->getAttenuationRadius() * internal->getAttenuationRadius());
37 output.color = Vector3(color.r, color.g, color.b);
38
39 // If directional lights, convert angular radius in degrees to radians
40 if (internal->getType() == LightType::Directional)
41 output.srcRadius *= Math::DEG2RAD;
42
43 output.shiftedLightPosition = getShiftedLightPosition();
44 }
45
46 void RendererLight::getParameters(SPtr<GpuParamBlockBuffer>& buffer) const
47 {
48 LightData lightData;
49 getParameters(lightData);
50
51 float type = 0.0f;
52 switch (internal->getType())
53 {
54 case LightType::Directional:
55 type = 0;
56 break;
57 case LightType::Radial:
58 type = 0.3f;
59 break;
60 case LightType::Spot:
61 type = 0.8f;
62 break;
63 default:
64 break;
65 }
66
67 gPerLightParamDef.gLightPositionAndSrcRadius.set(buffer, Vector4(lightData.position, lightData.srcRadius));
68 gPerLightParamDef.gLightColorAndLuminance.set(buffer, Vector4(lightData.color, lightData.luminance));
69 gPerLightParamDef.gLightSpotAnglesAndSqrdInvAttRadius.set(buffer, Vector4(lightData.spotAngles, lightData.attRadiusSqrdInv));
70 gPerLightParamDef.gLightDirectionAndBoundRadius.set(buffer, Vector4(lightData.direction, lightData.boundsRadius));
71 gPerLightParamDef.gShiftedLightPositionAndType.set(buffer, Vector4(lightData.shiftedLightPosition, type));
72
73 Vector4 lightGeometry;
74 lightGeometry.x = internal->getType() == LightType::Spot ? (float)Light::LIGHT_CONE_NUM_SIDES : 0;
75 lightGeometry.y = (float)Light::LIGHT_CONE_NUM_SLICES;
76 lightGeometry.z = internal->getBounds().getRadius();
77
78 float extraRadius = lightData.srcRadius / Math::tan(lightData.spotAngles.x * 0.5f);
79 float coneRadius = Math::sin(lightData.spotAngles.x) * (internal->getAttenuationRadius() + extraRadius);
80 lightGeometry.w = coneRadius;
81
82 gPerLightParamDef.gLightGeometry.set(buffer, lightGeometry);
83
84 const Transform& tfrm = internal->getTransform();
85
86 Quaternion lightRotation(BsIdentity);
87 lightRotation.lookRotation(-tfrm.getRotation().zAxis());
88
89 Matrix4 transform = Matrix4::TRS(lightData.shiftedLightPosition, lightRotation, Vector3::ONE);
90 gPerLightParamDef.gMatConeTransform.set(buffer, transform);
91 }
92
93 Vector3 RendererLight::getShiftedLightPosition() const
94 {
95 const Transform& tfrm = internal->getTransform();
96 Vector3 direction = -tfrm.getRotation().zAxis();
97
98 // Create position for fake attenuation for area spot lights (with disc center)
99 if (internal->getType() == LightType::Spot)
100 return tfrm.getPosition() - direction * (internal->getSourceRadius() / Math::tan(internal->getSpotAngle() * 0.5f));
101 else
102 return tfrm.getPosition();
103 }
104
105 GBufferParams::GBufferParams(GpuProgramType type, const SPtr<GpuParams>& gpuParams)
106 : mParams(gpuParams)
107 {
108 if(mParams->hasTexture(type, "gGBufferATex"))
109 mParams->getTextureParam(type, "gGBufferATex", mGBufferA);
110
111 if(mParams->hasTexture(type, "gGBufferBTex"))
112 mParams->getTextureParam(type, "gGBufferBTex", mGBufferB);
113
114 if(mParams->hasTexture(type, "gGBufferCTex"))
115 mParams->getTextureParam(type, "gGBufferCTex", mGBufferC);
116
117 if(mParams->hasTexture(type, "gDepthBufferTex"))
118 mParams->getTextureParam(type, "gDepthBufferTex", mGBufferDepth);
119
120 if(mParams->hasSamplerState(type, "gDepthBufferSamp"))
121 {
122 GpuParamSampState samplerStateParam;
123 mParams->getSamplerStateParam(type, "gDepthBufferSamp", samplerStateParam);
124
125 SAMPLER_STATE_DESC desc;
126 desc.minFilter = FO_POINT;
127 desc.magFilter = FO_POINT;
128 desc.mipFilter = FO_POINT;
129
130 SPtr<SamplerState> ss = SamplerState::create(desc);
131 samplerStateParam.set(ss);
132 }
133 }
134
135 void GBufferParams::bind(const GBufferTextures& gbuffer)
136 {
137 mGBufferA.set(gbuffer.albedo);
138 mGBufferB.set(gbuffer.normals);
139 mGBufferC.set(gbuffer.roughMetal);
140 mGBufferDepth.set(gbuffer.depth);
141 }
142
143 void ForwardLightingParams::populate(const SPtr<GpuParams>& params, bool clustered)
144 {
145 if (clustered)
146 {
147 params->getParamInfo()->getBindings(
148 GpuPipelineParamInfoBase::ParamType::ParamBlock,
149 "GridParams",
150 gridParamsBindings
151 );
152
153 if (params->hasBuffer(GPT_FRAGMENT_PROGRAM, "gLights"))
154 params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gLights", lightsBufferParam);
155
156 if (params->hasBuffer(GPT_FRAGMENT_PROGRAM, "gGridLightOffsetsAndSize"))
157 params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gGridLightOffsetsAndSize",
158 gridLightOffsetsAndSizeParam);
159
160 if (params->hasBuffer(GPT_FRAGMENT_PROGRAM, "gLightIndices"))
161 params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gLightIndices", gridLightIndicesParam);
162
163 if (params->hasBuffer(GPT_FRAGMENT_PROGRAM, "gGridProbeOffsetsAndSize"))
164 params->getBufferParam(GPT_FRAGMENT_PROGRAM, "gGridProbeOffsetsAndSize",
165 gridProbeOffsetsAndSizeParam);
166 }
167 else
168 {
169 params->getParamInfo()->getBinding(
170 GPT_FRAGMENT_PROGRAM,
171 GpuPipelineParamInfoBase::ParamType::ParamBlock,
172 "Lights",
173 lightsParamBlockBinding
174 );
175
176 params->getParamInfo()->getBinding(
177 GPT_FRAGMENT_PROGRAM,
178 GpuPipelineParamInfoBase::ParamType::ParamBlock,
179 "LightAndReflProbeParams",
180 lightAndReflProbeParamsParamBlockBinding
181 );
182 }
183 }
184
185 VisibleLightData::VisibleLightData()
186 :mNumLights{}, mNumShadowedLights{}
187 { }
188
189 void VisibleLightData::update(const SceneInfo& sceneInfo, const RendererViewGroup& viewGroup)
190 {
191 const VisibilityInfo& visibility = viewGroup.getVisibilityInfo();
192
193 for (UINT32 i = 0; i < (UINT32)LightType::Count; i++)
194 mVisibleLights[i].clear();
195
196 // Generate a list of lights and their GPU buffers
197 UINT32 numDirLights = (UINT32)sceneInfo.directionalLights.size();
198 for (UINT32 i = 0; i < numDirLights; i++)
199 mVisibleLights[(UINT32)LightType::Directional].push_back(&sceneInfo.directionalLights[i]);
200
201 UINT32 numRadialLights = (UINT32)sceneInfo.radialLights.size();
202 for(UINT32 i = 0; i < numRadialLights; i++)
203 {
204 if (!visibility.radialLights[i])
205 continue;
206
207 mVisibleLights[(UINT32)LightType::Radial].push_back(&sceneInfo.radialLights[i]);
208 }
209
210 UINT32 numSpotLights = (UINT32)sceneInfo.spotLights.size();
211 for (UINT32 i = 0; i < numSpotLights; i++)
212 {
213 if (!visibility.spotLights[i])
214 continue;
215
216 mVisibleLights[(UINT32)LightType::Spot].push_back(&sceneInfo.spotLights[i]);
217 }
218
219 for (UINT32 i = 0; i < (UINT32)LightType::Count; i++)
220 mNumLights[i] = (UINT32)mVisibleLights[i].size();
221
222 // Partition all visible lights so that unshadowed ones come first
223 auto partition = [](Vector<const RendererLight*>& entries)
224 {
225 UINT32 numUnshadowed = 0;
226 int first = -1;
227 for (UINT32 i = 0; i < (UINT32)entries.size(); ++i)
228 {
229 if(entries[i]->internal->getCastsShadow())
230 {
231 first = i;
232 break;
233 }
234 else
235 ++numUnshadowed;
236 }
237
238 if(first != -1)
239 {
240 for(UINT32 i = first + 1; i < (UINT32)entries.size(); ++i)
241 {
242 if(!entries[i]->internal->getCastsShadow())
243 {
244 std::swap(entries[i], entries[first]);
245 ++numUnshadowed;
246 }
247 }
248 }
249
250 return numUnshadowed;
251 };
252
253 for (UINT32 i = 0; i < (UINT32)LightType::Count; i++)
254 mNumShadowedLights[i] = mNumLights[i] - partition(mVisibleLights[i]);
255
256 // Generate light data to initialize the GPU buffer with
257 mVisibleLightData.clear();
258 for(auto& lightsPerType : mVisibleLights)
259 {
260 for(auto& entry : lightsPerType)
261 {
262 mVisibleLightData.push_back(LightData());
263 entry->getParameters(mVisibleLightData.back());
264 }
265 }
266
267 bool supportsStructuredBuffers = gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop;
268 if(supportsStructuredBuffers)
269 {
270 UINT32 size = (UINT32) mVisibleLightData.size() * sizeof(LightData);
271 UINT32 curBufferSize;
272
273 if (mLightBuffer != nullptr)
274 curBufferSize = mLightBuffer->getSize();
275 else
276 curBufferSize = 0;
277
278 if (size > curBufferSize || curBufferSize == 0)
279 {
280 // Allocate at least one block even if no lights, to avoid issues with null buffers
281 UINT32 bufferSize = std::max(1, Math::ceilToInt(size / (float) LIGHT_DATA_BUFFER_INCREMENT)) * LIGHT_DATA_BUFFER_INCREMENT;
282
283 GPU_BUFFER_DESC bufferDesc;
284 bufferDesc.type = GBT_STRUCTURED;
285 bufferDesc.elementCount = bufferSize / sizeof(LightData);
286 bufferDesc.elementSize = sizeof(LightData);
287 bufferDesc.format = BF_UNKNOWN;
288
289 mLightBuffer = GpuBuffer::create(bufferDesc);
290 }
291
292 if (size > 0)
293 mLightBuffer->writeData(0, size, mVisibleLightData.data(), BWT_DISCARD);
294 }
295 }
296
297 void VisibleLightData::gatherInfluencingLights(const Bounds& bounds,
298 const LightData* (&output)[STANDARD_FORWARD_MAX_NUM_LIGHTS], Vector3I& counts) const
299 {
300 UINT32 outputIndices[STANDARD_FORWARD_MAX_NUM_LIGHTS];
301 UINT32 numInfluencingLights = 0;
302
303 UINT32 numDirLights = getNumDirLights();
304 for(UINT32 i = 0; i < numDirLights; i++)
305 {
306 if (numInfluencingLights >= STANDARD_FORWARD_MAX_NUM_LIGHTS)
307 return;
308
309 outputIndices[numInfluencingLights] = i;
310 numInfluencingLights++;
311 }
312
313 UINT32 pointLightOffset = numInfluencingLights;
314
315 float distances[STANDARD_FORWARD_MAX_NUM_LIGHTS];
316 for(UINT32 i = 0; i < STANDARD_FORWARD_MAX_NUM_LIGHTS; i++)
317 distances[i] = std::numeric_limits<float>::max();
318
319 // Note: This is an ad-hoc way of evaluating light influence, a better way might be wanted
320 UINT32 numLights = (UINT32)mVisibleLightData.size();
321 UINT32 furthestLightIdx = (UINT32)-1;
322 float furthestDistance = 0.0f;
323 for (UINT32 j = numDirLights; j < numLights; j++)
324 {
325 const LightData* lightData = &mVisibleLightData[j];
326
327 Sphere lightSphere(lightData->position, lightData->boundsRadius);
328 if (bounds.getSphere().intersects(lightSphere))
329 {
330 float distance = bounds.getSphere().getCenter().squaredDistance(lightData->position);
331
332 // See where in the array can we fit the light
333 if (numInfluencingLights < STANDARD_FORWARD_MAX_NUM_LIGHTS)
334 {
335 outputIndices[numInfluencingLights] = j;
336 distances[numInfluencingLights] = distance;
337
338 if (distance > furthestDistance)
339 {
340 furthestLightIdx = numInfluencingLights;
341 furthestDistance = distance;
342 }
343
344 numInfluencingLights++;
345 }
346 else if (distance < furthestDistance)
347 {
348 outputIndices[furthestLightIdx] = j;
349 distances[furthestLightIdx] = distance;
350
351 furthestDistance = distance;
352 for (UINT32 k = 0; k < STANDARD_FORWARD_MAX_NUM_LIGHTS; k++)
353 {
354 if (distances[k] > furthestDistance)
355 {
356 furthestDistance = distances[k];
357 furthestLightIdx = k;
358 }
359 }
360 }
361 }
362 }
363
364 // Output actual light data, sorted by type
365 counts = Vector3I(0, 0, 0);
366
367 for(UINT32 i = 0; i < pointLightOffset; i++)
368 {
369 output[i] = &mVisibleLightData[outputIndices[i]];
370 counts.x += 1;
371 }
372
373 UINT32 outputIdx = pointLightOffset;
374 UINT32 spotLightIdx = getNumDirLights() + getNumRadialLights();
375 for(UINT32 i = pointLightOffset; i < numInfluencingLights; i++)
376 {
377 bool isSpot = outputIndices[i] >= spotLightIdx;
378 if(isSpot)
379 continue;
380
381 output[outputIdx++] = &mVisibleLightData[outputIndices[i]];
382 counts.y += 1;
383 }
384
385 for(UINT32 i = pointLightOffset; i < numInfluencingLights; i++)
386 {
387 bool isSpot = outputIndices[i] >= spotLightIdx;
388 if(!isSpot)
389 continue;
390
391 output[outputIdx++] = &mVisibleLightData[outputIndices[i]];
392 counts.z += 1;
393 }
394 }
395
396 LightsParamDef gLightsParamDef;
397 LightAndReflProbeParamsParamDef gLightAndReflProbeParamsParamDef;
398}}