ToonRenderer.cc 12.8 KB
Newer Older
Jan Möbius's avatar
Jan Möbius committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/*===========================================================================*\
*                                                                            *
*                              OpenFlipper                                   *
*      Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen       *
*                           www.openflipper.org                              *
*                                                                            *
*--------------------------------------------------------------------------- *
*  This file is part of OpenFlipper.                                         *
*                                                                            *
*  OpenFlipper is free software: you can redistribute it and/or modify       *
*  it under the terms of the GNU Lesser General Public License as            *
*  published by the Free Software Foundation, either version 3 of            *
*  the License, or (at your option) any later version with the               *
*  following exceptions:                                                     *
*                                                                            *
*  If other files instantiate templates or use macros                        *
*  or inline functions from this file, or you compile this file and          *
*  link it with other files to produce an executable, this file does         *
*  not by itself cause the resulting executable to be covered by the         *
*  GNU Lesser General Public License. This exception does not however        *
*  invalidate any other reasons why the executable file might be             *
*  covered by the GNU Lesser General Public License.                         *
*                                                                            *
*  OpenFlipper is distributed in the hope that it will be useful,            *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of            *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
*  GNU Lesser General Public License for more details.                       *
*                                                                            *
*  You should have received a copy of the GNU LesserGeneral Public           *
*  License along with OpenFlipper. If not,                                   *
*  see <http://www.gnu.org/licenses/>.                                       *
*                                                                            *
\*===========================================================================*/

/*===========================================================================*\
*                                                                            *
*   $Revision: 18127 $                                                       *
*   $LastChangedBy: moebius $                                                *
*   $Date: 2014-02-05 10:12:54 +0100 (Wed, 05 Feb 2014) $                     *
*                                                                            *
\*===========================================================================*/

#include "ToonRenderer.hh"

#include <OpenFlipper/common/GlobalOptions.hh>
#include <OpenFlipper/BasePlugin/PluginFunctions.hh>
#include <ACG/GL/ShaderCache.hh>
#include <ACG/GL/ScreenQuad.hh>
#include <ACG/GL/GLError.hh>

#undef QT_NO_OPENGL
#include <QGLFormat>
#define QT_NO_OPENGL


// =================================================

#define CELSHADING_INCLUDE_FILE "ToonRenderer/celshading.glsl"
#define OUTLINE_VERTEXSHADER_FILE "ToonRenderer/screenquad.glsl"
#define OUTLINE_FRAGMENTSHADER_FILE "ToonRenderer/outline.glsl"

class CelShadingModifier : public ACG::ShaderModifier{
public:

  void modifyVertexIO( ACG::ShaderGenerator* _shader )  {
    // include cel lighting functions defined in CELSHADING_INCLUDE_FILE
    QString includeCelShading = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(CELSHADING_INCLUDE_FILE);
    _shader->addIncludeFile(includeCelShading);

    // add shader constant that defines the number of different intensity levels used in lighting
    _shader->addUniform("float g_celPaletteSize", "//number of palettes/intensity levels for cel shading");
  }

  void modifyFragmentIO( ACG::ShaderGenerator* _shader )  {
    // include cel lighting functions defined in CELSHADING_INCLUDE_FILE
    QString includeCelShading = ACG::ShaderProgGenerator::getShaderDir() + QDir::separator() + QString(CELSHADING_INCLUDE_FILE);
    _shader->addIncludeFile(includeCelShading);

    // Note: We include the cel lighting functions in both shader stages
    // because the ShaderGenerator may call modifyLightingCode() for either a vertex or fragment shader.
    // It is not yet known in which stage the lighting is performed.


    // Additionally write the depth of each fragment to a secondary render-target.
    // This depth texture is used in a post-processing outlining step.
    _shader->addOutput("float outDepth");
    _shader->addUniform("float g_celPaletteSize", "//number of palettes/intensity levels for cel shading");
  }


  void modifyFragmentEndCode(QStringList* _code)  {
    _code->push_back("outDepth = gl_FragCoord.z;"); // write depth to secondary render texture
  }

  // modifier replaces default lighting with cel lighting
  bool replaceDefaultLightingCode() {return true;}

  void modifyLightingCode(QStringList* _code, int _lightId, ACG::ShaderGenLightType _lightType)  {
    // use cel shading functions instead of default lighting:

    QString buf;

    switch (_lightType)    {
    case ACG::SG_LIGHT_DIRECTIONAL:
      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);
      break;

    case ACG::SG_LIGHT_POINT:
      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);
      break;

    case ACG::SG_LIGHT_SPOT:
      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);
      break;

    default: break;
    }

    _code->push_back(buf);
  }
  

  static CelShadingModifier instance;
};


CelShadingModifier CelShadingModifier::instance;

// =================================================

ToonRenderer::ToonRenderer() 
  : progOutline_(0)
{
  ACG::ShaderProgGenerator::registerModifier(&CelShadingModifier::instance);
}


ToonRenderer::~ToonRenderer() {
  delete progOutline_;
}


void ToonRenderer::initializePlugin() {
  ACG::ShaderProgGenerator::setShaderDir(OpenFlipper::Options::shaderDirStr());
}

QString ToonRenderer::renderObjectsInfo(bool _outputShaderInfo) {
  std::vector<ACG::ShaderModifier*> modifiers;
  modifiers.push_back(&CelShadingModifier::instance);
  return dumpCurrentRenderObjectsToString(&sortedObjects_[0], _outputShaderInfo, &modifiers);
}

void ToonRenderer::render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties) {

  // Cel shading: 
  // - Restriction of the number of lighting intensity levels
  // - in shader: l dot n is quantized based on the number of allowed shading tones.
  // currently a constant sized step function is used to quantize the intensity

  // add this constant to render menu when available
  const float numShades = 2.0f;


  // collect renderobjects + prepare OpenGL state
  prepareRenderingPipeline(_glState, _properties.drawMode(), PluginFunctions::getSceneGraphRootNode());


  // clear back buffer
  ACG::Vec4f clearColor = _properties.backgroundColor();
  glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


  // save active fbo
  GLint curFbo = 0;
  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &curFbo);


  // init/update fbos
  ViewerResources* viewRes = &viewerRes_[_properties.viewerId()];
  viewRes->resize(_glState->viewport_width(), _glState->viewport_height());


  // --------------------------------------------------
  // 1st pass: draw scene to intermediate fbo

  // enable color/depth write access
  glDepthMask(1);
  glColorMask(1,1,1,1);

  // bind fbo for scene + depth tex
  viewRes->scene_->bind();

  // clear depth texture
  glDrawBuffer(GL_COLOR_ATTACHMENT1);
196
  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
Jan Möbius's avatar
Jan Möbius committed
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  glClear(GL_COLOR_BUFFER_BIT);

  // clear scene color
  glDrawBuffer(GL_COLOR_ATTACHMENT0);
  glClearColor(clearColor[0], clearColor[1], clearColor[2], 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // attachment0: scene colors
  // attachment1: scene depth
  GLenum colorDepthTarget[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
  glDrawBuffers(2, colorDepthTarget);

  // render every object
  for (int i = 0; i < getNumRenderObjects(); ++i) {

    // Take original shader and modify the output to take only the normal as the color
    GLSL::Program* prog = ACG::ShaderCache::getInstance()->getProgram(&sortedObjects_[i]->shaderDesc, CelShadingModifier::instance);

    int bRelink = 0;
    if (prog->getFragDataLocation("outFragment") != 0) {
      prog->bindFragDataLocation(0, "outFragment");
      bRelink++;
    }
    if (prog->getFragDataLocation("outDepth") != 1) {
      prog->bindFragDataLocation(1, "outDepth");
      bRelink++;
    }
    if (bRelink)
      prog->link();

    prog->use();
    prog->setUniform("g_celPaletteSize", numShades);

    renderObject(sortedObjects_[i], prog);
  }

  viewRes->scene_->unbind();

  // ----------------------------------------------------------
  // 2nd pass: compose final image with outlining as  scene color * edge factor

  if (!progOutline_)
    progOutline_ = GLSL::loadProgram(OUTLINE_VERTEXSHADER_FILE, OUTLINE_FRAGMENTSHADER_FILE);

  // restore previous fbo
  glBindFramebuffer(GL_FRAMEBUFFER, curFbo);
  glDrawBuffer(curFbo == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0);

  // enable color/depth write access
  glDepthMask(1);
  glColorMask(1,1,1,1);
248
249
250
251
252
253
254

  // note: using glDisable(GL_DEPTH_TEST) not only disables depth testing,
  //  but actually discards any write operations to the depth buffer.
  // However, we can provide scene depth for further post-processing. 
  //   -> Enable depth testing with func=always
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_ALWAYS);
Jan Möbius's avatar
Jan Möbius committed
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  glDisable(GL_BLEND);

  // setup composition shader
  progOutline_->use();
  progOutline_->setUniform("samplerScene", 0);
  progOutline_->setUniform("samplerDepth", 1);
  progOutline_->setUniform("texcoordOffset", ACG::Vec2f(1.0f / float(viewRes->scene_->width()), 1.0f / float(viewRes->scene_->height()) ));
  progOutline_->setUniform("clipPlanes", ACG::Vec2f(_glState->near_plane(), _glState->far_plane()));

  glActiveTexture(GL_TEXTURE1);
  glBindTexture(GL_TEXTURE_2D, viewRes->scene_->getAttachment(GL_COLOR_ATTACHMENT1));

  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, viewRes->scene_->getAttachment(GL_COLOR_ATTACHMENT0));


  ACG::ScreenQuad::draw(progOutline_);

  progOutline_->disable();
274
275
276

  // reset depth func to opengl default
  glDepthFunc(GL_LESS);
Jan Möbius's avatar
Jan Möbius committed
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
  
  ACG::glCheckErrors();

  // restore common opengl state
  // log window remains hidden otherwise
  finishRenderingPipeline();
}

QString ToonRenderer::checkOpenGL() {
  // Get version and check
  QGLFormat::OpenGLVersionFlags flags = QGLFormat::openGLVersionFlags();
  if ( !flags.testFlag(QGLFormat::OpenGL_Version_3_2) )
    return QString("Insufficient OpenGL Version! OpenGL 3.2 or higher required");

  // Check extensions
  QString glExtensions = QString((const char*)glGetString(GL_EXTENSIONS));
  QString missing("");
  if ( !glExtensions.contains("GL_ARB_vertex_buffer_object") )
    missing += "GL_ARB_vertex_buffer_object extension missing\n";

#ifndef __APPLE__
  if ( !glExtensions.contains("GL_ARB_vertex_program") )
    missing += "GL_ARB_vertex_program extension missing\n";
#endif

  return missing;
}

void ToonRenderer::ViewerResources::resize( int _newWidth, int _newHeight ) {
  if (!_newHeight || !_newWidth) return;


  if (!scene_)  {
    // scene fbo with 2 color attachments + depth buffer
    //  attachment0: scene color
    //  attachment1: scene depth
    scene_ = new ACG::FBO();
    scene_->init();
    scene_->attachTexture2D(GL_COLOR_ATTACHMENT0, _newWidth, _newHeight, GL_RGBA, GL_RGBA);
    scene_->attachTexture2D(GL_COLOR_ATTACHMENT1, _newWidth, _newHeight, GL_R32F, GL_RED);
    scene_->attachTexture2DDepth(_newWidth, _newHeight);
  }

  if (scene_->height() == _newHeight &&
    scene_->width()  == _newWidth)
    return;

  scene_->resize(_newWidth, _newHeight);
}



#if QT_VERSION < 0x050000
  Q_EXPORT_PLUGIN2( toonrenderer , ToonRenderer );
#endif