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 * *
44 * $Revision: 18127 $ *
45 * $LastChangedBy: moebius $ *
46 * $Date: 2014-02-05 10:12:54 +0100 (Wed, 05 Feb 2014) $ *
47 * *
48 \*===========================================================================*/
49 
50 #include <ACG/GL/acg_glew.hh>
51 #include "ToonRenderer.hh"
52 
55 #include <ACG/GL/ShaderCache.hh>
56 #include <ACG/GL/ScreenQuad.hh>
57 #include <ACG/GL/GLError.hh>
58 
59 // =================================================
60 
61 #define CELSHADING_INCLUDE_FILE "ToonRenderer/celshading.glsl"
62 #define OUTLINE_VERTEXSHADER_FILE "ToonRenderer/screenquad.glsl"
63 #define OUTLINE_FRAGMENTSHADER_FILE "ToonRenderer/outline.glsl"
64 
66 public:
67 
69  // include cel lighting functions defined in CELSHADING_INCLUDE_FILE
70  QString includeCelShading = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(CELSHADING_INCLUDE_FILE);
71  _shader->addIncludeFile(includeCelShading);
72 
73  // add shader constant that defines the number of different intensity levels used in lighting
74  _shader->addUniform("float g_celPaletteSize", "//number of palettes/intensity levels for cel shading");
75  }
76 
78  // include cel lighting functions defined in CELSHADING_INCLUDE_FILE
79  QString includeCelShading = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(CELSHADING_INCLUDE_FILE);
80  _shader->addIncludeFile(includeCelShading);
81 
82  // Note: We include the cel lighting functions in both shader stages
83  // because the ShaderGenerator may call modifyLightingCode() for either a vertex or fragment shader.
84  // It is not yet known in which stage the lighting is performed.
85 
86 
87  // Additionally write the depth of each fragment to a secondary render-target.
88  // This depth texture is used in a post-processing outlining step.
89  _shader->addOutput("float outDepth");
90  _shader->addUniform("float g_celPaletteSize", "//number of palettes/intensity levels for cel shading");
91  }
92 
93 
94  void modifyFragmentEndCode(QStringList* _code) {
95  _code->push_back("outDepth = gl_FragCoord.z;"); // write depth to secondary render texture
96  }
97 
98  // modifier replaces default lighting with cel lighting
99  bool replaceDefaultLightingCode() {return true;}
100 
101  void modifyLightingCode(QStringList* _code, int _lightId, ACG::ShaderGenLightType _lightType) {
102  // use cel shading functions instead of default lighting:
103 
104  QString buf;
105 
106  switch (_lightType) {
107  case ACG::SG_LIGHT_DIRECTIONAL:
108  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);
109  break;
110 
111  case ACG::SG_LIGHT_POINT:
112  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);
113  break;
114 
115  case ACG::SG_LIGHT_SPOT:
116  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);
117  break;
118 
119  default: break;
120  }
121 
122  _code->push_back(buf);
123  }
124 
125 
126  static CelShadingModifier instance;
127 };
128 
129 
130 CelShadingModifier CelShadingModifier::instance;
131 
132 // =================================================
133 
134 ToonRenderer::ToonRenderer()
135  : progOutline_(0), paletteSize_(2.0f), outlineCol_(0.0f, 0.0f, 0.0f)
136 {
137  ACG::ShaderProgGenerator::registerModifier(&CelShadingModifier::instance);
138 }
139 
140 
141 ToonRenderer::~ToonRenderer() {
142  delete progOutline_;
143 }
144 
145 QString ToonRenderer::checkOpenGL() {
146  if ( !ACG::openGLVersion(3, 2) )
147  return QString("Insufficient OpenGL Version! OpenGL 3.2 or higher required");
148 
149  // Check extensions
150  QString missing("");
151  if ( !ACG::checkExtensionSupported("GL_ARB_vertex_buffer_object") )
152  missing += "GL_ARB_vertex_buffer_object extension missing\n";
153 
154 #ifndef __APPLE__
155  if ( !ACG::checkExtensionSupported("GL_ARB_vertex_program") )
156  missing += "GL_ARB_vertex_program extension missing\n";
157 #endif
158 
159  return missing;
160 }
161 
162 void ToonRenderer::initializePlugin() {
163 }
164 
165 QString ToonRenderer::renderObjectsInfo(bool _outputShaderInfo) {
166  std::vector<ACG::ShaderModifier*> modifiers;
167  modifiers.push_back(&CelShadingModifier::instance);
168  return dumpCurrentRenderObjectsToString(&sortedObjects_[0], _outputShaderInfo, &modifiers);
169 }
170 
171 void ToonRenderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties) {
172 
173  // Cel shading:
174  // - Restriction of the number of lighting intensity levels
175  // - in shader: l dot n is quantized based on the number of allowed shading tones.
176  // currently a constant sized step function is used to quantize the intensity
177 
178  // collect renderobjects + prepare OpenGL state
179  prepareRenderingPipeline(_glState, _properties.drawMode(), PluginFunctions::getSceneGraphRootNode());
180 
181  // init/update fbos
182  ViewerResources* viewRes = &viewerRes_[_properties.viewerId()];
183  viewRes->resize(_glState->viewport_width(), _glState->viewport_height());
184 
185  // --------------------------------------------------
186  // 1st pass: draw scene to intermediate fbo
187 
188  // enable color/depth write access
189  glDepthMask(1);
190  glColorMask(1,1,1,1);
191 
192  // bind fbo for scene + depth tex
193  viewRes->scene_->bind();
194 
195  glViewport(0, 0, _glState->viewport_width(), _glState->viewport_height());
196 
197  // clear depth texture
198  glDrawBuffer(GL_COLOR_ATTACHMENT1);
199  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
200  glClear(GL_COLOR_BUFFER_BIT);
201 
202  // clear scene color
203  glDrawBuffer(GL_COLOR_ATTACHMENT0);
204  ACG::Vec4f clearColor = _properties.backgroundColor();
205  glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f);
206  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
207 
208  // attachment0: scene colors
209  // attachment1: scene depth
210  GLenum colorDepthTarget[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
211  glDrawBuffers(2, colorDepthTarget);
212 
213  // render every object
214  for (int i = 0; i < getNumRenderObjects(); ++i) {
215 
216  // Take original shader and modify the output to take only the normal as the color
217  GLSL::Program* prog = ACG::ShaderCache::getInstance()->getProgram(&sortedObjects_[i]->shaderDesc, CelShadingModifier::instance);
218 
219  int bRelink = 0;
220  if (prog->getFragDataLocation("outFragment") != 0) {
221  prog->bindFragDataLocation(0, "outFragment");
222  bRelink++;
223  }
224  if (prog->getFragDataLocation("outDepth") != 1) {
225  prog->bindFragDataLocation(1, "outDepth");
226  bRelink++;
227  }
228  if (bRelink)
229  prog->link();
230 
231  prog->use();
232  prog->setUniform("g_celPaletteSize", paletteSize_);
233 
234  renderObject(sortedObjects_[i], prog);
235  }
236 
237  viewRes->scene_->unbind();
238 
239  // ----------------------------------------------------------
240  // 2nd pass: compose final image with outlining as scene color * edge factor
241 
242  if (!progOutline_)
243  progOutline_ = GLSL::loadProgram(OUTLINE_VERTEXSHADER_FILE, OUTLINE_FRAGMENTSHADER_FILE);
244 
245  // restore previous fbo
246  restoreInputFbo();
247 
248 
249  // enable color/depth write access
250  glDepthMask(1);
251  glColorMask(1,1,1,1);
252 
253  // note: using glDisable(GL_DEPTH_TEST) not only disables depth testing,
254  // but actually discards any write operations to the depth buffer.
255  // However, we can provide scene depth for further post-processing.
256  // -> Enable depth testing with func=always
257  glEnable(GL_DEPTH_TEST);
258  glDepthFunc(GL_ALWAYS);
259  glDisable(GL_BLEND);
260 
261  // setup composition shader
262  progOutline_->use();
263  progOutline_->setUniform("samplerScene", 0);
264  progOutline_->setUniform("samplerDepth", 1);
265  progOutline_->setUniform("texcoordOffset", ACG::Vec2f(1.0f / float(viewRes->scene_->width()), 1.0f / float(viewRes->scene_->height()) ));
266  progOutline_->setUniform("clipPlanes", ACG::Vec2f(_glState->near_plane(), _glState->far_plane()));
267  progOutline_->setUniform("outlineColor", outlineCol_);
268 
269  glActiveTexture(GL_TEXTURE1);
270  glBindTexture(GL_TEXTURE_2D, viewRes->scene_->getAttachment(GL_COLOR_ATTACHMENT1));
271 
272  glActiveTexture(GL_TEXTURE0);
273  glBindTexture(GL_TEXTURE_2D, viewRes->scene_->getAttachment(GL_COLOR_ATTACHMENT0));
274 
275 
276  ACG::ScreenQuad::draw(progOutline_);
277 
278  progOutline_->disable();
279 
280  // reset depth func to opengl default
281  glDepthFunc(GL_LESS);
282 
284 
285  // restore common opengl state
286  // log window remains hidden otherwise
287  finishRenderingPipeline();
288 }
289 
290 
292 {
293  QAction * action = new QAction("Toon Renderer Options" , this );
294 
295  connect(action,SIGNAL(triggered( bool )),this,SLOT(actionDialog( bool )));
296 
297  return action;
298 }
299 
300 void ToonRenderer::paletteSizeChanged( int _val ) {
301  paletteSize_ = float(_val) / 100.0f;
302 }
303 
304 void ToonRenderer::outlineColorChanged( QColor _col ) {
305  outlineCol_[0] = _col.redF();
306  outlineCol_[1] = _col.greenF();
307  outlineCol_[2] = _col.blueF();
308 }
309 
310 void ToonRenderer::ViewerResources::resize( int _newWidth, int _newHeight ) {
311  if (!_newHeight || !_newWidth) return;
312 
313 
314  if (!scene_) {
315  // scene fbo with 2 color attachments + depth buffer
316  // attachment0: scene color
317  // attachment1: scene depth
318  scene_ = new ACG::FBO();
319  scene_->init();
320  scene_->attachTexture2D(GL_COLOR_ATTACHMENT0, _newWidth, _newHeight, GL_RGBA, GL_RGBA);
321  scene_->attachTexture2D(GL_COLOR_ATTACHMENT1, _newWidth, _newHeight, GL_R32F, GL_RED);
322  scene_->attachTexture2DDepth(_newWidth, _newHeight);
323  }
324 
325  if (scene_->height() == _newHeight &&
326  scene_->width() == _newWidth)
327  return;
328 
329  scene_->resize(_newWidth, _newHeight);
330 }
331 
332 
333 
334 #if QT_VERSION < 0x050000
335  Q_EXPORT_PLUGIN2( toonrenderer , ToonRenderer );
336 #endif
337 
void bindFragDataLocation(unsigned int _index, const char *_name)
Bind fragment output data to name.
Definition: GLSLShader.cc:698
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:147
void modifyFragmentIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
Definition: ToonRenderer.cc:77
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:90
Collection of fbos for each viewport.
void link()
Links the shader objects to the program.
Definition: GLSLShader.cc:332
QString renderObjectsInfo(bool _outputShaderInfo)
Return a qstring of the current render objects.
QAction * optionsAction()
Return options menu.
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:1082
bool replaceDefaultLightingCode()
Specify whether this modifier replaces or extends the default lighting code.
Definition: ToonRenderer.cc:99
int viewport_width() const
get viewport width
Definition: GLState.hh:825
Definition: FBO.hh:83
void use()
Enables the program object for using.
Definition: GLSLShader.cc:351
static QString getShaderDir()
void addIncludeFile(QString _fileName)
Imports another shader, same as #include.
void modifyVertexIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
Definition: ToonRenderer.cc:68
bool openGLVersion(const int _major, const int _minor)
Definition: gl.cc:95
int getFragDataLocation(const char *_name)
Get location of the fragment data output.
Definition: GLSLShader.cc:730
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:391
void addUniform(QString _uniform, QString _comment="")
Add one GLSL uniform specifier.
int viewerId()
Get the id of the viewer this viewerproperties belongs to.
void modifyLightingCode(QStringList *_code, int _lightId, ACG::ShaderGenLightType _lightType)
Modify the default lighting code of the shader generator.
void modifyFragmentEndCode(QStringList *_code)
Append code the the fragment shader.
Definition: ToonRenderer.cc:94
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:108
double near_plane() const
get near clipping distance
Definition: GLState.hh:836
void drawMode(ACG::SceneGraph::DrawModes::DrawMode _mode)
set draw mode (No test if this mode is available!)
bool checkExtensionSupported(const std::string &_extension)
Definition: gl.cc:73
int viewport_height() const
get viewport height
Definition: GLState.hh:827
ACG::SceneGraph::BaseNode * getSceneGraphRootNode()
get scenegraph root node
static unsigned int registerModifier(ShaderModifier *_modifier)
Shader modifiers have to be registered before they can be used. They also must remain allocated for t...
ACG::Vec4f backgroundColor()
Get current background color.
GLSL program class.
Definition: GLSLShader.hh:217
void addOutput(const QString &_output)
Add one GLSL output specifier.
double far_plane() const
get far clipping distance
Definition: GLState.hh:839
void glCheckErrors()
Definition: GLError.hh:105