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

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

Removes unused shadow settings forgotten in rP13877, removes direct SkipSubmit access.

  • Property svn:eol-style set to native
File size: 24.0 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 CFG_GET_VAL("shadowquality", QualityLevel);
437
438 // get shadow map size as next power of two up from view width/height
439 int shadow_map_size = (int)round_up_to_pow2((unsigned)std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight()));
440 switch (QualityLevel)
441 {
442 // Very Low
443 case -2:
444 shadow_map_size /= 4;
445 break;
446 // Low
447 case -1:
448 shadow_map_size /= 2;
449 break;
450 // High
451 case 1:
452 shadow_map_size *= 2;
453 break;
454 // Ultra
455 case 2:
456 shadow_map_size *= 4;
457 break;
458 // Medium as is
459 default:
460 break;
461 }
462 Width = Height = shadow_map_size;
463
464 // Clamp to the maximum texture size
465 Width = std::min(Width, (int)ogl_max_tex_size);
466 Height = std::min(Height, (int)ogl_max_tex_size);
467
468 // Since we're using a framebuffer object, the whole texture is available
469 EffectiveWidth = Width;
470 EffectiveHeight = Height;
471
472 GLenum format;
473 const char* formatName;
474#if CONFIG2_GLES
475 format = GL_DEPTH_COMPONENT;
476 formatName = "DEPTH_COMPONENT";
477#else
478 switch ( DepthTextureBits )
479 {
480 case 16: format = GL_DEPTH_COMPONENT16; formatName = "DEPTH_COMPONENT16"; break;
481 case 24: format = GL_DEPTH_COMPONENT24; formatName = "DEPTH_COMPONENT24"; break;
482 case 32: format = GL_DEPTH_COMPONENT32; formatName = "DEPTH_COMPONENT32"; break;
483 default: format = GL_DEPTH_COMPONENT; formatName = "DEPTH_COMPONENT"; break;
484 }
485#endif
486 ENSURE(formatName);
487
488 LOGMESSAGE("Creating shadow texture (size %dx%d) (format = %s)",
489 Width, Height, formatName);
490
491 if (g_RenderingOptions.GetShadowAlphaFix())
492 {
493 glGenTextures(1, &DummyTexture);
494 g_Renderer.BindTexture(0, DummyTexture);
495 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
496 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
497 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
498 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
499 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
500 }
501
502 glGenTextures(1, &Texture);
503 g_Renderer.BindTexture(0, Texture);
504
505 glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
506 // GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
507
508 // set texture parameters
509 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
510 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
511
512#if CONFIG2_GLES
513 // GLES doesn't do depth comparisons, so treat it as a
514 // basic unfiltered depth texture
515 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
516 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
517#else
518 // Enable automatic depth comparisons
519 glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
520 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
521 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
522
523 // Use GL_LINEAR to trigger automatic PCF on some devices
524 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
525 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
526#endif
527
528 ogl_WarnIfError();
529
530 // bind to framebuffer object
531 glBindTexture(GL_TEXTURE_2D, 0);
532 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Framebuffer);
533
534 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, Texture, 0);
535
536 if (g_RenderingOptions.GetShadowAlphaFix())
537 {
538 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, DummyTexture, 0);
539 }
540 else
541 {
542#if CONFIG2_GLES
543#warning TODO: figure out whether the glDrawBuffer/glReadBuffer stuff is needed, since it is not supported by GLES
544#else
545 glDrawBuffer(GL_NONE);
546#endif
547 }
548
549#if !CONFIG2_GLES
550 glReadBuffer(GL_NONE);
551#endif
552
553 ogl_WarnIfError();
554
555 GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
556
557 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, SavedViewFBO);
558
559 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
560 {
561 LOGWARNING("Framebuffer object incomplete: 0x%04X", status);
562
563 // Disable shadow rendering (but let the user try again if they want)
564 g_RenderingOptions.SetShadows(false);
565 }
566}
567
568// Set up to render into shadow map texture
569void ShadowMap::BeginRender()
570{
571 // Calc remaining shadow matrices
572 m->CalcShadowMatrices();
573
574 {
575 PROFILE("bind framebuffer");
576 glBindTexture(GL_TEXTURE_2D, 0);
577 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->Framebuffer);
578 }
579
580 // clear buffers
581 {
582 PROFILE("clear depth texture");
583 // In case we used m_ShadowAlphaFix, we ought to clear the unused
584 // color buffer too, else Mali 400 drivers get confused.
585 // Might as well clear stencil too for completeness.
586 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
587 glColorMask(0,0,0,0);
588 }
589
590 // setup viewport
591 const SViewPort vp = { 0, 0, m->EffectiveWidth, m->EffectiveHeight };
592 g_Renderer.SetViewport(vp);
593
594 m->SavedViewCamera = g_Renderer.GetViewCamera();
595
596 CCamera c = m->SavedViewCamera;
597 c.SetProjection(m->LightProjection);
598 c.GetOrientation() = m->InvLightTransform;
599 g_Renderer.SetViewCamera(c);
600
601#if !CONFIG2_GLES
602 glMatrixMode(GL_PROJECTION);
603 glLoadMatrixf(&m->LightProjection._11);
604 glMatrixMode(GL_MODELVIEW);
605 glLoadMatrixf(&m->LightTransform._11);
606#endif
607
608 glEnable(GL_SCISSOR_TEST);
609 glScissor(1,1, m->EffectiveWidth-2, m->EffectiveHeight-2);
610}
611
612// Finish rendering into shadow map texture
613void ShadowMap::EndRender()
614{
615 glDisable(GL_SCISSOR_TEST);
616
617 g_Renderer.SetViewCamera(m->SavedViewCamera);
618
619 {
620 PROFILE("unbind framebuffer");
621 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
622 }
623
624 const SViewPort vp = { 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight() };
625 g_Renderer.SetViewport(vp);
626
627 glColorMask(1,1,1,1);
628}
629
630void ShadowMap::BindTo(const CShaderProgramPtr& shader) const
631{
632 if (!shader->GetTextureBinding(str_shadowTex).Active())
633 return;
634
635 shader->BindTexture(str_shadowTex, m->Texture);
636 shader->Uniform(str_shadowTransform, m->TextureMatrix);
637 shader->Uniform(str_shadowScale, m->Width, m->Height, 1.0f / m->Width, 1.0f / m->Height);
638}
639
640// Depth texture bits
641int ShadowMap::GetDepthTextureBits() const
642{
643 return m->DepthTextureBits;
644}
645
646void ShadowMap::SetDepthTextureBits(int bits)
647{
648 if (bits != m->DepthTextureBits)
649 {
650 if (m->Texture)
651 {
652 glDeleteTextures(1, &m->Texture);
653 m->Texture = 0;
654 }
655 m->Width = m->Height = 0;
656
657 m->DepthTextureBits = bits;
658 }
659}
660
661void ShadowMap::RenderDebugBounds()
662{
663 CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
664 shaderTech->BeginPass();
665 CShaderProgramPtr shader = shaderTech->GetShader();
666
667 glDepthMask(0);
668 glDisable(GL_CULL_FACE);
669
670 // Render various shadow bounds:
671 // Yellow = bounds of objects in view frustum that receive shadows
672 // Red = culling frustum used to find potential shadow casters
673 // Green = bounds of objects in culling frustum that cast shadows
674 // Blue = frustum used for rendering the shadow map
675
676 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection() * m->InvLightTransform);
677
678 shader->Uniform(str_color, 1.0f, 1.0f, 0.0f, 1.0f);
679 m->ShadowReceiverBound.RenderOutline(shader);
680
681 shader->Uniform(str_color, 0.0f, 1.0f, 0.0f, 1.0f);
682 m->ShadowCasterBound.RenderOutline(shader);
683
684 glEnable(GL_BLEND);
685 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
686 shader->Uniform(str_color, 0.0f, 0.0f, 1.0f, 0.25f);
687 m->ShadowRenderBound.Render(shader);
688 glDisable(GL_BLEND);
689
690 shader->Uniform(str_color, 0.0f, 0.0f, 1.0f, 1.0f);
691 m->ShadowRenderBound.RenderOutline(shader);
692
693 // Render light frustum
694
695 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
696
697 CFrustum frustum = GetShadowCasterCullFrustum();
698 // We don't have a function to create a brush directly from a frustum, so use
699 // the ugly approach of creating a large cube and then intersecting with the frustum
700 CBoundingBoxAligned dummy(CVector3D(-1e4, -1e4, -1e4), CVector3D(1e4, 1e4, 1e4));
701 CBrush brush(dummy);
702 CBrush frustumBrush;
703 brush.Intersect(frustum, frustumBrush);
704
705 glEnable(GL_BLEND);
706 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
707 shader->Uniform(str_color, 1.0f, 0.0f, 0.0f, 0.25f);
708 frustumBrush.Render(shader);
709 glDisable(GL_BLEND);
710
711 shader->Uniform(str_color, 1.0f, 0.0f, 0.0f, 1.0f);
712 frustumBrush.RenderOutline(shader);
713
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.