framework2
1222 строки · 37.7 Кб
1//
2// ofShadow.cpp
3// openFrameworksLib
4//
5// Created by Nick Hardeman on 10/3/22.
6//
7
8#include "ofShadow.h"
9#include "of3dUtils.h"
10#include "ofGLBaseTypes.h"
11#include "ofGLUtils.h"
12#include "ofLight.h"
13#include "ofGLProgrammableRenderer.h"
14#include "ofConstants.h"
15
16#define GLM_FORCE_CTOR_INIT
17#include "glm/gtx/transform.hpp"
18#include "glm/gtc/quaternion.hpp"
19
20using std::weak_ptr;
21using std::vector;
22using std::shared_ptr;
23
24
25//----------------------------------------
26vector<weak_ptr<ofShadow::Data> > & ofShadowsData(){
27static vector<weak_ptr<ofShadow::Data> > * shadowsActive = ofIsGLProgrammableRenderer()?new vector<weak_ptr<ofShadow::Data> >:new vector<weak_ptr<ofShadow::Data> >(8);
28return *shadowsActive;
29}
30
31//--------------------------------------------------------------
32static std::map< int, ofShadow::GLData > & getGLDatas(){
33static std::map< int,ofShadow::GLData > * idsFB = new std::map< int,ofShadow::GLData >;
34return *idsFB;
35}
36
37//--------------------------------------------------------------
38static ofShadow::GLData& getGLData( int aLightType ) {
39if( getGLDatas().count(aLightType) < 1 ) {
40getGLDatas()[aLightType] = ofShadow::GLData();
41}
42return getGLDatas()[aLightType];
43}
44
45//--------------------------------------------------------------
46static void retainFBO(int aLightType){
47if( !getGLData(aLightType).bAllocated ) {
48ofLogVerbose("ofShadow :: retainFBO : for light ") << aLightType << " | " << ofGetFrameNum();
49getGLData(aLightType).bAllocated = true;
50glGenFramebuffers(1, &getGLDatas()[aLightType].fboId);
51glGenTextures(1, &getGLDatas()[aLightType].texId);
52}
53}
54
55//--------------------------------------------------------------
56static GLuint getFBOId( int aLightType ) {
57retainFBO(aLightType);
58return getGLData(aLightType).fboId;
59}
60//--------------------------------------------------------------
61static GLuint getFBODepthTexId( int aLightType ) {
62retainFBO(aLightType);
63return getGLData(aLightType).texId;
64}
65
66//--------------------------------------------------------------
67static void releaseFBO(int aLightType){
68if( getGLData(aLightType).bAllocated ){
69ofLogVerbose("ofShadow :: releaseFBO : for light ") << aLightType << " | " << ofGetFrameNum();
70glDeleteTextures(1, &getGLDatas()[aLightType].texId );
71glDeleteFramebuffers(1, &getGLDatas()[aLightType].fboId );
72getGLDatas()[aLightType].bAllocated = false;
73getGLDatas()[aLightType].bFboAllocated = false;
74}
75}
76
77//----------------------------------------
78int ofShadow::getNumTotalPossibleShadows( int aLightType ) {
79
80GLenum texTarget = getTextureTarget( aLightType );
81if( texTarget == GL_TEXTURE_CUBE_MAP) {
82return 1;
83}
84if( texTarget == GL_TEXTURE_2D ) {
85return 1;
86}
87// a bit of an arbitrary selection here
88return 64;
89}
90
91//----------------------------------------
92GLenum ofShadow::getTextureTarget( int aLightType ) {
93
94// #if !defined(TARGET_OPENGLES)
95if( aLightType == OF_LIGHT_POINT ) {
96#if !defined(TARGET_OPENGLES)
97if( ofGetGLRenderer() && ofGetGLRenderer()->getGLVersionMajor() < 4 ) {
98// does not support cube map arrays in openGL < 4 glsl shader
99return GL_TEXTURE_CUBE_MAP;
100}
101#else
102return GL_TEXTURE_CUBE_MAP;
103#endif
104}
105// #endif
106
107if( aLightType == OF_LIGHT_POINT ) {
108#ifdef GL_TEXTURE_CUBE_MAP_ARRAY
109#ifdef glTexImage3D
110return GL_TEXTURE_CUBE_MAP_ARRAY;
111#endif
112#endif
113return GL_TEXTURE_CUBE_MAP;
114}
115#ifdef GL_TEXTURE_2D_ARRAY
116#ifdef glTexImage3D
117return GL_TEXTURE_2D_ARRAY;
118#endif
119#endif
120return GL_TEXTURE_2D;
121}
122
123//--------------------------------------------------------------
124void ofShadow::setDepthMapResolution( int aLightType, int ares ) {
125setDepthMapResolution( aLightType, ares, ares );
126}
127
128//--------------------------------------------------------------
129void ofShadow::setDepthMapResolution( int aLightType, int awidth, int aheight ) {
130if( aLightType == OF_LIGHT_POINT ) {
131// rendering to a cube, so needs to be the same
132aheight = awidth;
133}
134
135ofLogVerbose("ofShadow :: setDepthMapResolution : incoming: " ) << awidth << " x " << aheight << " current: " << getDepthMapWidth(aLightType) << " x " << getDepthMapHeight(aLightType) << " | " << ofGetFrameNum();
136
137if( awidth != getDepthMapWidth(aLightType) || aheight != getDepthMapHeight(aLightType) ) {
138getGLData(aLightType).width = awidth;
139getGLData(aLightType).height = aheight;
140releaseFBO(aLightType);
141}
142}
143
144//--------------------------------------------------------------
145int ofShadow::getDepthMapWidth(int aLightType) {
146return getGLData(aLightType).width;
147}
148
149//--------------------------------------------------------------
150int ofShadow::getDepthMapHeight(int aLightType) {
151return getGLData(aLightType).height;
152}
153
154//--------------------------------------------------------------
155GLuint ofShadow::getPointTexId() {
156_updateTexDataIds();
157return getFBODepthTexId(OF_LIGHT_POINT);
158}
159
160//--------------------------------------------------------------
161GLuint ofShadow::getDirectionalTexId() {
162_updateTexDataIds();
163return getFBODepthTexId(OF_LIGHT_DIRECTIONAL);
164}
165
166//--------------------------------------------------------------
167GLuint ofShadow::getSpotTexId() {
168_updateTexDataIds();
169return getFBODepthTexId(OF_LIGHT_SPOT);
170}
171
172//--------------------------------------------------------------
173GLuint ofShadow::getAreaTexId() {
174_updateTexDataIds();
175return getFBODepthTexId(OF_LIGHT_AREA);
176}
177
178//--------------------------------------------------------------
179std::string ofShadow::getShadowTypeAsString( ofShadowType atype ) {
180switch( atype ) {
181case OF_SHADOW_TYPE_HARD:
182return "Hard";
183case OF_SHADOW_TYPE_PCF_LOW:
184return "PCF Low";
185case OF_SHADOW_TYPE_PCF_MED:
186return "PCF Medium";
187case OF_SHADOW_TYPE_PCF_HIGH:
188return "PCF High";
189case OF_SHADOW_TYPE_TOTAL:
190return "Total";
191}
192return "Default";
193}
194
195//--------------------------------------------------------------
196bool ofShadow::hasActiveShadows() {
197for(size_t i=0;i< ofShadowsData().size();i++){
198std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
199if(shadow && shadow->isEnabled && shadow->index > -1 ){
200return true;
201break;
202}
203}
204return false;
205}
206
207//--------------------------------------------------------------
208void ofShadow::enableAllShadows() {
209if( !areShadowsSupported() ) {
210ofLogWarning("ofShadow :: enableAllShadows : only works with programmable renderer.");
211return;
212}
213
214for(size_t i=0;i<ofShadowsData().size();i++){
215std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
216if(!shadow || shadow->index < 0 ){
217continue;
218}
219shadow->isEnabled = true;
220}
221}
222
223//--------------------------------------------------------------
224void ofShadow::disableAllShadows() {
225for(size_t i=0;i<ofShadowsData().size();i++){
226std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
227if(!shadow || shadow->index < 0 ){
228continue;
229}
230shadow->isEnabled = false;
231}
232}
233
234//--------------------------------------------------------------
235void ofShadow::setAllShadowTypes( ofShadowType atype ) {
236if( !ofIsGLProgrammableRenderer() ) {
237ofLogWarning("ofShadow :: setAllShadowTypes : only works with programmable renderer.");
238return;
239}
240for(size_t i=0;i<ofShadowsData().size();i++){
241std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
242if(!shadow || shadow->index < 0 ){
243continue;
244}
245shadow->shadowType = atype;
246}
247}
248
249//--------------------------------------------------------------
250void ofShadow::setAllShadowDepthResolutions(int awidth, int aheight ) {
251if( !ofIsGLProgrammableRenderer() ) {
252ofLogWarning("ofShadow :: setAllShadowDepthResolutions : only works with programmable renderer.");
253return;
254}
255setDepthMapResolution( OF_LIGHT_POINT, awidth, aheight );
256setDepthMapResolution( OF_LIGHT_DIRECTIONAL, awidth, aheight );
257setDepthMapResolution( OF_LIGHT_SPOT, awidth, aheight );
258setDepthMapResolution( OF_LIGHT_AREA, awidth, aheight );
259}
260
261//--------------------------------------------------------------
262void ofShadow::setAllShadowBias( float bias ) {
263if( !ofIsGLProgrammableRenderer() ) {
264ofLogWarning("ofShadow :: setAllShadowBias : only works with programmable renderer.");
265return;
266}
267for(size_t i=0;i<ofShadowsData().size();i++){
268std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
269if(!shadow || shadow->index < 0 ){
270continue;
271}
272shadow->bias = bias;
273}
274}
275
276//--------------------------------------------------------------
277void ofShadow::setAllShadowNormalBias( float normalBias ) {
278if( !ofIsGLProgrammableRenderer() ) {
279ofLogWarning("ofShadow :: setAllShadowNormalBias : only works with programmable renderer.");
280return;
281}
282for(size_t i=0;i<ofShadowsData().size();i++){
283std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
284if(!shadow || shadow->index < 0 ){
285continue;
286}
287shadow->normalBias = normalBias;
288}
289}
290
291//--------------------------------------------------------------
292void ofShadow::setAllShadowSampleRadius( float sampleRadius ) {
293if( !ofIsGLProgrammableRenderer() ) {
294ofLogWarning("ofShadow :: enableAllShadows : only works with programmable renderer.");
295return;
296}
297for(size_t i=0;i<ofShadowsData().size();i++){
298std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
299if(!shadow || shadow->index < 0 ){
300continue;
301}
302shadow->sampleRadius = sampleRadius;
303}
304}
305
306//--------------------------------------------------------------
307std::string ofShadow::getShaderDefinesAsString() {
308std::string definesString = "";
309if( areShadowsSupported() ) {
310if( ofShadowsData().size() > 0 && ofLightsData().size() > 0) {
311definesString += "#define HAS_SHADOWS 1\n";
312}
313
314if( ofShadow::getTextureTarget( OF_LIGHT_POINT ) != GL_TEXTURE_CUBE_MAP ) {
315definesString += "#define SHADOWS_USE_CUBE_MAP_ARRAY 1\n";
316}
317
318if( ofShadow::getTextureTarget( OF_LIGHT_DIRECTIONAL ) != GL_TEXTURE_2D ) {
319definesString += "#define SHADOWS_USE_TEXTURE_ARRAY 1\n";
320}
321}
322return definesString;
323}
324
325//--------------------------------------------------------------
326bool ofShadow::areShadowsSupported() {
327#if defined(TARGET_OPENGLES) && !defined(TARGET_EMSCRIPTEN)
328return false;
329#endif
330
331if(!ofIsGLProgrammableRenderer() ) {
332return false;
333}
334return true;
335}
336
337//--------------------------------------------------------------
338void ofShadow::_updateTexDataIds() {
339std::map<int, int> texIdMap;
340
341for(size_t i=0;i< ofShadowsData().size();i++){
342std::shared_ptr<ofShadow::Data> shadow = ofShadowsData()[i].lock();
343if(!shadow || !shadow->isEnabled || shadow->index < 0 ){
344continue;
345}
346
347if( texIdMap.count((int)shadow->lightType) < 0 ) {
348texIdMap[(int)shadow->lightType] = 0;
349}
350shadow->texIndex = texIdMap[(int)shadow->lightType];
351
352texIdMap[(int)shadow->lightType]++;
353}
354}
355
356//--------------------------------------------------------------
357ofShadow::ofShadow() {
358data = std::make_shared<ofShadow::Data>();
359data->shadowMatrix = glm::mat4(1.0f);
360setSingleOmniPass(true);
361#if defined(TARGET_OPENGLES)
362setSingleOmniPass(false);
363#endif
364_checkSetup();
365}
366
367//--------------------------------------------------------------
368ofShadow::~ofShadow() {
369clear();
370}
371
372//--------------------------------------------------------------
373void ofShadow::setLightType( int atype ) {
374
375if( (ofLightType)atype != data->lightType ) {
376clear();
377}
378data->lightType = atype;
379data->numDepthPasses = getNumShadowDepthPasses();
380if( getIsEnabled() ) {
381_checkFbos();
382_allocate();
383}
384}
385
386//--------------------------------------------------------------
387void ofShadow::update( const ofLight& alight ) {
388
389if( !data->isEnabled ) {
390return;
391}
392
393glm::quat rq = alight.getGlobalOrientation();
394glm::vec3 lookAtDir(glm::normalize( rq * glm::vec4(0.f,0.f,-1.f, 1.f)));
395
396setLightType( (ofLightType)alight.getType() );
397
398data->position = alight.getGlobalPosition();
399data->direction = glm::normalize(lookAtDir);
400data->up = rq * glm::vec3(0.0,1.0,0.0);
401data->right = rq * glm::vec3(1.0,0.0,0.0);
402
403unsigned int targetNumMatrices = 1;
404if(data->lightType == OF_LIGHT_POINT) {
405targetNumMatrices = 6;
406}
407
408if( mLookAtMats.size() != targetNumMatrices ) {
409mLookAtMats.clear();
410mLookAtMats.assign(targetNumMatrices, glm::mat4(1.0) );
411}
412if( mViewProjMats.size() != targetNumMatrices ) {
413mViewProjMats.clear();
414mViewProjMats.assign( targetNumMatrices, glm::mat4(1.0) );
415}
416
417if( data->lightType == OF_LIGHT_SPOT || data->lightType == OF_LIGHT_AREA ) {
418if(data->lightType == OF_LIGHT_SPOT) {
419mFov = alight.getSpotlightCutOff() * 2.0f;
420}
421float aspectRatio = (float)getDepthMapWidth() / (float)getDepthMapHeight();
422if( data->lightType == OF_LIGHT_AREA ) {
423mFov = 90;
424aspectRatio = mAreaLightWidth / mAreaLightHeight;
425}
426
427mShadowProjection = glm::perspective(glm::radians(mFov), aspectRatio, getNearClip(), getFarClip());
428mLookAtMats[0] = glm::lookAt( data->position, data->position + data->direction, glm::vec3(0.0, 1.0, 0.0) );
429mViewProjMats[0] = mShadowProjection * mLookAtMats[0];
430data->shadowMatrix = biasMatrix * mViewProjMats[0];
431} else if( data->lightType == OF_LIGHT_DIRECTIONAL ) {
432
433mOrthoScaleX = 1.0;
434if( mDirectionalBoundsWidth > -1 ) {
435mOrthoScaleX = mDirectionalBoundsWidth / (float)getDepthMapWidth();
436}
437
438mOrthoScaleY = 1.0;
439if( mDirectionalBoundsHeight > -1 ) {
440mOrthoScaleY = mDirectionalBoundsHeight / (float)getDepthMapHeight();
441}
442
443float viewWidth = getGLData(data->lightType).width;
444float viewHeight = getGLData(data->lightType).height;//
445mShadowProjection = glm::ortho(
446- viewWidth/2 * mOrthoScaleX,
447+ viewWidth/2 * mOrthoScaleX,
448- viewHeight/2 * mOrthoScaleY,
449+ viewHeight/2 * mOrthoScaleY,
450getNearClip(),
451getFarClip()
452);
453mLookAtMats[0] = glm::lookAt( data->position, data->position + data->direction, glm::vec3(0.0, 1.0, 0.0) );
454mViewProjMats[0] = mShadowProjection * mLookAtMats[0];
455data->shadowMatrix = biasMatrix * mViewProjMats[0];
456
457} else if( data->lightType == OF_LIGHT_POINT ) {
458mFov = 90.f;
459// aspect is 1.0 since width and height should be the same
460mShadowProjection = glm::perspective(glm::radians(mFov), 1.0f, getNearClip(), getFarClip() );
461// eye, target and up vector
462// +x, -x, +y, -y, +z and -z direction
463mLookAtMats[0] = glm::lookAt( data->position, data->position + glm::vec3(1,0,0), glm::vec3(0, -1, 0) );
464mLookAtMats[1] = glm::lookAt( data->position, data->position + glm::vec3(-1,0,0), glm::vec3(0, -1, 0) );
465mLookAtMats[2] = glm::lookAt( data->position, data->position + glm::vec3(0,1,0), glm::vec3(0, 0, 1) );
466mLookAtMats[3] = glm::lookAt( data->position, data->position + glm::vec3(0,-1,0), glm::vec3(0, 0, -1) );
467mLookAtMats[4] = glm::lookAt( data->position, data->position + glm::vec3(0,0,1), glm::vec3(0, -1, 0) );
468mLookAtMats[5] = glm::lookAt( data->position, data->position + glm::vec3(0,0,-1), glm::vec3(0, -1, 0) );
469
470for( size_t i = 0; i < 6; i++ ) {
471mViewProjMats[i] = mShadowProjection * mLookAtMats[i];
472}
473}
474}
475
476//----------------------------------------------------
477bool ofShadow::beginDepth() {
478
479if( !areShadowsSupported() ) {
480ofLogWarning("ofShadow :: beginDepth() : shadows only work with programmable renderer.");
481setEnabled(false);
482}
483
484if( !getIsEnabled() ) {
485return false;
486}
487
488_allocateFbo();
489
490auto glRenderer = ofGetGLRenderer();
491if(!glRenderer){
492return false;
493}
494
495if( data->lightType == OF_LIGHT_POINT ) {
496if( !isSingleOmniPass() ) {
497ofLogWarning("ofShadow :: beginDepth : must call beginDepth(cubeFace) when using point light without single pass.");
498return false;
499}
500}
501
502if( data->texIndex+1 > getNumTotalPossibleShadows(data->lightType) ) {
503ofLogWarning( "ofShadow :: too many shadows detected for light type " ) << data->lightType<<". Total supported for light type: " << getNumTotalPossibleShadows(data->lightType);
504}
505
506//
507
508glBindFramebuffer( GL_FRAMEBUFFER, getDepthMapFboId() );
509if( data->lightType == OF_LIGHT_POINT ) {
510// handled in the depthCubeGeom shader
511// glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getDepthMapTexId(), 0, (data->texIndex * 6));
512} else {
513#ifdef glFramebufferTextureLayer
514glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getDepthMapTexId(), 0, data->texIndex );
515#else
516
517auto texTarget = getTextureTarget( data->lightType );
518if( texTarget == GL_TEXTURE_2D ) {
519glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, getDepthMapTexId(), 0);
520} else {
521glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, data->texIndex, getDepthMapTexId(), 0);
522}
523
524//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, data->texIndex, getDepthMapTexId(), 0);
525#endif
526}
527
528ofPushView();
529ofViewport(0, 0, getDepthMapWidth(), getDepthMapHeight(), false);
530
531glClear(GL_DEPTH_BUFFER_BIT);
532
533glRenderer->bind(*this);
534
535if( isGlCullingEnabled() ) {
536glEnable(GL_CULL_FACE); // enables face culling
537glFrontFace(mGlFrontFaceWindingOrder);
538glCullFace(GL_FRONT); // tells OpenGL to cull front faces
539}
540return true;
541}
542
543//--------------------------------------------------------------
544bool ofShadow::endDepth() {
545if( !getIsEnabled() || !areShadowsSupported() ) {
546return false;
547}
548if( isGlCullingEnabled() ) {
549glDisable(GL_CULL_FACE);
550}
551
552auto glRenderer = ofGetGLRenderer();
553if(glRenderer){
554glRenderer->unbind(*this);
555}
556
557glBindFramebuffer(GL_FRAMEBUFFER, 0);
558ofPopView();
559return true;
560}
561
562//--------------------------------------------------------------
563bool ofShadow::beginDepth(GLenum aCubeFace) {
564if( !areShadowsSupported() ) {
565ofLogWarning("ofShadow :: beginDepth(cubeFace) : shadows are only available with programmable renderer.");
566setEnabled(false);
567}
568
569if( !getIsEnabled() ) {
570return false;
571}
572
573_allocateFbo();
574
575if( data->lightType != OF_LIGHT_POINT ) {
576ofLogWarning("ofShadow :: beginDepth(cubeFace) called from a light that does not use cube map. Use beginDepth() instead.");
577return false;
578}
579if( isSingleOmniPass() ) {
580ofLogWarning("ofShadow :: beginDepth(cubeFace) called using single pass, should be calling beginDepth().");
581return false;
582}
583
584if( data->texIndex+1 > getNumTotalPossibleShadows(data->lightType) ) {
585ofLogWarning( "ofShadow :: too many shadows detected for light type " ) << data->lightType<<". Total supported for light type: " << getNumTotalPossibleShadows(data->lightType);
586}
587
588// if( data->lightType == OF_LIGHT_POINT ) {
589// glEnable(GL_TEXTURE_CUBE_MAP_ARRAY);
590// }
591
592const auto glRenderer = ofGetGLRenderer();
593if(!glRenderer){
594return false;
595}
596
597glBindFramebuffer(GL_FRAMEBUFFER, getDepthMapFboId());
598if( getTextureTarget(OF_LIGHT_POINT) != GL_TEXTURE_CUBE_MAP ){
599#ifdef glFramebufferTextureLayer
600glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getDepthMapTexId(), 0, (data->texIndex * 6) + aCubeFace);
601#else
602ofLogWarning("ofShadow::beginDepth(GLenum aCubeFace) point light texture target needs to be GL_TEXTURE_CUBE_MAP");
603#endif
604} else {
605glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + aCubeFace, getDepthMapTexId(), 0);
606}
607// glViewport(0, 0, getDepthMapWidth(), getDepthMapHeight() );
608ofPushView();
609ofViewport(0, 0, getDepthMapWidth(), getDepthMapHeight(), false);
610glClear(GL_DEPTH_BUFFER_BIT);
611
612glRenderer->bind(*this,aCubeFace);
613
614if( isGlCullingEnabled() ) {
615glEnable(GL_CULL_FACE); // enables face culling
616glFrontFace(mGlFrontFaceWindingOrder);
617glCullFace(GL_FRONT); // tells OpenGL to cull back faces (the sane default setting)
618}
619return true;
620}
621
622//--------------------------------------------------------------
623bool ofShadow::endDepth(GLenum aCubeFace) {
624return endDepth();
625}
626
627//--------------------------------------------------------------
628void ofShadow::clear() {
629_checkFbos();
630
631mLookAtMats.clear();
632mViewProjMats.clear();
633
634}
635
636//--------------------------------------------------------------
637void ofShadow::setEnabled( bool ab ) {
638if( !areShadowsSupported() ) {
639ofLogWarning("ofShadow :: setEnabled : shadows only work with programmable renderer");
640ab = false;
641}
642
643data->isEnabled = ab;
644if( data->isEnabled ) {
645_allocate();
646}
647}
648
649//--------------------------------------------------------------
650const bool ofShadow::isMultiCubeFacePass() const {
651if( data->lightType == OF_LIGHT_POINT ) {
652return !isSingleOmniPass();
653}
654return false;
655}
656
657//--------------------------------------------------------------
658const bool ofShadow::isSingleOmniPass() const {
659return mBSinglePass;
660}
661
662//--------------------------------------------------------------
663const int ofShadow::getNumShadowDepthPasses() const {
664if(isMultiCubeFacePass()) {
665return 6;
666}
667return 1;
668}
669
670//--------------------------------------------------------------
671void ofShadow::setSingleOmniPass( bool ab ) {
672
673if( ofGetGLRenderer() && ofGetGLRenderer()->getGLVersionMajor() < 4 ) {
674ab = false;
675}
676
677#ifdef TARGET_OPENGLES
678// setting to false because the single pass uses a geometry shader
679ab = false;
680#endif
681
682if(mBSinglePass != ab ) {
683clear();
684}
685
686mBSinglePass = ab;
687data->numDepthPasses = getNumShadowDepthPasses();
688}
689
690//--------------------------------------------------------------
691int ofShadow::getDepthMapWidth() {
692return getDepthMapWidth( data->lightType );
693}
694
695//--------------------------------------------------------------
696int ofShadow::getDepthMapHeight() {
697return getDepthMapHeight( data->lightType );
698}
699
700//--------------------------------------------------------------
701void ofShadow::setDirectionalBounds( float aWorldWidth, float aWorldHeight ) {
702mDirectionalBoundsWidth = aWorldWidth;
703mDirectionalBoundsHeight = aWorldHeight;
704}
705
706//--------------------------------------------------------------
707GLuint ofShadow::getDepthMapFboId() {
708return getFBOId(data->lightType);
709}
710
711//--------------------------------------------------------------
712GLuint ofShadow::getDepthMapTexId() {
713return getFBODepthTexId(data->lightType);
714}
715
716//--------------------------------------------------------------
717void ofShadow::drawFrustum() {
718if( data->lightType == OF_LIGHT_POINT ) {
719_drawFrustum( data->up, data->right, data->direction );
720_drawFrustum( data->up, data->right, -data->direction );
721_drawFrustum( data->up, data->direction, data->right );
722_drawFrustum( data->up, data->direction, -data->right );
723} else {
724_drawFrustum( data->up, data->right, data->direction );
725}
726}
727
728//--------------------------------------------------------------
729void ofShadow::_drawFrustum( const glm::vec3& aup, const glm::vec3& aright, const glm::vec3& afwd ) {
730auto corners = getFrustumCorners(aup, aright, afwd );
731
732ofPushStyle();
733
734ofSetColor( ofColor::green );
735ofDrawArrow( data->position, data->position+data->up * 100.0, 10.0);
736ofSetColor( ofColor::red );
737ofDrawArrow( data->position, data->position+data->right * 100.0, 10.0);
738ofSetColor( ofColor::blue );
739ofDrawArrow( data->position, data->position+data->direction * 100.0, 10.0);
740
741vector<ofFloatColor> colors;
742
743ofMesh mesh;
744mesh.setMode(OF_PRIMITIVE_LINES);
745
746for( int i = 0; i < 4; i++ ) {
747mesh.addVertex( corners[i] );
748if( i == 3 ) {
749mesh.addVertex(corners[0]);
750} else {
751mesh.addVertex(corners[i+1]);
752}
753}
754
755colors.assign( 8, ofColor(220, 90, 190) );
756mesh.addColors(colors);
757
758for( int i = 0; i < 4; i++ ) {
759mesh.addVertex( corners[i+4] );
760if( i == 3 ) {
761mesh.addVertex(corners[4]);
762} else {
763mesh.addVertex(corners[i+4+1]);
764}
765}
766
767colors.assign( 8, ofColor(220, 220, 90) );
768mesh.addColors(colors);
769
770
771for( int i = 0; i < 4; i++ ) {
772if( data->lightType == OF_LIGHT_SPOT || data->lightType == OF_LIGHT_AREA) {
773mesh.addVertex(data->position);
774} else {
775mesh.addVertex(corners[i+4]);
776}
777mesh.addVertex(corners[i]);
778}
779colors.assign( 8, ofColor(220) );
780mesh.addColors(colors);
781
782if( data->lightType == OF_LIGHT_DIRECTIONAL ) {
783for( int i = 0; i < 4; i++ ) {
784mesh.addVertex(data->position);
785mesh.addVertex(corners[i+4]);
786}
787colors.assign( 8, ofColor(220) );
788mesh.addColors(colors);
789}
790
791
792mesh.draw();
793
794ofPopStyle();
795}
796
797//--------------------------------------------------------------
798std::vector<glm::vec3> ofShadow::getFrustumCorners( const glm::vec3& aup, const glm::vec3& aright, const glm::vec3& afwd ) {
799
800if( data->lightType == OF_LIGHT_DIRECTIONAL) {
801
802glm::vec3 fc = data->position + afwd * getFarClip();
803glm::vec3 nc = data->position + afwd * getNearClip();
804
805float viewWidth = getGLData(data->lightType).width;
806float viewHeight = getGLData(data->lightType).height;
807if( mDirectionalBoundsWidth > 0 ) {
808viewWidth = mDirectionalBoundsWidth;
809}
810if( mDirectionalBoundsHeight > 0 ) {
811viewHeight = mDirectionalBoundsHeight;
812}
813
814float hw = 0.5f * viewWidth;
815float hh = 0.5f * viewHeight;
816
817glm::vec3 X = glm::normalize(aright);
818glm::vec3 Y = glm::normalize(aup);
819
820std::vector<glm::vec3> corners(8);
821// ftl, ftr, fbl, fbr
822corners[0] = fc + (Y*hh) - (X*hw);
823corners[1] = fc + (Y*hh) + (X*hw);
824corners[2] = fc + (-Y*hh) + (X*hw);
825corners[3] = fc + (-Y*hh) - (X*hw);
826
827corners[4] = nc + (Y*hh) - (X*hw);
828corners[5] = nc + (Y*hh) + (X*hw);
829corners[6] = nc + (-Y*hh) + (X*hw);
830corners[7] = nc + (-Y*hh) - (X*hw);
831
832return corners;
833
834}
835
836glm::vec3 Z = glm::normalize(afwd);
837glm::vec3 X = glm::normalize(aright);
838glm::vec3 Y = glm::normalize(aup);
839
840glm::vec3 p = data->position;
841
842glm::vec3 nc = p + Z * getNearClip();
843glm::vec3 fc = p + Z * getFarClip();
844
845float ratio = (float)getDepthMapWidth() / (float)getDepthMapHeight();
846
847if( data->lightType == OF_LIGHT_AREA ) {
848ratio = mAreaLightWidth / mAreaLightHeight;
849}
850
851float Hnear = 2.f * tan( ofDegToRad( mFov ) / 2.f ) * getNearClip();
852float Wnear = Hnear * ratio;
853
854float Hfar = 2.f * tanf( ofDegToRad( mFov ) / 2.f ) * getFarClip();
855float Wfar = Hfar * ratio;
856
857std::vector<glm::vec3> corners(8);
858
859corners[0] = fc + ( Y * Hfar/2) - ( X * Wfar/2.f); // ftl
860corners[1] = fc + ( Y * Hfar/2) + ( X * Wfar/2); // ftr
861corners[2] = fc - ( Y * Hfar/2) + ( X * Wfar/2); // fbl
862corners[3] = fc - ( Y * Hfar/2) - ( X * Wfar/2); // fbr
863
864corners[4] = nc + ( Y * Hnear/2) - ( X * Wnear/2); // ntl
865corners[5] = nc + ( Y * Hnear/2) + ( X * Wnear/2); // ntr
866corners[6] = nc - ( Y * Hnear/2) + ( X * Wnear/2); // nbl
867corners[7] = nc - ( Y * Hnear/2) - ( X * Wnear/2); // nbr
868
869return corners;
870}
871
872
873
874//--------------------------------------------------------------
875std::string ofShadow::getShadowTypeAsString() {
876return ofShadow::getShadowTypeAsString(data->shadowType);
877}
878
879//--------------------------------------------------------------
880const ofShader & ofShadow::getDepthShader(ofGLProgrammableRenderer & renderer) const {
881initShaders( renderer );
882if( data->lightType == OF_LIGHT_POINT ) {
883#ifndef TARGET_OPENGLES
884if(isSingleOmniPass()) {
885return shaders[&renderer]->depthCube;
886} else {
887return shaders[&renderer]->depthCubeMultiPass;
888}
889#else
890return shaders[&renderer]->depthCubeMultiPass;
891#endif
892} else if( data->lightType == OF_LIGHT_AREA ) {
893return shaders[&renderer]->depth;
894} else {
895return shaders[&renderer]->depth;
896}
897}
898
899//--------------------------------------------------------------
900void ofShadow::updateDepth(const ofShader & shader,ofGLProgrammableRenderer & renderer) const {
901shader.setUniform3f("uLightPos", data->position );
902shader.setUniform1f("uNearPlane", data->nearClip );
903shader.setUniform1f("uFarPlane", data->farClip );
904
905if( data->lightType == OF_LIGHT_POINT ) {
906if( isSingleOmniPass() ) {
907for( unsigned int i = 0; i < 6; i++ ) {
908shader.setUniformMatrix4f("light["+ std::to_string(i) +"].viewProjectionMatrix", mViewProjMats[i]);
909}
910shader.setUniform1i("uStartLayer", data->texIndex );
911} else {
912ofLogWarning("Must call beginDepth(cubeFace) to render to point light in multiple passes");
913}
914} else {
915// just in case //
916if(mViewProjMats.size() > 0 ) {
917shader.setUniformMatrix4f("lightsViewProjectionMatrix", mViewProjMats[0]);
918}
919}
920}
921
922//--------------------------------------------------------------
923void ofShadow::updateDepth(const ofShader & shader,GLenum aCubeFace,ofGLProgrammableRenderer & renderer) const {
924// shader.begin();
925shader.setUniform3f("uLightPos", data->position );
926shader.setUniform1f("uNearPlane", data->nearClip );
927shader.setUniform1f("uFarPlane", data->farClip );
928
929if( aCubeFace < mViewProjMats.size() ) {
930shader.setUniformMatrix4f("lightsViewProjectionMatrix", mViewProjMats[aCubeFace] );
931}
932}
933
934//--------------------------------------------------------------
935void ofShadow::_checkSetup() {
936if( data->index < 0 ) {
937bool bShadowFound = false;
938// search for the first free block
939for(size_t i=0; i<ofShadowsData().size(); i++) {
940if(ofShadowsData()[i].expired()) {
941data->index = i;
942data->isEnabled = false;
943ofShadowsData()[i] = data;
944bShadowFound = true;
945break;
946}
947}
948if(!bShadowFound && ofIsGLProgrammableRenderer()){
949ofShadowsData().push_back(data);
950data->index = ofShadowsData().size() - 1;
951data->isEnabled = false;
952bShadowFound = true;
953}
954}
955_checkFbos(); // clean up
956}
957
958//--------------------------------------------------------------
959void ofShadow::_allocate() {
960_checkFbos(); // clean up
961_allocateFbo();
962}
963
964//--------------------------------------------------------------
965void ofShadow::_allocateFbo() {
966if(getGLData(data->lightType).bFboAllocated) {
967return;
968}
969
970GLenum gl_read_status = GL_FRAMEBUFFER_UNSUPPORTED;
971
972GLenum textureTarget = getTextureTarget(data->lightType);
973#if !defined(TARGET_OPENGLES)
974int depthComponent = GL_DEPTH_COMPONENT32F;
975int glType = GL_FLOAT;
976#elif defined(TARGET_EMSCRIPTEN)
977int depthComponent = GL_DEPTH_COMPONENT24;
978int glType = GL_UNSIGNED_INT;
979#else
980int depthComponent = GL_DEPTH_COMPONENT;
981int glType = GL_UNSIGNED_SHORT;
982#endif
983
984glBindTexture(textureTarget, getDepthMapTexId() );
985
986if( data->lightType == OF_LIGHT_POINT ) {
987// Create the cube map depth buffer
988#if !defined(TARGET_OPENGLES)
989glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
990#endif
991
992// OES_depth_texture_cube_map
993
994if( textureTarget == GL_TEXTURE_CUBE_MAP ) {
995for (GLint i = 0 ; i < 6 ; i++) {
996glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, depthComponent, getDepthMapWidth(), getDepthMapWidth(), 0, GL_DEPTH_COMPONENT, glType, NULL);
997}
998}
999
1000glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1001glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1002glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1003glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1004#ifdef GL_TEXTURE_WRAP_R
1005glTexParameteri(textureTarget, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
1006#endif
1007#if defined( GL_TEXTURE_CUBE_MAP_ARRAY ) && defined(glTexImage3D)
1008if( textureTarget == GL_TEXTURE_CUBE_MAP_ARRAY ) {
1009
1010glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, depthComponent, getDepthMapWidth(), getDepthMapWidth(), getGLData(data->lightType).totalShadows * 6, 0, GL_DEPTH_COMPONENT, glType, NULL);
1011}
1012#endif
1013} else {
1014if( textureTarget == GL_TEXTURE_2D ) {
1015glTexImage2D(GL_TEXTURE_2D, 0, depthComponent, getDepthMapWidth(), getDepthMapHeight(), 0, GL_DEPTH_COMPONENT, glType, NULL);
1016}
1017
1018glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1019glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1020
1021//-- This is to allow usage of shadow2DProj function in the shader --//
1022if( data->lightType != OF_LIGHT_AREA ) {
1023#ifdef GL_TEXTURE_COMPARE_MODE
1024glTexParameteri(textureTarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
1025glTexParameteri(textureTarget, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
1026#endif
1027}
1028//--! This is to allow usage of shadow2DProj function in the shader !--//
1029#ifdef GL_TEXTURE_2D_ARRAY
1030if( textureTarget == GL_TEXTURE_2D_ARRAY ) {
1031#ifdef GL_TEXTURE_WRAP_R
1032glTexParameteri(textureTarget, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
1033#endif
1034}
1035#endif
1036
1037#if defined(GL_CLAMP_TO_BORDER) && !defined(TARGET_EMSCRIPTEN)
1038glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
1039glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
1040float borderColor[] = { 0.0f, 0.0f, 0.0f, 1.0f };
1041glTexParameterfv(textureTarget, GL_TEXTURE_BORDER_COLOR, borderColor);
1042#else
1043glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1044glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1045#endif
1046
1047#ifdef GL_TEXTURE_2D_ARRAY
1048if( textureTarget == GL_TEXTURE_2D_ARRAY ) {
1049#ifdef glTexImage3D
1050glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, depthComponent, getDepthMapWidth(), getDepthMapHeight(), getGLData(data->lightType).totalShadows, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
1051#endif
1052}
1053#endif
1054}
1055glBindTexture(textureTarget, 0);
1056
1057
1058// Create the fbo
1059glBindFramebuffer(GL_FRAMEBUFFER, getDepthMapFboId() );
1060#if defined(TARGET_OPENGLES)
1061
1062if( textureTarget == GL_TEXTURE_CUBE_MAP ) {
1063glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, NULL, 0);
1064for(int c = 0; c < 6; c++) {
1065glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X+c, getDepthMapTexId(), 0);
1066}
1067} else {
1068glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textureTarget, getDepthMapTexId(), 0);
1069}
1070#else
1071glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, getDepthMapTexId(), 0);
1072#endif
1073
1074#ifndef TARGET_OPENGLES
1075// Disable writes to the color buffer
1076glDrawBuffer(GL_NONE);
1077
1078// Disable reads from the color buffer
1079glReadBuffer(GL_NONE);
1080#endif
1081
1082gl_read_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
1083
1084glBindFramebuffer(GL_FRAMEBUFFER, 0);
1085
1086if (gl_read_status != GL_FRAMEBUFFER_COMPLETE) {
1087releaseFBO(data->lightType);
1088ofLogError("ofShadow :: _allocateFbo : Frame buffer error, status") << gl_read_status;
1089} else {
1090getGLData(data->lightType).bFboAllocated = true;
1091}
1092
1093
1094ofLogVerbose("xxxxx ofShadow::_allocateFbo xxxxxxxxxxxxxxxxxxxxxxxxx") << std::endl;
1095}
1096
1097//--------------------------------------------------------------
1098void ofShadow::_checkFbos() {
1099// count the number of shadows here //
1100std::map<int, int> preNumShadows;
1101preNumShadows[OF_LIGHT_POINT] = getGLData(OF_LIGHT_POINT).totalShadows;
1102preNumShadows[OF_LIGHT_DIRECTIONAL] = getGLData(OF_LIGHT_DIRECTIONAL).totalShadows;
1103preNumShadows[OF_LIGHT_SPOT] = getGLData(OF_LIGHT_SPOT).totalShadows;
1104preNumShadows[OF_LIGHT_AREA] = getGLData(OF_LIGHT_AREA).totalShadows;
1105_updateNumShadows();
1106
1107if(getGLData(OF_LIGHT_POINT).totalShadows != preNumShadows[OF_LIGHT_POINT]) {
1108releaseFBO((int)OF_LIGHT_POINT);
1109}
1110if(getGLData(OF_LIGHT_DIRECTIONAL).totalShadows != preNumShadows[OF_LIGHT_DIRECTIONAL]) {
1111releaseFBO((int)OF_LIGHT_DIRECTIONAL);
1112}
1113if(getGLData(OF_LIGHT_SPOT).totalShadows != preNumShadows[OF_LIGHT_SPOT]) {
1114releaseFBO((int)OF_LIGHT_SPOT);
1115}
1116if(getGLData(OF_LIGHT_AREA).totalShadows != preNumShadows[OF_LIGHT_AREA]) {
1117releaseFBO((int)OF_LIGHT_AREA);
1118}
1119}
1120
1121//--------------------------------------------------------------
1122void ofShadow::_updateNumShadows() {
1123
1124getGLData(OF_LIGHT_POINT).totalShadows = 0;
1125getGLData(OF_LIGHT_DIRECTIONAL).totalShadows = 0;
1126getGLData(OF_LIGHT_SPOT).totalShadows = 0;
1127getGLData(OF_LIGHT_AREA).totalShadows = 0;
1128
1129for(size_t i=0; i < ofShadowsData().size(); i++){
1130if(!ofShadowsData()[i].expired()) {
1131auto shadow = ofShadowsData()[i].lock();
1132if( shadow ) {
1133getGLData(shadow->lightType).totalShadows++;
1134}
1135}
1136}
1137}
1138
1139#include "shaders/shadowDepth.vert"
1140#include "shaders/shadowDepth.frag"
1141#include "shaders/shadowDepthCubeGeom.glsl"
1142
1143bool ofShadow::setupShadowDepthShader(ofShader& ashader, const std::string aShaderMain) {
1144return setupShadowDepthShader( ashader, data->lightType, aShaderMain );
1145}
1146
1147bool ofShadow::setupShadowDepthShader(ofShader& ashader, int aLightType, const std::string aShaderMain) {
1148std::string gversion = "#version 150\n";
1149#ifdef TARGET_OPENGLES
1150gversion = "#version 300 es\nprecision highp float;\n";
1151#endif
1152std::string tdefines = "#define SINGLE_PASS\n";
1153
1154bool bDepthCubeSinglePass = false;
1155
1156if( aLightType == OF_LIGHT_POINT ) {
1157#ifndef TARGET_OPENGLES
1158if(isSingleOmniPass()) {
1159//return shaders[&renderer]->depthCube;
1160tdefines = "#define CUBE_MAP_SINGLE_PASS\n";
1161bDepthCubeSinglePass = true;
1162} else {
1163//return shaders[&renderer]->depthCubeMultiPass;
1164tdefines = "#define CUBE_MAP_MULTI_PASS\n";
1165}
1166#else
1167//return shaders[&renderer]->depthCubeMultiPass;
1168tdefines = "#define CUBE_MAP_MULTI_PASS\n";
1169#endif
1170} else if( aLightType == OF_LIGHT_AREA ) {
1171//return shaders[&renderer]->depth;
1172} else {
1173//return shaders[&renderer]->depth;
1174}
1175
1176ashader.setupShaderFromSource(GL_VERTEX_SHADER,gversion+tdefines+depthVertexShaderSource+aShaderMain);
1177ashader.setupShaderFromSource(GL_FRAGMENT_SHADER,gversion+tdefines+depthFragShaderSource);
1178
1179#ifndef TARGET_OPENGLES
1180if(bDepthCubeSinglePass) {
1181ashader.setupShaderFromSource(GL_GEOMETRY_SHADER_EXT,depthCubeGeometryShaderSource);
1182}
1183#endif
1184
1185ashader.bindDefaults();
1186return ashader.linkProgram();
1187}
1188
1189
1190void ofShadow::initShaders(ofGLProgrammableRenderer & renderer) const{
1191auto rendererShaders = shaders.find(&renderer);
1192
1193if(rendererShaders == shaders.end() ){
1194shaders[&renderer] = std::make_shared<ofShadow::Shaders>();
1195
1196std::string gversion = "#version 150\n";
1197#ifdef TARGET_OPENGLES
1198gversion = "#version 300 es\nprecision highp float;\n";
1199#endif
1200
1201std::string vertString = depthVertexShaderSource+depthVertexShader_Main;
1202
1203shaders[&renderer]->depth.setupShaderFromSource(GL_VERTEX_SHADER,gversion+"#define SINGLE_PASS\n"+vertString);
1204shaders[&renderer]->depth.setupShaderFromSource(GL_FRAGMENT_SHADER,gversion+"#define SINGLE_PASS\n"+depthFragShaderSource);
1205shaders[&renderer]->depth.bindDefaults();
1206shaders[&renderer]->depth.linkProgram();
1207
1208#ifndef TARGET_OPENGLES
1209shaders[&renderer]->depthCube.setupShaderFromSource(GL_VERTEX_SHADER,gversion+"#define CUBE_MAP_SINGLE_PASS\n"+vertString);
1210shaders[&renderer]->depthCube.setupShaderFromSource(GL_FRAGMENT_SHADER,gversion+"#define CUBE_MAP_SINGLE_PASS\n"+depthFragShaderSource);
1211shaders[&renderer]->depthCube.setupShaderFromSource(GL_GEOMETRY_SHADER_EXT,depthCubeGeometryShaderSource);
1212shaders[&renderer]->depthCube.bindDefaults();
1213shaders[&renderer]->depthCube.linkProgram();
1214#endif
1215shaders[&renderer]->depthCubeMultiPass.setupShaderFromSource(GL_VERTEX_SHADER,gversion+"#define CUBE_MAP_MULTI_PASS\n"+vertString);
1216shaders[&renderer]->depthCubeMultiPass.setupShaderFromSource(GL_FRAGMENT_SHADER,gversion+"#define CUBE_MAP_MULTI_PASS\n"+depthFragShaderSource);
1217shaders[&renderer]->depthCubeMultiPass.bindDefaults();
1218shaders[&renderer]->depthCubeMultiPass.linkProgram();
1219
1220}
1221
1222}
1223
1224
1225