1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * Shadow mapping related texture and matrix management
22 #include "precompiled.h"
24 #include "gui/GUIutil.h"
27 #include "ps/CLogger.h"
28 #include "ps/ConfigDB.h"
29 #include "ps/Profile.h"
31 #include "graphics/LightEnv.h"
32 #include "graphics/ShaderManager.h"
34 #include "maths/BoundingBoxAligned.h"
35 #include "maths/Brush.h"
36 #include "maths/MathUtil.h"
37 #include "maths/Matrix3D.h"
39 #include "renderer/Renderer.h"
40 #include "renderer/ShadowMap.h"
43 ///////////////////////////////////////////////////////////////////////////////////////////////////
44 // ShadowMap implementation
47 * Struct ShadowMapInternals: Internal data for the ShadowMap implementation
49 struct ShadowMapInternals
51 // bit depth for the depth texture
53 // the EXT_framebuffer_object framebuffer
55 // handle of shadow map
57 // width, height of shadow map
59 // Shadow map quality (-2 - Very Low, -1 - Low, 0 - Medium, 1 - High, 2 - Very High)
61 // used width, height of shadow map
62 int EffectiveWidth
, EffectiveHeight
;
63 // transform light space into projected light space
64 // in projected light space, the shadowbound box occupies the [-1..1] cube
65 // calculated on BeginRender, after the final shadow bounds are known
66 CMatrix3D LightProjection
;
67 // Transform world space into light space; calculated on SetupFrame
68 CMatrix3D LightTransform
;
69 // Transform world space into texture space of the shadow map;
70 // calculated on BeginRender, after the final shadow bounds are known
71 CMatrix3D TextureMatrix
;
73 // transform light space into world space
74 CMatrix3D InvLightTransform
;
75 // bounding box of shadowed objects in light space
76 CBoundingBoxAligned ShadowCasterBound
;
77 CBoundingBoxAligned ShadowReceiverBound
;
79 CBoundingBoxAligned ShadowRenderBound
;
81 // Camera transformed into light space
82 CCamera LightspaceCamera
;
84 // Some drivers (at least some Intel Mesa ones) appear to handle alpha testing
85 // incorrectly when the FBO has only a depth attachment.
86 // When m_ShadowAlphaFix is true, we use DummyTexture to store a useless
87 // alpha texture which is attached to the FBO as a workaround.
90 // Copy of renderer's standard view camera, saved between
91 // BeginRender and EndRender while we replace it with the shadow camera
92 CCamera SavedViewCamera
;
94 // Save the caller's FBO so it can be restored
98 void CalcShadowMatrices();
103 ///////////////////////////////////////////////////////////////////////////////////////////////////
104 // Construction/Destruction
105 ShadowMap::ShadowMap()
107 m
= new ShadowMapInternals
;
114 m
->EffectiveWidth
= 0;
115 m
->EffectiveHeight
= 0;
116 m
->DepthTextureBits
= 0;
117 // DepthTextureBits: 24/32 are very much faster than 16, on GeForce 4 and FX;
118 // but they're very much slower on Radeon 9800.
119 // In both cases, the default (no specified depth) is fast, so we just use
120 // that by default and hope it's alright. (Otherwise, we'd probably need to
121 // do some kind of hardware detection to work out what to use.)
123 // Avoid using uninitialised values in AddShadowedBound if SetupFrame wasn't called first
124 m
->LightTransform
.SetIdentity();
128 ShadowMap::~ShadowMap()
131 glDeleteTextures(1, &m
->Texture
);
133 glDeleteTextures(1, &m
->DummyTexture
);
135 pglDeleteFramebuffersEXT(1, &m
->Framebuffer
);
140 ///////////////////////////////////////////////////////////////////////////////////////////////////
141 // Force the texture/buffer/etc to be recreated, particularly when the renderer's
143 void ShadowMap::RecreateTexture()
146 glDeleteTextures(1, &m
->Texture
);
148 glDeleteTextures(1, &m
->DummyTexture
);
150 pglDeleteFramebuffersEXT(1, &m
->Framebuffer
);
156 // (Texture will be constructed in next SetupFrame)
159 //////////////////////////////////////////////////////////////////////////////
160 // SetupFrame: camera and light direction for this frame
161 void ShadowMap::SetupFrame(const CCamera
& camera
, const CVector3D
& lightdir
)
166 CVector3D z
= lightdir
;
168 CVector3D x
= camera
.m_Orientation
.GetIn();
169 CVector3D eyepos
= camera
.m_Orientation
.GetTranslation();
173 if (x
.Length() < 0.001)
175 // this is invoked if the camera and light directions almost coincide
176 // assumption: light direction has a significant Z component
177 x
= CVector3D(1.0, 0.0, 0.0);
183 // X axis perpendicular to light direction, flowing along with view direction
184 m
->LightTransform
._11
= x
.X
;
185 m
->LightTransform
._12
= x
.Y
;
186 m
->LightTransform
._13
= x
.Z
;
188 // Y axis perpendicular to light and view direction
189 m
->LightTransform
._21
= y
.X
;
190 m
->LightTransform
._22
= y
.Y
;
191 m
->LightTransform
._23
= y
.Z
;
193 // Z axis is in direction of light
194 m
->LightTransform
._31
= z
.X
;
195 m
->LightTransform
._32
= z
.Y
;
196 m
->LightTransform
._33
= z
.Z
;
198 // eye is at the origin of the coordinate system
199 m
->LightTransform
._14
= -x
.Dot(eyepos
);
200 m
->LightTransform
._24
= -y
.Dot(eyepos
);
201 m
->LightTransform
._34
= -z
.Dot(eyepos
);
203 m
->LightTransform
._41
= 0.0;
204 m
->LightTransform
._42
= 0.0;
205 m
->LightTransform
._43
= 0.0;
206 m
->LightTransform
._44
= 1.0;
208 m
->LightTransform
.GetInverse(m
->InvLightTransform
);
209 m
->ShadowCasterBound
.SetEmpty();
210 m
->ShadowReceiverBound
.SetEmpty();
213 m
->LightspaceCamera
= camera
;
214 m
->LightspaceCamera
.m_Orientation
= m
->LightTransform
* camera
.m_Orientation
;
215 m
->LightspaceCamera
.UpdateFrustum();
219 //////////////////////////////////////////////////////////////////////////////
220 // AddShadowedBound: add a world-space bounding box to the bounds of shadowed
222 void ShadowMap::AddShadowCasterBound(const CBoundingBoxAligned
& bounds
)
224 CBoundingBoxAligned lightspacebounds
;
226 bounds
.Transform(m
->LightTransform
, lightspacebounds
);
227 m
->ShadowCasterBound
+= lightspacebounds
;
230 void ShadowMap::AddShadowReceiverBound(const CBoundingBoxAligned
& bounds
)
232 CBoundingBoxAligned lightspacebounds
;
234 bounds
.Transform(m
->LightTransform
, lightspacebounds
);
235 m
->ShadowReceiverBound
+= lightspacebounds
;
238 CFrustum
ShadowMap::GetShadowCasterCullFrustum()
240 // Get the bounds of all objects that can receive shadows
241 CBoundingBoxAligned bound
= m
->ShadowReceiverBound
;
243 // Intersect with the camera frustum, so the shadow map doesn't have to get
244 // stretched to cover the off-screen parts of large models
245 bound
.IntersectFrustumConservative(m
->LightspaceCamera
.GetFrustum());
247 // ShadowBound might have been empty to begin with, producing an empty result
250 // CFrustum can't easily represent nothingness, so approximate it with
251 // a single point which won't match many objects
252 bound
+= CVector3D(0.0f
, 0.0f
, 0.0f
);
253 return bound
.ToFrustum();
256 // Extend the bounds a long way towards the light source, to encompass
257 // all objects that might cast visible shadows.
258 // (The exact constant was picked entirely arbitrarily.)
259 bound
[0].Z
-= 1000.f
;
261 CFrustum frustum
= bound
.ToFrustum();
262 frustum
.Transform(m
->InvLightTransform
);
266 ///////////////////////////////////////////////////////////////////////////////////////////////////
267 // CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
268 // projection and transformation matrices
269 void ShadowMapInternals::CalcShadowMatrices()
271 // Start building the shadow map to cover all objects that will receive shadows
272 CBoundingBoxAligned receiverBound
= ShadowReceiverBound
;
274 // Intersect with the camera frustum, so the shadow map doesn't have to get
275 // stretched to cover the off-screen parts of large models
276 receiverBound
.IntersectFrustumConservative(LightspaceCamera
.GetFrustum());
278 // Intersect with the shadow caster bounds, because there's no point
279 // wasting space around the edges of the shadow map that we're not going
281 ShadowRenderBound
[0].X
= std::max(receiverBound
[0].X
, ShadowCasterBound
[0].X
);
282 ShadowRenderBound
[0].Y
= std::max(receiverBound
[0].Y
, ShadowCasterBound
[0].Y
);
283 ShadowRenderBound
[1].X
= std::min(receiverBound
[1].X
, ShadowCasterBound
[1].X
);
284 ShadowRenderBound
[1].Y
= std::min(receiverBound
[1].Y
, ShadowCasterBound
[1].Y
);
286 // Set the near and far planes to include just the shadow casters,
287 // so we make full use of the depth texture's range. Add a bit of a
288 // delta so we don't accidentally clip objects that are directly on
290 ShadowRenderBound
[0].Z
= ShadowCasterBound
[0].Z
- 2.f
;
291 ShadowRenderBound
[1].Z
= ShadowCasterBound
[1].Z
+ 2.f
;
293 // ShadowBound might have been empty to begin with, producing an empty result
294 if (ShadowRenderBound
.IsEmpty())
297 LightProjection
.SetIdentity();
298 TextureMatrix
= LightTransform
;
302 // round off the shadow boundaries to sane increments to help reduce swim effect
303 float boundInc
= 16.0f
;
304 ShadowRenderBound
[0].X
= floor(ShadowRenderBound
[0].X
/ boundInc
) * boundInc
;
305 ShadowRenderBound
[0].Y
= floor(ShadowRenderBound
[0].Y
/ boundInc
) * boundInc
;
306 ShadowRenderBound
[1].X
= ceil(ShadowRenderBound
[1].X
/ boundInc
) * boundInc
;
307 ShadowRenderBound
[1].Y
= ceil(ShadowRenderBound
[1].Y
/ boundInc
) * boundInc
;
309 // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
310 CVector3D scale
= ShadowRenderBound
[1] - ShadowRenderBound
[0];
311 CVector3D shift
= (ShadowRenderBound
[1] + ShadowRenderBound
[0]) * -0.5;
320 scale
.X
= 2.0 / scale
.X
;
321 scale
.Y
= 2.0 / scale
.Y
;
322 scale
.Z
= 2.0 / scale
.Z
;
324 // make sure a given world position falls on a consistent shadowmap texel fractional offset
325 float offsetX
= fmod(ShadowRenderBound
[0].X
- LightTransform
._14
, 2.0f
/(scale
.X
*EffectiveWidth
));
326 float offsetY
= fmod(ShadowRenderBound
[0].Y
- LightTransform
._24
, 2.0f
/(scale
.Y
*EffectiveHeight
));
328 LightProjection
.SetZero();
329 LightProjection
._11
= scale
.X
;
330 LightProjection
._14
= (shift
.X
+ offsetX
) * scale
.X
;
331 LightProjection
._22
= scale
.Y
;
332 LightProjection
._24
= (shift
.Y
+ offsetY
) * scale
.Y
;
333 LightProjection
._33
= scale
.Z
;
334 LightProjection
._34
= shift
.Z
* scale
.Z
;
335 LightProjection
._44
= 1.0;
337 // Calculate texture matrix by creating the clip space to texture coordinate matrix
338 // and then concatenating all matrices that have been calculated so far
340 float texscalex
= scale
.X
* 0.5f
* (float)EffectiveWidth
/ (float)Width
;
341 float texscaley
= scale
.Y
* 0.5f
* (float)EffectiveHeight
/ (float)Height
;
342 float texscalez
= scale
.Z
* 0.5f
;
344 CMatrix3D lightToTex
;
345 lightToTex
.SetZero();
346 lightToTex
._11
= texscalex
;
347 lightToTex
._14
= (offsetX
- ShadowRenderBound
[0].X
) * texscalex
;
348 lightToTex
._22
= texscaley
;
349 lightToTex
._24
= (offsetY
- ShadowRenderBound
[0].Y
) * texscaley
;
350 lightToTex
._33
= texscalez
;
351 lightToTex
._34
= -ShadowRenderBound
[0].Z
* texscalez
;
352 lightToTex
._44
= 1.0;
354 TextureMatrix
= lightToTex
* LightTransform
;
359 //////////////////////////////////////////////////////////////////////////
360 // Create the shadow map
361 void ShadowMapInternals::CreateTexture()
366 glDeleteTextures(1, &Texture
);
371 glDeleteTextures(1, &DummyTexture
);
376 pglDeleteFramebuffersEXT(1, &Framebuffer
);
380 // save the caller's FBO
381 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &SavedViewFBO
);
383 pglGenFramebuffersEXT(1, &Framebuffer
);
385 if (g_Renderer
.m_ShadowMapSize
!= 0)
387 // non-default option to override the size
388 Width
= Height
= g_Renderer
.m_ShadowMapSize
;
392 CFG_GET_VAL("shadowquality", QualityLevel
);
394 // get shadow map size as next power of two up from view width/height
395 int shadow_map_size
= (int)round_up_to_pow2((unsigned)std::max(g_Renderer
.GetWidth(), g_Renderer
.GetHeight()));
396 switch (QualityLevel
)
400 shadow_map_size
/= 4;
404 shadow_map_size
/= 2;
408 shadow_map_size
*= 2;
412 shadow_map_size
*= 4;
418 Width
= Height
= shadow_map_size
;
420 // Clamp to the maximum texture size
421 Width
= std::min(Width
, (int)ogl_max_tex_size
);
422 Height
= std::min(Height
, (int)ogl_max_tex_size
);
424 // Since we're using a framebuffer object, the whole texture is available
425 EffectiveWidth
= Width
;
426 EffectiveHeight
= Height
;
428 const char* formatname
;
430 switch(DepthTextureBits
)
432 case 16: formatname
= "DEPTH_COMPONENT16"; break;
433 case 24: formatname
= "DEPTH_COMPONENT24"; break;
434 case 32: formatname
= "DEPTH_COMPONENT32"; break;
435 default: formatname
= "DEPTH_COMPONENT"; break;
438 LOGMESSAGE("Creating shadow texture (size %dx%d) (format = %s)",
439 Width
, Height
, formatname
);
442 if (g_Renderer
.m_Options
.m_ShadowAlphaFix
)
444 glGenTextures(1, &DummyTexture
);
445 g_Renderer
.BindTexture(0, DummyTexture
);
446 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
447 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
448 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
449 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
450 glTexImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, Width
, Height
, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, NULL
);
453 glGenTextures(1, &Texture
);
454 g_Renderer
.BindTexture(0, Texture
);
459 format
= GL_DEPTH_COMPONENT
;
461 switch (DepthTextureBits
)
463 case 16: format
= GL_DEPTH_COMPONENT16
; break;
464 case 24: format
= GL_DEPTH_COMPONENT24
; break;
465 case 32: format
= GL_DEPTH_COMPONENT32
; break;
466 default: format
= GL_DEPTH_COMPONENT
; break;
470 glTexImage2D(GL_TEXTURE_2D
, 0, format
, Width
, Height
, 0, GL_DEPTH_COMPONENT
, GL_UNSIGNED_SHORT
, NULL
);
471 // GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
473 // set texture parameters
474 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
475 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
478 // GLES doesn't do depth comparisons, so treat it as a
479 // basic unfiltered depth texture
480 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
481 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
483 // Enable automatic depth comparisons
484 glTexParameteri(GL_TEXTURE_2D
, GL_DEPTH_TEXTURE_MODE
, GL_INTENSITY
);
485 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_MODE
, GL_COMPARE_R_TO_TEXTURE
);
486 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_FUNC
, GL_LEQUAL
);
488 // Use GL_LINEAR to trigger automatic PCF on some devices
489 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
490 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
495 // bind to framebuffer object
496 glBindTexture(GL_TEXTURE_2D
, 0);
497 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, Framebuffer
);
499 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
, GL_DEPTH_ATTACHMENT_EXT
, GL_TEXTURE_2D
, Texture
, 0);
501 if (g_Renderer
.m_Options
.m_ShadowAlphaFix
)
503 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT
, GL_COLOR_ATTACHMENT0_EXT
, GL_TEXTURE_2D
, DummyTexture
, 0);
508 #warning TODO: figure out whether the glDrawBuffer/glReadBuffer stuff is needed, since it is not supported by GLES
510 glDrawBuffer(GL_NONE
);
515 glReadBuffer(GL_NONE
);
520 GLenum status
= pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT
);
522 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, SavedViewFBO
);
524 if (status
!= GL_FRAMEBUFFER_COMPLETE_EXT
)
526 LOGWARNING("Framebuffer object incomplete: 0x%04X", status
);
528 // Disable shadow rendering (but let the user try again if they want)
529 g_Renderer
.m_Options
.m_Shadows
= false;
534 ///////////////////////////////////////////////////////////////////////////////////////////////////
535 // Set up to render into shadow map texture
536 void ShadowMap::BeginRender()
538 // HACK HACK: this depends in non-obvious ways on the behaviour of the caller
541 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT
, &m
->SavedViewFBO
);
543 // Calc remaining shadow matrices
544 m
->CalcShadowMatrices();
547 PROFILE("bind framebuffer");
548 glBindTexture(GL_TEXTURE_2D
, 0);
549 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m
->Framebuffer
);
554 PROFILE("clear depth texture");
555 // In case we used m_ShadowAlphaFix, we ought to clear the unused
556 // color buffer too, else Mali 400 drivers get confused.
557 // Might as well clear stencil too for completeness.
558 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
| GL_STENCIL_BUFFER_BIT
);
559 glColorMask(0,0,0,0);
563 const SViewPort vp
= { 0, 0, m
->EffectiveWidth
, m
->EffectiveHeight
};
564 g_Renderer
.SetViewport(vp
);
566 m
->SavedViewCamera
= g_Renderer
.GetViewCamera();
568 CCamera c
= m
->SavedViewCamera
;
569 c
.SetProjection(m
->LightProjection
);
570 c
.GetOrientation() = m
->InvLightTransform
;
571 g_Renderer
.SetViewCamera(c
);
574 glMatrixMode(GL_PROJECTION
);
575 glLoadMatrixf(&m
->LightProjection
._11
);
576 glMatrixMode(GL_MODELVIEW
);
577 glLoadMatrixf(&m
->LightTransform
._11
);
580 glEnable(GL_SCISSOR_TEST
);
581 glScissor(1,1, m
->EffectiveWidth
-2, m
->EffectiveHeight
-2);
585 ///////////////////////////////////////////////////////////////////////////////////////////////////
586 // Finish rendering into shadow map texture
587 void ShadowMap::EndRender()
589 glDisable(GL_SCISSOR_TEST
);
591 g_Renderer
.SetViewCamera(m
->SavedViewCamera
);
594 PROFILE("unbind framebuffer");
595 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT
, m
->SavedViewFBO
);
598 const SViewPort vp
= { 0, 0, g_Renderer
.GetWidth(), g_Renderer
.GetHeight() };
599 g_Renderer
.SetViewport(vp
);
601 glColorMask(1,1,1,1);
605 ///////////////////////////////////////////////////////////////////////////////////////////////////
606 // Retrieve the texture handle and texture matrix for shadowing
607 GLuint
ShadowMap::GetTexture() const
612 const CMatrix3D
& ShadowMap::GetTextureMatrix() const
614 return m
->TextureMatrix
;
618 ///////////////////////////////////////////////////////////////////////////////////////////////////
619 // Depth texture bits
620 int ShadowMap::GetDepthTextureBits() const
622 return m
->DepthTextureBits
;
625 void ShadowMap::SetDepthTextureBits(int bits
)
627 if (bits
!= m
->DepthTextureBits
)
631 glDeleteTextures(1, &m
->Texture
);
634 m
->Width
= m
->Height
= 0;
636 m
->DepthTextureBits
= bits
;
640 ///////////////////////////////////////////////////////////////////////////////////////////////////
641 // Depth texture size
642 int ShadowMap::GetWidth() const
647 int ShadowMap::GetHeight() const
652 //////////////////////////////////////////////////////////////////////////////
654 void ShadowMap::RenderDebugBounds()
656 CShaderTechniquePtr shaderTech
= g_Renderer
.GetShaderManager().LoadEffect(str_gui_solid
);
657 shaderTech
->BeginPass();
658 CShaderProgramPtr shader
= shaderTech
->GetShader();
661 glDisable(GL_CULL_FACE
);
663 // Render various shadow bounds:
664 // Yellow = bounds of objects in view frustum that receive shadows
665 // Red = culling frustum used to find potential shadow casters
666 // Green = bounds of objects in culling frustum that cast shadows
667 // Blue = frustum used for rendering the shadow map
669 shader
->Uniform(str_transform
, g_Renderer
.GetViewCamera().GetViewProjection() * m
->InvLightTransform
);
671 shader
->Uniform(str_color
, 1.0f
, 1.0f
, 0.0f
, 1.0f
);
672 m
->ShadowReceiverBound
.RenderOutline(shader
);
674 shader
->Uniform(str_color
, 0.0f
, 1.0f
, 0.0f
, 1.0f
);
675 m
->ShadowCasterBound
.RenderOutline(shader
);
678 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
679 shader
->Uniform(str_color
, 0.0f
, 0.0f
, 1.0f
, 0.25f
);
680 m
->ShadowRenderBound
.Render(shader
);
683 shader
->Uniform(str_color
, 0.0f
, 0.0f
, 1.0f
, 1.0f
);
684 m
->ShadowRenderBound
.RenderOutline(shader
);
686 // Render light frustum
688 shader
->Uniform(str_transform
, g_Renderer
.GetViewCamera().GetViewProjection());
690 CFrustum frustum
= GetShadowCasterCullFrustum();
691 // We don't have a function to create a brush directly from a frustum, so use
692 // the ugly approach of creating a large cube and then intersecting with the frustum
693 CBoundingBoxAligned
dummy(CVector3D(-1e4
, -1e4
, -1e4
), CVector3D(1e4
, 1e4
, 1e4
));
696 brush
.Intersect(frustum
, frustumBrush
);
699 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
700 shader
->Uniform(str_color
, 1.0f
, 0.0f
, 0.0f
, 0.25f
);
701 frustumBrush
.Render(shader
);
704 shader
->Uniform(str_color
, 1.0f
, 0.0f
, 0.0f
, 1.0f
);
705 frustumBrush
.RenderOutline(shader
);
708 shaderTech
->EndPass();
711 CMatrix3D InvTexTransform
;
713 m
->TextureMatrix
.GetInverse(InvTexTransform
);
715 // Render representative texture rectangle
717 glMultMatrixf(&InvTexTransform
._11
);
720 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
721 glColor4ub(255,0,0,64);
723 glVertex3f(0.0, 0.0, 0.0);
724 glVertex3f(1.0, 0.0, 0.0);
725 glVertex3f(1.0, 1.0, 0.0);
726 glVertex3f(0.0, 1.0, 0.0);
730 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
733 glVertex3f(0.0, 0.0, 0.0);
734 glVertex3f(1.0, 0.0, 0.0);
735 glVertex3f(1.0, 1.0, 0.0);
736 glVertex3f(0.0, 1.0, 0.0);
738 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
742 glEnable(GL_CULL_FACE
);
746 void ShadowMap::RenderDebugTexture()
750 glDisable(GL_DEPTH_TEST
);
753 g_Renderer
.BindTexture(0, m
->Texture
);
754 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_MODE
, GL_NONE
);
757 CShaderTechniquePtr texTech
= g_Renderer
.GetShaderManager().LoadEffect(str_gui_basic
);
758 texTech
->BeginPass();
759 CShaderProgramPtr texShader
= texTech
->GetShader();
761 texShader
->Uniform(str_transform
, GetDefaultGuiMatrix());
762 texShader
->BindTexture(str_tex
, m
->Texture
);
774 texShader
->VertexPointer(2, GL_FLOAT
, 0, boxVerts
);
775 texShader
->TexCoordPointer(GL_TEXTURE0
, 2, GL_FLOAT
, 0, boxUV
);
776 texShader
->AssertPointersBound();
777 glDrawArrays(GL_TRIANGLES
, 0, 6);
782 g_Renderer
.BindTexture(0, m
->Texture
);
783 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_COMPARE_MODE
, GL_COMPARE_R_TO_TEXTURE
);
786 glEnable(GL_DEPTH_TEST
);