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.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 
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 ShaderCache * getInstance()
Return instance of the ShaderCache singleton.
Definition: ShaderCache.cc:84
static unsigned int registerModifier(ShaderModifier *_modifier)
Shader modifiers have to be registered before they can be used. They also must remain allocated for t...
void addOutput(const QString &_output)
Add one GLSL output specifier.
GLSL::Program * getProgram(const ShaderGenDesc *_desc, const std::vector< unsigned int > &_mods)
Query a dynamically generated program from cache.
Definition: ShaderCache.cc:102
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
void modifyFragmentEndCode(QStringList *_code)
Append code the the fragment shader.
Definition: ToonRenderer.cc:87
void bindFragDataLocation(unsigned int _index, const char *_name)
Bind fragment output data to name.
Definition: GLSLShader.cc:692
int viewport_width() const
get viewport width
Definition: GLState.hh:822
GLSL program class.
Definition: GLSLShader.hh:211
void modifyLightingCode(QStringList *_code, int _lightId, ACG::ShaderGenLightType _lightType)
Modify the default lighting code of the shader generator.
Definition: ToonRenderer.cc:94
void addIncludeFile(QString _fileName)
Imports another shader, same as #include.
int getFragDataLocation(const char *_name)
Get location of the fragment data output.
Definition: GLSLShader.cc:724
ACG::Vec4f backgroundColor()
Get current background color.
void link()
Links the shader objects to the program.
Definition: GLSLShader.cc:326
int viewport_height() const
get viewport height
Definition: GLState.hh:824
Definition: FBO.hh:71
static QString getShaderDir()
void glCheckErrors()
Definition: GLError.hh:96
QAction * optionsAction()
Return options menu.
void drawMode(ACG::SceneGraph::DrawModes::DrawMode _mode)
set draw mode (No test if this mode is available!)
double near_plane() const
get near clipping distance
Definition: GLState.hh:833
static void draw(GLSL::Program *_prog=0)
Draw the screen quad.
Definition: ScreenQuad.cc:138
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
bool replaceDefaultLightingCode()
Specify whether this modifier replaces or extends the default lighting code.
Definition: ToonRenderer.cc:92
bool checkExtensionSupported(const std::string &_extension)
Definition: gl.cc:107
void modifyFragmentIO(ACG::ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
Definition: ToonRenderer.cc:70
Collection of fbos for each viewport.
void setUniform(const char *_name, GLint _value)
Set int uniform to specified value.
Definition: GLSLShader.cc:385
double far_plane() const
get far clipping distance
Definition: GLState.hh:836
void use()
Enables the program object for using.
Definition: GLSLShader.cc:345
ACG::SceneGraph::BaseNode * getSceneGraphRootNode()
get scenegraph root node
int viewerId()
Get the id of the viewer this viewerproperties belongs to.
QString renderObjectsInfo(bool _outputShaderInfo)
Return a qstring of the current render objects.
bool openGLVersion(const int _major, const int _minor, bool _verbose)
Definition: gl.cc:129