diff --git a/include/openspace/rendering/framebufferrenderer.h b/include/openspace/rendering/framebufferrenderer.h index d21a2b3b9a..d116c54bce 100644 --- a/include/openspace/rendering/framebufferrenderer.h +++ b/include/openspace/rendering/framebufferrenderer.h @@ -69,16 +69,16 @@ public: void updateRaycastData(); void updateDeferredcastData(); void updateHDRAndFiltering(); + void updateFXAA(); void setResolution(glm::ivec2 res) override; - void setNAaSamples(int nAaSamples) override; void setHDRExposure(float hdrExposure) override; void setGamma(float gamma) override; void setHue(float hue) override; void setValue(float value) override; void setSaturation(float sat) override; - int nAaSamples() const override; + void enableFXAA(bool enable) override; void setDisableHDR(bool disable) override; void update() override; @@ -109,6 +109,7 @@ private: void resolveMSAA(float blackoutFactor); void applyTMO(float blackoutFactor); + void applyFXAA(); std::map _raycastData; RaycasterProgObjMap _exitPrograms; @@ -120,12 +121,13 @@ private: std::unique_ptr _hdrFilteringProgram; std::unique_ptr _tmoProgram; - std::unique_ptr _resolveProgram; - UniformCache(mainColorTexture, blackoutFactor, nAaSamples) _uniformCache; - + std::unique_ptr _fxaaProgram; + + UniformCache(mainColorTexture, blackoutFactor) _uniformCache; UniformCache(hdrFeedingTexture, blackoutFactor, hdrExposure, gamma, - Hue, Saturation, Value, nAaSamples) _hdrUniformCache; + Hue, Saturation, Value) _hdrUniformCache; + UniformCache(renderedTexture, inverseScreenSize) _fxaaUniformCache; GLint _defaultFBO; GLuint _screenQuad; @@ -148,19 +150,24 @@ private: } _pingPongBuffers; struct { - GLuint _hdrFilteringFramebuffer; - GLuint _hdrFilteringTexture; + GLuint hdrFilteringFramebuffer; + GLuint hdrFilteringTexture; } _hdrBuffers; + struct { + GLuint fxaaFramebuffer; + GLuint fxaaTexture; + } _fxaaBuffers; + unsigned int _pingPongIndex = 0u; bool _dirtyDeferredcastData; bool _dirtyRaycastData; bool _dirtyResolution; - bool _dirtyMsaaSamplingPattern; - + glm::ivec2 _resolution = glm::ivec2(0); int _nAaSamples; + bool _enableFXAA = true; bool _disableHDR = false; float _hdrExposure = 3.7f; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index b848712074..8093c16c4d 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -208,8 +208,9 @@ private: properties::BoolProperty _disableMasterRendering; properties::FloatProperty _globalBlackOutFactor; - properties::IntProperty _nAaSamples; + properties::BoolProperty _enableFXAA; + properties::BoolProperty _disableHDRPipeline; properties::FloatProperty _hdrExposure; properties::FloatProperty _gamma; diff --git a/include/openspace/rendering/renderer.h b/include/openspace/rendering/renderer.h index 77d5529244..d8b38f0450 100644 --- a/include/openspace/rendering/renderer.h +++ b/include/openspace/rendering/renderer.h @@ -49,13 +49,12 @@ public: virtual void deinitialize() = 0; virtual void setResolution(glm::ivec2 res) = 0; - virtual void setNAaSamples(int nAaSamples) = 0; virtual void setHDRExposure(float hdrExposure) = 0; virtual void setGamma(float gamma) = 0; virtual void setHue(float hue) = 0; virtual void setValue(float value) = 0; virtual void setSaturation(float sat) = 0; - virtual int nAaSamples() const = 0; + virtual void enableFXAA(bool enable) = 0; virtual void setDisableHDR(bool disable) = 0; /** diff --git a/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl b/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl index d8d1d4e28b..920e278a4d 100644 --- a/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl +++ b/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl @@ -65,15 +65,16 @@ out vec4 renderTarget; in vec3 interpolatedNDCPos; +in vec2 texCoord; uniform int nAaSamples; uniform int cullAtmosphere; uniform sampler2D irradianceTexture; uniform sampler3D inscatterTexture; -uniform sampler2DMS mainPositionTexture; -uniform sampler2DMS mainNormalTexture; -uniform sampler2DMS mainColorTexture; +uniform sampler2D mainPositionTexture; +uniform sampler2D mainNormalTexture; +uniform sampler2D mainColorTexture; uniform dmat4 dInverseModelTransformMatrix; uniform dmat4 dModelTransformMatrix; @@ -230,7 +231,7 @@ bool dAtmosphereIntersection(const dvec3 planetPosition, const dRay ray, const d * This method avoids matrices multiplications * wherever is possible. */ -void dCalculateRayRenderableGlobe(in int mssaSample, out dRay ray, +void dCalculateRayRenderableGlobe(out dRay ray, out dvec4 planetPositionObjectCoords, out dvec4 cameraPositionInObject) { dvec4 clipCoords = dvec4(interpolatedNDCPos.xy, 1.0, 1.0); @@ -529,8 +530,6 @@ vec3 sunColor(const vec3 x, const float t, const vec3 v, const vec3 s, const flo } void main() { - ivec2 fragCoords = ivec2(gl_FragCoord); - if (cullAtmosphere == 0) { vec4 atmosphereFinalColor = vec4(0.0f); int nSamples = 1; @@ -538,153 +537,141 @@ void main() { // First we determine if the pixel is complex (different fragments on it) bool complex = false; vec4 oldColor, currentColor; - vec4 colorArray[16]; + vec4 colorTexture; - colorArray[0] = texelFetch(mainColorTexture, fragCoords, 0); - for (int i = 1; i < nAaSamples; i++) { - colorArray[i] = texelFetch(mainColorTexture, fragCoords, i); - if (colorArray[i] != colorArray[i-1]) { - complex = true; - } - } - nSamples = complex ? nAaSamples / 2 : 1; + colorTexture = texture(mainColorTexture, texCoord); - for (int i = 0; i < nSamples; i++) { - // Color from G-Buffer - vec4 color = colorArray[i]; - // Ray in object space - dRay ray; - dvec4 planetPositionObjectCoords = dvec4(0.0); - dvec4 cameraPositionInObject = dvec4(0.0); + // Color from G-Buffer + vec4 color = colorTexture; + // Ray in object space + dRay ray; + dvec4 planetPositionObjectCoords = dvec4(0.0); + dvec4 cameraPositionInObject = dvec4(0.0); + + // Get the ray from camera to atm in object space + dCalculateRayRenderableGlobe(ray, planetPositionObjectCoords, + cameraPositionInObject); + + bool insideATM = false; + double offset = 0.0; // in Km + double maxLength = 0.0; // in Km + + bool intersectATM = false; + + intersectATM = dAtmosphereIntersection(planetPositionObjectCoords.xyz, ray, + Rt - (ATM_EPSILON * 0.001), insideATM, offset, maxLength); - // Get the ray from camera to atm in object space - dCalculateRayRenderableGlobe(i * 3, ray, planetPositionObjectCoords, - cameraPositionInObject); - - bool insideATM = false; - double offset = 0.0; // in Km - double maxLength = 0.0; // in Km + if ( intersectATM ) { + // Now we check is if the atmosphere is occluded, i.e., if the distance to the pixel + // in the G-Buffer positions is less than the distance to the atmosphere then the atmosphere + // is occluded + // Fragments positions into G-Buffer are written in SGCT Eye Space (View plus Camera Rig Coords) + // when using their positions later, one must convert them to the planet's coords + + // Get data from G-Buffer + vec4 normal = texture(mainNormalTexture, texCoord); + // Data in the mainPositionTexture are written in view space (view plus camera rig) + vec4 position = texture(mainPositionTexture, texCoord); - bool intersectATM = false; + // OS Eye to World coords + dvec4 positionWorldCoords = dSGCTViewToWorldMatrix * position; - intersectATM = dAtmosphereIntersection(planetPositionObjectCoords.xyz, ray, - Rt - (ATM_EPSILON * 0.001), insideATM, offset, maxLength); - - if ( intersectATM ) { - // Now we check is if the atmosphere is occluded, i.e., if the distance to the pixel - // in the G-Buffer positions is less than the distance to the atmosphere then the atmosphere - // is occluded - // Fragments positions into G-Buffer are written in SGCT Eye Space (View plus Camera Rig Coords) - // when using their positions later, one must convert them to the planet's coords - - // Get data from G-Buffer - vec4 normal = texelFetch(mainNormalTexture, fragCoords, i); - // Data in the mainPositionTexture are written in view space (view plus camera rig) - vec4 position = texelFetch(mainPositionTexture, fragCoords, i); + // World to Object (Normal and Position in meters) + dvec4 positionObjectsCoords = dInverseModelTransformMatrix * positionWorldCoords; - // OS Eye to World coords - dvec4 positionWorldCoords = dSGCTViewToWorldMatrix * position; + + // Distance of the pixel in the gBuffer to the observer + // JCC (12/12/2017): AMD distance function is buggy. + //double pixelDepth = distance(cameraPositionInObject.xyz, positionObjectsCoords.xyz); + double pixelDepth = length(cameraPositionInObject.xyz - positionObjectsCoords.xyz); + + // JCC (12/13/2017): Trick to remove floating error in texture. + // We see a squared noise on planet's surface when seeing the planet + // from far away. + float dC = float(length(cameraPositionInObject.xyz)); + float x1 = 1e8; + if (dC > x1) { + pixelDepth += 1000.0; + float alpha = 1000.0; + float beta = 1000000.0; + float x2 = 1e9; + float diffGreek = beta - alpha; + float diffDist = x2 - x1; + float varA = diffGreek/diffDist; + float varB = (alpha - varA * x1); + pixelDepth += double(varA * dC + varB); + } - // World to Object (Normal and Position in meters) - dvec4 positionObjectsCoords = dInverseModelTransformMatrix * positionWorldCoords; - - - // Distance of the pixel in the gBuffer to the observer - // JCC (12/12/2017): AMD distance function is buggy. - //double pixelDepth = distance(cameraPositionInObject.xyz, positionObjectsCoords.xyz); - double pixelDepth = length(cameraPositionInObject.xyz - positionObjectsCoords.xyz); - - // JCC (12/13/2017): Trick to remove floating error in texture. - // We see a squared noise on planet's surface when seeing the planet - // from far away. - float dC = float(length(cameraPositionInObject.xyz)); - float x1 = 1e8; - if (dC > x1) { - pixelDepth += 1000.0; - float alpha = 1000.0; - float beta = 1000000.0; - float x2 = 1e9; - float diffGreek = beta - alpha; - float diffDist = x2 - x1; - float varA = diffGreek/diffDist; - float varB = (alpha - varA * x1); - pixelDepth += double(varA * dC + varB); - } - - // All calculations are done in Km: - pixelDepth *= 0.001; - positionObjectsCoords.xyz *= 0.001; - - if (pixelDepth < offset) { - // ATM Occluded - Something in fron of ATM. - atmosphereFinalColor += color; - } else { - // Following paper nomenclature - double t = offset; - vec3 attenuation; - - // Moving observer from camera location to top atmosphere - // If the observer is already inside the atm, offset = 0.0 - // and no changes at all. - vec3 x = vec3(ray.origin.xyz + t*ray.direction.xyz); - float r = 0.0f;//length(x); - vec3 v = vec3(ray.direction.xyz); - float mu = 0.0f;//dot(x, v) / r; - vec3 s = vec3(sunDirectionObj); - float tF = float(maxLength - t); - - // Because we may move the camera origin to the top of atmosphere - // we also need to adjust the pixelDepth for tdCalculateRayRenderableGlobe' offset so the - // next comparison with the planet's ground make sense: - pixelDepth -= offset; - - dvec4 onATMPos = dModelTransformMatrix * dvec4(x * 1000.0, 1.0); - vec4 eclipseShadowATM = calcShadow(shadowDataArray, onATMPos.xyz, false); - float sunIntensityInscatter = sunRadiance * eclipseShadowATM.x; - - float irradianceFactor = 0.0; - - bool groundHit = false; - vec3 inscatterColor = inscatterRadiance(x, tF, irradianceFactor, v, - s, r, mu, attenuation, - vec3(positionObjectsCoords.xyz), - groundHit, maxLength, pixelDepth, - color, sunIntensityInscatter); - vec3 groundColorV = vec3(0.0); - vec3 sunColorV = vec3(0.0); - if (groundHit) { - vec4 eclipseShadowPlanet = calcShadow(shadowDataArray, positionWorldCoords.xyz, true); - float sunIntensityGround = sunRadiance * eclipseShadowPlanet.x; - groundColorV = groundColor(x, tF, v, s, r, mu, attenuation, - color, normal.xyz, irradianceFactor, - normal.a, sunIntensityGround); - } else { - // In order to get better performance, we are not tracing - // multiple rays per pixel when the ray doesn't intersect - // the ground. - sunColorV = sunColor(x, tF, v, s, r, mu, irradianceFactor); - } - - // Final Color of ATM plus terrain: - vec4 finalRadiance = vec4(inscatterColor + groundColorV + sunColorV, 1.0); - - atmosphereFinalColor += finalRadiance; - } - } - else { // no intersection - // Buffer color + // All calculations are done in Km: + pixelDepth *= 0.001; + positionObjectsCoords.xyz *= 0.001; + + if (pixelDepth < offset) { + // ATM Occluded - Something in fron of ATM. atmosphereFinalColor += color; - } - } + } else { + // Following paper nomenclature + double t = offset; + vec3 attenuation; - renderTarget = atmosphereFinalColor / float(nSamples); + // Moving observer from camera location to top atmosphere + // If the observer is already inside the atm, offset = 0.0 + // and no changes at all. + vec3 x = vec3(ray.origin.xyz + t*ray.direction.xyz); + float r = 0.0f;//length(x); + vec3 v = vec3(ray.direction.xyz); + float mu = 0.0f;//dot(x, v) / r; + vec3 s = vec3(sunDirectionObj); + float tF = float(maxLength - t); + + // Because we may move the camera origin to the top of atmosphere + // we also need to adjust the pixelDepth for tdCalculateRayRenderableGlobe' offset so the + // next comparison with the planet's ground make sense: + pixelDepth -= offset; + + dvec4 onATMPos = dModelTransformMatrix * dvec4(x * 1000.0, 1.0); + vec4 eclipseShadowATM = calcShadow(shadowDataArray, onATMPos.xyz, false); + float sunIntensityInscatter = sunRadiance * eclipseShadowATM.x; + + float irradianceFactor = 0.0; + + bool groundHit = false; + vec3 inscatterColor = inscatterRadiance(x, tF, irradianceFactor, v, + s, r, mu, attenuation, + vec3(positionObjectsCoords.xyz), + groundHit, maxLength, pixelDepth, + color, sunIntensityInscatter); + vec3 groundColorV = vec3(0.0); + vec3 sunColorV = vec3(0.0); + if (groundHit) { + vec4 eclipseShadowPlanet = calcShadow(shadowDataArray, positionWorldCoords.xyz, true); + float sunIntensityGround = sunRadiance * eclipseShadowPlanet.x; + groundColorV = groundColor(x, tF, v, s, r, mu, attenuation, + color, normal.xyz, irradianceFactor, + normal.a, sunIntensityGround); + } else { + // In order to get better performance, we are not tracing + // multiple rays per pixel when the ray doesn't intersect + // the ground. + sunColorV = sunColor(x, tF, v, s, r, mu, irradianceFactor); + } + + // Final Color of ATM plus terrain: + vec4 finalRadiance = vec4(inscatterColor + groundColorV + sunColorV, 1.0); + + atmosphereFinalColor += finalRadiance; + } + } + else { // no intersection + // Buffer color + atmosphereFinalColor += color; + } + + + renderTarget = atmosphereFinalColor; } else { // culling - vec4 bColor = vec4(0.0f); - for (int f = 0; f < nAaSamples; f++) { - bColor += texelFetch(mainColorTexture, fragCoords, f); - } - bColor /= float(nAaSamples); + vec4 bColor = texture(mainColorTexture, texCoord); renderTarget = bColor; } } diff --git a/modules/atmosphere/shaders/atmosphere_deferred_vs.glsl b/modules/atmosphere/shaders/atmosphere_deferred_vs.glsl index 52cd01cb6e..33f0eb1651 100644 --- a/modules/atmosphere/shaders/atmosphere_deferred_vs.glsl +++ b/modules/atmosphere/shaders/atmosphere_deferred_vs.glsl @@ -27,9 +27,11 @@ layout(location = 0) in vec4 in_position; out vec3 interpolatedNDCPos; +out vec2 texCoord; void main() { + texCoord = 0.5 + in_position.xy * 0.5; interpolatedNDCPos = in_position.xyz; gl_Position = in_position; } diff --git a/modules/base/rendering/renderabletrail.cpp b/modules/base/rendering/renderabletrail.cpp index 23ab9b9263..836ee76954 100644 --- a/modules/base/rendering/renderabletrail.cpp +++ b/modules/base/rendering/renderabletrail.cpp @@ -361,6 +361,8 @@ void RenderableTrail::render(const RenderData& data, RendererTasks&) { glBindVertexArray(info._vaoID); if (renderLines) { + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); p->setUniform(c.renderPhase, RenderPhaseLines); // Subclasses of this renderer might be using the index array or might now be // so we check if there is data available and if there isn't, we use the @@ -380,6 +382,7 @@ void RenderableTrail::render(const RenderData& data, RendererTasks&) { reinterpret_cast(info.first * sizeof(unsigned int)) ); } + glDisable(GL_LINE_SMOOTH); } if (renderPoints) { // Subclasses of this renderer might be using the index array or might now be diff --git a/shaders/framebuffer/fxaa.frag b/shaders/framebuffer/fxaa.frag new file mode 100644 index 0000000000..90c7674897 --- /dev/null +++ b/shaders/framebuffer/fxaa.frag @@ -0,0 +1,233 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +#define EDGE_THRESHOLD_MIN 0.0312f +#define EDGE_THRESHOLD_MAX 0.125f +#define ITERATIONS 12 +#define SUBPIXEL_QUALITY 0.75f + +const float[12] QUALITY = {1.f, 1.f, 1.f, 1.f, 1.f, 1.5f, 2.f, 2.f, 2.f, 2.f, 4.f, 8.f}; +// const float[24] QUALITY = {2.f, 4.f, 6.f, 8.f, 10.f, 12.f, 12.f, 12.f, 12.f, 12.f, 14.f, 18.f, +// 18.f, 18.f, 18.f, 18.f, 18.f, 18.f, 18.f, 18.f, 18.f, 18.f, +// 18.f, 18.f}; + +layout (location = 0) out vec4 aaFinalColor; + +uniform vec2 inverseScreenSize; +uniform sampler2D renderedTexture; + +in vec2 texCoord; + +// Relative luminance +float getLum(vec3 rgb){ + return dot(vec3(0.2126, 0.7152, 0.0722), rgb); +} + +void main() { + vec4 colorCenter = texture(renderedTexture,texCoord); + + // ============================ + // Detecting where to apply AA: + // ============================ + + float pixelLumCenter = getLum(colorCenter.rgb); + float pixelLumDown = getLum(textureOffset(renderedTexture, texCoord, ivec2(0,-1)).rgb); + float pixelLumUp = getLum(textureOffset(renderedTexture, texCoord, ivec2(0,1)).rgb); + float pixelLumLeft = getLum(textureOffset(renderedTexture, texCoord, ivec2(-1,0)).rgb); + float pixelLumRight = getLum(textureOffset(renderedTexture, texCoord, ivec2(1,0)).rgb); + + float pixelLumMin = min(pixelLumCenter, min(min(pixelLumDown, pixelLumUp), min(pixelLumLeft, pixelLumRight))); + float pixelLumMax = max(pixelLumCenter, max(max(pixelLumDown, pixelLumUp), max(pixelLumLeft, pixelLumRight))); + + // Delta. + float pixelLumRange = pixelLumMax - pixelLumMin; + + // If the pixelLum variation is lower that a threshold (or if we are in a really dark area), + // we are not on an edge, don't perform any AA. + if (pixelLumRange < max(EDGE_THRESHOLD_MIN, pixelLumMax * EDGE_THRESHOLD_MAX)) { + aaFinalColor = colorCenter; + return; + } + + // ============================ + // Estimating the gradient: + // ============================ + float pixelLumDownLeft = getLum(textureOffset(renderedTexture, texCoord, ivec2(-1,-1)).rgb); + float pixelLumUpRight = getLum(textureOffset(renderedTexture, texCoord, ivec2(1,1)).rgb); + float pixelLumUpLeft = getLum(textureOffset(renderedTexture, texCoord, ivec2(-1,1)).rgb); + float pixelLumDownRight = getLum(textureOffset(renderedTexture, texCoord, ivec2(1,-1)).rgb); + + float pixelLumDownUp = pixelLumDown + pixelLumUp; + float pixelLumLeftRight = pixelLumLeft + pixelLumRight; + float pixelLumLeftCorners = pixelLumDownLeft + pixelLumUpLeft; + float pixelLumDownCorners = pixelLumDownLeft + pixelLumDownRight; + float pixelLumRightCorners = pixelLumDownRight + pixelLumUpRight; + float pixelLumUpCorners = pixelLumUpRight + pixelLumUpLeft; + + // Compute an estimation of the gradient + float edgeHorizontal = abs(-2.0 * pixelLumLeft + pixelLumLeftCorners) + + abs(-2.0 * pixelLumCenter + pixelLumDownUp ) * 2.0 + abs(-2.0 * pixelLumRight + pixelLumRightCorners); + float edgeVertical = abs(-2.0 * pixelLumUp + pixelLumUpCorners) + + abs(-2.0 * pixelLumCenter + pixelLumLeftRight) * 2.0 + abs(-2.0 * pixelLumDown + pixelLumDownCorners); + + // ============================ + // Choosing Edge Orientation: + // ============================ + bool isHorizontal = (edgeHorizontal >= edgeVertical); + float pixelLum1 = isHorizontal ? pixelLumDown : pixelLumLeft; + float pixelLum2 = isHorizontal ? pixelLumUp : pixelLumRight; + + // Gradients + float gradient1 = pixelLum1 - pixelLumCenter; + float gradient2 = pixelLum2 - pixelLumCenter; + + bool is1Steepest = abs(gradient1) >= abs(gradient2); + float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2)); + + // Step size (one pixel) according to the edge direction. + float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x; + + float pixelLumLocalAverage = 0.0; + + if (is1Steepest) { + stepLength = - stepLength; + pixelLumLocalAverage = 0.5 * (pixelLum1 + pixelLumCenter); + } else { + pixelLumLocalAverage = 0.5 * (pixelLum2 + pixelLumCenter); + } + + vec2 currentUv = texCoord; + if (isHorizontal) { + currentUv.y += stepLength * 0.5; + } else { + currentUv.x += stepLength * 0.5; + } + + // ============================ + // Iterations: + // ============================ + vec2 offset = isHorizontal ? vec2(inverseScreenSize.x, 0.0) : vec2(0.0, inverseScreenSize.y); + + vec2 uv1 = currentUv - offset; + vec2 uv2 = currentUv + offset; + + // Read the pixelLums at both current extremities of the exploration segment, + // and compute the delta wrt to the local average pixelLum. + float pixelLumEnd1 = getLum(texture(renderedTexture, uv1).rgb); + float pixelLumEnd2 = getLum(texture(renderedTexture, uv2).rgb); + pixelLumEnd1 -= pixelLumLocalAverage; + pixelLumEnd2 -= pixelLumLocalAverage; + + bool reached1 = abs(pixelLumEnd1) >= gradientScaled; + bool reached2 = abs(pixelLumEnd2) >= gradientScaled; + bool reachedBoth = reached1 && reached2; + + if (!reached1) { + uv1 -= offset; + } + + if (!reached2) { + uv2 += offset; + } + + // Still exploring + if (!reachedBoth) { + for (int i = 2; i < ITERATIONS; i++) { + // If needed, read pixelLum in 1st direction, compute delta. + if (!reached1) { + pixelLumEnd1 = getLum(texture(renderedTexture, uv1).rgb); + pixelLumEnd1 = pixelLumEnd1 - pixelLumLocalAverage; + } + // If needed, read pixelLum in opposite direction, compute delta. + if (!reached2) { + pixelLumEnd2 = getLum(texture(renderedTexture, uv2).rgb); + pixelLumEnd2 = pixelLumEnd2 - pixelLumLocalAverage; + } + reached1 = abs(pixelLumEnd1) >= gradientScaled; + reached2 = abs(pixelLumEnd2) >= gradientScaled; + reachedBoth = reached1 && reached2; + + // If the side is not reached + if (!reached1) { + uv1 -= offset * QUALITY[i]; + } + + if (!reached2) { + uv2 += offset * QUALITY[i]; + } + + // If both sides have been reached + if (reachedBoth) { + break; + } + } + } + + // ============================ + // Estimating the offset: + // ============================ + float distance1 = isHorizontal ? (texCoord.x - uv1.x) : (texCoord.y - uv1.y); + float distance2 = isHorizontal ? (uv2.x - texCoord.x) : (uv2.y - texCoord.y); + + bool isDirection1 = distance1 < distance2; + float distanceFinal = min(distance1, distance2); + + float edgeThickness = (distance1 + distance2); + + // Read in the direction of the closest side of the edge. + float pixelOffset = - distanceFinal / edgeThickness + 0.5; + + bool ispixelLumCenterSmaller = pixelLumCenter < pixelLumLocalAverage; + + // If the pixelLum at center is smaller than at its neighbour, the delta pixelLum at + // each end should be positive (same variation). + bool correctVariation = ((isDirection1 ? pixelLumEnd1 : pixelLumEnd2) < 0.0) != ispixelLumCenterSmaller; + + // If the pixelLum variation is incorrect, do not offset. + float finalOffset = correctVariation ? pixelOffset : 0.0; + + // ============================ + // Subpixel antialiasing: + // ============================ + float pixelLumAverage = (1.0/12.0) * (2.0 * (pixelLumDownUp + pixelLumLeftRight) + + pixelLumLeftCorners + pixelLumRightCorners); + + float subPixelOffset1 = clamp(abs(pixelLumAverage - pixelLumCenter) / pixelLumRange, 0.0, 1.0); + float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1; + float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY; + + // Biggest of the two offsets. + finalOffset = max(finalOffset, subPixelOffsetFinal); + + vec2 finalUV = texCoord; + if (isHorizontal) { + finalUV.y += finalOffset * stepLength; + } else { + finalUV.x += finalOffset * stepLength; + } + + aaFinalColor = texture(renderedTexture, finalUV); +} \ No newline at end of file diff --git a/shaders/framebuffer/fxaa.vert b/shaders/framebuffer/fxaa.vert new file mode 100644 index 0000000000..f737e51882 --- /dev/null +++ b/shaders/framebuffer/fxaa.vert @@ -0,0 +1,33 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout(location = 0) in vec4 position; +out vec2 texCoord; + +void main() { + texCoord = 0.5 + position.xy * 0.5; + gl_Position = position; +} diff --git a/shaders/framebuffer/hdrAndFiltering.frag b/shaders/framebuffer/hdrAndFiltering.frag index 784577a77a..eda90c9b14 100644 --- a/shaders/framebuffer/hdrAndFiltering.frag +++ b/shaders/framebuffer/hdrAndFiltering.frag @@ -40,19 +40,12 @@ uniform float Value; uniform float Lightness; uniform int nAaSamples; -uniform sampler2DMS hdrFeedingTexture; +uniform sampler2D hdrFeedingTexture; in vec2 texCoord; void main() { - vec4 color = vec4(0.0); - - // Resolving... - for (int i = 0; i < nAaSamples; i++) { - color += texelFetch(hdrFeedingTexture, ivec2(gl_FragCoord), i); - } - - color /= nAaSamples; + vec4 color = texture(hdrFeedingTexture, texCoord); color.rgb *= blackoutFactor; // Applies TMO diff --git a/shaders/framebuffer/raycastframebuffer.frag b/shaders/framebuffer/raycastframebuffer.frag index a4ee03faf4..f7271aadeb 100644 --- a/shaders/framebuffer/raycastframebuffer.frag +++ b/shaders/framebuffer/raycastframebuffer.frag @@ -26,7 +26,7 @@ uniform sampler2D exitColorTexture; uniform sampler2D exitDepthTexture; -uniform sampler2DMS mainDepthTexture; +uniform sampler2D mainDepthTexture; uniform bool insideRaycaster; uniform vec3 cameraPosInRaycaster; @@ -46,9 +46,6 @@ out vec4 finalColor; #define ALPHA_LIMIT 0.99 #define RAYCAST_MAX_STEPS 1000 -#define MAX_AA_SAMPLES 8 - -uniform int nAaSamples; #include <#{getEntryPath}> @@ -76,25 +73,12 @@ void main() { vec3 direction = normalize(diff); float raycastDepth = length(diff); - float raycastDepths[MAX_AA_SAMPLES]; - int i, j; float tmp; - for (i = 0; i < nAaSamples; i++) { - float geoDepth = denormalizeFloat(texelFetch(mainDepthTexture, ivec2(gl_FragCoord), i).x); - float geoRatio = clamp((geoDepth - entryDepth) / (exitDepth - entryDepth), 0.0, 1.0); - raycastDepths[i] = geoRatio * raycastDepth; - } - - for(i = 1; i < nAaSamples; ++i) { - tmp = raycastDepths[i]; - for(j = i; j > 0 && tmp < raycastDepths[j - 1]; --j) { - raycastDepths[j] = raycastDepths[j-1]; - } - raycastDepths[j] = tmp; - } - + float geoDepth = denormalizeFloat(texelFetch(mainDepthTexture, ivec2(gl_FragCoord), 0).x); + float geoRatio = clamp((geoDepth - entryDepth) / (exitDepth - entryDepth), 0.0, 1.0); + raycastDepth = geoRatio * raycastDepth; float currentDepth = 0.0; // todo: shorten depth if geometry is intersecting! @@ -106,20 +90,20 @@ void main() { float aaOpacity = 1.0; int sampleIndex = 0; - float opacityDecay = 1.0 / nAaSamples; + float opacityDecay = 1.0; vec3 accumulatedColor = vec3(0.0); vec3 accumulatedAlpha = vec3(0.0); - for (steps = 0; (accumulatedAlpha.r < ALPHA_LIMIT || accumulatedAlpha.g < ALPHA_LIMIT || accumulatedAlpha.b < ALPHA_LIMIT) && steps < RAYCAST_MAX_STEPS; ++steps) { - while (sampleIndex < nAaSamples && currentDepth + nextStepSize * jitterFactor > raycastDepths[sampleIndex]) { - sampleIndex++; + for (steps = 0; (accumulatedAlpha.r < ALPHA_LIMIT || accumulatedAlpha.g < ALPHA_LIMIT || + accumulatedAlpha.b < ALPHA_LIMIT) && steps < RAYCAST_MAX_STEPS; ++steps) { + if (currentDepth + nextStepSize * jitterFactor > raycastDepth) { aaOpacity -= opacityDecay; } bool shortStepSize = nextStepSize < raycastDepth / 10000000000.0; - if (sampleIndex >= nAaSamples || shortStepSize) { + if (shortStepSize) { break; } @@ -139,7 +123,7 @@ void main() { previousJitterDistance = currentStepSize - jitteredStepSize; - float maxStepSize = raycastDepths[nAaSamples - 1] - currentDepth; + float maxStepSize = raycastDepth - currentDepth; nextStepSize = min(nextStepSize, maxStepSize); diff --git a/shaders/framebuffer/renderframebuffer.frag b/shaders/framebuffer/renderframebuffer.frag index e7e18698b5..a20d685f4e 100644 --- a/shaders/framebuffer/renderframebuffer.frag +++ b/shaders/framebuffer/renderframebuffer.frag @@ -33,7 +33,6 @@ layout(location = 0) out vec4 _out_color_; layout(location = 1) out vec4 gPosition; layout(location = 2) out vec4 gNormal; -layout(location = 3) out vec4 filterBuffer; void main() { Fragment f = getFragment(); diff --git a/src/rendering/framebufferrenderer.cpp b/src/rendering/framebufferrenderer.cpp index 5692ac1b77..4908ef7836 100644 --- a/src/rendering/framebufferrenderer.cpp +++ b/src/rendering/framebufferrenderer.cpp @@ -50,13 +50,17 @@ namespace { constexpr const char* _loggerCat = "FramebufferRenderer"; - constexpr const std::array UniformNames = { - "mainColorTexture", "blackoutFactor", "nAaSamples" + constexpr const std::array UniformNames = { + "mainColorTexture", "blackoutFactor" }; - constexpr const std::array HDRUniformNames = { + constexpr const std::array HDRUniformNames = { "hdrFeedingTexture", "blackoutFactor", "hdrExposure", "gamma", - "Hue", "Saturation", "Value", "nAaSamples" + "Hue", "Saturation", "Value" + }; + + constexpr const std::array FXAAUniformNames = { + "renderedTexture", "inverseScreenSize" }; constexpr const char* ExitFragmentShaderPath = @@ -177,8 +181,12 @@ void FramebufferRenderer::initialize() { glGenFramebuffers(1, &_exitFramebuffer); // HDR / Filtering Buffers - glGenFramebuffers(1, &_hdrBuffers._hdrFilteringFramebuffer); - glGenTextures(1, &_hdrBuffers._hdrFilteringTexture); + glGenFramebuffers(1, &_hdrBuffers.hdrFilteringFramebuffer); + glGenTextures(1, &_hdrBuffers.hdrFilteringTexture); + + // FXAA Buffers + glGenFramebuffers(1, &_fxaaBuffers.fxaaFramebuffer); + glGenTextures(1, &_fxaaBuffers.fxaaTexture); // Allocate Textures/Buffers Memory updateResolution(); @@ -190,31 +198,27 @@ void FramebufferRenderer::initialize() { //===== GBuffers Buffers =====// //==============================// glBindFramebuffer(GL_FRAMEBUFFER, _gBuffers.framebuffer); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.colorTexture, 0 ); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, - GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.positionTexture, 0 ); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, - GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.normalTexture, 0 ); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.depthTexture, 0 ); @@ -228,24 +232,21 @@ void FramebufferRenderer::initialize() { //===== PingPong Buffers =====// //==============================// glBindFramebuffer(GL_FRAMEBUFFER, _pingPongBuffers.framebuffer); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D_MULTISAMPLE, _pingPongBuffers.colorTexture[0], 0 ); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, - GL_TEXTURE_2D_MULTISAMPLE, _pingPongBuffers.colorTexture[1], 0 ); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.depthTexture, 0 ); @@ -260,17 +261,15 @@ void FramebufferRenderer::initialize() { //======================================// // Builds Exit Framebuffer glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, _exitColorTexture, 0 ); - glFramebufferTexture2D( + glFramebufferTexture( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_TEXTURE_2D, _exitDepthTexture, 0 ); @@ -283,12 +282,11 @@ void FramebufferRenderer::initialize() { //===================================// //===== HDR/Filtering Buffers =====// //===================================// - glBindFramebuffer(GL_FRAMEBUFFER, _hdrBuffers._hdrFilteringFramebuffer); - glFramebufferTexture2D( + glBindFramebuffer(GL_FRAMEBUFFER, _hdrBuffers.hdrFilteringFramebuffer); + glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - _hdrBuffers._hdrFilteringTexture, + _hdrBuffers.hdrFilteringTexture, 0 ); @@ -297,13 +295,28 @@ void FramebufferRenderer::initialize() { LERROR("HDR/Filtering framebuffer is not complete"); } + //===================================// + //========== FXAA Buffers =========// + //===================================// + glBindFramebuffer(GL_FRAMEBUFFER, _fxaaBuffers.fxaaFramebuffer); + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + _fxaaBuffers.fxaaTexture, + 0 + ); + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("FXAA framebuffer is not complete"); + } + // JCC: Moved to here to avoid NVidia: "Program/shader state performance warning" // Building programs updateHDRAndFiltering(); + updateFXAA(); updateDeferredcastData(); - _dirtyMsaaSamplingPattern = true; - // Sets back to default FBO glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); @@ -323,13 +336,17 @@ void FramebufferRenderer::initialize() { _hdrUniformCache, HDRUniformNames ); + ghoul::opengl::updateUniformLocations( + *_fxaaProgram, + _fxaaUniformCache, + FXAAUniformNames + ); global::raycasterManager.addListener(*this); global::deferredcasterManager.addListener(*this); // Default GL State for Blending glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } void FramebufferRenderer::deinitialize() { @@ -337,13 +354,15 @@ void FramebufferRenderer::deinitialize() { glDeleteFramebuffers(1, &_gBuffers.framebuffer); glDeleteFramebuffers(1, &_exitFramebuffer); - glDeleteFramebuffers(1, &_hdrBuffers._hdrFilteringFramebuffer); + glDeleteFramebuffers(1, &_hdrBuffers.hdrFilteringFramebuffer); + glDeleteFramebuffers(1, &_fxaaBuffers.fxaaFramebuffer); glDeleteFramebuffers(1, &_pingPongBuffers.framebuffer); glDeleteTextures(1, &_gBuffers.colorTexture); glDeleteTextures(1, &_gBuffers.depthTexture); - glDeleteTextures(1, &_hdrBuffers._hdrFilteringTexture); + glDeleteTextures(1, &_hdrBuffers.hdrFilteringTexture); + glDeleteTextures(1, &_fxaaBuffers.fxaaTexture); glDeleteTextures(1, &_gBuffers.positionTexture); glDeleteTextures(1, &_gBuffers.normalTexture); @@ -371,23 +390,6 @@ void FramebufferRenderer::deferredcastersChanged(Deferredcaster&, _dirtyDeferredcastData = true; } -void FramebufferRenderer::resolveMSAA(float blackoutFactor) { - _resolveProgram->activate(); - - ghoul::opengl::TextureUnit mainColorTextureUnit; - mainColorTextureUnit.activate(); - - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.colorTexture); - _resolveProgram->setUniform(_uniformCache.mainColorTexture, mainColorTextureUnit); - _resolveProgram->setUniform(_uniformCache.blackoutFactor, blackoutFactor); - _resolveProgram->setUniform(_uniformCache.nAaSamples, _nAaSamples); - glBindVertexArray(_screenQuad); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); - - _resolveProgram->deactivate(); -} - void FramebufferRenderer::applyTMO(float blackoutFactor) { const bool doPerformanceMeasurements = global::performanceManager.isEnabled(); std::unique_ptr perfInternal; @@ -402,7 +404,7 @@ void FramebufferRenderer::applyTMO(float blackoutFactor) { ghoul::opengl::TextureUnit hdrFeedingTextureUnit; hdrFeedingTextureUnit.activate(); glBindTexture( - GL_TEXTURE_2D_MULTISAMPLE, + GL_TEXTURE_2D, _pingPongBuffers.colorTexture[_pingPongIndex] ); @@ -418,8 +420,6 @@ void FramebufferRenderer::applyTMO(float blackoutFactor) { _hdrFilteringProgram->setUniform(_hdrUniformCache.Hue, _hue); _hdrFilteringProgram->setUniform(_hdrUniformCache.Saturation, _saturation); _hdrFilteringProgram->setUniform(_hdrUniformCache.Value, _value); - _hdrFilteringProgram->setUniform(_hdrUniformCache.nAaSamples, _nAaSamples); - glBindVertexArray(_screenQuad); glDrawArrays(GL_TRIANGLES, 0, 6); @@ -428,6 +428,40 @@ void FramebufferRenderer::applyTMO(float blackoutFactor) { _hdrFilteringProgram->deactivate(); } +void FramebufferRenderer::applyFXAA() { + const bool doPerformanceMeasurements = global::performanceManager.isEnabled(); + std::unique_ptr perfInternal; + + if (doPerformanceMeasurements) { + perfInternal = std::make_unique( + "FramebufferRenderer::render::FXAA" + ); + } + + _fxaaProgram->activate(); + + ghoul::opengl::TextureUnit renderedTextureUnit; + renderedTextureUnit.activate(); + glBindTexture( + GL_TEXTURE_2D, + _fxaaBuffers.fxaaTexture + ); + + _fxaaProgram->setUniform( + _fxaaUniformCache.renderedTexture, + renderedTextureUnit + ); + + glm::vec2 inverseScreenSize(1.f/_resolution.x, 1.f/_resolution.y); + _fxaaProgram->setUniform(_fxaaUniformCache.inverseScreenSize, inverseScreenSize); + + glBindVertexArray(_screenQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + _fxaaProgram->deactivate(); +} + void FramebufferRenderer::update() { if (_dirtyResolution) { updateResolution(); @@ -460,6 +494,16 @@ void FramebufferRenderer::update() { ); } + if (_fxaaProgram->isDirty()) { + _fxaaProgram->rebuildFromFile(); + + ghoul::opengl::updateUniformLocations( + *_fxaaProgram, + _fxaaUniformCache, + FXAAUniformNames + ); + } + using K = VolumeRaycaster*; using V = std::unique_ptr; for (const std::pair& program : _exitPrograms) { @@ -512,61 +556,7 @@ void FramebufferRenderer::update() { } void FramebufferRenderer::updateResolution() { - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.colorTexture); - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_RGBA32F, - _resolution.x, - _resolution.y, - GL_TRUE - ); - - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.positionTexture); - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_RGBA32F, - _resolution.x, - _resolution.y, - GL_TRUE - ); - - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.normalTexture); - - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_RGBA32F, - _resolution.x, - _resolution.y, - GL_TRUE - ); - - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.depthTexture); - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_DEPTH_COMPONENT32F, - _resolution.x, - _resolution.y, - GL_TRUE - ); - - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _pingPongBuffers.colorTexture[1]); - glTexImage2DMultisample( - GL_TEXTURE_2D_MULTISAMPLE, - _nAaSamples, - GL_RGBA32F, - _resolution.x, - _resolution.y, - GL_TRUE - ); - - - // HDR / Filtering - glBindTexture(GL_TEXTURE_2D, _hdrBuffers._hdrFilteringTexture); - + glBindTexture(GL_TEXTURE_2D, _gBuffers.colorTexture); glTexImage2D( GL_TEXTURE_2D, 0, @@ -578,7 +568,98 @@ void FramebufferRenderer::updateResolution() { GL_FLOAT, nullptr ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, _gBuffers.positionTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + _resolution.x, + _resolution.y, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, _gBuffers.normalTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + _resolution.x, + _resolution.y, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, _gBuffers.depthTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_DEPTH_COMPONENT32F, + _resolution.x, + _resolution.y, + 0, + GL_DEPTH_COMPONENT, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, _pingPongBuffers.colorTexture[1]); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + _resolution.x, + _resolution.y, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // HDR / Filtering + glBindTexture(GL_TEXTURE_2D, _hdrBuffers.hdrFilteringTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + _resolution.x, + _resolution.y, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // FXAA + glBindTexture(GL_TEXTURE_2D, _fxaaBuffers.fxaaTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + _resolution.x, + _resolution.y, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -754,6 +835,17 @@ void FramebufferRenderer::updateHDRAndFiltering() { //_hdrFilteringProgram->setIgnoreUniformLocationError(IgnoreError::Yes); } +void FramebufferRenderer::updateFXAA() { + _fxaaProgram = ghoul::opengl::ProgramObject::Build( + "FXAA Program", + absPath("${SHADERS}/framebuffer/fxaa.vert"), + absPath("${SHADERS}/framebuffer/fxaa.frag") + ); + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; + //_fxaaProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + //_fxaaProgram->setIgnoreUniformLocationError(IgnoreError::Yes); +} + void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFactor) { // Set OpenGL default rendering state glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); @@ -842,18 +934,25 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac // Disabling depth test for filtering and hdr glDisable(GL_DEPTH_TEST); - // When applying the TMO, the result is saved to the default FBO to be displayed - // by the Operating System. Also, the resolve procedure is executed in this step. - glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); - glViewport(0, 0, _resolution.x, _resolution.y); - - if (_disableHDR) { - resolveMSAA(blackoutFactor); + if (_enableFXAA) { + glBindFramebuffer(GL_FRAMEBUFFER, _fxaaBuffers.fxaaFramebuffer); } else { - // Apply the selected TMO on the results and resolve the result for the default FBO - applyTMO(blackoutFactor); + // When applying the TMO, the result is saved to the default FBO to be displayed + // by the Operating System. Also, the resolve procedure is executed in this step. + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); } + + glViewport(0, 0, _resolution.x, _resolution.y); + + // Apply the selected TMO on the results and resolve the result for the default FBO + applyTMO(blackoutFactor); + + if (_enableFXAA) { + glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); + applyFXAA(); + } + } void FramebufferRenderer::performRaycasterTasks(const std::vector& tasks) { @@ -916,10 +1015,9 @@ void FramebufferRenderer::performRaycasterTasks(const std::vector ghoul::opengl::TextureUnit mainDepthTextureUnit; mainDepthTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.depthTexture); + glBindTexture(GL_TEXTURE_2D, _gBuffers.depthTexture); raycastProgram->setUniform("mainDepthTexture", mainDepthTextureUnit); - raycastProgram->setUniform("nAaSamples", _nAaSamples); raycastProgram->setUniform("windowSize", static_cast(_resolution)); glDisable(GL_DEPTH_TEST); @@ -971,9 +1069,8 @@ void FramebufferRenderer::performDeferredTasks( // adding G-Buffer ghoul::opengl::TextureUnit mainDColorTextureUnit; mainDColorTextureUnit.activate(); - //glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers._colorTexture); glBindTexture( - GL_TEXTURE_2D_MULTISAMPLE, + GL_TEXTURE_2D, _pingPongBuffers.colorTexture[fromIndex] ); deferredcastProgram->setUniform( @@ -983,7 +1080,7 @@ void FramebufferRenderer::performDeferredTasks( ghoul::opengl::TextureUnit mainPositionTextureUnit; mainPositionTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.positionTexture); + glBindTexture(GL_TEXTURE_2D, _gBuffers.positionTexture); deferredcastProgram->setUniform( "mainPositionTexture", mainPositionTextureUnit @@ -991,14 +1088,12 @@ void FramebufferRenderer::performDeferredTasks( ghoul::opengl::TextureUnit mainNormalTextureUnit; mainNormalTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _gBuffers.normalTexture); + glBindTexture(GL_TEXTURE_2D, _gBuffers.normalTexture); deferredcastProgram->setUniform( "mainNormalTexture", mainNormalTextureUnit ); - deferredcastProgram->setUniform("nAaSamples", _nAaSamples); - deferredcaster->preRaycast( deferredcasterTask.renderData, _deferredcastData[deferredcaster], @@ -1036,22 +1131,6 @@ void FramebufferRenderer::setResolution(glm::ivec2 res) { _dirtyResolution = true; } -void FramebufferRenderer::setNAaSamples(int nAaSamples) { - ghoul_assert( - nAaSamples >= 1 && nAaSamples <= 8, - "Number of AA samples has to be between 1 and 8" - ); - _nAaSamples = nAaSamples; - if (_nAaSamples == 0) { - _nAaSamples = 1; - } - if (_nAaSamples > 8) { - LERROR("Framebuffer renderer does not support more than 8 MSAA samples."); - _nAaSamples = 8; - } - _dirtyMsaaSamplingPattern = true; -} - void FramebufferRenderer::setDisableHDR(bool disable) { _disableHDR = std::move(disable); } @@ -1079,8 +1158,8 @@ void FramebufferRenderer::setSaturation(float sat) { _saturation = std::move(sat); } -int FramebufferRenderer::nAaSamples() const { - return _nAaSamples; +void FramebufferRenderer::enableFXAA(bool enable) { + _enableFXAA = std::move(enable); } void FramebufferRenderer::updateRendererData() { diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index de5ed0052a..49981a7a1a 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -192,13 +192,6 @@ namespace { "direction for tilted display systems in clustered immersive environments." }; - constexpr openspace::properties::Property::PropertyInfo AaSamplesInfo = { - "AaSamples", - "Number of Anti-aliasing samples", - "This value determines the number of anti-aliasing samples to be used in the " - "rendering for the MSAA method." - }; - constexpr openspace::properties::Property::PropertyInfo DisableHDRPipelineInfo = { "DisableHDRPipeline", "Disable HDR Rendering", @@ -253,6 +246,12 @@ namespace { "The blackout factor of the rendering. This can be used for fading in or out the " "rendering window" }; + + constexpr openspace::properties::Property::PropertyInfo FXAAInfo = { + "FXAA", + "Enable FXAA", + "Enable FXAA" + }; } // namespace @@ -273,7 +272,7 @@ RenderEngine::RenderEngine() #endif // OPENSPACE_WITH_INSTRUMENTATION , _disableMasterRendering(DisableMasterInfo, false) , _globalBlackOutFactor(GlobalBlackoutFactorInfo, 1.f, 0.f, 1.f) - , _nAaSamples(AaSamplesInfo, 4, 1, 8) + , _enableFXAA(FXAAInfo, true) , _disableHDRPipeline(DisableHDRPipelineInfo, false) , _hdrExposure(HDRExposureInfo, 3.7f, 0.01f, 10.0f) , _gamma(GammaInfo, 0.95f, 0.01f, 5.0f) @@ -310,12 +309,12 @@ RenderEngine::RenderEngine() addProperty(_showVersionInfo); addProperty(_showCameraInfo); - _nAaSamples.onChange([this](){ + _enableFXAA.onChange([this]() { if (_renderer) { - _renderer->setNAaSamples(_nAaSamples); + _renderer->enableFXAA(_enableFXAA); } - }); - addProperty(_nAaSamples); + }); + addProperty(_enableFXAA); _disableHDRPipeline.onChange([this]() { if (_renderer) { @@ -457,8 +456,6 @@ void RenderEngine::initialize() { void RenderEngine::initializeGL() { LTRACE("RenderEngine::initializeGL(begin)"); - _nAaSamples = global::windowDelegate.currentNumberOfAaSamples(); - std::string renderingMethod = global::configuration.renderingMethod; if (renderingMethod == "ABuffer") { using Version = ghoul::systemcapabilities::Version; @@ -1086,7 +1083,7 @@ void RenderEngine::setRenderer(std::unique_ptr renderer) { _renderer = std::move(renderer); _renderer->setResolution(renderingResolution()); - _renderer->setNAaSamples(_nAaSamples); + _renderer->enableFXAA(true); _renderer->setHDRExposure(_hdrExposure); _renderer->initialize(); }