Commit f55e849d authored by Christopher Tenter's avatar Christopher Tenter

- IRenderer: renderobjects can request a depth map of the scene, which is...

- IRenderer: renderobjects can request a depth map of the scene, which is optionally rendered in a z-prepass
- GLMatrix: extraction of clipping planes in a projection matrix

_ ShaderGen: control glsl version in shader descriptor
- GLSL::Shader: add suppport for tessellation shaders


git-svn-id: http://www.openflipper.org/svnrepo/OpenFlipper/branches/Free@18555 383ad7c9-94d9-4d36-a494-682f7c89f535
parent 3efa974b
......@@ -1968,13 +1968,17 @@ void GLState::drawBuffers(GLsizei _n, const GLenum* _bufs)
/// get current framebuffer of a target
GLuint GLState::getFramebufferDraw()
{
return stateStack_.back().framebuffers_[0];
GLint curfbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint*)&curfbo);
return curfbo;
}
/// get current framebuffer of a target
GLuint GLState::getFramebufferRead()
{
return stateStack_.back().framebuffers_[1];
GLint curfbo;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, (GLint*)&curfbo);
return curfbo;
}
void GLState::bindFramebuffer(GLenum _target, GLuint _framebuffer)
......
......@@ -60,13 +60,14 @@
#include <ACG/ShaderUtils/GLSLShader.hh>
#include <ACG/GL/ShaderCache.hh>
#include <ACG/GL/ScreenQuad.hh>
#include <ACG/GL/FBO.hh>
namespace ACG
{
IRenderer::IRenderer()
: numLights_(0), renderObjects_(0), prevFbo_(0), prevFboSaved_(false), depthCopyShader_(0)
: numLights_(0), renderObjects_(0), depthMapUsed_(false), curViewerID_(0), prevFbo_(0), prevFboSaved_(false), depthCopyShader_(0)
{
prevViewport_[0] = 0;
prevViewport_[1] = 0;
......@@ -81,6 +82,10 @@ IRenderer::IRenderer()
IRenderer::~IRenderer()
{
delete depthCopyShader_;
// free depth map fbos
for (std::map<int, ACG::FBO*>::iterator it = depthMaps_.begin(); it != depthMaps_.end(); ++it)
delete it->second;
}
void IRenderer::addRenderObject(ACG::RenderObject* _renderObject)
......@@ -224,7 +229,6 @@ void IRenderer::prepareRenderingPipeline(ACG::GLState* _glState, ACG::SceneGraph
if (renderObjects_.empty())
return;
// ==========================================================
// Sort renderable objects based on their priority
// ==========================================================
......@@ -267,6 +271,20 @@ void IRenderer::prepareRenderingPipeline(ACG::GLState* _glState, ACG::SceneGraph
GLfloat lightModelAmbient[4];
glGetFloatv(GL_LIGHT_MODEL_AMBIENT, lightModelAmbient);
globalLightModelAmbient_ = ACG::Vec3f(lightModelAmbient[0], lightModelAmbient[1], lightModelAmbient[2]);
// search list of render object for objects requiring the scene depth map
bool requiresSceneDepths = false;
for (size_t i = 0; i < numRenderObjects; ++i)
{
if (sortedObjects_[i]->depthMapUniformName)
requiresSceneDepths = true;
}
// render scene depth map
if (requiresSceneDepths)
renderDepthMap(curViewerID_, _glState->viewport_width(), _glState->viewport_height());
}
......@@ -283,7 +301,7 @@ void IRenderer::finishRenderingPipeline()
// Renderer check:
// Print a warning if the currently active fbo / viewport is not the same as the saved fbo.
// Restore previously to previous fbo and viewport if not done already.
// Restore to previous fbo and viewport if not done already.
GLint curFbo;
GLint curViewport[4];
......@@ -414,7 +432,13 @@ void IRenderer::bindObjectUniforms( ACG::RenderObject* _obj, GLSL::Program* _pro
if ( !_obj->uniformPool_.empty() )
_obj->uniformPool_.bind(_prog);
// texture
// textures:
// maybe consider using bindless textures some time in the future
// to get rid of the old and problematic glActiveTexture(), glBindTexture() state machine usage:
// https://www.opengl.org/registry/specs/ARB/bindless_texture.txt
int maxTextureStage = 0;
for (std::map<size_t,RenderObject::Texture>::const_iterator iter = _obj->textures().begin();
iter != _obj->textures().end();++iter)
{
......@@ -427,6 +451,20 @@ void IRenderer::bindObjectUniforms( ACG::RenderObject* _obj, GLSL::Program* _pro
glActiveTexture(GL_TEXTURE0 + (GLenum)texture_stage);
glBindTexture(iter->second.type, tex.id);
_prog->setUniform(QString("g_Texture%1").arg(texture_stage).toStdString().c_str(), (int)texture_stage);
maxTextureStage = std::max(maxTextureStage, (int)texture_stage);
}
// scene depth texture
if (_obj->depthMapUniformName)
{
// bind to (hopefully) unused texture stage
int depthMapSlot = maxTextureStage + 1;
_prog->setUniform(_obj->depthMapUniformName, depthMapSlot);
glActiveTexture(GL_TEXTURE0 + depthMapSlot);
glBindTexture(GL_TEXTURE_2D, depthMaps_[curViewerID_]->getAttachment(GL_COLOR_ATTACHMENT0));
}
......@@ -797,5 +835,110 @@ void IRenderer::copyDepthToBackBuffer( GLuint _depthTex, float _scale /*= 1.0f*/
}
// depth map computation with a modifier
IRenderer::DepthMapPass IRenderer::DepthMapPass::instance;
void IRenderer::DepthMapPass::modifyFragmentEndCode(QStringList* _code)
{
_code->push_back("outFragment = gl_FragCoord.zzzz;");
}
void IRenderer::renderDepthMap(int _viewerID, int _width, int _height)
{
ACG::FBO* fbo = depthMaps_[_viewerID];
// setup fbo for depth map
if (!fbo)
{
fbo = depthMaps_[_viewerID] = new ACG::FBO();
fbo->bind();
fbo->attachTexture2D(GL_COLOR_ATTACHMENT0, _width, _height, GL_R32F, GL_RED);
fbo->addDepthBuffer(_width, _height);
}
else
{
fbo->bind();
fbo->resize(_width, _height);
}
// make sure modifier is registered
ACG::ShaderProgGenerator::registerModifier(&DepthMapPass::instance);
/* note:
It is possible to directly read the depth buffer if it was attached as a texture.
Then, we would disable the color write mask and just write to the depth buffer in this function.
However, doing this has shown that the actual depth values written to the depth buffer highly depend on the gpu driver.
Or, at least sampling from a texture with format GL_DEPTH_COMPONENT returns driver dependent values.
For instance, in the amd-gl driver sampling the depth texture returns the value of gl_FragDepth (as expected).
But in the nvidia driver, sampling directly from the depth buffer returns something different!
Therefore we render the value of gl_FragDepth to a custom floating-point texture bound to a color slot in order to get driver independent behavior.
*/
fbo->bind();
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glViewport(0, 0, _width, _height);
// set depth to max
glColorMask(1,1,1,1);
glDepthMask(1);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// render z-prepass
for (int i = 0; i < getNumRenderObjects(); ++i)
{
RenderObject* obj = getRenderObject(i);
if (obj->inZPrePass)
{
// apply depth map modifier to get the depth pass shader
GLSL::Program* depthPassShader = ShaderCache::getInstance()->getProgram(&obj->shaderDesc, DepthMapPass::instance);
// temporarily prevent read/write access to the same texture (the depth map)
const char* depthMapUniformName = obj->depthMapUniformName;
obj->depthMapUniformName = 0;
if (depthMapUniformName)
{
depthPassShader->use();
depthPassShader->setUniform(depthMapUniformName, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
}
// we are interested in the depth value only, so temporarily modify the write mask to allow writing to the red channel
// (for instance, an object might not write a color value, but a depth value)
const GLboolean redWriteMask = obj->colorWriteMask[0];
obj->colorWriteMask[0] = obj->depthWrite;
renderObject(obj, depthPassShader);
// reset modified render object states
obj->depthMapUniformName = depthMapUniformName;
obj->colorWriteMask[0]= redWriteMask;
}
}
// restore input fbo state
fbo->unbind();
restoreInputFbo();
}
void IRenderer::setViewerID(int _viewerID)
{
curViewerID_ = _viewerID;
}
} // namespace ACG end
......@@ -63,6 +63,7 @@ namespace ACG
// forward declaration
class VertexDeclaration;
class GLState;
class FBO;
namespace SceneGraph {
namespace DrawModes {
......@@ -133,6 +134,11 @@ protected:
* - collecting renderobjects ( collectRenderObjects() )
* - sorting renderobjects ( sortRenderObjects() )
* - resetting OpenGL state machine for shader-based rendering
*
* @param _glState pointer to glstate
* @param _drawMode default drawmode
* @param _scenegraphRoot root node of scenegraph
* @param _viewerID viewport id (ie. ViewerProperties::viewerId())
*/
virtual void prepareRenderingPipeline(ACG::GLState* _glState, ACG::SceneGraph::DrawModes::DrawMode _drawMode, ACG::SceneGraph::BaseNode* _scenegraphRoot);
......@@ -232,6 +238,7 @@ protected:
*/
virtual void drawObject(ACG::RenderObject* _obj);
//=========================================================================
// Restore OpenGL State
//=========================================================================
......@@ -295,6 +302,36 @@ protected:
*/
virtual void copyDepthToBackBuffer(GLuint _depthTex, float _scale = 1.0f);
/** \brief Render the depth map of the scene.
*
* When setting up a shader of a render-object, a scenegraph node can use the depth map for custom rendering techniques.
* If at least one render object calls this function, the depth map is later rendered before the scene pass.
* and bound to the shader of an renderobject in bindObjectUniforms according to the provided "depthMapUniformName".
* Depth Map format: GL_TEXTURE_2D, GL_R32F, depth value = gl_FragCoord.z
* This function is automatically called in prepareRenderingPipeline() when required.
*
* @param _viewerID id of viewport,
* @param _width viewport width
* @param _height viewport height
*/
virtual void renderDepthMap(int _viewerID, int _width, int _height);
//=========================================================================
// Internal shader modifiers
//=========================================================================
protected:
// depth map modifier: writes gl_FragCoord.z to red color channel
class DepthMapPass : public ShaderModifier
{
public:
void modifyFragmentEndCode(QStringList* _code);
static DepthMapPass instance;
};
//=========================================================================
// Debugging
......@@ -339,8 +376,23 @@ protected:
/// Get global ambient light contribution from GL_LIGHT_MODEL_AMBIENT
const ACG::Vec3f& getGlobalAmbientScale() const {return globalLightModelAmbient_;}
protected:
//=========================================================================
// Internally called by OpenFlipper core
//=========================================================================
public:
/** \brief Set currently active viewer id
*
* If the scenegraph makes use of the z-prepass feature and the application uses multiple viewports,
* the currently active viewport should be set prior rendering.
* Otherwise, this function can be ignored.
*
* @param _viewerID unique id of the current viewport (i.e. ViewerProperties::viewerID() )
*/
void setViewerID(int _viewerID);
protected:
/// Number of Lights
int numLights_;
......@@ -358,6 +410,18 @@ protected:
/// sorted list of renderobjects (sorted in rendering order)
std::vector<ACG::RenderObject*> sortedObjects_;
/**
* Stores fbo containing a depth map for each viewport.
* The depth map is computed in a z-prepass if at least one RenderObject makes use of the scene depth map.
* (optional convenience feature)
*/
std::map<int, ACG::FBO*> depthMaps_;
/// true if at least one renderobject requires a scene depthmap, false otherwise
bool depthMapUsed_;
/// currently active viewer ID as specified in prepareRenderObjects()
int curViewerID_;
/// previous fbo
GLint prevFbo_;
......
......@@ -7,6 +7,8 @@
#include <iostream>
#include <sstream>
#include <algorithm>
//#include <unordered_map> // requires c++0x
#include <QHash> // alternative to unordered_map
#include <ACG/Geometry/GPUCacheOptimizer.hh>
......@@ -2822,6 +2824,84 @@ void MeshCompiler::getVertexBuffer( void* _dst, const int _offset /*= 0*/, const
}
}
struct MeshCompiler_EdgeTriMapKey
{
int e0, e1;
MeshCompiler_EdgeTriMapKey(int a, int b)
: e0(std::min(a,b)), e1(std::max(a,b)) {}
bool operator ==(const MeshCompiler_EdgeTriMapKey& k) const
{
return e0 == k.e0 && e1 == k.e1;
}
};
/* // requires c++0x
struct MeshCompiler_EdgeTriMapKey_Hash
{
std::size_t operator()(const MeshCompiler_EdgeTriMapKey& k) const
{
return ((std::hash<int>()(k.e0) << 1) ^ std::hash<int>()(k.e1)) >> 1;
}
};
*/
uint MeshCompiler_EdgeTriMapKey_Hash(const MeshCompiler_EdgeTriMapKey& k)
{
return ((qHash(k.e0) << 1) ^ qHash(k.e1)) >> 1;
}
void MeshCompiler::getIndexAdjBuffer(void* _dst, const int _borderIndex /* = -1 */)
{
return; // not implemented with QHash;
/*
int* idst = (int*)_dst;
for (int i = 0; i < getNumTriangles(); ++i)
{
// initialize all triangles
idst[i*6] = getIndex(i*3);
idst[i*6+2] = getIndex(i*3+1);
idst[i*6+4] = getIndex(i*3+2);
idst[i*6+1] = _borderIndex;
idst[i*6+3] = _borderIndex;
idst[i*6+5] = _borderIndex;
}
// adjacency mapping: edge -> triangle
// edge e0-e1 key: (min(e0,e1), max(e0,e1))
std::unordered_map<MeshCompiler_EdgeTriMapKey, int, MeshCompiler_EdgeTriMapKey_Hash> edgeTriAdjMap;
// count number of edges
for (int i = 0; i < getNumTriangles(); ++i)
{
int* tri = idst + i*6;
// edges: 01, 12, 20
for (int e = 0; e < 3; ++e)
{
MeshCompiler_EdgeTriMapKey edge( getIndex(i*3 + e), getIndex(i*3 + (e%3)) );
std::unordered_map<MeshCompiler_EdgeTriMapKey, int, MeshCompiler_EdgeTriMapKey_Hash>::iterator it = edgeTriAdjMap.find(edge);
if (it != edgeTriAdjMap.end())
{
// found pair of adjacent triangles for edges:
// this triangle: i
// adjacent triangle: it->second
tri[2*e + 1] = it->second;
}
else
edgeTriAdjMap[edge] = i; // adjacent triangle not found -> insert reference to current tri
}
}
*/
}
int MeshCompiler::mapToOriginalFaceID( const int _i ) const
{
// int faceID = _i;
......
......@@ -488,6 +488,35 @@ public:
*/
int* getIndexBuffer() const {return indices_;}
/** \brief Get index buffer with adjacency information ready for rendering.
*
* This index buffer can be used to render in GL_TRIANGLES_ADJACENCY mode.
* It contains the opposite vertex id of adjacent triangles for each edge of the triangle.
* Each triangle requires 6 indices:
* 0 - first vertex of triangle
* 1 - opposite vertex to first edge of adjacent triangle
* 2 - second vertex of triangle
* 3 - opposite vertex to second edge of adjacent triangle
* 4 - third vertex of triangle
* 5 - opposite vertex to third edge of adjacent triangle
*
* 5___4___3
* \ /\ /
* \/__\/
* 0\ /2
* \/
* 1
*
*
* TODO:
* - check correctness of internal adjacency computation
* - add option to let user provide edge-triangle adjaceny list (should be fast and easy with openmesh)
*
* @param _dst [out] Pointer to memory address where the 32bit index buffer should be copied to. Must have size of at least 6 * numTris * 4 bytes
* @param _borderIndex index to use for border edges (missing adjacent triangles)
*/
void getIndexAdjBuffer(void* _dst, const int _borderIndex = -1);
/** Get number of triangles in final buffer.
*/
int getNumTriangles() const {return numTris_;}
......
......@@ -187,6 +187,9 @@ RenderObject::RenderObject()
specular(0.0f, 0.0f, 0.0f), emissive(0.05f, 0.05f, 0.05f),
alpha(1.0f), shininess(100.0f),
inZPrePass(true),
depthMapUniformName(0),
debugID(0), debugName(0),
internalFlags_(0)
{
......@@ -305,6 +308,11 @@ QString RenderObject::toString() const
resultStrm << "\nshininess: " << shininess;
resultStrm << "\nalpha: " << alpha;
resultStrm << "\ninZPrePass: " << inZPrePass;
resultStrm << "\ndepthMapUniformName: " << depthMapUniformName;
resultStrm << "\ninternalFlags: " << internalFlags_;
if (vertexDecl)
......
......@@ -113,7 +113,7 @@ struct ACGDLLEXPORT RenderObject
/** \brief Priority to allow sorting of objects
*
* The renderer sorts objects based on priority from high to low before rendering.
* The renderer sorts objects based on priority in ascending order before rendering.
*
* \note negative values allowed
*/
......@@ -224,6 +224,28 @@ struct ACGDLLEXPORT RenderObject
shininess;
/** \brief Specify whether this object should be rendered in a z-prepass
*
* The renderer might do a z-prepass for some rendering techniques.
* You can control whether an object should be taken into account for a z-prepass or not.
* For instance, scene objects should be rendered in the z-prepass, but overlays (coordsys, selection stuff..) should not.
*
* default: true
*/
bool inZPrePass;
/** \brief Uniform name of the depth map in the used shader
*
* If a shader used by this object requires a depth map of the scene, you can specify the name of the texture sampler uniform used here.
* This depth map is automatically computed in a z-prepass of the scene later in the renderer and assigned to the shader.
* It will be a 2D texture storing the values of the depth buffer (gl_FragCoord.z) in GL_TEXTURE_2D, GL_R32F format.
* Should be set to 0 if the used shader does not require a depth map.
*
* default: 0
*/
const char* depthMapUniformName;
/** \brief Texture to be used
*
* eventually a more flexible texture system with user defined:
......
......@@ -65,7 +65,8 @@ ShaderModifier* ShaderProgGenerator::modifiers_[32] = {0};
ShaderGenerator::ShaderGenerator()
{
version_ = "#version 150";
// version_ = "#version 150";
version_ = 150;
}
ShaderGenerator::~ShaderGenerator()
......@@ -343,7 +344,10 @@ void ShaderGenerator::addIOToCode(const QStringList& _cmds)
void ShaderGenerator::buildShaderCode(QStringList* _pMainCode)
{
code_.push_back(version_);
QString glslversion;
glslversion.sprintf("#version %d", version_);
code_.push_back(glslversion);
// provide defines
QString it;
......@@ -422,6 +426,42 @@ const QStringList& ShaderGenerator::getShaderCode()
return code_;
}
void ShaderGenerator::setGLSLVersion( int _version )
{
version_ = _version;
}
void ShaderGenerator::matchInputs(ShaderGenerator& _previousShaderStage,
bool _passToNextStage,
QString _inputPrefix,
QString _outputPrefix)
{
QString it;
foreach(it, _previousShaderStage.outputs_)
{
QString input = it;
QString outKeyword = "out ";
QString inKeyword = "in ";
// replace first occurrence of "out" with "in"
input.replace(input.indexOf(outKeyword), outKeyword.size(), inKeyword);
// add to input list with duplicate check
addStringToList(input, &inputs_);
if (_passToNextStage)
{
// replace prefixes of in/outputs to avoid name collision
QString output = input;
output.replace(output.indexOf(_inputPrefix), _inputPrefix.size(), _outputPrefix);
// add to output list with duplicate check
addStringToList(output, &outputs_);
}
}
}
......@@ -552,7 +592,7 @@ void ShaderProgGenerator::buildVertexShader()
delete vertex_;
vertex_ = new ShaderGenerator();
vertex_->setGLSLVersion(desc_.version);
// vertex_->initDefaultVertexShaderIO();
vertex_->initVertexShaderIO(&desc_);
......@@ -743,6 +783,7 @@ void ShaderProgGenerator::buildGeometryShader()
delete geometry_;
geometry_ = new ShaderGenerator();
geometry_->setGLSLVersion(desc_.version);
geometry_->initGeometryShaderIO(&desc_);
......@@ -782,6 +823,8 @@ void ShaderProgGenerator::buildGeometryShader()
mainCode.push_back("outGeometryPosVS = outVertexPosVS[inIdx];");
}
mainCode.push_back("gl_PrimitiveID = gl_PrimitiveIDIn;");
mainCode.push_back("}");
}
......@@ -807,6 +850,7 @@ void ShaderProgGenerator::buildFragmentShader()
delete fragment_;
fragment_ = new ShaderGenerator();
fragment_->setGLSLVersion(desc_.version);
fragment_->initFragmentShaderIO(&desc_);
......
......@@ -83,6 +83,7 @@ class ShaderGenDesc
public:
ShaderGenDesc() :
version(150),
numLights(0),
shadeMode(SG_SHADE_UNLIT),
vertexColors(false),
......@@ -99,21 +100,30 @@ public:
//In case, something crashes with the light types, try this hammer ;-)
// const ShaderGenDesc& operator= (const ShaderGenDesc& _rhs) {
//
//
// version = _rhs.version;
//
// numLights = _rhs.numLights;
//
//
// std::copy(_rhs.lightTypes,_rhs.lightTypes+SG_MAX_SHADER_LIGHTS,lightTypes);
// textureTypes_ = _rhs.textureTypes_;
//
//
// shadeMode = _rhs.shadeMode;
// vertexColors = _rhs.vertexColors;
// vertexTemplateFile = _rhs.vertexTemplateFile;
// geometryTemplateFile = _rhs.geometryTemplateFile;
// fragmentTemplateFile = _rhs.fragmentTemplateFile;
//
//
// textureTypes_ = _rhs.textureTypes_;
//
// return *this;
// }
// glsl version, i.e. 130, 150, 330 etc.
// Versions before 130 are not supported (deprecated "varying", "ftransform".. stuff)