├── LICENSE ├── README.md ├── cinderblock.png ├── cinderblock.xml └── src ├── Composer.h ├── composer ├── Layer.h ├── Postprocess.h └── Scene.h └── postprocessing └── StockShaderProcesses.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Stephen Schieberl 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cinder-Composer 2 | 3 | ALPHA 4 | 5 | A header only 'photoshop' style scene graph for composing scenes, objects, rasterised layers and post processing effects. 6 | 7 | TODO: add examples + documentation. 8 | 9 | 10 | -------------------------------------------------------------------------------- /cinderblock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/felixfaire/Cinder-Composer/386240d8c319102079ceaca3b36b939fc6e86541/cinderblock.png -------------------------------------------------------------------------------- /cinderblock.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | src 15 | 16 |
src/Composer.h
17 | 18 |
19 |
20 | -------------------------------------------------------------------------------- /src/Composer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ====== Composer ====== // 4 | #include "composer/Scene.h" 5 | #include "composer/Postprocess.h" 6 | #include "composer/Layer.h" 7 | 8 | -------------------------------------------------------------------------------- /src/composer/Layer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Layer.h 3 | // 4 | // Created by Felix Faire on 18/01/2017. 5 | // 6 | // 7 | 8 | #ifndef Layer_h 9 | #define Layer_h 10 | 11 | 12 | #include "cinder/app/App.h" 13 | #include "cinder/app/RendererGl.h" 14 | #include "cinder/gl/gl.h" 15 | 16 | using namespace ci; 17 | using namespace ci::app; 18 | 19 | class Layer; 20 | typedef std::shared_ptr LayerRef; 21 | 22 | /** 23 | This base class represents a layer in 24 | a renderable scene. This layer can be given a scenes 25 | postprocessing fx and even other layers to render 26 | */ 27 | class Layer 28 | { 29 | public: 30 | 31 | static LayerRef create( int width, int height, int msaa = 0 ) 32 | { 33 | return std::make_shared( width, height, msaa ); 34 | } 35 | 36 | Layer( int width , int height, int msaa = 0 ) 37 | { 38 | mFbo = gl::Fbo::create( width, height, gl::Fbo::Format().samples( msaa ) ); 39 | } 40 | 41 | void update() 42 | { 43 | for (const auto& scene : mScenes) 44 | scene->update(); 45 | 46 | renderInternal(); 47 | } 48 | 49 | void renderInternal() 50 | { 51 | if (mAlpha.value() == 0.0f) 52 | return; 53 | 54 | for (const auto& layer : mLayers) 55 | layer->renderInternal(); 56 | 57 | { 58 | gl::ScopedFramebuffer frameScp( mFbo ); 59 | gl::ScopedViewport viewScp( mFbo->getSize() ); 60 | 61 | if (mFade == 0.0f) 62 | { 63 | gl::clear( ColorA( 0.0f, 0.0f, 0.0f, 0.0f ) ); 64 | } 65 | else 66 | { 67 | gl::ScopedGlslProg colScp( gl::getStockShader( gl::ShaderDef().color() ) ); 68 | gl::color( 0.0f, 0.0f, 0.0f, 1.0f - mFade ); 69 | gl::drawSolidRect( getWindowBounds() ); 70 | } 71 | 72 | for (const auto& layer : mLayers) 73 | layer->drawTex( getWindowBounds() ); 74 | 75 | for (const auto& scene : mScenes) 76 | scene->drawInternal(); 77 | 78 | } 79 | 80 | } 81 | 82 | void drawTex( Rectf bounds ) const 83 | { 84 | if (mAlpha == 0.0f) 85 | return; 86 | 87 | gl::ScopedBlendAlpha alphaScp; 88 | gl::color( 1.0f, 1.0f, 1.0f, mAlpha.value() ); 89 | gl::draw( mFbo->getColorTexture(), bounds ); 90 | } 91 | 92 | void drawFinalTex( Rectf bounds ) 93 | { 94 | if (mAlpha == 0.0f) 95 | return; 96 | 97 | gl::FboRef finalTexture = mFbo; 98 | 99 | if (mPostprocesses.size() > 0) 100 | { 101 | postProcessTexture(); 102 | finalTexture = mFinalFbo; 103 | } 104 | 105 | gl::ScopedBlendAlpha alphaScp; 106 | gl::color( 1.0f, 1.0f, 1.0f, mAlpha.value() ); 107 | gl::draw( finalTexture->getColorTexture(), bounds ); 108 | } 109 | 110 | void postProcessTexture() 111 | { 112 | // Copy Fbo into post pipeline 113 | { 114 | gl::ScopedFramebuffer fboScp( mFinalFbo ); 115 | gl::ScopedViewport viewScp( mFinalFbo->getSize() ); 116 | gl::ScopedGlslProg glScp( gl::getStockShader( gl::ShaderDef().texture() )); 117 | gl::ScopedTextureBind texScp( mFbo->getColorTexture() ); 118 | gl::ScopedMatrices matScp; 119 | gl::setMatricesWindow( mFinalFbo->getSize() ); 120 | gl::ScopedBlendAlpha alphaScp; 121 | gl::clear(); 122 | 123 | gl::drawSolidRect( Rectf(0, 0, mFinalFbo->getWidth(), mFinalFbo->getHeight()) ); 124 | } 125 | 126 | // Process all post activities 127 | for (const auto& process : mPostprocesses) 128 | { 129 | process->process( mFinalFbo->getColorTexture(), mProcessFbo ); 130 | std::swap( mProcessFbo, mFinalFbo ); 131 | } 132 | } 133 | 134 | void addLayer( std::shared_ptr newLayer ) { mLayers.push_back( newLayer ); } 135 | void addScene( std::shared_ptr newScene ) { mScenes.push_back( newScene ); } 136 | 137 | void addPostProcess( std::shared_ptr newProcess ) 138 | { 139 | mPostprocesses.push_back( newProcess ); 140 | 141 | if (mProcessFbo == nullptr) 142 | { 143 | mProcessFbo = gl::Fbo::create( mFbo->getWidth(), mFbo->getHeight() ); 144 | mFinalFbo = gl::Fbo::create( mFbo->getWidth(), mFbo->getHeight() ); 145 | } 146 | } 147 | 148 | void showScene( int index, float duration ) 149 | { 150 | CI_ASSERT( index < mScenes.size() ); 151 | 152 | for (int i = 0; i < mScenes.size(); ++i) 153 | { 154 | float alpha = (index == i) ? 1.0f : 0.0f; 155 | timeline().apply( &mScenes[i]->mAlpha, alpha, duration ); 156 | } 157 | } 158 | 159 | void showLayer( int index, float duration ) 160 | { 161 | CI_ASSERT( index < mLayers.size() ); 162 | 163 | for (int i = 0; i < mLayers.size(); ++i) 164 | { 165 | float alpha = (index == i) ? 1.0f : 0.0f; 166 | timeline().apply( &mLayers[i]->mAlpha, alpha, duration ); 167 | } 168 | } 169 | 170 | 171 | Anim mAlpha = 1.0f; 172 | float mFade = 0.0f; 173 | 174 | // Optional array of other layers 175 | std::vector> mLayers; 176 | 177 | // Optional Array of scenes 178 | std::vector> mScenes; 179 | 180 | // Optional array of postprocessing stages 181 | std::vector> mPostprocesses; 182 | 183 | gl::FboRef mFbo; 184 | gl::FboRef mProcessFbo; 185 | gl::FboRef mFinalFbo; 186 | }; 187 | 188 | #endif /* Layer_h */ 189 | -------------------------------------------------------------------------------- /src/composer/Postprocess.h: -------------------------------------------------------------------------------- 1 | // 2 | // Postprocess.h 3 | // 4 | // Created by Felix Faire on 18/01/2017. 5 | // 6 | // 7 | 8 | #ifndef SceneLayer_h 9 | #define SceneLayer_h 10 | 11 | 12 | #include "cinder/gl/gl.h" 13 | #include "cinder/app/App.h" 14 | 15 | using namespace ci; 16 | using namespace ci::app; 17 | 18 | 19 | /** 20 | This abstract class represents a post processing stage that can happen 21 | on a layer 22 | */ 23 | class Postprocess; 24 | typedef std::shared_ptr PostprocessRef; 25 | 26 | class Postprocess 27 | { 28 | public: 29 | Postprocess(){} 30 | 31 | virtual void process( const gl::TextureRef& inputTexture, gl::FboRef& targetFbo ) = 0; 32 | }; 33 | 34 | /** 35 | This class allows the simple specification of a fragment shader to do the post processing 36 | the shader must specify 37 | 38 | uniform sampler2D tex0; 39 | in vec2 TexCoord 40 | */ 41 | class ShaderPostprocess; 42 | typedef std::shared_ptr ShaderPostprocessRef; 43 | 44 | class ShaderPostprocess : public Postprocess 45 | { 46 | public: 47 | ShaderPostprocess( DataSourceRef fragmentShaderSource ) 48 | { 49 | CI_ASSERT(fragmentShaderSource->isFilePath()); 50 | 51 | gl::GlslProg::Format fmt; 52 | fmt.vertex( getThruVertex() ).fragment( fragmentShaderSource ); 53 | createShader( fmt ); 54 | } 55 | 56 | ShaderPostprocess( const std::string& fragmentShaderString ) 57 | { 58 | gl::GlslProg::Format fmt; 59 | fmt.vertex( getThruVertex() ).fragment( fragmentShaderString ); 60 | createShader( fmt ); 61 | } 62 | 63 | void process( const gl::TextureRef& texture, gl::FboRef& targetFbo ) override 64 | { 65 | CI_ASSERT(texture != nullptr); 66 | CI_ASSERT(targetFbo != nullptr); 67 | CI_ASSERT(mPostProcessShader != nullptr); 68 | 69 | gl::ScopedFramebuffer fboScp( targetFbo ); 70 | gl::ScopedViewport viewScp( targetFbo->getSize() ); 71 | gl::ScopedGlslProg glScp( mPostProcessShader ); 72 | gl::ScopedTextureBind texScp( texture ); 73 | gl::ScopedMatrices matScp; 74 | gl::setMatricesWindow( targetFbo->getSize() ); 75 | gl::clear(); 76 | 77 | mPostProcessShader->uniform( "tex0", 0 ); 78 | updateUniforms( mPostProcessShader ); 79 | gl::drawSolidRect( Rectf(0.0f, 0.0f, (float)targetFbo->getWidth(), (float)targetFbo->getHeight()) ); 80 | } 81 | 82 | virtual void updateUniforms( gl::GlslProgRef& prog ) {} 83 | 84 | 85 | protected: 86 | 87 | gl::GlslProgRef mPostProcessShader; 88 | 89 | const std::string getThruVertex() 90 | { 91 | return CI_GLSL( 150, 92 | 93 | uniform mat4 ciModelViewProjection; 94 | 95 | in vec4 ciPosition; 96 | in vec2 ciTexCoord0; 97 | 98 | out vec2 TexCoord; 99 | 100 | void main() 101 | { 102 | TexCoord = ciTexCoord0; 103 | gl_Position = ciModelViewProjection * ciPosition; 104 | } 105 | 106 | ); 107 | } 108 | 109 | void createShader( const gl::GlslProg::Format& fmt ) 110 | { 111 | try 112 | { 113 | mPostProcessShader = gl::GlslProg::create( fmt ); 114 | } 115 | catch( const std::exception& e ) 116 | { 117 | console() << "PostProcess shader error: " << e.what() << std::endl; 118 | CI_ASSERT( false ); 119 | } 120 | } 121 | 122 | }; 123 | 124 | 125 | 126 | #endif /* SceneLayer_h */ 127 | -------------------------------------------------------------------------------- /src/composer/Scene.h: -------------------------------------------------------------------------------- 1 | // 2 | // Scene.h 3 | // 4 | // Created by Felix Faire on 18/01/2017. 5 | // 6 | // 7 | 8 | #ifndef Scene_h 9 | #define Scene_h 10 | 11 | #include "cinder/gl/gl.h" 12 | #include "cinder/Timeline.h" 13 | 14 | using namespace ci; 15 | using namespace ci::app; 16 | 17 | class Scene; 18 | typedef std::shared_ptr SceneRef; 19 | 20 | /** 21 | This abstract class contains the contents of the scene 22 | and can be rendered to a layer 23 | */ 24 | class Scene 25 | { 26 | public: 27 | 28 | Scene() 29 | { 30 | } 31 | 32 | void drawInternal() 33 | { 34 | if (mAlpha != 0.0f) 35 | draw(); 36 | } 37 | 38 | virtual void update() = 0; 39 | virtual void draw() = 0; 40 | 41 | Anim mAlpha = 1.0f; 42 | }; 43 | 44 | 45 | 46 | #endif /* Scene_h */ 47 | -------------------------------------------------------------------------------- /src/postprocessing/StockShaderProcesses.h: -------------------------------------------------------------------------------- 1 | // 2 | // StockShaderProcesses.h 3 | // 4 | // Created by Felix Faire on 28/02/2017. 5 | // 6 | // 7 | 8 | #ifndef StockShaderProcesses_h 9 | #define StockShaderProcesses_h 10 | 11 | #include "composer/Postprocess.h" 12 | 13 | 14 | // === MIRROR ================================================================= 15 | 16 | class MirrorProcess 17 | { 18 | public: 19 | static ShaderPostprocessRef create() 20 | { 21 | return std::make_shared( CI_GLSL( 400, 22 | 23 | uniform sampler2D tex0; 24 | in vec2 TexCoord; 25 | out vec4 FragColor; 26 | 27 | void main() 28 | { 29 | vec2 p = TexCoord; 30 | FragColor = texture( tex0, vec2( 0.5 - abs( (p.x - 0.5) ), p.y ) ); 31 | } 32 | 33 | )); 34 | } 35 | }; 36 | 37 | 38 | // === INVERT ================================================================= 39 | 40 | class InvertProcess 41 | { 42 | public: 43 | static ShaderPostprocessRef create() 44 | { 45 | return std::make_shared( CI_GLSL( 400, 46 | 47 | uniform sampler2D tex0; 48 | in vec2 TexCoord; 49 | out vec4 FragColor; 50 | 51 | void main() 52 | { 53 | vec2 p = TexCoord; 54 | vec4 col = texture( tex0, p ); 55 | col.xyz = vec3( 1.0 ) - col.xyz; 56 | FragColor = col; 57 | } 58 | 59 | )); 60 | } 61 | }; 62 | 63 | 64 | // === GREYSCALE ============================================================== 65 | 66 | class GreyscaleProcess 67 | { 68 | public: 69 | static ShaderPostprocessRef create() 70 | { 71 | return std::make_shared( CI_GLSL( 400, 72 | 73 | uniform sampler2D tex0; 74 | in vec2 TexCoord; 75 | out vec4 FragColor; 76 | 77 | void main() 78 | { 79 | vec2 p = TexCoord; 80 | vec4 tex = texture( tex0, p ); 81 | vec3 col = vec3( 0.0 ); 82 | 83 | float gray = dot( tex.rgb, vec3( 0.299, 0.587, 0.114 ) ); 84 | 85 | FragColor = vec4( gray, gray, gray, tex.a ); 86 | } 87 | 88 | )); 89 | } 90 | }; 91 | 92 | 93 | // === BLACK LEVELS =========================================================== 94 | 95 | class BlackLevelsProcess : public ShaderPostprocess 96 | { 97 | public: 98 | 99 | static std::shared_ptr create( float black = 0.0f, float white = 1.0f ) 100 | { 101 | return std::make_shared( black, white ); 102 | } 103 | 104 | BlackLevelsProcess( float black = 0.0f, float white = 1.0f ) 105 | : ShaderPostprocess( getFragmentShader() ) 106 | { 107 | } 108 | 109 | void updateUniforms( gl::GlslProgRef& prog ) override 110 | { 111 | prog->uniform( "u_black", mBlackOut ); 112 | prog->uniform( "u_white", mWhiteOut ); 113 | prog->uniform( "u_brightness", mBrightness ); 114 | prog->uniform( "u_contrast", mContrast ); 115 | } 116 | 117 | void setLevels( float blackOut, float whiteOut ) 118 | { 119 | mBlackOut = blackOut; 120 | mWhiteOut = whiteOut; 121 | } 122 | 123 | void setBrightness( float brightness ) { mBrightness = brightness; } 124 | void setContrast( float contrast ) { mContrast = contrast; } 125 | 126 | static const std::string getFragmentShader() 127 | { 128 | return CI_GLSL( 400, 129 | 130 | uniform sampler2D tex0; 131 | uniform float u_black; 132 | uniform float u_white; 133 | uniform float u_brightness; 134 | uniform float u_contrast; 135 | 136 | in vec2 TexCoord; 137 | 138 | out vec4 FragColor; 139 | 140 | vec3 map(vec3 value, vec3 inMin, vec3 inMax, vec3 outMin, vec3 outMax) 141 | { 142 | return outMin + (outMax - outMin) * (value - inMin) / (inMax - inMin); 143 | } 144 | 145 | void main() 146 | { 147 | vec4 tex = texture( tex0, TexCoord ); 148 | 149 | vec3 b = vec3( u_black ); 150 | vec3 w = vec3( u_white ); 151 | 152 | // Levels 153 | tex.xyz = map( clamp( tex.xyz, b, w), b, w, vec3(0.0), vec3(1.0) ); 154 | 155 | // Brightness Curve 156 | tex.xyz = vec3(1.0) - pow( vec3(1.0) - tex.xyz, vec3(u_brightness) ); 157 | 158 | // Contrast Curve 159 | tex.xyz = mix( vec3( 0.5 ), tex.xyz, vec3(u_contrast) ); 160 | 161 | FragColor = tex; 162 | } 163 | 164 | ); 165 | } 166 | 167 | private: 168 | 169 | float mBlackOut = 0.0; 170 | float mWhiteOut = 1.0; 171 | 172 | float mBrightness = 1.0; 173 | float mContrast = 1.0; 174 | 175 | }; 176 | 177 | 178 | // === VIGNETTE ============================================================== 179 | 180 | class VignetteProcess : public ShaderPostprocess 181 | { 182 | public: 183 | 184 | static std::shared_ptr create( float black = 0.0f, float white = 1.0f ) 185 | { 186 | return std::make_shared( black, white ); 187 | } 188 | 189 | VignetteProcess( float black = 0.0f, float white = 1.0f ) 190 | : ShaderPostprocess( getFragmentShader() ) 191 | { 192 | } 193 | 194 | void updateUniforms( gl::GlslProgRef& prog ) override 195 | { 196 | 197 | } 198 | 199 | static const std::string getFragmentShader() 200 | { 201 | return CI_GLSL( 400, 202 | 203 | uniform sampler2D tex0; 204 | 205 | in vec2 TexCoord; 206 | 207 | out vec4 FragColor; 208 | 209 | float lengthSquared( vec2 p ) 210 | { 211 | return p.x * p.x + p.y * p.y; 212 | } 213 | 214 | void main() 215 | { 216 | vec4 tex = texture( tex0, TexCoord ); 217 | 218 | vec2 p = (TexCoord - 0.5) * 2.0; 219 | 220 | tex.xyz -= pow( lengthSquared( p * 0.8 ) - 0.1, 1.8 ) * 0.3; 221 | 222 | FragColor = tex; 223 | } 224 | 225 | ); 226 | } 227 | 228 | private: 229 | 230 | 231 | }; 232 | 233 | #endif /* StockShaderProcesses_h */ 234 | --------------------------------------------------------------------------------