Developer Documentation

ToonRenderer.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openflipper.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenFlipper. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41
42
43 #include <ACG/GL/acg_glew.hh>
44 #include "ToonRenderer.hh"
45
46 #include <OpenFlipper/common/GlobalOptions.hh>
47 #include <OpenFlipper/BasePlugin/PluginFunctions.hh>
48 #include <ACG/GL/ShaderCache.hh>
49 #include <ACG/GL/ScreenQuad.hh>
50 #include <ACG/GL/GLError.hh>
51
52 // =================================================
53
54 #define CELSHADING_INCLUDE_FILE "ToonRenderer/celshading.glsl"
55 #define OUTLINE_VERTEXSHADER_FILE "ToonRenderer/screenquad.glsl"
56 #define OUTLINE_FRAGMENTSHADER_FILE "ToonRenderer/outline.glsl"
57
58 class CelShadingModifier : public ACG::ShaderModifier{
59 public:
60
61 void modifyVertexIO( ACG::ShaderGenerator* _shader ) {
62 // include cel lighting functions defined in CELSHADING_INCLUDE_FILE
63 QString includeCelShading = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(CELSHADING_INCLUDE_FILE);
64 _shader->addIncludeFile(includeCelShading);
65
66 // add shader constant that defines the number of different intensity levels used in lighting
67 _shader->addUniform("float g_celPaletteSize", "//number of palettes/intensity levels for cel shading");
68 }
69
70 void modifyFragmentIO( ACG::ShaderGenerator* _shader ) {
71 // include cel lighting functions defined in CELSHADING_INCLUDE_FILE
72 QString includeCelShading = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(CELSHADING_INCLUDE_FILE);
73 _shader->addIncludeFile(includeCelShading);
74
75 // Note: We include the cel lighting functions in both shader stages
76 // because the ShaderGenerator may call modifyLightingCode() for either a vertex or fragment shader.
77 // It is not yet known in which stage the lighting is performed.
78
79
80 // Additionally write the depth of each fragment to a secondary render-target.
81 // This depth texture is used in a post-processing outlining step.
82 _shader->addOutput("float outDepth");
83 _shader->addUniform("float g_celPaletteSize", "//number of palettes/intensity levels for cel shading");
84 }
85
86
87 void modifyFragmentEndCode(QStringList* _code) {
88 _code->push_back("outDepth = gl_FragCoord.z;"); // write depth to secondary render texture
89 }
90
91 // modifier replaces default lighting with cel lighting
92 bool replaceDefaultLightingCode() {return true;}
93
94 void modifyLightingCode(QStringList* _code, int _lightId, ACG::ShaderGenLightType _lightType) {
95 // use cel shading functions instead of default lighting:
96
97 QString buf;
98
99 switch (_lightType) {
100 case ACG::SG_LIGHT_DIRECTIONAL:
101 buf.sprintf("sg_cColor.xyz += LitDirLight_Cel(sg_vPosVS.xyz, sg_vNormalVS, g_vLightDir_%d, g_cLightAmbient_%d, g_cLightDiffuse_%d, g_cLightSpecular_%d, g_celPaletteSize);", _lightId, _lightId, _lightId, _lightId);
102 break;
103
104 case ACG::SG_LIGHT_POINT:
105 buf.sprintf("sg_cColor.xyz += LitPointLight_Cel(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%d, g_cLightAmbient_%d, g_cLightDiffuse_%d, g_cLightSpecular_%d, g_vLightAtten_%d, g_celPaletteSize);", _lightId, _lightId, _lightId, _lightId, _lightId);
106 break;
107
108 case ACG::SG_LIGHT_SPOT:
109 buf.sprintf("sg_cColor.xyz += LitSpotLight_Cel(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%d, g_vLightDir_%d, g_cLightAmbient_%d, g_cLightDiffuse_%d, g_cLightSpecular_%d, g_vLightAtten_%d, g_vLightAngleExp_%d, g_celPaletteSize);", _lightId, _lightId, _lightId, _lightId, _lightId, _lightId, _lightId);
110 break;
111
112 default: break;
113 }
114
115 _code->push_back(buf);
116 }
117
118
119 static CelShadingModifier instance;
120 };
121
122
123 CelShadingModifier CelShadingModifier::instance;
124
125 // =================================================
126
127 ToonRenderer::ToonRenderer()
128 : progOutline_(0), paletteSize_(2.0f), outlineCol_(0.0f, 0.0f, 0.0f)
129 {
130 ACG::ShaderProgGenerator::registerModifier(&CelShadingModifier::instance);
131 }
132
133
134 ToonRenderer::~ToonRenderer() {
135 }
136
137 QString ToonRenderer::checkOpenGL() {
138 if ( !ACG::openGLVersion(3, 2) )
139 return QString("Insufficient OpenGL Version! OpenGL 3.2 or higher required");
140
141 // Check extensions
142 QString missing("");
143 if(!ACG::openGLVersion(1,5)) // extension is part of opengl spec since version 1.5
144 { // i recommend removing this check in favor of restricting
145 // to a more modern version of opengl e.g. 2.1 should be minimum
146 if ( !ACG::checkExtensionSupported("GL_ARB_vertex_buffer_object") )
147 missing += "GL_ARB_vertex_buffer_object extension missing\n";
148 }
149 #ifndef __APPLE__
150 if(!ACG::openGLVersion(1,4))
151 {
152 if ( !ACG::checkExtensionSupported("GL_ARB_vertex_program") )
153 missing += "GL_ARB_vertex_program extension missing\n";
154 }
155 #endif
156 return missing;
157 }
158
159 void ToonRenderer::initializePlugin() {
160 }
161
162 void ToonRenderer::exit() {
163 delete progOutline_;
164 progOutline_ = 0;
165
166 viewerRes_.clear();
167 }
168
169 QString ToonRenderer::renderObjectsInfo(bool _outputShaderInfo) {
170 std::vector<ACG::ShaderModifier*> modifiers;
171 modifiers.push_back(&CelShadingModifier::instance);
172 return dumpCurrentRenderObjectsToString(&sortedObjects_[0], _outputShaderInfo, &modifiers);
173 }
174
175 void ToonRenderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties) {
176
177 // Cel shading:
178 // - Restriction of the number of lighting intensity levels
179 // - in shader: l dot n is quantized based on the number of allowed shading tones.
180 // currently a constant sized step function is used to quantize the intensity
181
182 // collect renderobjects + prepare OpenGL state
183 prepareRenderingPipeline(_glState, _properties.drawMode(), PluginFunctions::getSceneGraphRootNode());
184
185 // init/update fbos
186 ViewerResources* viewRes = &viewerRes_[_properties.viewerId()];
187 viewRes->resize(_glState->viewport_width(), _glState->viewport_height());
188
189 // --------------------------------------------------
190 // 1st pass: draw scene to intermediate fbo
191
192 // enable color/depth write access
193 glDepthMask(1);
194 glColorMask(1,1,1,1);
195
196 // bind fbo for scene + depth tex
197 viewRes->scene_->bind();
198
199 glViewport(0, 0, _glState->viewport_width(), _glState->viewport_height());
200
201 // clear depth texture
202 glDrawBuffer(GL_COLOR_ATTACHMENT1);
203 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
204 glClear(GL_COLOR_BUFFER_BIT);
205
206 // clear scene color
207 glDrawBuffer(GL_COLOR_ATTACHMENT0);
208 ACG::Vec4f clearColor = _properties.backgroundColor();
209 glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f);
210 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
211
212 // attachment0: scene colors
213 // attachment1: scene depth
214 GLenum colorDepthTarget[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
215 glDrawBuffers(2, colorDepthTarget);
216
217 // render every object
218 for (int i = 0; i < getNumRenderObjects(); ++i) {
219
220 // Take original shader and modify the output to take only the normal as the color
221 GLSL::Program* prog = ACG::ShaderCache::getInstance()->getProgram(&sortedObjects_[i]->shaderDesc, CelShadingModifier::instance);
222
223 int bRelink = 0;
224 if (prog->getFragDataLocation("outFragment") != 0) {
225 prog->bindFragDataLocation(0, "outFragment");
226 bRelink++;
227 }
228 if (prog->getFragDataLocation("outDepth") != 1) {
229 prog->bindFragDataLocation(1, "outDepth");
230 bRelink++;
231 }
232 if (bRelink)
233 prog->link();
234
235 prog->use();
236 prog->setUniform("g_celPaletteSize", paletteSize_);
237
238 renderObject(sortedObjects_[i], prog);
239 }
240
241 viewRes->scene_->unbind();
242
243 // ----------------------------------------------------------
244 // 2nd pass: compose final image with outlining as scene color * edge factor
245
246 if (!progOutline_)
247 progOutline_ = GLSL::loadProgram(OUTLINE_VERTEXSHADER_FILE, OUTLINE_FRAGMENTSHADER_FILE);
248
249 // restore previous fbo
250 restoreInputFbo();
251
252
253 // enable color/depth write access
254 glDepthMask(1);
255 glColorMask(1,1,1,1);
256
257 // note: using glDisable(GL_DEPTH_TEST) not only disables depth testing,
258 // but actually discards any write operations to the depth buffer.
259 // However, we can provide scene depth for further post-processing.
260 // -> Enable depth testing with func=always
261 glEnable(GL_DEPTH_TEST);
262 glDepthFunc(GL_ALWAYS);
263 glDisable(GL_BLEND);
264
265 // setup composition shader
266 progOutline_->use();
267 progOutline_->setUniform("samplerScene", 0);
268 progOutline_->setUniform("samplerDepth", 1);
269 progOutline_->setUniform("texcoordOffset", ACG::Vec2f(1.0f / float(viewRes->scene_->width()), 1.0f / float(viewRes->scene_->height()) ));
270 progOutline_->setUniform("clipPlanes", ACG::Vec2f(_glState->near_plane(), _glState->far_plane()));
271 progOutline_->setUniform("outlineColor", outlineCol_);
272
273 glActiveTexture(GL_TEXTURE1);
274 glBindTexture(GL_TEXTURE_2D, viewRes->scene_->getAttachment(GL_COLOR_ATTACHMENT1));
275
276 glActiveTexture(GL_TEXTURE0);
277 glBindTexture(GL_TEXTURE_2D, viewRes->scene_->getAttachment(GL_COLOR_ATTACHMENT0));
278
279
280 ACG::ScreenQuad::draw(progOutline_);
281
282 progOutline_->disable();
283
284 // reset depth func to opengl default
285 glDepthFunc(GL_LESS);
286
287 ACG::glCheckErrors();
288
289 // restore common opengl state
290 // log window remains hidden otherwise
291 finishRenderingPipeline();
292 }
293
294
295 QAction* ToonRenderer::optionsAction()
296 {
297 QAction * action = new QAction("Toon Renderer Options" , this );
298
299 connect(action,SIGNAL(triggered( bool )),this,SLOT(actionDialog( bool )));
300
301 return action;
302 }
303
304 void ToonRenderer::paletteSizeChanged( int _val ) {
305 paletteSize_ = float(_val) / 100.0f;
306 }
307
308 void ToonRenderer::outlineColorChanged( QColor _col ) {
309 outlineCol_[0] = _col.redF();
310 outlineCol_[1] = _col.greenF();
311 outlineCol_[2] = _col.blueF();
312 }
313
314 void ToonRenderer::ViewerResources::resize( int _newWidth, int _newHeight ) {
315 if (!_newHeight || !_newWidth) return;
316
317
318 if (!scene_) {
319 // scene fbo with 2 color attachments + depth buffer
320 // attachment0: scene color
321 // attachment1: scene depth
322 scene_ = new ACG::FBO();
323 scene_->init();
324 scene_->attachTexture2D(GL_COLOR_ATTACHMENT0, _newWidth, _newHeight, GL_RGBA, GL_RGBA);
325 scene_->attachTexture2D(GL_COLOR_ATTACHMENT1, _newWidth, _newHeight, GL_R32F, GL_RED);
326 scene_->attachTexture2DDepth(_newWidth, _newHeight);
327 }
328
329 if (scene_->height() == _newHeight &&
330 scene_->width() == _newWidth)
331 return;
332
333 scene_->resize(_newWidth, _newHeight);
334 }
335
336
337
338
ACG::ShaderProgGenerator::getShaderDir
static QString getShaderDir()
Definition: ShaderGenerator.cc:2451
ToonRenderer::ViewerResources
Collection of fbos for each viewport.
Definition: ToonRenderer.hh:107
Viewer::ViewerProperties
Definition: ViewerProperties.hh:107
ACG::FBO
Definition: FBO.hh:71
ACG::ShaderGenerator::addOutput
void addOutput(const QString &_output)
Add one GLSL output specifier.
Definition: ShaderGenerator.cc:464
ACG::GLState
Definition: GLState.hh:213
ACG::ScreenQuad::draw
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:138
GLSL::Program::use
void use()
Enables the program object for using.
Definition: GLSLShader.cc:345
ACG::GLState::viewport_width
int viewport_width() const
get viewport width
Definition: GLState.hh:822
CelShadingModifier::replaceDefaultLightingCode
bool replaceDefaultLightingCode()
Specify whether this modifier replaces or extends the default lighting code.
Definition: ToonRenderer.cc:92
ACG::ShaderGenerator::addIncludeFile
void addIncludeFile(QString _fileName)
Imports another shader, same as #include.
Definition: ShaderGenerator.cc:626
ACG::GLState::near_plane
double near_plane() const
get near clipping distance
Definition: GLState.hh:833
ToonRenderer::renderObjectsInfo
QString renderObjectsInfo(bool _outputShaderInfo)
Return a qstring of the current render objects.
Definition: ToonRenderer.cc:169
ToonRenderer::optionsAction
QAction * optionsAction()
Return options menu.
Definition: ToonRenderer.cc:295
GlobalOptions.hh
PluginFunctions::getSceneGraphRootNode
ACG::SceneGraph::BaseNode * getSceneGraphRootNode()
get scenegraph root node
Definition: PluginFunctions.cc:959
Viewer::ViewerProperties::drawMode
void drawMode(ACG::SceneGraph::DrawModes::DrawMode _mode)
set draw mode (No test if this mode is available!)
Definition: ViewerProperties.cc:151
OpenMesh::VectorT
Definition: Vector11T.hh:83
GLSL::Program::getFragDataLocation
int getFragDataLocation(const char *_name)
Get location of the fragment data output.
Definition: GLSLShader.cc:724
PluginFunctions.hh
ACG::glCheckErrors
void glCheckErrors()
Definition: GLError.hh:96
ACG::ShaderGenerator
Definition: ShaderGenerator.hh:557
GLSL::loadProgram
GLSL::PtrProgram loadProgram(const char *vertexShaderFile, const char *tessControlShaderFile, const char *tessEvaluationShaderFile, const char *geometryShaderFile, const char *fragmentShaderFile, const GLSL::StringList *macros, bool verbose)
Definition: GLSLShader.cc:1076
GLSL::Program
GLSL program class.
Definition: GLSLShader.hh:211
ACG::ShaderCache::getInstance
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:84
CelShadingModifier::modifyLightingCode
void modifyLightingCode(QStringList *_code, int _lightId, ACG::ShaderGenLightType _lightType)
Modify the default lighting code of the shader generator.
Definition: ToonRenderer.cc:94
CelShadingModifier::modifyFragmentEndCode
void modifyFragmentEndCode(QStringList *_code)
Append code the the fragment shader.
Definition: ToonRenderer.cc:87
ACG::GLState::viewport_height
int viewport_height() const
get viewport height
Definition: GLState.hh:824
ACG::ShaderCache::getProgram
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:102
GLSL::Program::setUniform
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:385
ACG::openGLVersion
bool openGLVersion(const int _major, const int _minor, bool _verbose)
Definition: gl.cc:129
ACG::ShaderGenerator::addUniform
void addUniform(QString _uniform, QString _comment="")
Add one GLSL uniform specifier.
Definition: ShaderGenerator.cc:528
CelShadingModifier::modifyVertexIO
void modifyVertexIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
Definition: ToonRenderer.cc:61
Viewer::ViewerProperties::backgroundColor
ACG::Vec4f backgroundColor()
Get current background color.
Definition: ViewerProperties.cc:215
ACG::ShaderModifier
Definition: ShaderGenerator.hh:969
GLSL::Program::bindFragDataLocation
void bindFragDataLocation(unsigned int _index, const char *_name)
Bind fragment output data to name.
Definition: GLSLShader.cc:692
ACG::checkExtensionSupported
bool checkExtensionSupported(const std::string &_extension)
Definition: gl.cc:107
ACG::ShaderProgGenerator::registerModifier
static unsigned int registerModifier(ShaderModifier *_modifier)
Shader modifiers have to be registered before they can be used. They also must remain allocated for t...
Definition: ShaderGenerator.cc:2457
Viewer::ViewerProperties::viewerId
int viewerId()
Get the id of the viewer this viewerproperties belongs to.
Definition: ViewerProperties.cc:142
CelShadingModifier::modifyFragmentIO
void modifyFragmentIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
Definition: ToonRenderer.cc:70
CelShadingModifier
Definition: ToonRenderer.cc:58
GLSL::Program::link
void link()
Links the shader objects to the program.
Definition: GLSLShader.cc:326
ACG::GLState::far_plane
double far_plane() const
get far clipping distance
Definition: GLState.hh:836