source: ps/trunk/source/renderer/ShadowMap.cpp@ 25159

Last change on this file since 25159 was 25159, checked in by Vladislav Belov, 3 years ago

Moves Frustum from graphics to maths to more related geometric primitives like bounding ones.

  • Property svn:eol-style set to native
File size: 24.1 KB
Line 
1/* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
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.
8 *
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.
13 *
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/>.
16 */
17
18#include "precompiled.h"
19
20#include "ShadowMap.h"
21
22#include "graphics/Camera.h"
23#include "graphics/LightEnv.h"
24#include "graphics/ShaderManager.h"
25#include "gui/GUIMatrix.h"
26#include "lib/bits.h"
27#include "lib/ogl.h"
28#include "maths/BoundingBoxAligned.h"
29#include "maths/Brush.h"
30#include "maths/Frustum.h"
31#include "maths/MathUtil.h"
32#include "maths/Matrix3D.h"
33#include "ps/CLogger.h"
34#include "ps/ConfigDB.h"
35#include "ps/Profile.h"
36#include "renderer/Renderer.h"
37#include "renderer/RenderingOptions.h"
38
39/**
40 * Struct ShadowMapInternals: Internal data for the ShadowMap implementation
41 */
42struct ShadowMapInternals
43{
44 // bit depth for the depth texture
45 int DepthTextureBits;
46 // the EXT_framebuffer_object framebuffer
47 GLuint Framebuffer;
48 // handle of shadow map
49 GLuint Texture;
50 // width, height of shadow map
51 int Width, Height;
52 // Shadow map quality (-2 - Very Low, -1 - Low, 0 - Medium, 1 - High, 2 - Very High)
53 int QualityLevel;
54 // used width, height of shadow map
55 int EffectiveWidth, EffectiveHeight;
56 // transform light space into projected light space
57 // in projected light space, the shadowbound box occupies the [-1..1] cube
58 // calculated on BeginRender, after the final shadow bounds are known
59 CMatrix3D LightProjection;
60 // Transform world space into light space; calculated on SetupFrame
61 CMatrix3D LightTransform;
62 // Transform world space into texture space of the shadow map;
63 // calculated on BeginRender, after the final shadow bounds are known
64 CMatrix3D TextureMatrix;
65
66 // transform light space into world space
67 CMatrix3D InvLightTransform;
68 // bounding box of shadowed objects in light space
69 CBoundingBoxAligned ShadowCasterBound;
70 CBoundingBoxAligned ShadowReceiverBound;
71
72 CBoundingBoxAligned ShadowRenderBound;
73
74 CBoundingBoxAligned FixedFrustumBounds;
75 bool FixedShadowsEnabled;
76 float FixedShadowsDistance;
77
78 // Camera transformed into light space
79 CCamera LightspaceCamera;
80
81 // Some drivers (at least some Intel Mesa ones) appear to handle alpha testing
82 // incorrectly when the FBO has only a depth attachment.
83 // When m_ShadowAlphaFix is true, we use DummyTexture to store a useless
84 // alpha texture which is attached to the FBO as a workaround.
85 GLuint DummyTexture;
86
87 // Copy of renderer's standard view camera, saved between
88 // BeginRender and EndRender while we replace it with the shadow camera
89 CCamera SavedViewCamera;
90
91 // Save the caller's FBO so it can be restored
92 GLint SavedViewFBO;
93
94 // Helper functions
95 void CalcShadowMatrices();
96 void CreateTexture();
97};
98
99void CalculateBoundsForFixedShadows(
100 const CCamera& camera, const CMatrix3D& lightTransform,
101 const float nearPlane, const float farPlane, CBoundingBoxAligned* bbaa)
102{
103 // We need to calculate a circumscribed sphere for the camera to
104 // create a rotation stable bounding box.
105 const CVector3D cameraIn = camera.m_Orientation.GetIn();
106 const CVector3D cameraTranslation = camera.m_Orientation.GetTranslation();
107 const CVector3D centerNear = cameraTranslation + cameraIn * nearPlane;
108 const CVector3D centerDist = cameraTranslation + cameraIn * farPlane;
109
110 // We can solve 3D problem in 2D space, because the frustum is
111 // symmetric by 2 planes. Than means we can use only one corner
112 // to find a circumscribed sphere.
113 CCamera::Quad corners;
114 camera.GetViewQuad(nearPlane, corners);
115 const CVector3D cornerNear = camera.GetOrientation().Transform(corners[0]);
116 camera.GetViewQuad(farPlane, corners);
117 const CVector3D cornerDist = camera.GetOrientation().Transform(corners[0]);
118
119 // We solve 2D case for the right trapezoid.
120 const float firstBase = (cornerNear - centerNear).Length();
121 const float secondBase = (cornerDist - centerDist).Length();
122 const float height = (centerDist - centerNear).Length();
123 const float distanceToCenter =
124 (height * height + secondBase * secondBase - firstBase * firstBase) * 0.5f / height;
125
126 CVector3D position = cameraTranslation + cameraIn * (camera.GetNearPlane() + distanceToCenter);
127 const float radius = (cornerNear - position).Length();
128
129 // We need to convert the bounding box to the light space.
130 position = lightTransform.Rotate(position);
131
132 const float insets = 0.2f;
133 *bbaa = CBoundingBoxAligned(position, position);
134 bbaa->Expand(radius);
135 bbaa->Expand(insets);
136}
137
138ShadowMap::ShadowMap()
139{
140 m = new ShadowMapInternals;
141 m->Framebuffer = 0;
142 m->Texture = 0;
143 m->DummyTexture = 0;
144 m->Width = 0;
145 m->Height = 0;
146 m->QualityLevel = 0;
147 m->EffectiveWidth = 0;
148 m->EffectiveHeight = 0;
149 m->DepthTextureBits = 0;
150 // DepthTextureBits: 24/32 are very much faster than 16, on GeForce 4 and FX;
151 // but they're very much slower on Radeon 9800.
152 // In both cases, the default (no specified depth) is fast, so we just use
153 // that by default and hope it's alright. (Otherwise, we'd probably need to
154 // do some kind of hardware detection to work out what to use.)
155
156 // Avoid using uninitialised values in AddShadowedBound if SetupFrame wasn't called first
157 m->LightTransform.SetIdentity();
158
159 m->FixedShadowsEnabled = false;
160 m->FixedShadowsDistance = 300.0f;
161 CFG_GET_VAL("shadowsfixed", m->FixedShadowsEnabled);
162 CFG_GET_VAL("shadowsfixeddistance", m->FixedShadowsDistance);
163}
164
165ShadowMap::~ShadowMap()
166{
167 if (m->Texture)
168 glDeleteTextures(1, &m->Texture);
169 if (m->DummyTexture)
170 glDeleteTextures(1, &m->DummyTexture);
171 if (m->Framebuffer)
172 pglDeleteFramebuffersEXT(1, &m->Framebuffer);
173
174 delete m;
175}
176
177// Force the texture/buffer/etc to be recreated, particularly when the renderer's
178// size has changed
179void ShadowMap::RecreateTexture()
180{
181 if (m->Texture)
182 glDeleteTextures(1, &m->Texture);
183 if (m->DummyTexture)
184 glDeleteTextures(1, &m->DummyTexture);
185 if (m->Framebuffer)
186 pglDeleteFramebuffersEXT(1, &m->Framebuffer);
187
188 m->Texture = 0;
189 m->DummyTexture = 0;
190 m->Framebuffer = 0;
191
192 // (Texture will be constructed in next SetupFrame)
193}
194
195// SetupFrame: camera and light direction for this frame
196void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
197{
198 if (!m->Texture)
199 m->CreateTexture();
200
201 CVector3D x, eyepos;
202 if (!m->FixedShadowsEnabled)
203 {
204 x = camera.m_Orientation.GetIn();
205 eyepos = camera.m_Orientation.GetTranslation();
206 }
207 else
208 x = CVector3D(0, 1, 0);
209
210 CVector3D z = lightdir;
211 z.Normalize();
212 x -= z * z.Dot(x);
213 if (x.Length() < 0.001)
214 {
215 // this is invoked if the camera and light directions almost coincide
216 // assumption: light direction has a significant Z component
217 x = CVector3D(1.0, 0.0, 0.0);
218 x -= z * z.Dot(x);
219 }
220 x.Normalize();
221 CVector3D y = z.Cross(x);
222
223 // X axis perpendicular to light direction, flowing along with view direction
224 m->LightTransform._11 = x.X;
225 m->LightTransform._12 = x.Y;
226 m->LightTransform._13 = x.Z;
227
228 // Y axis perpendicular to light and view direction
229 m->LightTransform._21 = y.X;
230 m->LightTransform._22 = y.Y;
231 m->LightTransform._23 = y.Z;
232
233 // Z axis is in direction of light
234 m->LightTransform._31 = z.X;
235 m->LightTransform._32 = z.Y;
236 m->LightTransform._33 = z.Z;
237
238 // eye is at the origin of the coordinate system
239 m->LightTransform._14 = -x.Dot(eyepos);
240 m->LightTransform._24 = -y.Dot(eyepos);
241 m->LightTransform._34 = -z.Dot(eyepos);
242
243 m->LightTransform._41 = 0.0;
244 m->LightTransform._42 = 0.0;
245 m->LightTransform._43 = 0.0;
246 m->LightTransform._44 = 1.0;
247
248 m->LightTransform.GetInverse(m->InvLightTransform);
249 m->ShadowCasterBound.SetEmpty();
250 m->ShadowReceiverBound.SetEmpty();
251
252 //
253 m->LightspaceCamera = camera;
254 m->LightspaceCamera.m_Orientation = m->LightTransform * camera.m_Orientation;
255 m->LightspaceCamera.UpdateFrustum();
256
257 if (m->FixedShadowsEnabled)
258 CalculateBoundsForFixedShadows(camera, m->LightTransform, camera.GetNearPlane(), m->FixedShadowsDistance, &m->FixedFrustumBounds);
259}
260
261// AddShadowedBound: add a world-space bounding box to the bounds of shadowed
262// objects
263void ShadowMap::AddShadowCasterBound(const CBoundingBoxAligned& bounds)
264{
265 CBoundingBoxAligned lightspacebounds;
266
267 bounds.Transform(m->LightTransform, lightspacebounds);
268 m->ShadowCasterBound += lightspacebounds;
269}
270
271void ShadowMap::AddShadowReceiverBound(const CBoundingBoxAligned& bounds)
272{
273 CBoundingBoxAligned lightspacebounds;
274
275 bounds.Transform(m->LightTransform, lightspacebounds);
276 m->ShadowReceiverBound += lightspacebounds;
277}
278
279CFrustum ShadowMap::GetShadowCasterCullFrustum()
280{
281 // Get the bounds of all objects that can receive shadows
282 CBoundingBoxAligned bound = m->ShadowReceiverBound;
283
284 // Intersect with the camera frustum, so the shadow map doesn't have to get
285 // stretched to cover the off-screen parts of large models
286 bound.IntersectFrustumConservative(m->LightspaceCamera.GetFrustum());
287
288 // ShadowBound might have been empty to begin with, producing an empty result
289 if (bound.IsEmpty())
290 {
291 // CFrustum can't easily represent nothingness, so approximate it with
292 // a single point which won't match many objects
293 bound += CVector3D(0.0f, 0.0f, 0.0f);
294 return bound.ToFrustum();
295 }
296
297 // Extend the bounds a long way towards the light source, to encompass
298 // all objects that might cast visible shadows.
299 // (The exact constant was picked entirely arbitrarily.)
300 bound[0].Z -= 1000.f;
301
302 CFrustum frustum = bound.ToFrustum();
303 frustum.Transform(m->InvLightTransform);
304 return frustum;
305}
306
307// CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
308// projection and transformation matrices
309void ShadowMapInternals::CalcShadowMatrices()
310{
311 if (FixedShadowsEnabled)
312 {
313 ShadowRenderBound = FixedFrustumBounds;
314
315 // Set the near and far planes to include just the shadow casters,
316 // so we make full use of the depth texture's range. Add a bit of a
317 // delta so we don't accidentally clip objects that are directly on
318 // the planes.
319 ShadowRenderBound[0].Z = ShadowCasterBound[0].Z - 2.f;
320 ShadowRenderBound[1].Z = ShadowCasterBound[1].Z + 2.f;
321 }
322 else
323 {
324 // Start building the shadow map to cover all objects that will receive shadows
325 CBoundingBoxAligned receiverBound = ShadowReceiverBound;
326
327 // Intersect with the camera frustum, so the shadow map doesn't have to get
328 // stretched to cover the off-screen parts of large models
329 receiverBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());
330
331 // Intersect with the shadow caster bounds, because there's no point
332 // wasting space around the edges of the shadow map that we're not going
333 // to draw into
334 ShadowRenderBound[0].X = std::max(receiverBound[0].X, ShadowCasterBound[0].X);
335 ShadowRenderBound[0].Y = std::max(receiverBound[0].Y, ShadowCasterBound[0].Y);
336 ShadowRenderBound[1].X = std::min(receiverBound[1].X, ShadowCasterBound[1].X);
337 ShadowRenderBound[1].Y = std::min(receiverBound[1].Y, ShadowCasterBound[1].Y);
338
339 // Set the near and far planes to include just the shadow casters,
340 // so we make full use of the depth texture's range. Add a bit of a
341 // delta so we don't accidentally clip objects that are directly on
342 // the planes.
343 ShadowRenderBound[0].Z = ShadowCasterBound[0].Z - 2.f;
344 ShadowRenderBound[1].Z = ShadowCasterBound[1].Z + 2.f;
345
346 // ShadowBound might have been empty to begin with, producing an empty result
347 if (ShadowRenderBound.IsEmpty())
348 {
349 // no-op
350 LightProjection.SetIdentity();
351 TextureMatrix = LightTransform;
352 return;
353 }
354
355 // round off the shadow boundaries to sane increments to help reduce swim effect
356 float boundInc = 16.0f;
357 ShadowRenderBound[0].X = floor(ShadowRenderBound[0].X / boundInc) * boundInc;
358 ShadowRenderBound[0].Y = floor(ShadowRenderBound[0].Y / boundInc) * boundInc;
359 ShadowRenderBound[1].X = ceil(ShadowRenderBound[1].X / boundInc) * boundInc;
360 ShadowRenderBound[1].Y = ceil(ShadowRenderBound[1].Y / boundInc) * boundInc;
361 }
362
363 // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
364 CVector3D scale = ShadowRenderBound[1] - ShadowRenderBound[0];
365 CVector3D shift = (ShadowRenderBound[1] + ShadowRenderBound[0]) * -0.5;
366
367 if (scale.X < 1.0)
368 scale.X = 1.0;
369 if (scale.Y < 1.0)
370 scale.Y = 1.0;
371 if (scale.Z < 1.0)
372 scale.Z = 1.0;
373
374 scale.X = 2.0 / scale.X;
375 scale.Y = 2.0 / scale.Y;
376 scale.Z = 2.0 / scale.Z;
377
378 // make sure a given world position falls on a consistent shadowmap texel fractional offset
379 float offsetX = fmod(ShadowRenderBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth));
380 float offsetY = fmod(ShadowRenderBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight));
381
382 LightProjection.SetZero();
383 LightProjection._11 = scale.X;
384 LightProjection._14 = (shift.X + offsetX) * scale.X;
385 LightProjection._22 = scale.Y;
386 LightProjection._24 = (shift.Y + offsetY) * scale.Y;
387 LightProjection._33 = scale.Z;
388 LightProjection._34 = shift.Z * scale.Z;
389 LightProjection._44 = 1.0;
390
391 // Calculate texture matrix by creating the clip space to texture coordinate matrix
392 // and then concatenating all matrices that have been calculated so far
393
394 float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width;
395 float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height;
396 float texscalez = scale.Z * 0.5f;
397
398 CMatrix3D lightToTex;
399 lightToTex.SetZero();
400 lightToTex._11 = texscalex;
401 lightToTex._14 = (offsetX - ShadowRenderBound[0].X) * texscalex;
402 lightToTex._22 = texscaley;
403 lightToTex._24 = (offsetY - ShadowRenderBound[0].Y) * texscaley;
404 lightToTex._33 = texscalez;
405 lightToTex._34 = -ShadowRenderBound[0].Z * texscalez;
406 lightToTex._44 = 1.0;
407
408 TextureMatrix = lightToTex * LightTransform;
409}
410
411// Create the shadow map
412void ShadowMapInternals::CreateTexture()
413{
414 // Cleanup
415 if (Texture)
416 {
417 glDeleteTextures(1, &Texture);
418 Texture = 0;
419 }
420 if (DummyTexture)
421 {
422 glDeleteTextures(1, &DummyTexture);
423 DummyTexture = 0;
424 }
425 if (Framebuffer)
426 {
427 pglDeleteFramebuffersEXT(1, &Framebuffer);
428 Framebuffer = 0;
429 }
430
431 // save the caller's FBO
432 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &SavedViewFBO);
433
434 pglGenFramebuffersEXT(1, &Framebuffer);
435
436 if (g_Renderer.m_ShadowMapSize != 0)
437 {
438 // non-default option to override the size
439 Width = Height = g_Renderer.m_ShadowMapSize;
440 }
441 else
442 {
443 CFG_GET_VAL("shadowquality", QualityLevel);
444
445 // get shadow map size as next power of two up from view width/height
446 int shadow_map_size = (int)round_up_to_pow2((unsigned)std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight()));
447 switch (QualityLevel)
448 {
449 // Very Low
450 case -2:
451 shadow_map_size /= 4;
452 break;
453 // Low
454 case -1:
455 shadow_map_size /= 2;
456 break;
457 // High
458 case 1:
459 shadow_map_size *= 2;
460 break;
461 // Ultra
462 case 2:
463 shadow_map_size *= 4;
464 break;
465 // Medium as is
466 default:
467 break;
468 }
469 Width = Height = shadow_map_size;
470 }
471 // Clamp to the maximum texture size
472 Width = std::min(Width, (int)ogl_max_tex_size);
473 Height = std::min(Height, (int)ogl_max_tex_size);
474
475 // Since we're using a framebuffer object, the whole texture is available
476 EffectiveWidth = Width;
477 EffectiveHeight = Height;
478
479 GLenum format;
480 const char* formatName;
481#if CONFIG2_GLES
482 format = GL_DEPTH_COMPONENT;
483 formatName = "DEPTH_COMPONENT";
484#else
485 switch ( DepthTextureBits )
486 {
487 case 16: format = GL_DEPTH_COMPONENT16; formatName = "DEPTH_COMPONENT16"; break;
488 case 24: format = GL_DEPTH_COMPONENT24; formatName = "DEPTH_COMPONENT24"; break;
489 case 32: format = GL_DEPTH_COMPONENT32; formatName = "DEPTH_COMPONENT32"; break;
490 default: format = GL_DEPTH_COMPONENT; formatName = "DEPTH_COMPONENT"; break;
491 }
492#endif
493 ENSURE(formatName);
494
495 LOGMESSAGE("Creating shadow texture (size %dx%d) (format = %s)",
496 Width, Height, formatName);
497
498 if (g_RenderingOptions.GetShadowAlphaFix())
499 {
500 glGenTextures(1, &DummyTexture);
501 g_Renderer.BindTexture(0, DummyTexture);
502 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
503 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
504 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
505 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
506 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
507 }
508
509 glGenTextures(1, &Texture);
510 g_Renderer.BindTexture(0, Texture);
511
512 glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
513 // GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
514
515 // set texture parameters
516 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
517 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
518
519#if CONFIG2_GLES
520 // GLES doesn't do depth comparisons, so treat it as a
521 // basic unfiltered depth texture
522 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
523 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
524#else
525 // Enable automatic depth comparisons
526 glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
528 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
529
530 // Use GL_LINEAR to trigger automatic PCF on some devices
531 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
532 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
533#endif
534
535 ogl_WarnIfError();
536
537 // bind to framebuffer object
538 glBindTexture(GL_TEXTURE_2D, 0);
539 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Framebuffer);
540
541 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, Texture, 0);
542
543 if (g_RenderingOptions.GetShadowAlphaFix())
544 {
545 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, DummyTexture, 0);
546 }
547 else
548 {
549#if CONFIG2_GLES
550#warning TODO: figure out whether the glDrawBuffer/glReadBuffer stuff is needed, since it is not supported by GLES
551#else
552 glDrawBuffer(GL_NONE);
553#endif
554 }
555
556#if !CONFIG2_GLES
557 glReadBuffer(GL_NONE);
558#endif
559
560 ogl_WarnIfError();
561
562 GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
563
564 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, SavedViewFBO);
565
566 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
567 {
568 LOGWARNING("Framebuffer object incomplete: 0x%04X", status);
569
570 // Disable shadow rendering (but let the user try again if they want)
571 g_RenderingOptions.SetShadows(false);
572 }
573}
574
575// Set up to render into shadow map texture
576void ShadowMap::BeginRender()
577{
578 // Calc remaining shadow matrices
579 m->CalcShadowMatrices();
580
581 {
582 PROFILE("bind framebuffer");
583 glBindTexture(GL_TEXTURE_2D, 0);
584 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->Framebuffer);
585 }
586
587 // clear buffers
588 {
589 PROFILE("clear depth texture");
590 // In case we used m_ShadowAlphaFix, we ought to clear the unused
591 // color buffer too, else Mali 400 drivers get confused.
592 // Might as well clear stencil too for completeness.
593 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
594 glColorMask(0,0,0,0);
595 }
596
597 // setup viewport
598 const SViewPort vp = { 0, 0, m->EffectiveWidth, m->EffectiveHeight };
599 g_Renderer.SetViewport(vp);
600
601 m->SavedViewCamera = g_Renderer.GetViewCamera();
602
603 CCamera c = m->SavedViewCamera;
604 c.SetProjection(m->LightProjection);
605 c.GetOrientation() = m->InvLightTransform;
606 g_Renderer.SetViewCamera(c);
607
608#if !CONFIG2_GLES
609 glMatrixMode(GL_PROJECTION);
610 glLoadMatrixf(&m->LightProjection._11);
611 glMatrixMode(GL_MODELVIEW);
612 glLoadMatrixf(&m->LightTransform._11);
613#endif
614
615 glEnable(GL_SCISSOR_TEST);
616 glScissor(1,1, m->EffectiveWidth-2, m->EffectiveHeight-2);
617}
618
619// Finish rendering into shadow map texture
620void ShadowMap::EndRender()
621{
622 glDisable(GL_SCISSOR_TEST);
623
624 g_Renderer.SetViewCamera(m->SavedViewCamera);
625
626 {
627 PROFILE("unbind framebuffer");
628 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
629 }
630
631 const SViewPort vp = { 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight() };
632 g_Renderer.SetViewport(vp);
633
634 glColorMask(1,1,1,1);
635}
636
637void ShadowMap::BindTo(const CShaderProgramPtr& shader) const
638{
639 if (!shader->GetTextureBinding(str_shadowTex).Active())
640 return;
641
642 shader->BindTexture(str_shadowTex, m->Texture);
643 shader->Uniform(str_shadowTransform, m->TextureMatrix);
644 shader->Uniform(str_shadowScale, m->Width, m->Height, 1.0f / m->Width, 1.0f / m->Height);
645}
646
647// Depth texture bits
648int ShadowMap::GetDepthTextureBits() const
649{
650 return m->DepthTextureBits;
651}
652
653void ShadowMap::SetDepthTextureBits(int bits)
654{
655 if (bits != m->DepthTextureBits)
656 {
657 if (m->Texture)
658 {
659 glDeleteTextures(1, &m->Texture);
660 m->Texture = 0;
661 }
662 m->Width = m->Height = 0;
663
664 m->DepthTextureBits = bits;
665 }
666}
667
668void ShadowMap::RenderDebugBounds()
669{
670 CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
671 shaderTech->BeginPass();
672 CShaderProgramPtr shader = shaderTech->GetShader();
673
674 glDepthMask(0);
675 glDisable(GL_CULL_FACE);
676
677 // Render various shadow bounds:
678 // Yellow = bounds of objects in view frustum that receive shadows
679 // Red = culling frustum used to find potential shadow casters
680 // Green = bounds of objects in culling frustum that cast shadows
681 // Blue = frustum used for rendering the shadow map
682
683 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection() * m->InvLightTransform);
684
685 shader->Uniform(str_color, 1.0f, 1.0f, 0.0f, 1.0f);
686 m->ShadowReceiverBound.RenderOutline(shader);
687
688 shader->Uniform(str_color, 0.0f, 1.0f, 0.0f, 1.0f);
689 m->ShadowCasterBound.RenderOutline(shader);
690
691 glEnable(GL_BLEND);
692 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
693 shader->Uniform(str_color, 0.0f, 0.0f, 1.0f, 0.25f);
694 m->ShadowRenderBound.Render(shader);
695 glDisable(GL_BLEND);
696
697 shader->Uniform(str_color, 0.0f, 0.0f, 1.0f, 1.0f);
698 m->ShadowRenderBound.RenderOutline(shader);
699
700 // Render light frustum
701
702 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
703
704 CFrustum frustum = GetShadowCasterCullFrustum();
705 // We don't have a function to create a brush directly from a frustum, so use
706 // the ugly approach of creating a large cube and then intersecting with the frustum
707 CBoundingBoxAligned dummy(CVector3D(-1e4, -1e4, -1e4), CVector3D(1e4, 1e4, 1e4));
708 CBrush brush(dummy);
709 CBrush frustumBrush;
710 brush.Intersect(frustum, frustumBrush);
711
712 glEnable(GL_BLEND);
713 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
714 shader->Uniform(str_color, 1.0f, 0.0f, 0.0f, 0.25f);
715 frustumBrush.Render(shader);
716 glDisable(GL_BLEND);
717
718 shader->Uniform(str_color, 1.0f, 0.0f, 0.0f, 1.0f);
719 frustumBrush.RenderOutline(shader);
720
721
722 shaderTech->EndPass();
723
724#if 0
725 CMatrix3D InvTexTransform;
726
727 m->TextureMatrix.GetInverse(InvTexTransform);
728
729 // Render representative texture rectangle
730 glPushMatrix();
731 glMultMatrixf(&InvTexTransform._11);
732
733 glEnable(GL_BLEND);
734 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
735 glColor4ub(255,0,0,64);
736 glBegin(GL_QUADS);
737 glVertex3f(0.0, 0.0, 0.0);
738 glVertex3f(1.0, 0.0, 0.0);
739 glVertex3f(1.0, 1.0, 0.0);
740 glVertex3f(0.0, 1.0, 0.0);
741 glEnd();
742 glDisable(GL_BLEND);
743
744 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
745 glColor3ub(255,0,0);
746 glBegin(GL_QUADS);
747 glVertex3f(0.0, 0.0, 0.0);
748 glVertex3f(1.0, 0.0, 0.0);
749 glVertex3f(1.0, 1.0, 0.0);
750 glVertex3f(0.0, 1.0, 0.0);
751 glEnd();
752 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
753 glPopMatrix();
754#endif
755
756 glEnable(GL_CULL_FACE);
757 glDepthMask(1);
758
759 ogl_WarnIfError();
760}
761
762void ShadowMap::RenderDebugTexture()
763{
764 glDepthMask(0);
765
766 glDisable(GL_DEPTH_TEST);
767
768#if !CONFIG2_GLES
769 g_Renderer.BindTexture(0, m->Texture);
770 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
771#endif
772
773 CShaderTechniquePtr texTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_basic);
774 texTech->BeginPass();
775 CShaderProgramPtr texShader = texTech->GetShader();
776
777 texShader->Uniform(str_transform, GetDefaultGuiMatrix());
778 texShader->BindTexture(str_tex, m->Texture);
779
780 float s = 256.f;
781 float boxVerts[] = {
782 0,0, 0,s, s,0,
783 s,0, 0,s, s,s
784 };
785 float boxUV[] = {
786 0,0, 0,1, 1,0,
787 1,0, 0,1, 1,1
788 };
789
790 texShader->VertexPointer(2, GL_FLOAT, 0, boxVerts);
791 texShader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, boxUV);
792 texShader->AssertPointersBound();
793 glDrawArrays(GL_TRIANGLES, 0, 6);
794
795 texTech->EndPass();
796
797#if !CONFIG2_GLES
798 g_Renderer.BindTexture(0, m->Texture);
799 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
800#endif
801
802 glEnable(GL_DEPTH_TEST);
803 glDepthMask(1);
804
805 ogl_WarnIfError();
806}
Note: See TracBrowser for help on using the repository browser.