!I (1670409):
[CRYENGINE.git] / Code / CryEngine / Cry3DEngine / FogVolumeRenderNode.cpp
blob726fc6da888a718c4a94d71a208148b15ff9b56a
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "FogVolumeRenderNode.h"
5 #include "VisAreas.h"
6 #include <CryRenderer/RenderElements/CREFogVolume.h>
7 #include <CryMath/Cry_Geo.h>
8 #include "ObjMan.h"
9 #include "ClipVolumeManager.h"
11 #include <limits>
13 AABB CFogVolumeRenderNode::s_tracableFogVolumeArea(Vec3(0, 0, 0), Vec3(0, 0, 0));
14 CFogVolumeRenderNode::CachedFogVolumes CFogVolumeRenderNode::s_cachedFogVolumes;
15 CFogVolumeRenderNode::GlobalFogVolumeMap CFogVolumeRenderNode::s_globalFogVolumeMap;
16 bool CFogVolumeRenderNode::s_forceTraceableAreaUpdate(false);
18 void CFogVolumeRenderNode::StaticReset()
20 stl::free_container(s_cachedFogVolumes);
23 void CFogVolumeRenderNode::ForceTraceableAreaUpdate()
25 s_forceTraceableAreaUpdate = true;
28 void CFogVolumeRenderNode::SetTraceableArea(const AABB& traceableArea, const SRenderingPassInfo& passInfo)
30 // do we bother?
31 if (!GetCVars()->e_Fog || !GetCVars()->e_FogVolumes)
32 return;
34 if (GetCVars()->e_VolumetricFog != 0)
35 return;
37 // is update of traceable areas necessary
38 if (!s_forceTraceableAreaUpdate)
39 if ((s_tracableFogVolumeArea.GetCenter() - traceableArea.GetCenter()).GetLengthSquared() < 1e-4f && (s_tracableFogVolumeArea.GetSize() - traceableArea.GetSize()).GetLengthSquared() < 1e-4f)
40 return;
42 // set new area and reset list of traceable fog volumes
43 s_tracableFogVolumeArea = traceableArea;
44 s_cachedFogVolumes.resize(0);
46 // collect all candidates
47 Vec3 traceableAreaCenter(s_tracableFogVolumeArea.GetCenter());
48 IVisArea* pVisAreaOfCenter(GetVisAreaManager() ? GetVisAreaManager()->GetVisAreaFromPos(traceableAreaCenter) : NULL);
50 GlobalFogVolumeMap::const_iterator itEnd(s_globalFogVolumeMap.end());
51 for (GlobalFogVolumeMap::const_iterator it(s_globalFogVolumeMap.begin()); it != itEnd; ++it)
53 const CFogVolumeRenderNode* pFogVolume(*it);
54 if (pVisAreaOfCenter || (!pVisAreaOfCenter && !pFogVolume->GetEntityVisArea())) // if outside only add fog volumes which are outside as well
55 if (Overlap::AABB_AABB(s_tracableFogVolumeArea, pFogVolume->m_WSBBox)) // bb of fog volume overlaps with traceable area
56 s_cachedFogVolumes.push_back(SCachedFogVolume(pFogVolume, Vec3(pFogVolume->m_pos - traceableAreaCenter).GetLengthSquared()));
59 // sort by distance
60 std::sort(s_cachedFogVolumes.begin(), s_cachedFogVolumes.end());
62 // reset force-update flags
63 s_forceTraceableAreaUpdate = false;
66 void CFogVolumeRenderNode::RegisterFogVolume(const CFogVolumeRenderNode* pFogVolume)
68 GlobalFogVolumeMap::const_iterator it(s_globalFogVolumeMap.find(pFogVolume));
69 assert(it == s_globalFogVolumeMap.end() &&
70 "CFogVolumeRenderNode::RegisterFogVolume() -- Fog volume already registered!");
71 if (it == s_globalFogVolumeMap.end())
73 s_globalFogVolumeMap.insert(pFogVolume);
74 ForceTraceableAreaUpdate();
78 void CFogVolumeRenderNode::UnregisterFogVolume(const CFogVolumeRenderNode* pFogVolume)
80 GlobalFogVolumeMap::iterator it(s_globalFogVolumeMap.find(pFogVolume));
81 assert(it != s_globalFogVolumeMap.end() &&
82 "CFogVolumeRenderNode::UnRegisterFogVolume() -- Fog volume previously not registered!");
83 if (it != s_globalFogVolumeMap.end())
85 s_globalFogVolumeMap.erase(it);
86 ForceTraceableAreaUpdate();
90 CFogVolumeRenderNode::CFogVolumeRenderNode()
91 : m_matNodeWS()
92 , m_matWS()
93 , m_matWSInv()
94 , m_volumeType(IFogVolumeRenderNode::eFogVolumeType_Ellipsoid)
95 , m_pos(0, 0, 0)
96 , m_x(1, 0, 0)
97 , m_y(0, 1, 0)
98 , m_z(0, 0, 1)
99 , m_scale(1, 1, 1)
100 , m_globalDensity(1)
101 , m_densityOffset(0)
102 , m_nearCutoff(0)
103 , m_fHDRDynamic(0)
104 , m_softEdges(1)
105 , m_color(1, 1, 1, 1)
106 , m_useGlobalFogColor(false)
107 , m_affectsThisAreaOnly(false)
108 , m_rampParams(0, 1, 0)
109 , m_updateFrameID(0)
110 , m_windInfluence(1)
111 , m_noiseElapsedTime(-5000.0f)
112 , m_densityNoiseScale(0)
113 , m_densityNoiseOffset(0)
114 , m_densityNoiseTimeFrequency(0)
115 , m_densityNoiseFrequency(1, 1, 1)
116 , m_emission(0, 0, 0)
117 , m_heightFallOffDir(0, 0, 1)
118 , m_heightFallOffDirScaled(0, 0, 1)
119 , m_heightFallOffShift(0, 0, 0)
120 , m_heightFallOffBasePoint(0, 0, 0)
121 , m_localBounds(Vec3(-1, -1, -1), Vec3(1, 1, 1))
122 , m_globalDensityFader()
123 , m_pMatFogVolEllipsoid(0)
124 , m_pMatFogVolBox(0)
125 , m_WSBBox()
126 , m_cachedSoftEdgesLerp(1, 0)
127 , m_cachedFogColor(1, 1, 1, 1)
129 GetInstCount(GetRenderNodeType())++;
131 m_matNodeWS.SetIdentity();
133 m_matWS.SetIdentity();
134 m_matWSInv.SetIdentity();
136 m_windOffset.x = cry_random(0.0f, 1000.0f);
137 m_windOffset.y = cry_random(0.0f, 1000.0f);
138 m_windOffset.z = cry_random(0.0f, 1000.0f);
140 for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
142 m_pFogVolumeRenderElement[i] = GetRenderer() ? ((CREFogVolume*) GetRenderer()->EF_CreateRE(eDATA_FogVolume)) : nullptr;
145 m_pMatFogVolEllipsoid = Get3DEngine()->m_pMatFogVolEllipsoid;
146 m_pMatFogVolBox = Get3DEngine()->m_pMatFogVolBox;
148 //Get3DEngine()->RegisterEntity( this );
149 RegisterFogVolume(this);
152 CFogVolumeRenderNode::~CFogVolumeRenderNode()
154 GetInstCount(GetRenderNodeType())--;
156 for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
158 if (m_pFogVolumeRenderElement[i])
160 m_pFogVolumeRenderElement[i]->Release(false);
161 m_pFogVolumeRenderElement[i] = 0;
165 UnregisterFogVolume(this);
166 Get3DEngine()->FreeRenderNodeState(this);
169 void CFogVolumeRenderNode::UpdateFogVolumeMatrices()
171 // update matrices used for ray tracing, distance sorting, etc.
172 Matrix34 mtx = Matrix34::CreateFromVectors(m_scale.x * m_x, m_scale.y * m_y, m_scale.z * m_z, m_pos);
173 m_matWS = mtx;
174 m_matWSInv = mtx.GetInverted();
177 void CFogVolumeRenderNode::UpdateWorldSpaceBBox()
179 // update bounding box in world space used for culling
180 m_WSBBox.SetTransformedAABB(m_matNodeWS, m_localBounds);
183 void CFogVolumeRenderNode::UpdateHeightFallOffBasePoint()
185 m_heightFallOffBasePoint = m_pos + m_heightFallOffShift;
188 void CFogVolumeRenderNode::SetFogVolumeProperties(const SFogVolumeProperties& properties)
190 m_globalDensityFader.SetInvalid();
192 assert(properties.m_size.x > 0 && properties.m_size.y > 0 && properties.m_size.z > 0);
193 Vec3 scale(properties.m_size * 0.5f);
194 if ((m_scale - scale).GetLengthSquared() > 1e-4)
196 m_scale = properties.m_size * 0.5f;
197 m_localBounds.min = Vec3(-1, -1, -1).CompMul(m_scale);
198 m_localBounds.max = -m_localBounds.min;
199 UpdateWorldSpaceBBox();
202 m_volumeType = clamp_tpl<int32>(properties.m_volumeType,
203 IFogVolumeRenderNode::eFogVolumeType_Ellipsoid,
204 IFogVolumeRenderNode::eFogVolumeType_Box);
205 m_color = properties.m_color;
206 assert(properties.m_globalDensity >= 0);
207 m_useGlobalFogColor = properties.m_useGlobalFogColor;
208 m_globalDensity = properties.m_globalDensity;
209 m_densityOffset = properties.m_densityOffset;
210 m_nearCutoff = properties.m_nearCutoff;
211 m_fHDRDynamic = properties.m_fHDRDynamic;
212 assert(properties.m_softEdges >= 0 && properties.m_softEdges <= 1);
213 m_softEdges = properties.m_softEdges;
215 // IgnoreVisArea and AffectsThisAreaOnly don't work concurrently.
216 SetRndFlags(ERF_RENDER_ALWAYS, properties.m_ignoresVisAreas && !properties.m_affectsThisAreaOnly);
218 m_affectsThisAreaOnly = properties.m_affectsThisAreaOnly;
220 float latiArc(DEG2RAD(90.0f - properties.m_heightFallOffDirLati));
221 float longArc(DEG2RAD(properties.m_heightFallOffDirLong));
222 float sinLati(sinf(latiArc));
223 float cosLati(cosf(latiArc));
224 float sinLong(sinf(longArc));
225 float cosLong(cosf(longArc));
226 m_heightFallOffDir = Vec3(sinLati * cosLong, sinLati * sinLong, cosLati);
227 m_heightFallOffShift = m_heightFallOffDir * properties.m_heightFallOffShift;
228 m_heightFallOffDirScaled = m_heightFallOffDir * properties.m_heightFallOffScale;
229 UpdateHeightFallOffBasePoint();
231 m_rampParams = Vec3(properties.m_rampStart, properties.m_rampEnd, properties.m_rampInfluence);
233 m_windInfluence = properties.m_windInfluence;
234 m_densityNoiseScale = properties.m_densityNoiseScale;
235 m_densityNoiseOffset = properties.m_densityNoiseOffset + 1.0f;
236 m_densityNoiseTimeFrequency = properties.m_densityNoiseTimeFrequency;
237 m_densityNoiseFrequency = properties.m_densityNoiseFrequency * 0.01f;// scale the value to useful range
238 m_emission = properties.m_emission;
241 const Matrix34& CFogVolumeRenderNode::GetMatrix() const
243 return m_matNodeWS;
246 void CFogVolumeRenderNode::GetLocalBounds(AABB& bbox)
248 bbox = m_localBounds;
251 void CFogVolumeRenderNode::SetMatrix(const Matrix34& mat)
253 if (m_matNodeWS == mat)
254 return;
256 m_matNodeWS = mat;
258 // get translation and rotational part of fog volume from entity matrix
259 // scale is specified explicitly as fog volumes can be non-uniformly scaled
260 m_pos = mat.GetTranslation();
261 m_x = mat.GetColumn(0);
262 m_y = mat.GetColumn(1);
263 m_z = mat.GetColumn(2);
265 UpdateFogVolumeMatrices();
266 UpdateWorldSpaceBBox();
267 UpdateHeightFallOffBasePoint();
269 Get3DEngine()->RegisterEntity(this);
270 ForceTraceableAreaUpdate();
273 void CFogVolumeRenderNode::FadeGlobalDensity(float fadeTime, float newGlobalDensity)
275 if (newGlobalDensity >= 0)
277 if (fadeTime == 0)
279 m_globalDensity = newGlobalDensity;
280 m_globalDensityFader.SetInvalid();
282 else if (fadeTime > 0)
284 float curFrameTime(gEnv->pTimer->GetCurrTime());
285 m_globalDensityFader.Set(curFrameTime, curFrameTime + fadeTime, m_globalDensity, newGlobalDensity);
290 const char* CFogVolumeRenderNode::GetEntityClassName() const
292 return "FogVolume";
295 const char* CFogVolumeRenderNode::GetName() const
297 return "FogVolume";
300 ColorF CFogVolumeRenderNode::GetFogColor() const
302 //FUNCTION_PROFILER_3DENGINE
303 Vec3 fogColor(m_color.r, m_color.g, m_color.b);
305 bool bVolFogEnabled = (GetCVars()->e_VolumetricFog != 0);
306 if (bVolFogEnabled)
308 if (m_useGlobalFogColor)
310 Get3DEngine()->GetGlobalParameter(E3DPARAM_VOLFOG2_COLOR, fogColor);
313 else
315 if (m_useGlobalFogColor)
317 fogColor = Get3DEngine()->GetFogColor();
321 bool bHDRModeEnabled = false;
322 GetRenderer()->EF_Query(EFQ_HDRModeEnabled, bHDRModeEnabled);
323 if (bHDRModeEnabled)
324 fogColor *= powf(HDRDynamicMultiplier, m_fHDRDynamic);
326 return fogColor;
329 Vec2 CFogVolumeRenderNode::GetSoftEdgeLerp(const Vec3& viewerPosOS) const
331 // Volumetric fog doesn't need special treatment when camera is in the ellipsoid.
332 if (GetCVars()->e_VolumetricFog != 0)
334 return Vec2(m_softEdges, 1.0f - m_softEdges);
337 //FUNCTION_PROFILER_3DENGINE
338 // ramp down soft edge factor as soon as camera enters the ellipsoid
339 float softEdge(m_softEdges * clamp_tpl((viewerPosOS.GetLength() - 0.95f) * 20.0f, 0.0f, 1.0f));
340 return Vec2(softEdge, 1.0f - softEdge);
343 bool CFogVolumeRenderNode::IsViewerInsideVolume(const SRenderingPassInfo& passInfo) const
345 const CCamera& cam(passInfo.GetCamera());
347 // check if fog volumes bounding box intersects the near clipping plane
348 const Plane* pNearPlane(cam.GetFrustumPlane(FR_PLANE_NEAR));
349 Vec3 pntOnNearPlane(cam.GetPosition() - pNearPlane->DistFromPlane(cam.GetPosition()) * pNearPlane->n);
350 Vec3 pntOnNearPlaneOS(m_matWSInv.TransformPoint(pntOnNearPlane));
352 Vec3 nearPlaneOS_n(m_matWSInv.TransformVector(pNearPlane->n) /*.GetNormalized()*/);
353 f32 nearPlaneOS_d(-nearPlaneOS_n.Dot(pntOnNearPlaneOS));
355 // get extreme lengths
356 float t(fabsf(nearPlaneOS_n.x) + fabsf(nearPlaneOS_n.y) + fabsf(nearPlaneOS_n.z));
357 //float t( 0.0f );
358 //if( nearPlaneOS_n.x >= 0 ) t += -nearPlaneOS_n.x; else t += nearPlaneOS_n.x;
359 //if( nearPlaneOS_n.y >= 0 ) t += -nearPlaneOS_n.y; else t += nearPlaneOS_n.y;
360 //if( nearPlaneOS_n.z >= 0 ) t += -nearPlaneOS_n.z; else t += nearPlaneOS_n.z;
362 float t0 = t + nearPlaneOS_d;
363 float t1 = -t + nearPlaneOS_d;
365 return t0 * t1 < 0.0f;
368 void CFogVolumeRenderNode::Render(const SRendParams& rParam, const SRenderingPassInfo& passInfo)
370 FUNCTION_PROFILER_3DENGINE;
372 // anything to render?
373 if (!passInfo.IsGeneralPass())
374 return;
376 if (!m_pMatFogVolBox || !m_pMatFogVolEllipsoid || GetCVars()->e_Fog == 0 || GetCVars()->e_FogVolumes == 0)
377 return;
379 const int32 fillThreadID = passInfo.ThreadID();
381 if (!m_pFogVolumeRenderElement[fillThreadID])
382 return;
384 if (m_globalDensityFader.IsValid())
386 float curFrameTime(gEnv->pTimer->GetCurrTime());
387 m_globalDensity = m_globalDensityFader.GetValue(curFrameTime);
388 if (!m_globalDensityFader.IsTimeInRange(curFrameTime))
389 m_globalDensityFader.SetInvalid();
392 // transform camera into fog volumes object space (where fog volume is a unit-sphere at (0,0,0))
393 const CCamera& cam(passInfo.GetCamera());
394 Vec3 viewerPosWS(cam.GetPosition());
395 Vec3 viewerPosOS(m_matWSInv * viewerPosWS);
397 m_cachedFogColor = GetFogColor();
398 m_cachedSoftEdgesLerp = GetSoftEdgeLerp(viewerPosOS);
400 bool bVolFog = (GetCVars()->e_VolumetricFog != 0);
402 // reset elapsed time for noise when FogVolume stayed out of viewport for 30 frames.
403 // this prevents the time from being too large number.
404 if ((m_updateFrameID + 30) < passInfo.GetMainFrameID() && m_noiseElapsedTime > 5000.0f)
406 m_noiseElapsedTime = -5000.0f;
409 if (bVolFog && m_densityNoiseScale > 0.0f && m_updateFrameID != passInfo.GetMainFrameID())
411 Vec3 wind = Get3DEngine()->GetGlobalWind(false);
412 const float elapsedTime = gEnv->pTimer->GetFrameTime();
414 m_windOffset = ((-m_windInfluence * elapsedTime) * wind) + m_windOffset;
416 const float windOffsetSpan = 1000.0f;// it should match the constant value in FogVolume.cfx
417 m_windOffset.x = m_windOffset.x - floor(m_windOffset.x / windOffsetSpan) * windOffsetSpan;
418 m_windOffset.y = m_windOffset.y - floor(m_windOffset.y / windOffsetSpan) * windOffsetSpan;
419 m_windOffset.z = m_windOffset.z - floor(m_windOffset.z / windOffsetSpan) * windOffsetSpan;
421 m_noiseElapsedTime += m_densityNoiseTimeFrequency * elapsedTime;
423 m_updateFrameID = passInfo.GetMainFrameID();
426 float densityOffset = bVolFog ? (m_densityOffset * 0.001f) : m_densityOffset;// scale the value to useful range
428 // set render element attributes
429 m_pFogVolumeRenderElement[fillThreadID]->m_center = m_pos;
430 m_pFogVolumeRenderElement[fillThreadID]->m_viewerInsideVolume = IsViewerInsideVolume(passInfo) ? 1 : 0;
431 m_pFogVolumeRenderElement[fillThreadID]->m_affectsThisAreaOnly = m_affectsThisAreaOnly ? 1 : 0;
432 m_pFogVolumeRenderElement[fillThreadID]->m_stencilRef = rParam.nClipVolumeStencilRef;
433 m_pFogVolumeRenderElement[fillThreadID]->m_volumeType = m_volumeType;
434 m_pFogVolumeRenderElement[fillThreadID]->m_localAABB = m_localBounds;
435 m_pFogVolumeRenderElement[fillThreadID]->m_matWSInv = m_matWSInv;
436 m_pFogVolumeRenderElement[fillThreadID]->m_fogColor = m_cachedFogColor;
437 m_pFogVolumeRenderElement[fillThreadID]->m_globalDensity = m_globalDensity;
438 m_pFogVolumeRenderElement[fillThreadID]->m_densityOffset = densityOffset;
439 m_pFogVolumeRenderElement[fillThreadID]->m_nearCutoff = m_nearCutoff;
440 m_pFogVolumeRenderElement[fillThreadID]->m_softEdgesLerp = m_cachedSoftEdgesLerp;
441 m_pFogVolumeRenderElement[fillThreadID]->m_heightFallOffDirScaled = m_heightFallOffDirScaled;
442 m_pFogVolumeRenderElement[fillThreadID]->m_heightFallOffBasePoint = m_heightFallOffBasePoint;
443 m_pFogVolumeRenderElement[fillThreadID]->m_eyePosInWS = viewerPosWS;
444 m_pFogVolumeRenderElement[fillThreadID]->m_eyePosInOS = viewerPosOS;
445 m_pFogVolumeRenderElement[fillThreadID]->m_rampParams = m_rampParams;
446 m_pFogVolumeRenderElement[fillThreadID]->m_windOffset = m_windOffset;
447 m_pFogVolumeRenderElement[fillThreadID]->m_noiseScale = m_densityNoiseScale;
448 m_pFogVolumeRenderElement[fillThreadID]->m_noiseFreq = m_densityNoiseFrequency;
449 m_pFogVolumeRenderElement[fillThreadID]->m_noiseOffset = m_densityNoiseOffset;
450 m_pFogVolumeRenderElement[fillThreadID]->m_noiseElapsedTime = m_noiseElapsedTime;
451 m_pFogVolumeRenderElement[fillThreadID]->m_emission = m_emission;
453 IRenderView* pRenderView = passInfo.GetIRenderView();
454 if (bVolFog)
456 pRenderView->AddFogVolume(m_pFogVolumeRenderElement[fillThreadID]);
458 else
460 IRenderer* pRenderer = GetRenderer();
461 CRenderObject* pRenderObject = pRenderView->AllocateTemporaryRenderObject();
463 if (!pRenderObject)
464 return;
466 // set basic render object properties
467 pRenderObject->SetMatrix(m_matNodeWS, passInfo);
468 pRenderObject->m_ObjFlags |= FOB_TRANS_MASK;
469 pRenderObject->m_fSort = 0;
471 int nAfterWater = GetObjManager()->IsAfterWater(m_pos, passInfo.GetCamera().GetPosition(), passInfo, Get3DEngine()->GetWaterLevel()) ? 1 : 0;
473 // TODO: add constant factor to sortID to make fog volumes render before all other alpha transparent geometry (or have separate render list?)
474 pRenderObject->m_fSort = WATER_LEVEL_SORTID_OFFSET * 0.5f;
476 // get shader item
477 IMaterial* pMaterial =
478 (m_volumeType == IFogVolumeRenderNode::eFogVolumeType_Box)
479 ? m_pMatFogVolBox
480 : m_pMatFogVolEllipsoid;
481 pMaterial = (rParam.pMaterial != nullptr) ? rParam.pMaterial : pMaterial;
482 SShaderItem& shaderItem = pMaterial->GetShaderItem(0);
484 pRenderObject->m_pCurrMaterial = pMaterial;
486 // add to renderer
487 pRenderView->AddRenderObject(m_pFogVolumeRenderElement[fillThreadID], shaderItem, pRenderObject, passInfo, EFSLIST_TRANSP, nAfterWater);
491 IPhysicalEntity* CFogVolumeRenderNode::GetPhysics() const
493 return 0;
496 void CFogVolumeRenderNode::SetPhysics(IPhysicalEntity*)
500 void CFogVolumeRenderNode::SetMaterial(IMaterial* pMat)
504 void CFogVolumeRenderNode::GetMemoryUsage(ICrySizer* pSizer) const
506 SIZER_COMPONENT_NAME(pSizer, "FogVolumeNode");
507 pSizer->AddObject(this, sizeof(*this));
510 void CFogVolumeRenderNode::OffsetPosition(const Vec3& delta)
512 if (const auto pTempData = m_pTempData.load()) pTempData->OffsetPosition(delta);
513 m_pos += delta;
514 m_matNodeWS.SetTranslation(m_matNodeWS.GetTranslation() + delta);
515 m_matWS.SetTranslation(m_matWS.GetTranslation() + delta);
516 m_matWSInv = m_matWS.GetInverted();
517 m_heightFallOffBasePoint += delta;
518 m_WSBBox.Move(delta);
521 ///////////////////////////////////////////////////////////////////////////////
522 inline static float expf_s(float arg)
524 return expf(clamp_tpl(arg, -80.0f, 80.0f));
527 ///////////////////////////////////////////////////////////////////////////////
528 ///////////////////////////////////////////////////////////////////////////////
529 void CFogVolumeRenderNode::TraceFogVolumes(const Vec3& worldPos, ColorF& fogColor, const SRenderingPassInfo& passInfo)
531 FUNCTION_PROFILER_3DENGINE;
532 PrefetchLine(&s_tracableFogVolumeArea, 0);
534 // init default result
535 ColorF localFogColor = ColorF(0.0f, 0.0f, 0.0f, 0.0f);
537 // trace is needed when volumetric fog is off.
538 if (GetCVars()->e_Fog && GetCVars()->e_FogVolumes && (GetCVars()->e_VolumetricFog == 0))
540 // init view ray
541 Vec3 camPos(s_tracableFogVolumeArea.GetCenter());
542 Lineseg lineseg(camPos, worldPos);
544 #ifdef _DEBUG
545 const SCachedFogVolume* prev(0);
546 #endif
548 // loop over all traceable fog volumes
549 CachedFogVolumes::const_iterator itEnd(s_cachedFogVolumes.end());
550 for (CachedFogVolumes::const_iterator it(s_cachedFogVolumes.begin()); it != itEnd; ++it)
552 // get current fog volume
553 const CFogVolumeRenderNode* pFogVol((*it).m_pFogVol);
555 // only trace visible fog volumes
556 if (!(pFogVol->GetRndFlags() & ERF_HIDDEN))
558 // check if view ray intersects with bounding box of current fog volume
559 if (Overlap::Lineseg_AABB(lineseg, pFogVol->m_WSBBox))
561 // compute contribution of current fog volume
562 ColorF color;
563 if (0 == pFogVol->m_volumeType)
564 pFogVol->GetVolumetricFogColorEllipsoid(worldPos, passInfo, color);
565 else
566 pFogVol->GetVolumetricFogColorBox(worldPos, passInfo, color);
568 color.a = 1.0f - color.a; // 0 = transparent, 1 = opaque
570 // blend fog colors
571 localFogColor.r = Lerp(localFogColor.r, color.r, color.a);
572 localFogColor.g = Lerp(localFogColor.g, color.g, color.a);
573 localFogColor.b = Lerp(localFogColor.b, color.b, color.a);
574 localFogColor.a = Lerp(localFogColor.a, 1.0f, color.a);
578 #ifdef _DEBUG
579 if (prev)
581 assert(prev->m_distToCenterSq >= (*it).m_distToCenterSq);
582 prev = &(*it);
584 #endif
588 const float fDivisor = (float)__fsel(-localFogColor.a, 1.0f, localFogColor.a);
589 const float fMultiplier = (float)__fsel(-localFogColor.a, 0.0f, 1.0f / fDivisor);
591 localFogColor.r *= fMultiplier;
592 localFogColor.g *= fMultiplier;
593 localFogColor.b *= fMultiplier;
596 localFogColor.a = 1.0f - localFogColor.a;
598 fogColor = localFogColor;
601 ///////////////////////////////////////////////////////////////////////////////
602 void CFogVolumeRenderNode::GetVolumetricFogColorEllipsoid(const Vec3& worldPos, const SRenderingPassInfo& passInfo, ColorF& resultColor) const
604 const CCamera& cam(passInfo.GetCamera());
605 Vec3 camPos(cam.GetPosition());
606 Vec3 camDir(cam.GetViewdir());
607 Vec3 cameraLookDir(worldPos - camPos);
609 resultColor = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
611 if (cameraLookDir.GetLengthSquared() > 1e-4f)
613 // setup ray tracing in OS
614 Vec3 cameraPosInOSx2(m_matWSInv.TransformPoint(camPos) * 2.0f);
615 Vec3 cameraLookDirInOS(m_matWSInv.TransformVector(cameraLookDir));
617 float tI(sqrtf(cameraLookDirInOS.Dot(cameraLookDirInOS)));
618 float invOfScaledCamDirLength(1.0f / tI);
619 cameraLookDirInOS *= invOfScaledCamDirLength;
621 // calc coefficients for ellipsoid parametrization (just a simple unit-sphere in its own space)
622 float B(cameraPosInOSx2.Dot(cameraLookDirInOS));
623 float Bsq(B * B);
624 float C(cameraPosInOSx2.Dot(cameraPosInOSx2) - 4.0f);
626 // solve quadratic equation
627 float discr(Bsq - C);
628 if (discr >= 0.0)
630 float discrSqrt = sqrtf(discr);
632 // ray hit
633 Vec3 cameraPosInWS(camPos);
634 Vec3 cameraLookDirInWS((worldPos - camPos) * invOfScaledCamDirLength);
636 //////////////////////////////////////////////////////////////////////////
638 float tS(max(0.5f * (-B - discrSqrt), 0.0f)); // clamp to zero so front ray-ellipsoid intersection is NOT behind camera
639 float tE(max(0.5f * (-B + discrSqrt), 0.0f)); // clamp to zero so back ray-ellipsoid intersection is NOT behind camera
640 //float tI( ( worldPos - camPos ).Dot( camDir ) / cameraLookDirInWS.Dot( camDir ) );
641 tI = max(tS, min(tI, tE)); // clamp to range [tS, tE]
643 Vec3 front(tS * cameraLookDirInWS + cameraPosInWS);
644 Vec3 dist((tI - tS) * cameraLookDirInWS);
645 float distLength(dist.GetLength());
646 float fogInt(distLength * expf_s(-(front - m_heightFallOffBasePoint).Dot(m_heightFallOffDirScaled)));
648 //////////////////////////////////////////////////////////////////////////
650 float heightDiff(dist.Dot(m_heightFallOffDirScaled));
651 if (fabsf(heightDiff) > 0.001f)
652 fogInt *= (1.0f - expf_s(-heightDiff)) / heightDiff;
654 float softArg(clamp_tpl(discr * m_cachedSoftEdgesLerp.x + m_cachedSoftEdgesLerp.y, 0.0f, 1.0f));
655 fogInt *= softArg * (2.0f - softArg);
657 float fog(expf_s(-m_globalDensity * fogInt));
659 resultColor = ColorF(m_cachedFogColor.r, m_cachedFogColor.g, m_cachedFogColor.b, min(fog, 1.0f));
664 ///////////////////////////////////////////////////////////////////////////////
665 void CFogVolumeRenderNode::GetVolumetricFogColorBox(const Vec3& worldPos, const SRenderingPassInfo& passInfo, ColorF& resultColor) const
667 const CCamera& cam(passInfo.GetCamera());
668 Vec3 camPos(cam.GetPosition());
669 Vec3 cameraLookDir(worldPos - camPos);
671 resultColor = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
673 if (cameraLookDir.GetLengthSquared() > 1e-4f)
675 // setup ray tracing in OS
676 Vec3 cameraPosInOS(m_matWSInv.TransformPoint(camPos));
677 Vec3 cameraLookDirInOS(m_matWSInv.TransformVector(cameraLookDir));
679 float tI(sqrtf(cameraLookDirInOS.Dot(cameraLookDirInOS)));
680 float invOfScaledCamDirLength(1.0f / tI);
681 cameraLookDirInOS *= invOfScaledCamDirLength;
683 const float fMax = std::numeric_limits<float>::max();
684 float tS(0), tE(fMax);
686 //TODO:
687 // May be worth profiling use of a loop here, iterating over elements of vector;
688 // might save on i-cache, but suspect we'll lose instruction interleaving and hit
689 // more register dependency issues.
691 //These fsels mean that the result is ignored if cameraLookDirInOS.x is 0.0f,
692 // avoiding a floating point compare. Avoiding the fcmp is ~15% faster
693 const float fXSelect = -fabsf(cameraLookDirInOS.x);
694 const float fXDivisor = (float)__fsel(fXSelect, 1.0f, cameraLookDirInOS.x);
695 const float fXMultiplier = (float)__fsel(fXSelect, 0.0f, 1.0f);
696 const float fXInvMultiplier = 1.0f - fXMultiplier;
698 //Accurate to 255/256ths on console
699 float invCameraDirInOSx = __fres(fXDivisor); //(1.0f / fXDivisor);
701 float tPosPlane((1 - cameraPosInOS.x) * invCameraDirInOSx);
702 float tNegPlane((-1 - cameraPosInOS.x) * invCameraDirInOSx);
704 float tFrontFace = (float)__fsel(-cameraLookDirInOS.x, tPosPlane, tNegPlane);
705 float tBackFace = (float)__fsel(-cameraLookDirInOS.x, tNegPlane, tPosPlane);
707 tS = max(tS, tFrontFace * fXMultiplier);
708 tE = min(tE, (tBackFace * fXMultiplier) + (fXInvMultiplier * fMax));
710 const float fYSelect = -fabsf(cameraLookDirInOS.y);
711 const float fYDivisor = (float)__fsel(fYSelect, 1.0f, cameraLookDirInOS.y);
712 const float fYMultiplier = (float)__fsel(fYSelect, 0.0f, 1.0f);
713 const float fYInvMultiplier = 1.0f - fYMultiplier;
715 //Accurate to 255/256ths on console
716 float invCameraDirInOSy = __fres(fYDivisor); //(1.0f / fYDivisor);
718 tPosPlane = ((1 - cameraPosInOS.y) * invCameraDirInOSy);
719 tNegPlane = ((-1 - cameraPosInOS.y) * invCameraDirInOSy);
721 tFrontFace = (float)__fsel(-cameraLookDirInOS.y, tPosPlane, tNegPlane);
722 tBackFace = (float)__fsel(-cameraLookDirInOS.y, tNegPlane, tPosPlane);
724 tS = max(tS, tFrontFace * fYMultiplier);
725 tE = min(tE, (tBackFace * fYMultiplier) + (fYInvMultiplier * fMax));
727 const float fZSelect = -fabsf(cameraLookDirInOS.z);
728 const float fZDivisor = (float)__fsel(fZSelect, 1.0f, cameraLookDirInOS.z);
729 const float fZMultiplier = (float)__fsel(fZSelect, 0.0f, 1.0f);
730 const float fZInvMultiplier = 1.0f - fZMultiplier;
732 //Accurate to 255/256ths on console
733 float invCameraDirInOSz = __fres(fZDivisor); //(1.0f / fZDivisor);
735 tPosPlane = ((1 - cameraPosInOS.z) * invCameraDirInOSz);
736 tNegPlane = ((-1 - cameraPosInOS.z) * invCameraDirInOSz);
738 tFrontFace = (float)__fsel(-cameraLookDirInOS.z, tPosPlane, tNegPlane);
739 tBackFace = (float)__fsel(-cameraLookDirInOS.z, tNegPlane, tPosPlane);
741 tS = max(tS, tFrontFace * fZMultiplier);
742 tE = min(tE, (tBackFace * fZMultiplier) + (fZInvMultiplier * fMax));
744 tE = max(tE, 0.0f);
746 if (tS <= tE)
748 Vec3 cameraPosInWS(camPos);
749 Vec3 cameraLookDirInWS((worldPos - camPos) * invOfScaledCamDirLength);
751 //////////////////////////////////////////////////////////////////////////
753 tI = max(tS, min(tI, tE)); // clamp to range [tS, tE]
755 Vec3 front(tS * cameraLookDirInWS + cameraPosInWS);
756 Vec3 dist((tI - tS) * cameraLookDirInWS);
757 float distLength(dist.GetLength());
758 float fogInt(distLength * expf_s(-(front - m_heightFallOffBasePoint).Dot(m_heightFallOffDirScaled)));
760 //////////////////////////////////////////////////////////////////////////
762 float heightDiff(dist.Dot(m_heightFallOffDirScaled));
764 //heightDiff = fabsf( heightDiff ) > 0.001f ? heightDiff : 0.001f
765 heightDiff = (float)__fsel((-fabsf(heightDiff) + 0.001f), 0.001f, heightDiff);
767 fogInt *= (1.0f - expf_s(-heightDiff)) * __fres(heightDiff);
769 float fog(expf_s(-m_globalDensity * fogInt));
771 resultColor = ColorF(m_cachedFogColor.r, m_cachedFogColor.g, m_cachedFogColor.b, min(fog, 1.0f));
776 ///////////////////////////////////////////////////////////////////////////////
777 void CFogVolumeRenderNode::FillBBox(AABB& aabb)
779 aabb = CFogVolumeRenderNode::GetBBox();
782 ///////////////////////////////////////////////////////////////////////////////
783 EERType CFogVolumeRenderNode::GetRenderNodeType()
785 return eERType_FogVolume;
788 ///////////////////////////////////////////////////////////////////////////////
789 float CFogVolumeRenderNode::GetMaxViewDist()
791 if (GetMinSpecFromRenderNodeFlags(m_dwRndFlags) == CONFIG_DETAIL_SPEC)
792 return max(GetCVars()->e_ViewDistMin, CFogVolumeRenderNode::GetBBox().GetRadius() * GetCVars()->e_ViewDistRatioDetail * GetViewDistRatioNormilized());
794 return max(GetCVars()->e_ViewDistMin, CFogVolumeRenderNode::GetBBox().GetRadius() * GetCVars()->e_ViewDistRatio * GetViewDistRatioNormilized());
798 ///////////////////////////////////////////////////////////////////////////////
799 Vec3 CFogVolumeRenderNode::GetPos(bool bWorldOnly) const
801 return m_pos;
804 ///////////////////////////////////////////////////////////////////////////////
805 IMaterial* CFogVolumeRenderNode::GetMaterial(Vec3* pHitPos) const
807 return 0;