Commit 45def237 authored by Jan Möbius's avatar Jan Möbius
Browse files

Added toon shader

git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@18213 383ad7c9-94d9-4d36-a494-682f7c89f535
parent cb6e0d82
include (plugin)
openflipper_plugin ( INSTALLDATA Shaders )
// cel shading functions:
// downgrade precision of diffuse and specular lighting to given palette size
#ifndef SHININESS
#define SHININESS g_vMaterial.x
#endif
#ifndef TRANSPARENCY
#define TRANSPARENCY g_vMaterial.y
#endif
#ifndef ALPHA
#define ALPHA g_vMaterial.y
#endif
// downgrade precision of an intensity in [0, 1] range to given paletteSize
float QuantizeDiffuse_Cel(float intensity, float paletteSize)
{
// linear quantification
float x = intensity * paletteSize;
float linQ = trunc(x) / paletteSize;
// return linQ;
// quadratic
// return linQ * linQ;
// exponential
return pow(linQ, 1.4);
return sqrt(linQ);
}
// quantifier for specular lighting intensity
float QuantizeSpecular_Cel(float intensity, float paletteSize)
{
// linear quantification
float x = intensity * paletteSize;
float linQ = trunc(x) / paletteSize;
return linQ * linQ;
}
vec3 LitPointLight_Cel(vec3 vPosition,
vec3 vNormal,
vec3 vLightPos,
vec3 cLightAmbient,
vec3 cLightDiffuse,
vec3 cLightSpecular,
vec3 vLightAtten,
float paletteSize)
{
vec3 vLightVS = vPosition - vLightPos;
float fLen2 = dot(vLightVS, vLightVS);
float fLen = sqrt(fLen2);
vec3 vLightDir = vLightVS / fLen;
// ambient
vec3 cLight = cLightAmbient * g_cAmbient;
// diffuse
float ldotn = clamp(dot(vLightDir, vNormal), 0.0, 1.0);
// quantification of lighting intensity
ldotn = QuantizeDiffuse_Cel(ldotn, paletteSize);
cLight += ldotn * cLightDiffuse * g_cDiffuse;
// specular
vec3 h = normalize(vLightDir - normalize(vPosition)); // half vector between light and view direction
float hdotn = max(dot(h, vNormal), 0.0);
// hdotn = QuantizeSpecular_Cel(hdotn, paletteSize);
cLight += QuantizeSpecular_Cel(pow(hdotn, SHININESS), paletteSize) * cLightSpecular * g_cSpecular;
// attenuate
float fAtten = vLightAtten.x + vLightAtten.y * fLen + vLightAtten.z * fLen2;
return cLight / fAtten;
}
vec3 LitDirLight_Cel(vec3 vPosition,
vec3 vNormal,
vec3 vLightDir,
vec3 cLightAmbient,
vec3 cLightDiffuse,
vec3 cLightSpecular,
float paletteSize)
{
// ambient
vec3 cLight = cLightAmbient * g_cAmbient;
// diffuse
float ldotn = clamp(dot(vLightDir, vNormal), 0.0, 1.0);
ldotn = QuantizeDiffuse_Cel(ldotn, paletteSize);
cLight += ldotn * cLightDiffuse * g_cDiffuse;
// specular
vec3 h = normalize(vLightDir - normalize(vPosition)); // half vector between light and view direction
float hdotn = max(dot(h, vNormal), 0.0);
// hdotn = QuantizeSpecular_Cel(hdotn, paletteSize);
cLight += QuantizeSpecular_Cel(pow(hdotn, SHININESS), paletteSize) * cLightSpecular * g_cSpecular;
return cLight;
}
vec3 LitSpotLight_Cel(vec3 vPosition,
vec3 vNormal,
vec3 vLightPos,
vec3 vSpotLightDir,
vec3 cLightAmbient,
vec3 cLightDiffuse,
vec3 cLightSpecular,
vec3 vLightAtten,
vec2 vLightSpotAngleExp,
float paletteSize)
{
vec3 vLightVS = vPosition - vLightPos;
float fLen2 = dot(vLightVS, vLightVS);
float fLen = sqrt(fLen2);
vec3 vLightDir = vLightVS / fLen;
// ambient
vec3 cLight = cLightAmbient * g_cAmbient;
// diffuse
float ldotn = clamp(dot(vLightDir, vNormal), 0.0, 1.0);
ldotn = QuantizeDiffuse_Cel(ldotn, paletteSize);
cLight += ldotn * cLightDiffuse * g_cDiffuse;
// specular
vec3 h = normalize(vLightDir - normalize(vPosition)); // half vector between light and view direction
float hdotn = max(dot(h, vNormal), 0.0);
// hdotn = QuantizeSpecular_Cel(hdotn, paletteSize);
cLight += QuantizeSpecular_Cel(pow(hdotn, SHININESS), paletteSize) * cLightSpecular * g_cSpecular;
// attenuate
float fAtten = vLightAtten.x + vLightAtten.y * fLen + vLightAtten.z * fLen2;
// spot angle falloff
float fSpot = -dot(vLightDir, vSpotLightDir);
fSpot *= step(vLightSpotAngleExp.x, fSpot);
fSpot *= pow(fSpot, vLightSpotAngleExp.y);
return cLight * (fSpot / fAtten);
}
#version 150
uniform sampler2D samplerScene;
uniform sampler2D samplerDepth;
uniform vec2 clipPlanes;
in vec2 vTexCoord;
out vec4 oColor;
// The inverse of the texture dimensions along X and Y
uniform vec2 texcoordOffset;
void main()
{
// detect edges in depth buffer with sobel filter
vec2 coord_tl = vec2( vTexCoord.x + texcoordOffset.x , vTexCoord.y - texcoordOffset.y );
vec2 coord_t = vec2( vTexCoord.x + texcoordOffset.x , vTexCoord.y );
vec2 coord_tr = vec2( vTexCoord.x + texcoordOffset.x , vTexCoord.y + texcoordOffset.y );
vec2 coord_cl = vec2( vTexCoord.x,vTexCoord.y-texcoordOffset.y );
vec2 coord_cr = vec2( vTexCoord.x,vTexCoord.y+texcoordOffset.y );
vec2 coord_bl = vec2( vTexCoord.x-texcoordOffset.x,vTexCoord.y-texcoordOffset.y );
vec2 coord_b = vec2( vTexCoord.x-texcoordOffset.x,vTexCoord.y );
vec2 coord_br = vec2( vTexCoord.x-texcoordOffset.x,vTexCoord.y+texcoordOffset.y );
float depth_horizontal = abs(texture2D(samplerDepth, coord_tl).x - texture2D(samplerDepth, coord_tr).x);
depth_horizontal += abs(2.0 * texture2D(samplerDepth, coord_cl).x - 2.0 * texture2D(samplerDepth, coord_cr).x);
depth_horizontal += abs(texture2D(samplerDepth, coord_bl).x - texture2D(samplerDepth, coord_br).x);
float depth_vertical = abs(texture2D(samplerDepth, coord_tl).x - texture2D(samplerDepth, coord_bl).x);
depth_vertical += abs(2.0 * texture2D(samplerDepth, coord_t).x - 2.0 * texture2D(samplerDepth, coord_b).x);
depth_vertical += abs(texture2D(samplerDepth, coord_tr).x - texture2D(samplerDepth, coord_br).x);
// compute edge factor
float edge = (depth_horizontal + depth_vertical); // / clipPlanes.y;
float edge_factor = min(edge, 1.0);
edge_factor = 1.0 - step(0.1, edge_factor * edge_factor);
// outline edges
vec4 color_scene = texture2D(samplerScene, vTexCoord);
oColor = vec4(color_scene.xyz * edge_factor, color_scene.w);
// oColor = vec4(edge_factor, edge_factor, edge_factor, 1.0);
// oColor = color_scene;
// oColor = edge;
// vec4 fragD = texture2D(samplerDepth, vTexCoord);
// fragD.x *= fragD.x;
// oColor = -fragD.xxxx;
// write depth in case any future post-processors require scene depth information
gl_FragDepth = texture2D(samplerDepth, vTexCoord).x;
}
#version 150
in vec4 inPosition;
out vec2 vTexCoord;
void main()
{
gl_Position = inPosition;
vTexCoord = inPosition.xy * 0.5 + vec2(0.5, 0.5);
}
\ No newline at end of file
/*===========================================================================*\
* *
* 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);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
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);
glDisable(GL_DEPTH_TEST);
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();
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
/*===========================================================================*\
* *
* 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, *