1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
4 #include "FogVolumeRenderNode.h"
6 #include <CryRenderer/RenderElements/CREFogVolume.h>
7 #include <CryMath/Cry_Geo.h>
9 #include "ClipVolumeManager.h"
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
)
31 if (!GetCVars()->e_Fog
|| !GetCVars()->e_FogVolumes
)
34 if (GetCVars()->e_VolumetricFog
!= 0)
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
)
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()));
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()
94 , m_volumeType(IFogVolumeRenderNode::eFogVolumeType_Ellipsoid
)
105 , m_color(1, 1, 1, 1)
106 , m_useGlobalFogColor(false)
107 , m_affectsThisAreaOnly(false)
108 , m_rampParams(0, 1, 0)
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)
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
);
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
246 void CFogVolumeRenderNode::GetLocalBounds(AABB
& bbox
)
248 bbox
= m_localBounds
;
251 void CFogVolumeRenderNode::SetMatrix(const Matrix34
& mat
)
253 if (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)
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
295 const char* CFogVolumeRenderNode::GetName() const
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);
308 if (m_useGlobalFogColor
)
310 Get3DEngine()->GetGlobalParameter(E3DPARAM_VOLFOG2_COLOR
, fogColor
);
315 if (m_useGlobalFogColor
)
317 fogColor
= Get3DEngine()->GetFogColor();
321 bool bHDRModeEnabled
= false;
322 GetRenderer()->EF_Query(EFQ_HDRModeEnabled
, bHDRModeEnabled
);
324 fogColor
*= powf(HDRDynamicMultiplier
, m_fHDRDynamic
);
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
));
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())
376 if (!m_pMatFogVolBox
|| !m_pMatFogVolEllipsoid
|| GetCVars()->e_Fog
== 0 || GetCVars()->e_FogVolumes
== 0)
379 const int32 fillThreadID
= passInfo
.ThreadID();
381 if (!m_pFogVolumeRenderElement
[fillThreadID
])
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();
456 pRenderView
->AddFogVolume(m_pFogVolumeRenderElement
[fillThreadID
]);
460 IRenderer
* pRenderer
= GetRenderer();
461 CRenderObject
* pRenderObject
= pRenderView
->AllocateTemporaryRenderObject();
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
;
477 IMaterial
* pMaterial
=
478 (m_volumeType
== IFogVolumeRenderNode::eFogVolumeType_Box
)
480 : m_pMatFogVolEllipsoid
;
481 pMaterial
= (rParam
.pMaterial
!= nullptr) ? rParam
.pMaterial
: pMaterial
;
482 SShaderItem
& shaderItem
= pMaterial
->GetShaderItem(0);
484 pRenderObject
->m_pCurrMaterial
= pMaterial
;
487 pRenderView
->AddRenderObject(m_pFogVolumeRenderElement
[fillThreadID
], shaderItem
, pRenderObject
, passInfo
, EFSLIST_TRANSP
, nAfterWater
);
491 IPhysicalEntity
* CFogVolumeRenderNode::GetPhysics() const
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
);
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))
541 Vec3
camPos(s_tracableFogVolumeArea
.GetCenter());
542 Lineseg
lineseg(camPos
, worldPos
);
545 const SCachedFogVolume
* prev(0);
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
563 if (0 == pFogVol
->m_volumeType
)
564 pFogVol
->GetVolumetricFogColorEllipsoid(worldPos
, passInfo
, color
);
566 pFogVol
->GetVolumetricFogColorBox(worldPos
, passInfo
, color
);
568 color
.a
= 1.0f
- color
.a
; // 0 = transparent, 1 = opaque
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
);
581 assert(prev
->m_distToCenterSq
>= (*it
).m_distToCenterSq
);
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
));
624 float C(cameraPosInOSx2
.Dot(cameraPosInOSx2
) - 4.0f
);
626 // solve quadratic equation
627 float discr(Bsq
- C
);
630 float discrSqrt
= sqrtf(discr
);
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
);
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
));
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
804 ///////////////////////////////////////////////////////////////////////////////
805 IMaterial
* CFogVolumeRenderNode::GetMaterial(Vec3
* pHitPos
) const