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 
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 
59 public:
60 
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 
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 = QString("sg_cColor.xyz += LitDirLight_Cel(sg_vPosVS.xyz, sg_vNormalVS, g_vLightDir_%1, g_cLightAmbient_%2, g_cLightDiffuse_%3, g_cLightSpecular_%4, g_celPaletteSize);").arg(_lightId).arg(_lightId).arg(_lightId).arg(_lightId);
102  break;
103 
104  case ACG::SG_LIGHT_POINT:
105  buf = QString("sg_cColor.xyz += LitPointLight_Cel(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%1, g_cLightAmbient_%2, g_cLightDiffuse_%3, g_cLightSpecular_%4, g_vLightAtten_%5, g_celPaletteSize);").arg(_lightId).arg(_lightId).arg(_lightId).arg(_lightId).arg(_lightId);
106  break;
107 
108  case ACG::SG_LIGHT_SPOT:
109  buf = QString("sg_cColor.xyz += LitSpotLight_Cel(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%1, g_vLightDir_%2, g_cLightAmbient_%3, g_cLightDiffuse_%4, g_cLightSpecular_%5, g_vLightAtten_%6, g_vLightAngleExp_%7, g_celPaletteSize);").arg(_lightId).arg(_lightId).arg(_lightId).arg(_lightId).arg(_lightId).arg(_lightId).arg(_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 
288 
289  // restore common opengl state
290  // log window remains hidden otherwise
291  finishRenderingPipeline();
292 }
293 
294 
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 
static QString getShaderDir()
Collection of fbos for each viewport.
Definition: FBO.hh:71
void addOutput(const QString &_output)
Add one GLSL output specifier.
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:138
void use()
Enables the program object for using.
Definition: GLSLShader.cc:345
int viewport_width() const
get viewport width
Definition: GLState.hh:847
bool replaceDefaultLightingCode()
Specify whether this modifier replaces or extends the default lighting code.
Definition: ToonRenderer.cc:92
void addIncludeFile(QString _fileName)
Imports another shader, same as #include.
double near_plane() const
get near clipping distance
Definition: GLState.hh:858
QString renderObjectsInfo(bool _outputShaderInfo)
Return a qstring of the current render objects.
QAction * optionsAction()
Return options menu.
ACG::SceneGraph::BaseNode * getSceneGraphRootNode()
get scenegraph root node
void drawMode(ACG::SceneGraph::DrawModes::DrawMode _mode)
set draw mode (No test if this mode is available!)
int getFragDataLocation(const char *_name)
Get location of the fragment data output.
Definition: GLSLShader.cc:724
void glCheckErrors()
Definition: GLError.hh:96
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 class.
Definition: GLSLShader.hh:211
static ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:84
void modifyLightingCode(QStringList *_code, int _lightId, ACG::ShaderGenLightType _lightType)
Modify the default lighting code of the shader generator.
Definition: ToonRenderer.cc:94
void modifyFragmentEndCode(QStringList *_code)
Append code the the fragment shader.
Definition: ToonRenderer.cc:87
int viewport_height() const
get viewport height
Definition: GLState.hh:849
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:102
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:385
bool openGLVersion(const int _major, const int _minor, bool _verbose)
Definition: gl.cc:129
void addUniform(QString _uniform, QString _comment="")
Add one GLSL uniform specifier.
void modifyVertexIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
Definition: ToonRenderer.cc:61
ACG::Vec4f backgroundColor()
Get current background color.
void bindFragDataLocation(unsigned int _index, const char *_name)
Bind fragment output data to name.
Definition: GLSLShader.cc:692
bool checkExtensionSupported(const std::string &_extension)
Definition: gl.cc:107
static unsigned int registerModifier(ShaderModifier *_modifier)
Shader modifiers have to be registered before they can be used. They also must remain allocated for t...
int viewerId()
Get the id of the viewer this viewerproperties belongs to.
void modifyFragmentIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
Definition: ToonRenderer.cc:70
void link()
Links the shader objects to the program.
Definition: GLSLShader.cc:326
double far_plane() const
get far clipping distance
Definition: GLState.hh:861