491 lines
17 KiB
C++
491 lines
17 KiB
C++
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions
|
|
// are met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of NVIDIA CORPORATION nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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.
|
|
//
|
|
// Copyright (c) 2018 NVIDIA Corporation. All rights reserved.
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// Implementation of different antialiasing methods.
|
|
// - typical MSAA
|
|
// - CSAA
|
|
// - Hardware AA mixed with FBO for supersampling pass
|
|
// - simple downsampling
|
|
// - downsampling with 1 or 2 kernel filters
|
|
//
|
|
// AABox is the class that will handle everything related to supersampling through
|
|
// an offscreen surface defined thanks to FBO
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <map>
|
|
|
|
#include <GL/glew.h>
|
|
#include <windows.h>
|
|
#include "GL/glut.h"
|
|
|
|
#include <Cg/CgGL.h>
|
|
|
|
#include "AABox.h"
|
|
|
|
AABox::AABox(std::string path) :
|
|
bValid(false),
|
|
vpx(0), vpy(0), vpw(0), vph(0),
|
|
posx(0), posy(0),
|
|
cgContext(NULL),
|
|
cgEffect(NULL),
|
|
cgPassDownSample(NULL), path(path), oldFbo(0)
|
|
{
|
|
for(int i=0; i<4; i++) cgTechnique[i] = NULL;
|
|
}
|
|
AABox::~AABox()
|
|
{
|
|
}
|
|
void AABox::Destroy()
|
|
{
|
|
if(depth_rb)
|
|
glDeleteRenderbuffersEXT(1, &depth_rb);
|
|
if(color_rb)
|
|
glDeleteRenderbuffersEXT(1, &color_rb);
|
|
if(textureID)
|
|
glDeleteTextures(1, &textureID);
|
|
if(textureDepthID)
|
|
glDeleteTextures(1, &textureDepthID);
|
|
if(fb)
|
|
glDeleteFramebuffersEXT(1, &fb);
|
|
if(fbms)
|
|
glDeleteFramebuffersEXT(1, &fbms);
|
|
if(cgEffect)
|
|
cgDestroyEffect(cgEffect);
|
|
if(cgContext)
|
|
cgDestroyContext(cgContext);
|
|
depth_rb=0;
|
|
color_rb=0;
|
|
textureID=0;
|
|
textureDepthID=0;
|
|
fb=0;
|
|
fbms=0;
|
|
cgEffect=0;
|
|
cgContext=0;
|
|
}
|
|
/*-------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------*/
|
|
void CheckFramebufferStatus()
|
|
{
|
|
GLenum status;
|
|
status = (GLenum) glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
switch(status) {
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
break;
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
printf("Unsupported framebuffer format\n");
|
|
fprintf(stderr, "Unsupported framebuffer format");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
|
|
printf("Framebuffer incomplete, missing attachment\n");
|
|
fprintf(stderr, "Framebuffer incomplete, missing attachment");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
|
|
printf("Framebuffer incomplete, attached images must have same dimensions\n");
|
|
fprintf(stderr, "Framebuffer incomplete, attached images must have same dimensions");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
|
|
printf("Framebuffer incomplete, attached images must have same format\n");
|
|
fprintf(stderr, "Framebuffer incomplete, attached images must have same format");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
|
|
printf("Framebuffer incomplete, missing draw buffer\n");
|
|
fprintf(stderr, "Framebuffer incomplete, missing draw buffer");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
|
|
printf("Framebuffer incomplete, missing read buffer\n");
|
|
fprintf(stderr, "Framebuffer incomplete, missing read buffer");
|
|
break;
|
|
default:
|
|
printf("Error %x\n", status);
|
|
break;
|
|
}
|
|
}
|
|
bool AABox::initRT(int depthSamples, int coverageSamples)
|
|
{
|
|
bool multisample = depthSamples > 0;
|
|
bool csaa = coverageSamples > depthSamples;
|
|
bool ret = true;
|
|
int query;
|
|
//
|
|
// init the texture that will also be the buffer to render to
|
|
//
|
|
glGenTextures(1, &textureID);
|
|
glBindTexture( GL_TEXTURE_2D, textureID);
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA16F, bufw, bufh, 0,
|
|
GL_RGBA, GL_FLOAT, NULL);
|
|
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glGenFramebuffersEXT(1, &fb);
|
|
|
|
//
|
|
// Handle multisample FBO's first
|
|
//
|
|
if (multisample)
|
|
{
|
|
//multisample : so we need to resolve from the MS FBO down to a FBO at non MS resolution
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureID, 0);
|
|
glGenTextures(1, &textureDepthID);
|
|
glBindTexture(GL_TEXTURE_2D, textureDepthID);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, bufw, bufh, 0,
|
|
GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, textureDepthID, 0);
|
|
CheckFramebufferStatus();
|
|
|
|
//now handle the FBO in MS resolution
|
|
glGenFramebuffersEXT(1, &fbms);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbms);
|
|
// initialize color renderbuffer
|
|
glGenRenderbuffersEXT(1, &color_rb);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, color_rb);
|
|
|
|
if (csaa & bCSAA)
|
|
{
|
|
glRenderbufferStorageMultisampleCoverageNV( GL_RENDERBUFFER_EXT, coverageSamples, depthSamples, GL_RGBA16F,
|
|
bufw, bufh);
|
|
|
|
glGetRenderbufferParameterivEXT( GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_COVERAGE_SAMPLES_NV, &query);
|
|
|
|
if ( query < coverageSamples)
|
|
ret = false;
|
|
else if ( query > coverageSamples)
|
|
{
|
|
// report back the actual number
|
|
coverageSamples = query;
|
|
}
|
|
|
|
glGetRenderbufferParameterivEXT( GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_COLOR_SAMPLES_NV, &query);
|
|
|
|
if ( query < depthSamples)
|
|
ret = false;
|
|
else if ( query > depthSamples)
|
|
{
|
|
// report back the actual number
|
|
depthSamples = query;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// create a regular MSAA color buffer
|
|
glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, depthSamples, GL_RGBA16F, bufw, bufh);
|
|
|
|
// check the number of samples
|
|
glGetRenderbufferParameterivEXT( GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &query);
|
|
|
|
if ( query < depthSamples)
|
|
ret = false;
|
|
else if ( query > depthSamples)
|
|
{
|
|
depthSamples = query;
|
|
}
|
|
|
|
}
|
|
|
|
// attach the multisampled color buffer
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, color_rb);
|
|
CheckFramebufferStatus();
|
|
|
|
// bind the multisampled depth buffer
|
|
glGenRenderbuffersEXT(1, &depth_rb);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb);
|
|
|
|
// create the multisampled depth buffer (with or without coverage sampling)
|
|
if (csaa & bCSAA)
|
|
{
|
|
|
|
// create a coverage sampled MSAA depth buffer
|
|
glRenderbufferStorageMultisampleCoverageNV( GL_RENDERBUFFER_EXT, coverageSamples, depthSamples, GL_DEPTH_COMPONENT24,
|
|
bufw, bufh);
|
|
|
|
// check the number of coverage samples
|
|
glGetRenderbufferParameterivEXT( GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_COVERAGE_SAMPLES_NV, &query);
|
|
|
|
if ( query < coverageSamples)
|
|
ret = false;
|
|
else if ( query > coverageSamples)
|
|
// set the coverage samples value to return the actual value
|
|
coverageSamples = query;
|
|
|
|
// cehck the number of stored color samples (same as depth samples)
|
|
glGetRenderbufferParameterivEXT( GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_COLOR_SAMPLES_NV, &query);
|
|
|
|
if ( query < depthSamples)
|
|
ret = false;
|
|
else if ( query > depthSamples)
|
|
// set the depth samples value to return the actual value
|
|
depthSamples = query;
|
|
}
|
|
else {
|
|
|
|
// create a regular (not coverage sampled) MSAA depth buffer
|
|
glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, depthSamples, GL_DEPTH_COMPONENT24, bufw, bufh);
|
|
|
|
// check the number of depth samples
|
|
glGetRenderbufferParameterivEXT( GL_RENDERBUFFER_EXT, GL_RENDERBUFFER_SAMPLES_EXT, &query);
|
|
|
|
if ( query < depthSamples)
|
|
ret = false;
|
|
else if ( query < depthSamples)
|
|
depthSamples = query;
|
|
}
|
|
|
|
// attach the depth buffer
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb);
|
|
|
|
CheckFramebufferStatus();
|
|
|
|
} // if (multisample)
|
|
else // Depth buffer created as a texture...
|
|
{
|
|
//non-multisample, so bind things directly to the FBO
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureID, 0);
|
|
|
|
glGenTextures(1, &textureDepthID);
|
|
glBindTexture( GL_TEXTURE_2D, textureDepthID);
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, bufw, bufh, 0,
|
|
GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
|
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
|
|
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, textureDepthID, 0);
|
|
|
|
CheckFramebufferStatus();
|
|
}
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, multisample ? fbms : fb);
|
|
glGetIntegerv( GL_RED_BITS, &query);
|
|
if ( query != 16)
|
|
{
|
|
printf( "Got %d red bits expected %d\n", query, 16);
|
|
ret = false;
|
|
}
|
|
glGetIntegerv( GL_DEPTH_BITS, &query);
|
|
if ( query != 24)
|
|
{
|
|
printf( "Got %d depth bits expected %d\n", query, 24);;
|
|
ret = false;
|
|
}
|
|
if (multisample)
|
|
{
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb);
|
|
glGetIntegerv( GL_RED_BITS, &query);
|
|
if ( query != 16)
|
|
{
|
|
printf( "Got %d red bits expected %d\n", query, 16);
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, oldFbo);
|
|
|
|
return ret;
|
|
}
|
|
/*-------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------*/
|
|
bool AABox::Initialize(int w, int h, float ssfact, int depthSamples, int coverageSamples)
|
|
{
|
|
if (!glewIsSupported(
|
|
"GL_VERSION_2_0 "
|
|
"GL_EXT_framebuffer_object "
|
|
))
|
|
{
|
|
printf("Unable to load extensions\n");
|
|
return false;
|
|
}
|
|
bCSAA = glewIsExtensionSupported("GL_NV_framebuffer_multisample_coverage") ? true : false;
|
|
if((!bCSAA) && coverageSamples)
|
|
{
|
|
printf("Note: GL_NV_framebuffer_multisample_coverage unavailable, using regular MSAA only\n");
|
|
}
|
|
|
|
Destroy();
|
|
|
|
bufw = (int)(ssfact*(float)w+0.99f);
|
|
bufh = (int)(ssfact*(float)h+0.99f);
|
|
//
|
|
// FBO
|
|
//
|
|
initRT(depthSamples, coverageSamples);
|
|
CheckFramebufferStatus();
|
|
//
|
|
// CGFX things
|
|
//
|
|
cgContext = cgCreateContext();
|
|
cgGLRegisterStates(cgContext);
|
|
|
|
char fileName[200];
|
|
sprintf(fileName, "%s/AABox.cgfx", path.c_str());
|
|
|
|
cgEffect = cgCreateEffectFromFile(cgContext, fileName, NULL);
|
|
if(!cgEffect)
|
|
{
|
|
const char *listing = cgGetLastListing(cgContext);
|
|
//fprintf(stderr, "CgFx Parse error : \n %s\n", listing);
|
|
bValid = false;
|
|
//printf("Trying other path...\n");
|
|
cgEffect = cgCreateEffectFromFile(cgContext, fileName, NULL);
|
|
if(!cgEffect)
|
|
{
|
|
fprintf(stderr, "CgFx Parse error : \n %s\n", listing);
|
|
bValid = false;
|
|
return false;
|
|
}
|
|
}
|
|
cgTechnique[0] = cgGetFirstTechnique(cgEffect);
|
|
cgTechnique[1] = cgGetNextTechnique(cgTechnique[0]);
|
|
cgTechnique[2] = cgGetNextTechnique(cgTechnique[1]);
|
|
cgTechnique[3] = cgGetNextTechnique(cgTechnique[2]);
|
|
cgBlendFactor = cgGetNamedEffectParameter(cgEffect, "blendFactor");
|
|
cgSSsampler = cgGetNamedEffectParameter(cgEffect, "SSsampler");
|
|
cgDepthSSsampler = cgGetNamedEffectParameter(cgEffect, "DepthSSsampler");
|
|
cgTexelSize = cgGetNamedEffectParameter(cgEffect, "SSTexelSize");
|
|
cgBlendFactor = cgGetNamedEffectParameter(cgEffect, "blendFactor");
|
|
|
|
cgGLSetParameter2f(cgTexelSize, 1.0f/(float)bufw, 1.0f/(float)bufh);
|
|
|
|
bValid = true;
|
|
return true;
|
|
}
|
|
|
|
#define FULLSCRQUAD()\
|
|
glBegin(GL_QUADS);\
|
|
glTexCoord2f(0,0);\
|
|
glVertex4f(-1, -1, 0.0,1);\
|
|
glTexCoord2f(1,0);\
|
|
glVertex4f(1, -1,0.0,1);\
|
|
glTexCoord2f(1,1);\
|
|
glVertex4f(1, 1,0.0,1);\
|
|
glTexCoord2f(0,1);\
|
|
glVertex4f(-1, 1,0.0,1);\
|
|
glEnd();
|
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------*/
|
|
void AABox::Draw(int technique)
|
|
{
|
|
CGbool bRes;
|
|
if(technique > 4)
|
|
return;
|
|
bRes = cgValidateTechnique(cgTechnique[technique]);
|
|
if(!bRes)
|
|
{
|
|
if(bValid)
|
|
{
|
|
fprintf(stderr, "Validation of FilterRect failed\n");
|
|
bValid = false;
|
|
}
|
|
return;
|
|
}
|
|
else bValid = true;
|
|
if(!bValid)
|
|
return;
|
|
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
|
|
//
|
|
// if this FBO is multisampled, resolve it, so it can be displayed
|
|
// the blit will allow the multisampled buffer to be stretched to a normal buffer at res bufw/bufh
|
|
//
|
|
if( fbms )
|
|
{
|
|
|
|
glBindFramebufferEXT( GL_READ_FRAMEBUFFER_EXT, fbms);
|
|
glBindFramebufferEXT( GL_DRAW_FRAMEBUFFER_EXT, fb);
|
|
glBlitFramebufferEXT(0, 0, bufw, bufh, 0, 0, bufw, bufh, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
|
}
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, oldFbo);
|
|
|
|
cgGLSetupSampler(cgSSsampler, textureID);
|
|
cgGLSetupSampler(cgDepthSSsampler, textureDepthID);
|
|
|
|
|
|
|
|
CGpass cgPass = cgGetFirstPass(cgTechnique[technique]);
|
|
cgSetPassState(cgPass);
|
|
//
|
|
// During this full screen pass, we will down-sample the buffer and
|
|
// eventually filter it
|
|
//
|
|
FULLSCRQUAD();
|
|
cgResetPassState(cgPass);
|
|
}
|
|
/*-------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------*/
|
|
void AABox::Activate(int x, int y)
|
|
{
|
|
if(!bValid)
|
|
return;
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
//
|
|
// Bind the framebuffer to render on : can be either Multisampled one or normal one
|
|
//
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbms ? fbms : fb);
|
|
|
|
glPushAttrib(GL_VIEWPORT_BIT);
|
|
posx = x;
|
|
posy = y;
|
|
glViewport(0, 0, bufw, bufh);
|
|
}
|
|
/*-------------------------------------------------------------------------
|
|
|
|
-------------------------------------------------------------------------*/
|
|
void AABox::Deactivate()
|
|
{
|
|
if(!bValid)
|
|
return;
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, oldFbo);
|
|
glPopAttrib();
|
|
}
|
|
|
|
void AABox::Rebind() {
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbms ? fbms : fb);
|
|
glViewport(0, 0, bufw, bufh);
|
|
} |