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

Reply via email to