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

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

Removes low-level GL calls from graphics and geometrics primitives and adds DebugRenderer.

Tested By: Freagarach

Differential Revision: https://code.wildfiregames.com/D3857

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