diff --git a/examples/3d/ofxAssimpExample/src/ofApp.cpp b/examples/3d/ofxAssimpExample/src/ofApp.cpp index a15f6b22aac..30126002879 100644 --- a/examples/3d/ofxAssimpExample/src/ofApp.cpp +++ b/examples/3d/ofxAssimpExample/src/ofApp.cpp @@ -175,7 +175,7 @@ void ofApp::draw(){ // // Shadows have the following gl culling enabled by default // // this helps reduce z fighting by only rendering the rear facing triangles to the depth map // // the culling can be disabled by calling -// light.getShadow().setGlCullingEnabled(false); +// light.getShadow().setCullingEnabled(false); // // or the culling winding order can be changed by calling // light.getShadow().setFrontFaceWindingOrder(GL_CCW); // default is GL_CW renderScene(); diff --git a/examples/gl/materialPBRAdvancedExample/src/ofApp.cpp b/examples/gl/materialPBRAdvancedExample/src/ofApp.cpp index 4a129a59b2b..f8c448d8b59 100644 --- a/examples/gl/materialPBRAdvancedExample/src/ofApp.cpp +++ b/examples/gl/materialPBRAdvancedExample/src/ofApp.cpp @@ -12,7 +12,7 @@ void ofApp::setup(){ light.setPosition(100.1, 400, 600 ); light.lookAt(glm::vec3(0,0,0)); light.getShadow().setEnabled(true); - light.getShadow().setGlCullingEnabled(true); + light.getShadow().setCullingEnabled(true); light.getShadow().setDirectionalBounds(2000, 1000); light.getShadow().setNearClip(200); light.getShadow().setFarClip(2000); diff --git a/examples/gl/shadowsExample/src/ofApp.cpp b/examples/gl/shadowsExample/src/ofApp.cpp index 201c3767227..125fcdae911 100644 --- a/examples/gl/shadowsExample/src/ofApp.cpp +++ b/examples/gl/shadowsExample/src/ofApp.cpp @@ -151,7 +151,7 @@ void ofApp::draw(){ //glCullFace(GL_FRONT); // the culling can be disabled by calling - //light->getShadow().setGlCullingEnabled(false); + //light->getShadow().setCullingEnabled(false); // or the culling winding order can be changed by calling //light->getShadow().setFrontFaceWindingOrder(GL_CCW); // default is GL_CW renderScene(); diff --git a/examples/gl/textureBufferInstancedExample/src/ofApp.cpp b/examples/gl/textureBufferInstancedExample/src/ofApp.cpp index 2b8420c9e7f..52e7e2a7473 100644 --- a/examples/gl/textureBufferInstancedExample/src/ofApp.cpp +++ b/examples/gl/textureBufferInstancedExample/src/ofApp.cpp @@ -65,7 +65,7 @@ void ofApp::setup(){ light.setPosition(0.0, 3600, 600 ); light.lookAt( glm::vec3(0,0,0) ); light.getShadow().setEnabled(true); - light.getShadow().setGlCullingEnabled(true); + light.getShadow().setCullingEnabled(true); light.getShadow().setStrength(0.2); light.getShadow().setNearClip(200); light.getShadow().setFarClip(7500); diff --git a/libs/openFrameworks/gl/ofCubeMap.cpp b/libs/openFrameworks/gl/ofCubeMap.cpp index 258d3491196..5d5471bee73 100644 --- a/libs/openFrameworks/gl/ofCubeMap.cpp +++ b/libs/openFrameworks/gl/ofCubeMap.cpp @@ -1,9 +1,3 @@ -// -// ofCubeMap.cpp -// -// Created by Nick Hardeman on 10/16/22. -// - #include "ofShader.h" #include "ofCubeMap.h" #include "ofImage.h" @@ -15,6 +9,7 @@ #include "ofFbo.h" #include "ofTexture.h" #include "ofFileUtils.h" +#include "ofMaterial.h" #ifdef TARGET_ANDROID #include "ofAppAndroidWindow.h" @@ -90,9 +85,9 @@ static void release(GLuint id){ #ifdef TARGET_ANDROID // TODO: Hook this up to an event void ofCubeMap::regenerateAllTextures() { - for(size_t i=0; i cubeMap = ofCubeMapsData()[i].lock(); + for(size_t i=0;i > & ofCubeMapsData(){ - static vector > * cubeMapsActive = new vector >; - return *cubeMapsActive; +//-------------------------------------------------------------- +std::vector>& ofCubeMap::getCubeMapsData() { + static std::vector> cubeMapsDataActive; + return cubeMapsDataActive; } //-------------------------------------------------------------- bool ofCubeMap::hasActiveCubeMap() { - for(size_t i=0;i< ofCubeMapsData().size();i++){ - std::shared_ptr cubeMap = ofCubeMapsData()[i].lock(); + for(size_t i=0;iisEnabled && cubeMap->index > -1 ){ return true; break; @@ -122,8 +115,8 @@ bool ofCubeMap::hasActiveCubeMap() { //-------------------------------------------------------------- std::shared_ptr ofCubeMap::getActiveData() { - for(size_t i=0;i< ofCubeMapsData().size();i++){ - std::shared_ptr cubeMap = ofCubeMapsData()[i].lock(); + for(size_t i=0; i < getCubeMapsData().size();i++){ + auto cubeMap = getCubeMapsData()[i].lock(); if(cubeMap && cubeMap->isEnabled && cubeMap->index > -1 ){ return cubeMap; } @@ -155,17 +148,17 @@ void ofCubeMap::_checkSetup() { if( data->index < 0 ) { bool bFound = false; // search for the first free block - for(size_t i=0; iindex = i; - ofCubeMapsData()[i] = data; + getCubeMapsData()[i] = data; bFound = true; break; } } if(!bFound && ofIsGLProgrammableRenderer()){ - ofCubeMapsData().push_back(data); - data->index = ofCubeMapsData().size() - 1; + getCubeMapsData().push_back(data); + data->index = getCubeMapsData().size() - 1; bFound = true; } } diff --git a/libs/openFrameworks/gl/ofCubeMap.h b/libs/openFrameworks/gl/ofCubeMap.h index ceef3e6bd3a..1203d87cbd3 100644 --- a/libs/openFrameworks/gl/ofCubeMap.h +++ b/libs/openFrameworks/gl/ofCubeMap.h @@ -1,9 +1,3 @@ -// -// ofCubeMap.h -// -// Created by Nick Hardeman on 10/16/22. -// - #pragma once #include "ofShader.h" @@ -18,55 +12,84 @@ class ofVboMesh; class ofGLProgrammableRenderer; - -class ofCubeMap { -public: - struct ofCubeMapSettings { - of::filesystem::path filePath { "" }; - of::filesystem::path cacheDirectory { "" }; - - bool overwriteCache = false; - bool useCache = false; - bool useLutTex = false; - bool flipVertically = true; - bool useMaximumPrecision = false; - - int resolution = 512; +class ofMaterial; + + +struct ofCubeMapSettings { + of::filesystem::path filePath { "" }; ///< path to load the cube map, a single texture + of::filesystem::path cacheDirectory { "" }; ///< directory to cache the irradiance and pre filter maps + + bool overwriteCache = false; ///< overwrite the cache of the irradiance and pre filter maps + bool useCache = false; ///< check for cached irradiance and pre filter images on disk + bool useLutTex = false; ///< create a lut texture to pass to the shaders + bool flipVertically = true; ///< flip the loaded cubemap image vertically + bool useMaximumPrecision = false; ///< use 32 bit float (GL_RGB32F) precision, default is 16 bit (GL_RGB16F) + + int resolution = 512; ///< resolution of each side of the cube map to be generated #ifdef TARGET_OPENGLES - int irradianceRes = 16; - int preFilterRes = 64; + int irradianceRes = 16; ///< resolution of each side of the irradiance map to be generated + int preFilterRes = 64;///< resolution of each side of the pre filter map to be generated #else - int irradianceRes = 32; - int preFilterRes = 128; + int irradianceRes = 32; ///< resolution of each side of the irradiance map to be generated + int preFilterRes = 128; ///< resolution of each side of the pre filter map to be generated #endif - }; +}; +class ofCubeMap { +protected: + + /// \class ofCubeMap::Data + /// + /// \brief Data class structure for storing GL texture ids and other data regarding cube maps + /// Used internally for access to cube map data in the rendering pipeline, ie. ofMaterial. + /// A Data class is created for each instance of ofCubeMap and all of them can be accessed by ofCubeMapsData(). + /// Which is declared towards the bottom of this file. class Data { public: - GLuint cubeMapId = 0; + GLuint cubeMapId = 0; ///< GL texture id of the cube map bool bCubeMapAllocated = false; - GLuint irradianceMapId = 0; + GLuint irradianceMapId = 0; ///< GL texture id of the irradiance map bool bIrradianceAllocated = false; - GLuint preFilteredMapId = 0; + GLuint preFilteredMapId = 0; ///< GL texture id of the pre filter map bool bPreFilteredMapAllocated = false; - + int index = -1; bool isEnabled = true; - - ofCubeMapSettings settings; - - int maxMipLevels = 5; - float exposure = 1.0; + + ofCubeMapSettings settings; ///< settings associated with this cube map + + int maxMipLevels = 5; ///< number of mip map levels for generating the prefilted map + float exposure = 1.0; ///< used for storing the exposure of the cube map, which is set with ofCubeMap::setExposure() }; - - static GLenum getTextureTarget(); + + /// \brief Global storage for all cube maps created. The Data class holds infomation about textures and other information + /// relevant for rendering. This function is utilized internally. + /// More about the Data class above. + /// \return std::vector>&. + static std::vector>& getCubeMapsData(); + + /// \brief Is there any active cube map. + /// \return true if there is any active cube map. static bool hasActiveCubeMap(); + /// \brief Get the first cube map data that is enabled. + /// \return std::shared_ptr of the active cube map data. static std::shared_ptr getActiveData(); + /// \brief Internal function for releasing the GL texture memory. + /// \param std::shared_ptr cube map data holding texture ids to clear. static void clearTextureData(std::shared_ptr adata); #ifdef TARGET_ANDROID static void regenerateAllTextures(); #endif static const ofTexture & getBrdfLutTexture(); + + friend class ofMaterial; + friend class ofShader; + +public: + + /// \brief Return the GL texture type. + /// \return GLenum GL_TEXTURE_CUBE_MAP. + static GLenum getTextureTarget(); ofCubeMap(); ofCubeMap(const ofCubeMap & mom); @@ -74,12 +97,12 @@ class ofCubeMap { ~ofCubeMap(); - /// \load an image and convert to cube map. + /// \load An image and convert to cube map. /// \param apath path to the image to load. /// \param aFaceResolution resolution of the cube map image sides. /// \param aBFlipY flip the images upside down. bool load(const of::filesystem::path & apath, int aFaceResolution, bool aBFlipY = true); - /// \load an image and convert to cube map. + /// \load An image and convert to cube map. /// \param apath path to the image to load. /// \param aFaceResolution resolution of the cube map image sides. /// \param aBFlipY flip the images upside down. @@ -94,14 +117,18 @@ class ofCubeMap { /// \param mom The ofCubeMap to copy from. Reuses internal GL texture IDs. ofCubeMap & operator=(const ofCubeMap & mom); ofCubeMap & operator=(ofCubeMap && mom); - + /// \brief Clear the GL textures. void clear(); - + /// \brief Calls drawCubeMap(); void draw(); + /// \brief Render the cube map. void drawCubeMap(); + /// \brief Render the irradiance map. void drawIrradiance(); + /// \brief Render prefilted map. + /// \param aRoughness from 0 - 1. void drawPrefilteredCube(float aRoughness); - + bool isEnabled() { return data->isEnabled; } bool isEnabled() const { return data->isEnabled; } void setEnabled(bool ab) { data->isEnabled = ab; } @@ -111,18 +138,36 @@ class ofCubeMap { bool hasIrradianceMap(); int getFaceResolution() { return data->settings.resolution; } + /// \brief Get the GL texture id of the cube map. + /// \return GLuint. GLuint getTextureId(); + /// \brief Does this platform support HDR. + /// \return bool. bool doesSupportHdr(); + /// \brief Is this cube map HDR. + /// \return bool. bool isHdr(); + /// \brief Are the textures GL_RGB16F. + /// \return bool. bool isMediumPrecision(); - + /// \brief Set the exposure of the cube map. + /// \param aExposure from 0 - 1. void setExposure(float aExposure); + /// \brief Get the exposure of the cube map. + /// \return float from 0 - 1. float getExposure() { return data->exposure; } - + /// \brief Use as precalculated BRDF Lut texture. + /// \param ab bool to use texture. void setUseBrdfLutTexture(bool ab); + /// \brief Is Using BRDF Lut texture. + /// \return bool. bool isUsingLutBrdfTexture() { return data->settings.useLutTex; } - + + /// \brief Get the texture id of the irradiance map. + /// \return GLuint, texture id. GLuint getIrradianceMapId() { return data->irradianceMapId; } + /// \brief Get the texture id of the pre filtered map. + /// \return GLuint, texture id. GLuint getPrefilterMapId() { return data->preFilteredMapId; } protected: @@ -178,4 +223,3 @@ class ofCubeMap { static ofShader shaderBrdfLUT; }; -std::vector> & ofCubeMapsData(); diff --git a/libs/openFrameworks/gl/ofMaterial.cpp b/libs/openFrameworks/gl/ofMaterial.cpp index 0ad1de76c50..4b7d355789d 100644 --- a/libs/openFrameworks/gl/ofMaterial.cpp +++ b/libs/openFrameworks/gl/ofMaterial.cpp @@ -870,7 +870,7 @@ void ofMaterial::initShaders(ofGLProgrammableRenderer & renderer) const{ size_t numLights = ofLightsData().size(); // only support for a single cube map at a time - size_t numCubeMaps = ofCubeMapsData().size() > 0 ? 1 : 0; + size_t numCubeMaps = ofCubeMap::getCubeMapsData().size() > 0 ? 1 : 0; const std::string shaderId = getShaderStringId(); if(rendererShaders == shaders.end() || @@ -1077,7 +1077,7 @@ void ofMaterial::updateMaterial(const ofShader & shader,ofGLProgrammableRenderer shader.setUniform1f("mat_normal_mix", data.normalGeomToNormalMapMix ); } - std::shared_ptr cubeMapData = ofCubeMap::getActiveData(); + auto cubeMapData = ofCubeMap::getActiveData(); if( cubeMapData ) { shader.setUniform1f("mat_ibl_exposure", cubeMapData->exposure ); shader.setUniform1f("uCubeMapEnabled", 1.0f ); @@ -1446,7 +1446,7 @@ const std::string ofMaterial::getDefinesString() const { #endif } - if(isPBR() && ofCubeMapsData().size() > 0 && ofIsGLProgrammableRenderer() ) { + if(isPBR() && ofCubeMap::getCubeMapsData().size() > 0 && ofIsGLProgrammableRenderer() ) { // const auto& cubeMapData = ofCubeMap::getActiveData(); definesString += "#define HAS_CUBE_MAP 1\n"; @@ -1454,7 +1454,7 @@ const std::string ofMaterial::getDefinesString() const { bool bHasIrradiance = false; bool bPreFilteredMap = false; bool bBrdfLutTex = false; - for( auto cmdWeak : ofCubeMapsData() ) { + for( auto cmdWeak : ofCubeMap::getCubeMapsData() ) { auto cmd = cmdWeak.lock(); if( !cmd ) continue; if( cmd->bIrradianceAllocated ) { diff --git a/libs/openFrameworks/gl/ofShader.cpp b/libs/openFrameworks/gl/ofShader.cpp index 00eb4fdb12c..208f6f313fe 100644 --- a/libs/openFrameworks/gl/ofShader.cpp +++ b/libs/openFrameworks/gl/ofShader.cpp @@ -1469,9 +1469,9 @@ bool ofShader::setShadowUniforms(int textureLocation) const { setUniformTexture("uShadowMapSpot", ofShadow::getTextureTarget(OF_LIGHT_SPOT), ofShadow::getSpotTexId(), textureLocation + 2); setUniformTexture("uShadowMapArea", ofShadow::getTextureTarget(OF_LIGHT_AREA), ofShadow::getAreaTexId(), textureLocation + 3); - for (size_t i = 0; i < ofShadowsData().size(); i++) { + for (size_t i = 0; i < ofShadow::getShadowsData().size(); i++) { std::string idx = ofToString(i, 0); - std::shared_ptr shadow = ofShadowsData()[i].lock(); + auto shadow = ofShadow::getShadowsData()[i].lock(); std::string shadowAddress = "shadows[" + idx + "]"; if (!shadow || !shadow->isEnabled || shadow->index < 0) { setUniform1f(shadowAddress + ".enabled", 0); @@ -1508,7 +1508,7 @@ bool ofShader::setPbrEnvironmentMapUniforms(int textureLocation) const { return false; } - std::shared_ptr cubeMapData = ofCubeMap::getActiveData(); + auto cubeMapData = ofCubeMap::getActiveData(); if (cubeMapData) { if (cubeMapData->bIrradianceAllocated) { setUniformTexture("tex_irradianceMap", GL_TEXTURE_CUBE_MAP, cubeMapData->irradianceMapId, textureLocation); diff --git a/libs/openFrameworks/gl/ofShadow.cpp b/libs/openFrameworks/gl/ofShadow.cpp index 03fca652a3c..6b1445804fb 100644 --- a/libs/openFrameworks/gl/ofShadow.cpp +++ b/libs/openFrameworks/gl/ofShadow.cpp @@ -1,10 +1,3 @@ -// -// ofShadow.cpp -// openFrameworksLib -// -// Created by Nick Hardeman on 10/3/22. -// - #include "ofShadow.h" #include "of3dUtils.h" #include "ofGLBaseTypes.h" @@ -13,6 +6,7 @@ #include "ofGLProgrammableRenderer.h" // MARK: ofConstants Targets #include "ofConstants.h" +#include "ofMaterial.h" #if !defined(GLM_FORCE_CTOR_INIT) #define GLM_FORCE_CTOR_INIT @@ -27,23 +21,40 @@ using std::weak_ptr; using std::vector; using std::shared_ptr; +struct ofShadowGLData { + GLuint fboId = 0; + GLuint texId = 0; + bool bAllocated = false; + bool bFboAllocated = false; + +#if defined(TARGET_OPENGLES) + int width = 512; + int height = 512; +#else + int width = 1024; + int height = 1024; +#endif + + int totalShadows = 0; +}; + //---------------------------------------- -vector > & ofShadowsData(){ - static vector > * shadowsActive = ofIsGLProgrammableRenderer()?new vector >:new vector >(8); +vector > & ofShadow::getShadowsData(){ + static vector > * shadowsActive = ofIsGLProgrammableRenderer() ? new vector > : new vector >(8); return *shadowsActive; } //-------------------------------------------------------------- -static std::map< int, ofShadow::GLData > & getGLDatas(){ - static std::map< int,ofShadow::GLData > * idsFB = new std::map< int,ofShadow::GLData >; +static std::map< int, ofShadowGLData > & getGLDatas(){ + static std::map< int,ofShadowGLData > * idsFB = new std::map< int,ofShadowGLData >; return *idsFB; } //-------------------------------------------------------------- -static ofShadow::GLData& getGLData( int aLightType ) { +static ofShadowGLData& getGLData( int aLightType ) { if( getGLDatas().count(aLightType) < 1 ) { - getGLDatas()[aLightType] = ofShadow::GLData(); + getGLDatas()[aLightType] = ofShadowGLData(); } return getGLDatas()[aLightType]; } @@ -200,8 +211,8 @@ std::string ofShadow::getShadowTypeAsString( ofShadowType atype ) { //-------------------------------------------------------------- bool ofShadow::hasActiveShadows() { - for(size_t i=0;i< ofShadowsData().size();i++){ - std::shared_ptr shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iisEnabled && shadow->index > -1 ){ return true; break; @@ -217,8 +228,8 @@ void ofShadow::enableAllShadows() { return; } - for(size_t i=0;i shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iindex < 0 ){ continue; } @@ -228,8 +239,8 @@ void ofShadow::enableAllShadows() { //-------------------------------------------------------------- void ofShadow::disableAllShadows() { - for(size_t i=0;i shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iindex < 0 ){ continue; } @@ -243,8 +254,8 @@ void ofShadow::setAllShadowTypes( ofShadowType atype ) { ofLogWarning("ofShadow :: setAllShadowTypes : only works with programmable renderer."); return; } - for(size_t i=0;i shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iindex < 0 ){ continue; } @@ -270,8 +281,8 @@ void ofShadow::setAllShadowBias( float bias ) { ofLogWarning("ofShadow :: setAllShadowBias : only works with programmable renderer."); return; } - for(size_t i=0;i shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iindex < 0 ){ continue; } @@ -285,8 +296,8 @@ void ofShadow::setAllShadowNormalBias( float normalBias ) { ofLogWarning("ofShadow :: setAllShadowNormalBias : only works with programmable renderer."); return; } - for(size_t i=0;i shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iindex < 0 ){ continue; } @@ -300,8 +311,8 @@ void ofShadow::setAllShadowSampleRadius( float sampleRadius ) { ofLogWarning("ofShadow :: enableAllShadows : only works with programmable renderer."); return; } - for(size_t i=0;i shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iindex < 0 ){ continue; } @@ -313,7 +324,7 @@ void ofShadow::setAllShadowSampleRadius( float sampleRadius ) { std::string ofShadow::getShaderDefinesAsString() { std::string definesString = ""; if( areShadowsSupported() ) { - if( ofShadowsData().size() > 0 && ofLightsData().size() > 0) { + if( getShadowsData().size() > 0 && ofLightsData().size() > 0) { definesString += "#define HAS_SHADOWS 1\n"; } @@ -344,8 +355,8 @@ bool ofShadow::areShadowsSupported() { void ofShadow::_updateTexDataIds() { std::map texIdMap; - for(size_t i=0;i< ofShadowsData().size();i++){ - std::shared_ptr shadow = ofShadowsData()[i].lock(); + for(size_t i=0;iisEnabled || shadow->index < 0 ){ continue; } @@ -538,7 +549,7 @@ bool ofShadow::beginDepth() { glRenderer->bind(*this); - if( isGlCullingEnabled() ) { + if( isCullingEnabled() ) { glEnable(GL_CULL_FACE); // enables face culling glFrontFace(mGlFrontFaceWindingOrder); glCullFace(GL_FRONT); // tells OpenGL to cull front faces @@ -551,7 +562,7 @@ bool ofShadow::endDepth() { if( !getIsEnabled() || !areShadowsSupported() ) { return false; } - if( isGlCullingEnabled() ) { + if( isCullingEnabled() ) { glDisable(GL_CULL_FACE); } @@ -617,7 +628,7 @@ bool ofShadow::beginDepth(GLenum aCubeFace) { glRenderer->bind(*this,aCubeFace); - if( isGlCullingEnabled() ) { + if( isCullingEnabled() ) { glEnable(GL_CULL_FACE); // enables face culling glFrontFace(mGlFrontFaceWindingOrder); glCullFace(GL_FRONT); // tells OpenGL to cull back faces (the sane default setting) @@ -942,18 +953,18 @@ void ofShadow::_checkSetup() { if( data->index < 0 ) { bool bShadowFound = false; // search for the first free block - for(size_t i=0; iindex = i; data->isEnabled = false; - ofShadowsData()[i] = data; + getShadowsData()[i] = data; bShadowFound = true; break; } } if(!bShadowFound && ofIsGLProgrammableRenderer()){ - ofShadowsData().push_back(data); - data->index = ofShadowsData().size() - 1; + getShadowsData().push_back(data); + data->index = getShadowsData().size() - 1; data->isEnabled = false; bShadowFound = true; } @@ -1132,9 +1143,9 @@ void ofShadow::_updateNumShadows() { getGLData(OF_LIGHT_SPOT).totalShadows = 0; getGLData(OF_LIGHT_AREA).totalShadows = 0; - for(size_t i=0; i < ofShadowsData().size(); i++){ - if(!ofShadowsData()[i].expired()) { - auto shadow = ofShadowsData()[i].lock(); + for(size_t i=0;ilightType).totalShadows++; } diff --git a/libs/openFrameworks/gl/ofShadow.h b/libs/openFrameworks/gl/ofShadow.h index 55938fe3ad5..035da23c25a 100644 --- a/libs/openFrameworks/gl/ofShadow.h +++ b/libs/openFrameworks/gl/ofShadow.h @@ -1,10 +1,3 @@ -// -// ofShadow.h -// openFrameworksLib -// -// Created by Nick Hardeman on 10/3/22. -// - #pragma once #include "ofShader.h" @@ -20,75 +13,40 @@ enum ofShadowType { class ofLight; class ofGLProgrammableRenderer; +class ofMaterial; +/// \class ofShadow +/// Used for determining the amount of light blocked, emanating from ofLights. +/// Intended use as class inside ofLight. +/// see ofLight::shadow class ofShadow { public: - - struct GLData { - GLuint fboId = 0; - GLuint texId = 0; - bool bAllocated = false; - bool bFboAllocated = false; - -#if defined(TARGET_OPENGLES) - int width = 512; - int height = 512; -#else - int width = 1024; - int height = 1024; -#endif - - int totalShadows = 0; - }; - - class Data{ - public: - // position, direction, up and right are in world space - glm::vec3 position = {0,0,0}; - glm::vec3 direction = {1,0,0}; - - glm::vec3 up = {0,1,0}; - glm::vec3 right = {1,0,0}; - - int lightType = 0; -#if defined(TARGET_OPENGLES) - ofShadowType shadowType = OF_SHADOW_TYPE_HARD; -#else - ofShadowType shadowType = OF_SHADOW_TYPE_PCF_LOW; -#endif - - glm::mat4 shadowMatrix; - float strength = 0.5; - - int texIndex = 0; - - bool isEnabled = false; - float bias = 0.005f; - float normalBias = 0.0; - - int numDepthPasses = 1; - - int index = -1; - float nearClip = 1; - float farClip = 1500; - float sampleRadius = 1.f; - }; - static GLenum getTextureTarget( int aLightType ); static int getNumTotalPossibleShadows( int aLightType ); + /// \brief Set the depth map resolution, default is 1024 and 512 for OPENGL_ES. + /// \param aLightType, ofLightType, as int, ie. OF_LIGHT_DIRECTIONAL. + /// \param ares, int, depth resolution. static void setDepthMapResolution( int aLightType, int ares ); static void setDepthMapResolution( int aLightType, int awidth, int aheight ); + /// \brief Get the depth map width for a light type. + /// \param aLightType, ofLightType, as int, ie. OF_LIGHT_DIRECTIONAL. static int getDepthMapWidth(int aLightType); + /// \brief Get the depth map height for a light type. + /// \param aLightType, ofLightType, as int, ie. OF_LIGHT_DIRECTIONAL. static int getDepthMapHeight(int aLightType); + /// \brief Get the GL texture ids for the corresponding light type for passing to an ofShader. static GLuint getPointTexId(); static GLuint getDirectionalTexId(); static GLuint getSpotTexId(); static GLuint getAreaTexId(); - + /// \brief Determine if any shadow is active. + /// \return bool true if at least one is enabled. static bool hasActiveShadows(); + /// \brief Get the ofShadowType as a string representation. + /// \return std::string. static std::string getShadowTypeAsString( ofShadowType atype ); static void enableAllShadows(); @@ -98,80 +56,205 @@ class ofShadow { static void setAllShadowBias( float bias ); static void setAllShadowNormalBias( float normalBias ); static void setAllShadowSampleRadius( float sampleRadius ); - static std::string getShaderDefinesAsString(); static bool areShadowsSupported(); ofShadow(); ~ofShadow(); - void setLightType( int atype ); - - void update( const ofLight& alight ); - - bool beginDepth(); - bool endDepth(); - - bool beginDepth(GLenum aCubeFace); - bool endDepth(GLenum aCubeFace); - + /// \brief Clear out any unused texture resources. void clear(); - - const std::shared_ptr& getData() const { return data; } + /// \brief Get the enabled state. + /// \return bool, true if enabled. bool getIsEnabled() { return data->isEnabled; } + /// \brief Set the enabled state. + /// \param bool, true to enable. void setEnabled( bool ab ); - + /// \brief Get the near clip. + /// \return float, near clip value. float getNearClip() { return data->nearClip; } + /// \brief Get the far clip. + /// \return float, far clip value. float getFarClip() { return data->farClip; } + /// \brief Set the near clip. + /// \param anear float, near clip value. void setNearClip( float anear ) { data->nearClip = anear; } + /// \brief Set the far clip. + /// \param afar float, far clip value. void setFarClip( float afar ) { data->farClip = afar; } - + /// \brief Is multi pass rendering enabled for point lights. + /// \return bool, true if multi cube pass rendering is enabled. bool isMultiCubeFacePass() const; bool isSingleOmniPass() const; - int getNumShadowDepthPasses() const; + /// \brief Set omni pass render for point lights. Cube maps are rendered for point lights. + /// Omni pass is enabled by default. + /// \warning Disabled for OPENGL_ES. + /// \param ab bool to enable or disable omni pass rendering. void setSingleOmniPass( bool ab ); + /// \brief Get number of shadow depth pases, 1 or 6 point lights without single omni pass. + /// \return int, number of passes required. + int getNumShadowDepthPasses() const; + /// \brief Get the depth map width for this shadow type. + /// \return int, depth width resolution. int getDepthMapWidth(); + /// \brief Get the depth map height for this shadow type. + /// \return int, depth height resolution. int getDepthMapHeight(); + /// \brief Set the shadow type, the fidelity of the shadow. + /// \param aWorldWidth width of the bounds. + /// \param aWorldHeight height of the bounds. void setDirectionalBounds( float aWorldWidth, float aWorldHeight ); + /// \brief Set the shadow type, the fidelity of the shadow. + /// \param atype ofShadowType. void setShadowType(ofShadowType atype) {data->shadowType = atype;} - - bool isGlCullingEnabled() { return mBEnableCulling; } - void setGlCullingEnabled(bool ab) { mBEnableCulling=ab;} + /// \brief Is culling of triangles enabled. Change the winding order using setFrontFaceWindingOrder() + /// \return bool true if front facing triangles are culled. + bool isCullingEnabled() { return mBEnableCulling; } + /// \brief Is culling of triangles enabled. Change the winding order using setFrontFaceWindingOrder() + /// \param ab set to true to enable culling of front facing triangles. + void setCullingEnabled(bool ab) { mBEnableCulling=ab;} + /// \brief Get winding order of front faces used for culling. + /// \return GLenum, GL_CW or GL_CCW. GLenum getFrontFaceWindingOrder() { return mGlFrontFaceWindingOrder; } + /// \brief Set winding order of front faces used for culling. + /// \param GLenum aw GL_CW or GL_CCW. void setFrontFaceWindingOrder( GLenum aw ) { mGlFrontFaceWindingOrder = aw; } + /// \brief Get the depth map fbo id. + /// \return GLuint representing the fbo id. GLuint getDepthMapFboId(); + /// \brief Get the depth map GL texture id. + /// \return GLuint representing the GL texture id. GLuint getDepthMapTexId(); - + /// \brief Get the strength of the shadow. + /// /// \return float representing the shadow strength. const float& getStrength() { return data->strength; } + /// \brief Set the strength of the shadow, higher values yield darker results. + /// \param astrength float value of strength, can be higher than 1.0. void setStrength(float astrength) { data->strength = astrength; } - + /// \brief Get the bias of the shadow depth sampling. + /// \return float representing the bias amount. float getBias() const { return data->bias; } + /// \brief Set the bias of the shadow depth sampling, helpful for reducing shadow acne. + /// \param abias float value of the bias amount. void setBias( float abias ) { data->bias = abias; } - + /// \brief Get the bias the shadow depth sampling along the normal by this amount. + /// \return float value of the bias along normal amount. float getNormalBias() const { return data->normalBias; } + /// \brief Bias the shadow depth sampling along the normal by this amount. + /// \param abias float value of the bias along normal amount. void setNormalBias( float abias ) { data->normalBias = abias; } - + /// \brief Get the sample radius which the shadow is sampled around a disk. + /// \return float value sample radius. float getSampleRadius() const { return data->sampleRadius; } + /// \brief Set the sample radius which the shadow is sampled around a disk. + /// \param aradius float value of the sample radius. void setSampleRadius( float aradius ) { data->sampleRadius = aradius; } + /// \brief Draw the bounds of the shadow. Helpful for determing if the area of interest + /// is inside the shadow frustum. If outside, the shadow will have no effect. + void drawFrustum(); + /// \brief Get the ofShadowType as a string representation. + /// \return std::string. + std::string getShadowTypeAsString(); + + /// \brief Configure the ofShader with the correct defines and uniforms for this shadow type. + /// \param ashader ofShader to configure. + /// \param aShaderMain string, the shader source main function as a string, vert shader only. + /// + /// \see: "openFrameworks/gl/shaders/shadowDepth.vert" + /// \note: If using in conjunction with ofMaterials, try ofMaterial::setDepthShaderMain(std::string aShaderSrc, std::string skey). + /// Which will use the depth shader only on that ofMaterial. + /// + /// + /// \note: #define OF_SHADOW_DEPTH_PASS is added to the file. + /// + /// \note: Find a sample vert shader below to pass as a string to the setupShadowDepthShader() function as aShaderMain. + /// + /// void main (void){ + /// #if defined(OF_SHADOW_DEPTH_PASS) // Define added by the setupShadowDepthShader() function. Unnecessary here, shown for reference. + /// vec3 worldPosition = (modelMatrix * vec4(position.xyz, 1.0)).xyz; + /// // this method is added via ofShadow.setupShadowDepthShader + /// sendShadowDepthWorldPosition(worldPosition); + /// #endif + /// } + bool setupShadowDepthShader(ofShader& ashader, const std::string aShaderMain) const; + /// \brief Configure the ofShader with the correct defines and uniforms a light type, ie. OF_LIGHT_POINT. + /// \param ashader ofShader to configure. + /// \param aLightType int the shader source main function as a string, vert shader only. + /// \param aShaderMain string, the shader source main function as a string, vert shader only. + /// \note: "openFrameworks/gl/shaders/shadowDepth.vert" + bool setupShadowDepthShader(ofShader& ashader, int aLightType, const std::string aShaderMain, bool abSinglePass) const; + + +protected: + /// \class ofShadowData + /// Used for holding information for rendering, accessed by other classes like ofShader. + class Data{ + public: + // position, direction, up and right are in world space + glm::vec3 position = {0,0,0}; + glm::vec3 direction = {1,0,0}; + + glm::vec3 up = {0,1,0}; + glm::vec3 right = {1,0,0}; + + int lightType = 0; +#if defined(TARGET_OPENGLES) + ofShadowType shadowType = OF_SHADOW_TYPE_HARD; +#else + ofShadowType shadowType = OF_SHADOW_TYPE_PCF_LOW; +#endif + + glm::mat4 shadowMatrix; + float strength = 0.5; + + int texIndex = 0; + + bool isEnabled = false; + float bias = 0.005f; + float normalBias = 0.0; + + int numDepthPasses = 1; + + int index = -1; + float nearClip = 1; + float farClip = 1500; + float sampleRadius = 1.f; + }; + + static std::vector > & getShadowsData(); + + const std::shared_ptr& getData() const { return data; } + + /// \note: These protected functions are called from ofLight. + void setLightType( int atype ); void setAreaLightSize( float awidth, float aheight ) {mAreaLightWidth=awidth;mAreaLightHeight=aheight;}; - void drawFrustum(); + void update( const ofLight& alight ); + + bool beginDepth(); + bool endDepth(); + + bool beginDepth(GLenum aCubeFace); + bool endDepth(GLenum aCubeFace); + + friend class ofLight; + friend class ofGLProgrammableRenderer; + friend class ofMaterial; + friend class ofShader; + + static std::string getShaderDefinesAsString(); + std::vector getFrustumCorners( const glm::vec3& aup, const glm::vec3& aright, const glm::vec3& afwd ); - std::string getShadowTypeAsString(); - + /// \note: The following 3 functions are used by the renderer, ofGLProgrammableRenderer for example. const ofShader & getDepthShader(ofGLProgrammableRenderer & renderer) const; - bool setupShadowDepthShader(ofShader& ashader, const std::string aShaderMain) const; - bool setupShadowDepthShader(ofShader& ashader, int aLightType, const std::string aShaderMain, bool abSinglePass) const; void updateDepth(const ofShader & shader,ofGLProgrammableRenderer & renderer) const; void updateDepth(const ofShader & shader,GLenum aCubeFace,ofGLProgrammableRenderer & renderer) const; -protected: - static void _updateTexDataIds(); void _drawFrustum( const glm::vec3& aup, const glm::vec3& aright, const glm::vec3& afwd ); @@ -221,6 +304,14 @@ class ofShadow { void initShaders(ofGLProgrammableRenderer & renderer) const; mutable std::map> shaders; + }; -std::vector > & ofShadowsData(); +//namespace of{ +//namespace priv{ +///// \brief Function for storing all of the ofShadows data. +//std::vector > & ofShadowsData(); +//} +//} + +