Hi David,
> - Have you create an opengl context with a stencil buffer ?
> - Have you try to use osg:ClearNode to clear the stencil buffer ?
> - Have you check opengl call order in a debugger like gDEBugger ?
- Yes I the context that osgViewer creates when realizing has a working stencil
(I tested it).
- No, I didn't know that one was allowed to put nodes such as osg::ClearNode
inbetween osgPPU::Unit's
- No, but I was planning to use GLintercept as a last resort
> yes please, could be more simple to find the bug.
Sure!, here it is:
This function generates the whole pipeline and attaches its processor to an
osg::Group which is returned
Code:
// This is a dirty workaround for not using an osg::ClearNode
struct CamClearMaskCallback : public osgPPU::Unit::NotifyCallback
{
osg::Camera * m_cam;
GLbitfield m_mask;
CamClearMaskCallback(osg::Camera *cam, GLbitfield mask) :
osgPPU::Unit::NotifyCallback(), m_cam(cam), m_mask(mask) {}
virtual void operator()(osg::RenderInfo& ri, const osgPPU::Unit* u)
const
{
m_cam->setClearMask(m_mask);
if(m_mask & GL_STENCIL_BUFFER_BIT)
{
glClear(GL_STENCIL_BUFFER_BIT);
// printf("CLEAR\n");
} else {
// printf("\n");
}
}
};
osg::Group* MLAARendering::createMLAAPipeline(osg::Camera* camera)
{
osg::Group* finalGroup = new osg::Group;
osg::ref_ptr<CamClearMaskCallback> clearStencilCallback = new
CamClearMaskCallback(camera, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
osg::ref_ptr<CamClearMaskCallback> dontClearStencilCallback = new
CamClearMaskCallback(camera, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
osg::ref_ptr<osgDB::ReaderWriter::Options> fragmentOptions = new
osgDB::ReaderWriter::Options("fragment");
osg::ref_ptr<osgDB::ReaderWriter::Options> vertexOptions = new
osgDB::ReaderWriter::Options("vertex");
// Setup the clear mask and the stencil clear value
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
camera->setClearStencil(0);
osg::Vec2f pixelSize(1.0/camera->getGraphicsContext()->getTraits()->width,
1.0/camera->getGraphicsContext()->getTraits()->height);
// This stencil is intended to create the mask by writting a 1 to the
stencil wherever a fragment is not discarded in the shader of the unit it's
// attached to. However, the stencil test has to pass always and osgPPU
disables the Z test, so I don't know if this is really working as I want!
osg::Stencil * createMaskStencil = new osg::Stencil;
{
createMaskStencil->setFunctionRef(1);
createMaskStencil->setFunction(osg::Stencil::ALWAYS);
createMaskStencil->setWriteMask(1);
createMaskStencil->setOperation(osg::Stencil::REPLACE,
osg::Stencil::REPLACE, osg::Stencil::REPLACE);
}
// This one is intended to discard every fragment not masked by a 1 in
the stencil
osg::Stencil * useMaskStencil = new osg::Stencil;
{
useMaskStencil->setFunctionRef(1);
useMaskStencil->setFunction(osg::Stencil::EQUAL); // TODO: TEMP!!,
Should be osg::Stencil::EQUAL!
useMaskStencil->setWriteMask(1);
useMaskStencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP,
osg::Stencil::KEEP);
}
// This is for testing purposes (it always passes and doesn't change
the buffer)
osg::Stencil * testStencil = new osg::Stencil;
{
testStencil->setFunction(osg::Stencil::ALWAYS,1,~0u);
testStencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP,
osg::Stencil::KEEP);
}
// Create a processor for this pipeline
m_processor = new osgPPU::Processor();
m_processor->setName("Processor");
m_processor->setCamera(camera);
osgPPU::UnitCameraAttachmentBypass* bypass = new
osgPPU::UnitCameraAttachmentBypass();
{
bypass->setBufferComponent(osg::Camera::COLOR_BUFFER0);
bypass->setName("MLAA.mainCamOutputTex");
// I want the stencil to be cleared here, I don't really care
if it's done before or after 'drawing'
bypass->setBeginDrawCallback(clearStencilCallback.get());
}
osgPPU::UnitCameraAttachmentBypass* bypassDepth = new
osgPPU::UnitCameraAttachmentBypass();
{
bypassDepth->setBufferComponent(osg::Camera::DEPTH_BUFFER);
bypassDepth->setName("MLAA.mainCamOutputDepthTex");
}
// First step: Edges detection and stencil mask creation
edgeDetection = new osgPPU::UnitInOut();
edgeDetection->setName("MLAA.edgeDetectionUnit");
{
edgeDepthShader = new osgPPU::ShaderAttribute();
edgeDepthShader->addShader(osgDB::readShaderFile("Shaders\\edge_depth_frag.frag",
fragmentOptions.get()));
edgeDepthShader->addShader(osgDB::readShaderFile("Shaders\\offset_vert.vert",
vertexOptions.get()));
edgeDepthShader->setName("EdgeDetectionShader");
edgeDepthShader->add("threshold", osg::Uniform::FLOAT);
edgeDepthShader->set("threshold", edgeThreshold);
edgeDepthShader->add("pixelSize", osg::Uniform::FLOAT_VEC2);
edgeDepthShader->set("pixelSize", pixelSize);
edgeDepthShader->add("bias", osg::Uniform::FLOAT);
edgeDepthShader->set("bias", edgeThreshold);
edgeDepthShader->add("scale", osg::Uniform::FLOAT);
edgeDepthShader->set("scale", 0.1f);
edgeDetection->setInputToUniform(bypass, "colorMapG", true);
edgeDetection->setInputToUniform(bypassDepth, "depthMap", true);
edgeDetection->getOrCreateStateSet()->setAttributeAndModes(edgeDepthShader);
// Attach the 'createMask' stencil and enable the
GL_STENCIL_TEST
edgeDetection->getOrCreateStateSet()->setAttributeAndModes(createMaskStencil,
osg::StateAttribute::ON);
edgeDetection->getOrCreateStateSet()->setMode(GL_STENCIL_TEST,
osg::StateAttribute::ON);
edgeDetection->setBeginDrawCallback(dontClearStencilCallback.get());
}
// This unit just loads up a texture which is needed by the next shader
osgPPU::UnitTexture* areaTexture = new osgPPU::UnitTexture();
areaTexture->setName("MLAA.areaTextureUnit");
{
osg::Image *areaImg = osgDB::readImageFile("Textures\\AreaMap65.dds");
// An extremely dirty error checking
if(!areaImg)
{
osg::notify(osg::FATAL) << "> Can't load the area map, aborting
...\n";
exit(-1);
}
osg::Texture2D *areaTex = new osg::Texture2D;
areaTex->setResizeNonPowerOfTwoHint(false);
areaTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
areaTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
areaTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
areaTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
areaTex->setImage(areaImg);
areaTex->setNumMipmapLevels(1);
areaTexture->setTexture(areaTex);
}
// Second step: Weights calculation only in the fragments masked by the
stencil (whose values are == 1)
weightsCalculation = new osgPPU::UnitInOut();
weightsCalculation->setName("MLAA.weightsCalculationUnit");
{
weightsShader = new osgPPU::ShaderAttribute();
weightsShader->addShader(osgDB::readShaderFile("Shaders\\weights_frag.frag",
fragmentOptions.get()));
weightsShader->addShader(osgDB::readShaderFile("Shaders\\offset_vert.vert",
vertexOptions.get()));
weightsShader->setName("WeightsCalculationShader");
weightsCalculation->setInputToUniform(areaTexture, "areaMap", true);
weightsCalculation->setInputToUniform(edgeDetection, "edgesMapL", true);
weightsShader->add("maxSearchSteps", osg::Uniform::INT);
weightsShader->set("maxSearchSteps", 12);
weightsShader->add("pixelSize", osg::Uniform::FLOAT_VEC2);
weightsShader->set("pixelSize", pixelSize);
weightsCalculation->getOrCreateStateSet()->setAttributeAndModes(weightsShader);
weightsCalculation->setInputTextureIndexForViewportReference(1); //
Choose to have an output the same size as edgesMap has
// Attach the 'usueMask' stencil here
weightsCalculation->getOrCreateStateSet()->setAttributeAndModes(useMaskStencil,
osg::StateAttribute::ON);
weightsCalculation->getOrCreateStateSet()->setMode(GL_STENCIL_TEST,
osg::StateAttribute::ON);
// Just to make sure that the stencil is not being cleared
anymore
weightsCalculation->setBeginDrawCallback(dontClearStencilCallback.get());
}
// Last step: Blend the edge pixels with their 4-neighborhood according
to the blending weights
// (again, only the masked pixels have to be processed)
osgPPU::UnitInOut* pixelBlend = new osgPPU::UnitInOut();
pixelBlend->setName("MLAA.pixelblendingUnit");
{
osgPPU::ShaderAttribute* blendShader = new osgPPU::ShaderAttribute();
blendShader->addShader(osgDB::readShaderFile("Shaders\\blend_frag.frag",
fragmentOptions.get()));
blendShader->addShader(osgDB::readShaderFile("Shaders\\offset_vert.vert",
vertexOptions.get()));
blendShader->setName("BlendShader");
pixelBlend->setInputToUniform(weightsCalculation, "blendMap", true);
blendShader->add("pixelSize", osg::Uniform::FLOAT_VEC2);
blendShader->set("pixelSize", pixelSize);
pixelBlend->setInputToUniform(bypass, "colorMapL", true);
pixelBlend->getOrCreateStateSet()->setAttributeAndModes(blendShader);
// Attach the 'useMask' stencil
pixelBlend->getOrCreateStateSet()->setAttributeAndModes(useMaskStencil,
osg::StateAttribute::ON);
pixelBlend->getOrCreateStateSet()->setMode(GL_STENCIL_TEST,
osg::StateAttribute::ON);
}
// This output unit just acts as a switch for displaying the original RTT,
the final processed image or the textures inbetween (edges and weights)
unitOut= new osgPPU::UnitOut();
shaderAttributeOut= new osgPPU::ShaderAttribute();
{
osg::Shader* shader= new osg::Shader(osg::Shader::FRAGMENT);
const char* shaderSource=
"uniform sampler2D finalMap;\n"
"uniform sampler2D edgesMap;\n"
"uniform sampler2D weightsMap;\n"
"uniform sampler2D colorMap;\n"
"uniform int switchFlag;\n"
"void main()\n"
"{\n"
"if(switchFlag == 0)\n"
" gl_FragColor=texture2D(edgesMap,gl_TexCoord[0].st);\n"
"else if(switchFlag == 1)\n"
" gl_FragColor=texture2D(weightsMap,gl_TexCoord[0].st);\n"
"else if(switchFlag == 2)\n"
" gl_FragColor=texture2D(finalMap,gl_TexCoord[0].st);\n"
"else if(switchFlag == 3)\n"
" gl_FragColor=texture2D(colorMap,gl_TexCoord[0].st);\n"
"}";
shader->setShaderSource(shaderSource);
shaderAttributeOut->addShader(shader);
shaderAttributeOut->setName("nomShaderAttribute");
shaderAttributeOut->add("switchFlag", osg::Uniform::INT);
shaderAttributeOut->set("switchFlag", 2);
unitOut->setInputToUniform(pixelBlend, "finalMap", true);
unitOut->setInputToUniform(edgeDetection, "edgesMap", true);
unitOut->setInputToUniform(weightsCalculation, "weightsMap", true);
unitOut->setInputToUniform(bypass, "colorMap", true);
unitOut->setInputTextureIndexForViewportReference(1); // Choose to
have an output the same size as edgesMap has
unitOut->setName("MLAA.finalOutputUnit");
unitOut->setViewport(new osg::Viewport(0,0,
camera->getGraphicsContext()->getTraits()->width,
camera->getGraphicsContext()->getTraits()->height));
unitOut->getOrCreateStateSet()->setAttributeAndModes(shaderAttributeOut.get());
// It looks like for some reason no stencil testing is performed at
all unless I enable the GL_STENCIL_TEST in this unit
// Let's recall that the testStencil does always pass
unitOut->getOrCreateStateSet()->setAttributeAndModes(testStencil,
osg::StateAttribute::ON);
unitOut->getOrCreateStateSet()->setMode(GL_STENCIL_TEST,
osg::StateAttribute::ON);
}
// Link the units
bypass->addChild(edgeDetection);
bypassDepth->addChild(edgeDetection);
edgeDetection->addChild(areaTexture);
edgeDetection->addChild(weightsCalculation);
areaTexture->addChild(weightsCalculation);
weightsCalculation->addChild(pixelBlend);
pixelBlend->addChild(unitOut);
m_processor->addChild(bypass);
m_processor->addChild(bypassDepth);
finalGroup->addChild(m_processor);
m_processor->dirtyUnitSubgraph();
// Set the edges texture properties
{
osg::Texture2D *outTex =
static_cast<osg::Texture2D*>(edgeDetection->getOrCreateOutputTexture());
if(outTex)
{
outTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
outTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
outTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
outTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
}
}
// Set the weights texture properties
{
osg::Texture2D *outTex =
static_cast<osg::Texture2D*>(weightsCalculation->getOrCreateOutputTexture());
if(outTex)
{
outTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
outTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
outTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
outTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
}
}
isInit = true;
return finalGroup;
}
... the first shader looks like this ...
Code:
/**
* Copyright (C) 2010 Jorge Jimenez ()
* Copyright (C) 2010 Belen Masia ()
* Copyright (C) 2010 Jose I. Echevarria ()
* Copyright (C) 2010 Fernando Navarro ()
* Copyright (C) 2010 Diego Gutierrez ()
* All rights reserved.
*
* Adaptated by Miguel Angel Exposito ()
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the following statement:
*
* "Uses Jimenez's MLAA. Copyright (C) 2010 by Jorge Jimenez, Belen Masia,
* Jose I. Echevarria, Fernando Navarro and Diego Gutierrez."
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are
* those of the authors and should not be interpreted as representing official
* policies, either expressed or implied, of the copyright holders.
*/
#version 120
varying vec4 offset[2];
uniform sampler2D depthMap;
uniform float threshold;
void main()
{
float D = texture2D(depthMap, gl_TexCoord[0].st).r;
float Dleft = texture2D(depthMap, offset[0].xy).r;
float Dtop = texture2D(depthMap, offset[0].zw).r;
float Dright = texture2D(depthMap, offset[1].xy).r;
float Dbottom = texture2D(depthMap, offset[1].zw).r;
vec4 delta = abs(vec4(D) - vec4(Dleft, Dtop, Dright, Dbottom));
vec4 edges = step(vec4(threshold) / 10.0, delta); // Dividing by 10 give us
results similar to the color-based detection.
if (dot(edges, vec4(1.0)) == 0.0)
discard;
gl_FragData[0] = edges;
}
(Note that in order to make it work without the stencil test I have to comment
the discard part in the sader).
... and the main program goes like this ...
Code:
void main(int argc, char **argv)
{
printHelp(); // Output some console text
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
MLAA = new MLAARendering;
unsigned int screenWidth;
unsigned int screenHeight;
unsigned int windowWidth = 1280;
unsigned int windowHeight = 720;
// Init the stencil and disable the default MSAA
osg::DisplaySettings::instance()->setMinimumNumStencilBits(8);
osg::DisplaySettings::instance()->setNumMultiSamples(0);
osg::ref_ptr<KeyboardEventHandler> kbHandler;
// Get the screen resolution
osg::GraphicsContext::getWindowingSystemInterface()->
getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0),screenWidth,
screenHeight);
// Setup the view in a screen centered window
viewer->setUpViewInWindow((screenWidth-windowWidth)/2,
(screenHeight-windowHeight)/2, windowWidth, windowHeight);
// Set single-threading model
viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
// Setup the viewer camera and RTT
osg::Camera* camera = viewer->getCamera();
camera->setViewport(new osg::Viewport(0,0,windowWidth, windowHeight));
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
osg::Texture* textureView = createRenderTextureWrks(windowWidth,
windowHeight);
osg::Texture* textureDepth = createDepthTextureWrks(windowWidth,
windowHeight);
camera->attach(osg::Camera::COLOR_BUFFER0, textureView);
camera->attach(osg::Camera::DEPTH_BUFFER, textureDepth);
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new
osg::GraphicsContext::Traits;
traits->samples = 0; // No multisampling AA
traits->width = windowWidth;
traits->height = windowHeight;
traits->x = (screenWidth-windowWidth)/2;
traits->y = (screenHeight-windowHeight)/2;
traits->doubleBuffer = true;
traits->sharedContext = 0;
traits->stencil = 8;
traits->supportsResize = false;
traits->windowDecoration = true;
traits->windowName = "Jimenez's MLAA for OSG";
osg::ref_ptr<osg::GraphicsContext> gc =
osg::GraphicsContext::createGraphicsContext(traits.get());
camera->setGraphicsContext(gc);
// Load up the scene
osg::ref_ptr<osg::Node> scene;
if(argc > 1)
{
osg::notify(osg::NOTICE) << "> Loading requested model '" << argv[1] <<
"' ...\n";
scene = osgDB::readNodeFile(std::string(argv[1]));
} else {
osg::notify(osg::NOTICE) << "> No model specified, defaulting to
'Models\\cow.osg' ...\n";
scene = osgDB::readNodeFile("Models\\cow.osg");
}
if(!scene)
{
if(argc == 1)
osg::notify(osg::FATAL) << "> Can't load default model
'Models\\cow.osg', aborting...\n";
else
osg::notify(osg::FATAL) << "> Can't load requested model, '" <<
argv[1] << "' aborting...\n";
exit(-1);
}
osg::ref_ptr<osg::Group> group = new osg::Group();
group->setName("Antialiased Scene");
group = MLAA->createMLAAPipeline(camera);
group->addChild(scene);
osg::ref_ptr<osgViewer::StatsHandler> statsHandler = new
osgViewer::StatsHandler();
statsHandler->setKeyEventPrintsOutStats('s'); // Map the statistics HUD
to the 's' key
viewer->addEventHandler(statsHandler);
kbHandler = new KeyboardEventHandler(viewer, MLAA);
// Add the keyboard event handler
viewer->addEventHandler(kbHandler);
scene->getOrCreateStateSet()->setMode(GL_STENCIL_TEST,
osg::StateAttribute::OFF);
viewer->realize();
viewer->setSceneData(group);
viewer->run();
}
...
Thank you!
Cheers,
Miguel
------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=39976#39976
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org