From: dbp Date: Wed, 13 Jul 2016 09:19:27 +0000 (+0300) Subject: 0027607: Visualization - Implement adaptive screen space sampling in path tracing X-Git-Tag: V7_1_0_beta~117 X-Git-Url: http://git.dev.opencascade.org/gitweb/?p=occt.git;a=commitdiff_plain;h=3a9b5dc86a1d69f2deaf0f6f2e8b2a8c87668c39 0027607: Visualization - Implement adaptive screen space sampling in path tracing This commit provides useful functionality for path tracing rendering core. 1) Graphic3d_RenderingParams class was extended with additional AdaptiveScreenSampling option (disabled by default). If this option is enabled, path tracing tries to adjust the number of samples for different screen areas. In this way, the more complex areas (from the point of light conditions) are sampled more intensively, while the simple areas are sampled very rarely. For example, caustics and glossy reflections are typical candidates for more precise sampling. In general, this allows to equalize image convergence and not to waste resources for already converged areas. It is also possible to visualize sampling densities by enabling ShowSamplingTiles option (activating and deactivating this option does not affect on the accumulated image). 2) Mixing OpenGL and ray-tracing output has been changed. Now blending is performed using OpenGL functionality, while ray-tracing shaders only output correct Z-value. Test case bugs vis bug27083 has been updated, since the alpha value is now correctly set by Ray-Tracing to 1, opaque. --- diff --git a/samples/tcl/pathtrace.tcl b/samples/tcl/pathtrace.tcl index 670b4ce85d..d1bf4499f2 100644 --- a/samples/tcl/pathtrace.tcl +++ b/samples/tcl/pathtrace.tcl @@ -15,7 +15,7 @@ vlight del 0 vlight del 1 vlight add positional head 0 pos 0.5 0.5 0.85 vlight change 0 sm 0.06 -vlight change 0 int 60.0 +vlight change 0 int 30.0 vvbo 0 vsetdispmode 1 diff --git a/src/Graphic3d/Graphic3d_RenderingParams.hxx b/src/Graphic3d/Graphic3d_RenderingParams.hxx index 8e0f231332..687fe0623a 100644 --- a/src/Graphic3d/Graphic3d_RenderingParams.hxx +++ b/src/Graphic3d/Graphic3d_RenderingParams.hxx @@ -61,6 +61,8 @@ public: IsTransparentShadowEnabled (Standard_False), UseEnvironmentMapBackground (Standard_False), CoherentPathTracingMode (Standard_False), + AdaptiveScreenSampling (Standard_False), + ShowSamplingTiles (Standard_False), // stereoscopic parameters StereoMode (Graphic3d_StereoMode_QuadBuffer), AnaglyphFilter (Anaglyph_RedCyan_Optimized), @@ -99,6 +101,8 @@ public: Standard_Boolean IsTransparentShadowEnabled; //!< enables/disables light propagation through transparent media, False by default Standard_Boolean UseEnvironmentMapBackground; //!< enables/disables environment map background Standard_Boolean CoherentPathTracingMode; //!< enables/disables 'coherent' tracing mode (single RNG seed within 16x16 image blocks) + Standard_Boolean AdaptiveScreenSampling; //!< enables/disables adaptive screen sampling mode for path tracing, FALSE by default + Standard_Boolean ShowSamplingTiles; //!< enables/disables debug mode for adaptive screen sampling, FALSE by default Graphic3d_StereoMode StereoMode; //!< stereoscopic output mode, Graphic3d_StereoMode_QuadBuffer by default Anaglyph AnaglyphFilter; //!< filter for anaglyph output, Anaglyph_RedCyan_Optimized by default diff --git a/src/Graphic3d/Graphic3d_TypeOfLimit.hxx b/src/Graphic3d/Graphic3d_TypeOfLimit.hxx index 8cc1cbde68..beaefd95a3 100644 --- a/src/Graphic3d/Graphic3d_TypeOfLimit.hxx +++ b/src/Graphic3d/Graphic3d_TypeOfLimit.hxx @@ -17,12 +17,15 @@ //! Type of graphic resource limit. enum Graphic3d_TypeOfLimit { - Graphic3d_TypeOfLimit_MaxNbLights, //!< maximum number of active light sources - Graphic3d_TypeOfLimit_MaxNbClipPlanes, //!< maximum number of active clipping planes - Graphic3d_TypeOfLimit_MaxNbViews, //!< maximum number of views - Graphic3d_TypeOfLimit_MaxTextureSize, //!< maximum size of texture - Graphic3d_TypeOfLimit_MaxMsaa, //!< maximum number of MSAA samples - Graphic3d_TypeOfLimit_NB //!< number of elements in this enumeration + Graphic3d_TypeOfLimit_MaxNbLights, //!< maximum number of active light sources + Graphic3d_TypeOfLimit_MaxNbClipPlanes, //!< maximum number of active clipping planes + Graphic3d_TypeOfLimit_MaxNbViews, //!< maximum number of views + Graphic3d_TypeOfLimit_MaxTextureSize, //!< maximum size of texture + Graphic3d_TypeOfLimit_MaxMsaa, //!< maximum number of MSAA samples + Graphic3d_TypeOfLimit_HasRayTracing, //!< indicates whether ray tracing is supported + Graphic3d_TypeOfLimit_HasRayTracingTextures, //!< indicates whether ray tracing textures are supported + Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSampling, //!< indicates whether adaptive screen sampling is supported + Graphic3d_TypeOfLimit_NB //!< number of elements in this enumeration }; #endif // _Graphic3d_TypeOfLimit_HeaderFile diff --git a/src/OpenGl/FILES b/src/OpenGl/FILES index 7fe3a11a1c..326d2bcf33 100755 --- a/src/OpenGl/FILES +++ b/src/OpenGl/FILES @@ -131,6 +131,8 @@ OpenGl_Sphere.cxx OpenGl_Sphere.hxx OpenGl_StencilTest.cxx OpenGl_StencilTest.hxx +OpenGl_TileSampler.hxx +OpenGl_TileSampler.cxx OpenGl_TextureBufferArb.cxx OpenGl_TextureBufferArb.hxx OpenGl_Vec.hxx @@ -141,4 +143,5 @@ OpenGl_VertexBufferCompat.cxx OpenGl_VertexBufferCompat.hxx OpenGl_VertexBufferEditor.hxx OpenGl_TextBuilder.hxx -OpenGl_TextBuilder.cxx \ No newline at end of file +OpenGl_TextBuilder.cxx +OpenGl_HaltonSampler.hxx diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index cf724a84f2..fe8be66d39 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -157,6 +157,9 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps) myIsInitialized (Standard_False), myIsStereoBuffers (Standard_False), myIsGlNormalizeEnabled (Standard_False), + myHasRayTracing (Standard_False), + myHasRayTracingTextures (Standard_False), + myHasRayTracingAdaptiveSampling (Standard_False), #if !defined(GL_ES_VERSION_2_0) myPointSpriteOrig (GL_UPPER_LEFT), myRenderMode (GL_RENDER), @@ -2302,6 +2305,20 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile) arbTBO = (OpenGl_ArbTBO* )(&(*myFuncs)); arbIns = (OpenGl_ArbIns* )(&(*myFuncs)); + // check whether ray tracing mode is supported + myHasRayTracing = has31 + && arbTboRGB32 + && arbFBOBlit != NULL; + + // check whether textures in ray tracing mode are supported + myHasRayTracingTextures = myHasRayTracing + && arbTexBindless != NULL; + + // check whether adaptive screen sampling in ray tracing mode is supported + myHasRayTracingAdaptiveSampling = myHasRayTracing + && has44 + && CheckExtension ("GL_NV_shader_atomic_float"); + if (!has32) { checkWrongVersion (3, 2); diff --git a/src/OpenGl/OpenGl_Context.hxx b/src/OpenGl/OpenGl_Context.hxx index 8bb04444c7..1533183f78 100644 --- a/src/OpenGl/OpenGl_Context.hxx +++ b/src/OpenGl/OpenGl_Context.hxx @@ -458,6 +458,15 @@ public: //! @return value for GL_MAX_CLIP_PLANES Standard_Integer MaxClipPlanes() const { return myMaxClipPlanes; } + //! @return TRUE if ray tracing mode is supported + Standard_Boolean HasRayTracing() const { return myHasRayTracing; } + + //! @return TRUE if textures in ray tracing mode are supported + Standard_Boolean HasRayTracingTextures() const { return myHasRayTracingTextures; } + + //! @return TRUE if adaptive screen sampling in ray tracing mode is supported + Standard_Boolean HasRayTracingAdaptiveSampling() const { return myHasRayTracingAdaptiveSampling; } + //! Returns true if VBO is supported and permitted. inline bool ToUseVbo() const { @@ -778,6 +787,10 @@ private: // context info Standard_Boolean myIsGlNormalizeEnabled; //!< GL_NORMALIZE flag //!< Used to tell OpenGl that normals should be normalized + Standard_Boolean myHasRayTracing; //! indicates whether ray tracing mode is supported + Standard_Boolean myHasRayTracingTextures; //! indicates whether textures in ray tracing mode are supported + Standard_Boolean myHasRayTracingAdaptiveSampling; //! indicates whether adaptive screen sampling in ray tracing mode is supported + Handle(OpenGl_ShaderManager) myShaderManager; //! support object for managing shader programs private: //! @name fields tracking current state diff --git a/src/OpenGl/OpenGl_GlCore11Fwd.hxx b/src/OpenGl/OpenGl_GlCore11Fwd.hxx index 358e8333ef..8f84f63dea 100644 --- a/src/OpenGl/OpenGl_GlCore11Fwd.hxx +++ b/src/OpenGl/OpenGl_GlCore11Fwd.hxx @@ -352,6 +352,13 @@ public: //! @name Texture mapping { ::glCopyTexSubImage1D(target, level, xoffset, x, y, width); } + + inline void glGetTexImage (GLenum target, GLint level, + GLenum format, GLenum type, + GLvoid* pixels) + { + ::glGetTexImage (target, level, format, type, pixels); + } #endif #if !defined(GL_ES_VERSION_2_0) @@ -400,13 +407,6 @@ public: //! @name Texture mapping ::glGetTexEnviv (target, pname, params); } - inline void glGetTexImage (GLenum target, GLint level, - GLenum format, GLenum type, - GLvoid* pixels) - { - ::glGetTexImage(target, level, format, type, pixels); - } - inline void glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat* params) { ::glGetTexLevelParameterfv (target, level, pname, params); diff --git a/src/OpenGl/OpenGl_GlFunctions.hxx b/src/OpenGl/OpenGl_GlFunctions.hxx index ff09ebb4aa..37af4709af 100644 --- a/src/OpenGl/OpenGl_GlFunctions.hxx +++ b/src/OpenGl/OpenGl_GlFunctions.hxx @@ -108,11 +108,13 @@ #define GL_ALPHA8 0x803C #define GL_ALPHA16 0x803E - #define GL_RG 0x8227 - #define GL_RG8 0x822B - #define GL_RG16 0x822C - #define GL_RG16F 0x822F - #define GL_RG32F 0x8230 + #define GL_RG 0x8227 + #define GL_RG8 0x822B + #define GL_RG16 0x822C + #define GL_RG16F 0x822F + #define GL_RG32F 0x8230 + #define GL_RG_INTEGER 0x8228 + #define GL_RED_INTEGER 0x8D94 #define GL_R8I 0x8231 #define GL_R8UI 0x8232 diff --git a/src/OpenGl/OpenGl_GraphicDriver.cxx b/src/OpenGl/OpenGl_GraphicDriver.cxx index 2b61a73cdc..2ca1251feb 100644 --- a/src/OpenGl/OpenGl_GraphicDriver.cxx +++ b/src/OpenGl/OpenGl_GraphicDriver.cxx @@ -386,6 +386,12 @@ Standard_Integer OpenGl_GraphicDriver::InquireLimit (const Graphic3d_TypeOfLimit return !aCtx.IsNull() ? aCtx->MaxTextureSize() : 1024; case Graphic3d_TypeOfLimit_MaxMsaa: return !aCtx.IsNull() ? aCtx->MaxMsaaSamples() : 0; + case Graphic3d_TypeOfLimit_HasRayTracing: + return (!aCtx.IsNull() && aCtx->HasRayTracing()) ? 1 : 0; + case Graphic3d_TypeOfLimit_HasRayTracingTextures: + return (!aCtx.IsNull() && aCtx->HasRayTracingTextures()) ? 1 : 0; + case Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSampling: + return (!aCtx.IsNull() && aCtx->HasRayTracingAdaptiveSampling()) ? 1 : 0; case Graphic3d_TypeOfLimit_NB: return 0; } diff --git a/src/OpenGl/OpenGl_HaltonSampler.hxx b/src/OpenGl/OpenGl_HaltonSampler.hxx new file mode 100644 index 0000000000..63f8a4c02f --- /dev/null +++ b/src/OpenGl/OpenGl_HaltonSampler.hxx @@ -0,0 +1,188 @@ +// Copyright (c) 2012 Leonhard Gruenschloss (leonhard@gruenschloss.org) +// +// 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. + +#ifndef _OpenGl_HaltonSampler_H +#define _OpenGl_HaltonSampler_H + +#include +#include + +//! Compute points of the Halton sequence with with digit-permutations for different bases. +class OpenGl_HaltonSampler +{ +public: + + //! Return the number of supported dimensions. + static unsigned get_num_dimensions() { return 3u; } + +public: + + //! Init the permutation arrays using Faure-permutations. + //! Alternatively, initRandom() can be called before the sampling functionality can be used. + void initFaure(); + + //! Init the permutation arrays using randomized permutations. + //! Alternatively, initFaure() can be called before the sampling functionality can be used. + //! The client needs to specify a random number generator function object that can be used to generate a random sequence of integers. + //! That is: if f is a random number generator and N is a positive integer, + //! then f(N) will return an integer less than N and greater than or equal to 0. + template + void initRandom (Random_number_generator& theRand); + + //! Return the Halton sample for the given dimension (component) and index. + //! The client must have called initRandom or initFaure() at least once before. + //! dimension must be smaller than the value returned by get_num_dimensions(). + float sample (unsigned theDimension, unsigned theIndex) const + { + switch (theDimension) + { + case 0: return halton2 (theIndex); + case 1: return halton3 (theIndex); + case 2: return halton5 (theIndex); + } + return 0.0f; + } + +private: + + static unsigned short invert (unsigned short theBase, unsigned short theDigits, + unsigned short theIndex, const std::vector& thePerm) + { + unsigned short aResult = 0; + for (unsigned short i = 0; i < theDigits; ++i) + { + aResult = aResult * theBase + thePerm[theIndex % theBase]; + theIndex /= theBase; + } + return aResult; + } + + void initTables (const std::vector >& thePerm) + { + for (unsigned short i = 0; i < 243; ++i) + { + myPerm3[i] = invert (3, 5, i, thePerm[3]); + } + for (unsigned short i = 0; i < 125; ++i) + { + myPerm5[i] = invert (5, 3, i, thePerm[5]); + } + } + + //! Special case: radical inverse in base 2, with direct bit reversal. + float halton2 (unsigned theIndex) const + { + theIndex = (theIndex << 16) | (theIndex >> 16); + theIndex = ((theIndex & 0x00ff00ff) << 8) | ((theIndex & 0xff00ff00) >> 8); + theIndex = ((theIndex & 0x0f0f0f0f) << 4) | ((theIndex & 0xf0f0f0f0) >> 4); + theIndex = ((theIndex & 0x33333333) << 2) | ((theIndex & 0xcccccccc) >> 2); + theIndex = ((theIndex & 0x55555555) << 1) | ((theIndex & 0xaaaaaaaa) >> 1); + union Result { unsigned u; float f; } aResult; // Write reversed bits directly into floating-point mantissa. + aResult.u = 0x3f800000u | (theIndex >> 9); + return aResult.f - 1.0f; + } + + float halton3 (unsigned theIndex) const + { + return (myPerm3[theIndex % 243u] * 14348907u + + myPerm3[(theIndex / 243u) % 243u] * 59049u + + myPerm3[(theIndex / 59049u) % 243u] * 243u + + myPerm3[(theIndex / 14348907u) % 243u]) * float(0.999999999999999f / 3486784401u); // Results in [0,1). + } + + float halton5 (unsigned theIndex) const + { + return (myPerm5[theIndex % 125u] * 1953125u + + myPerm5[(theIndex / 125u) % 125u] * 15625u + + myPerm5[(theIndex / 15625u) % 125u] * 125u + + myPerm5[(theIndex / 1953125u) % 125u]) * float(0.999999999999999f / 244140625u); // Results in [0,1). + } + +private: + + unsigned short myPerm3[243]; + unsigned short myPerm5[125]; + +}; + +inline void OpenGl_HaltonSampler::initFaure() +{ + const unsigned THE_MAX_BASE = 5u; + std::vector > aPerms(THE_MAX_BASE + 1); + for (unsigned k = 1; k <= 3; ++k) // Keep identity permutations for base 1, 2, 3. + { + aPerms[k].resize(k); + for (unsigned i = 0; i < k; ++i) + { + aPerms[k][i] = static_cast (i); + } + } + + for (unsigned aBase = 4; aBase <= THE_MAX_BASE; ++aBase) + { + aPerms[aBase].resize(aBase); + const unsigned b = aBase / 2; + if (aBase & 1) // odd + { + for (unsigned i = 0; i < aBase - 1; ++i) + { + aPerms[aBase][i + (i >= b)] = aPerms[aBase - 1][i] + (aPerms[aBase - 1][i] >= b); + } + aPerms[aBase][b] = static_cast (b); + } + else // even + { + for (unsigned i = 0; i < b; ++i) + { + aPerms[aBase][i] = 2 * aPerms[b][i]; + aPerms[aBase][b + i] = 2 * aPerms[b][i] + 1; + } + } + } + initTables (aPerms); +} + +template +void OpenGl_HaltonSampler::initRandom (Random_number_generator& theRand) +{ + const unsigned THE_MAX_BASE = 5u; + std::vector > aPerms(THE_MAX_BASE + 1); + for (unsigned k = 1; k <= 3; ++k) // Keep identity permutations for base 1, 2, 3. + { + aPerms[k].resize (k); + for (unsigned i = 0; i < k; ++i) + { + aPerms[k][i] = i; + } + } + + for (unsigned aBase = 4; aBase <= THE_MAX_BASE; ++aBase) + { + aPerms[aBase].resize (aBase); + for (unsigned i = 0; i < aBase; ++i) + { + aPerms[aBase][i] = i; + } + std::random_shuffle (aPerms[aBase].begin(), aPerms[aBase].end(), theRand); + } + initTables (aPerms); +} + +#endif // _OpenGl_HaltonSampler_H diff --git a/src/OpenGl/OpenGl_SceneGeometry.cxx b/src/OpenGl/OpenGl_SceneGeometry.cxx index d363629004..3864a62442 100755 --- a/src/OpenGl/OpenGl_SceneGeometry.cxx +++ b/src/OpenGl/OpenGl_SceneGeometry.cxx @@ -502,6 +502,11 @@ Standard_Boolean OpenGl_RaytraceGeometry::ReleaseTextures (const Handle(OpenGl_C // ======================================================================= Standard_Integer OpenGl_RaytraceGeometry::AddTexture (const Handle(OpenGl_Texture)& theTexture) { + if (theTexture->TextureId() == OpenGl_Texture::NO_TEXTURE) + { + return -1; + } + NCollection_Vector::iterator anIter = std::find (myTextures.begin(), myTextures.end(), theTexture); diff --git a/src/OpenGl/OpenGl_TileSampler.cxx b/src/OpenGl/OpenGl_TileSampler.cxx new file mode 100644 index 0000000000..6e8a1dc393 --- /dev/null +++ b/src/OpenGl/OpenGl_TileSampler.cxx @@ -0,0 +1,187 @@ +// Created on: 2016-06-16 +// Created by: Denis BOGOLEPOV & Danila ULYANOV +// Copyright (c) 2016 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#include +#include +#include + +namespace +{ + //! Scale factor for estimating visual error. + static const float THE_SCALE_FACTOR = 1.0f / 1e6f; +} + +//======================================================================= +//function : OpenGl_TileSampler +//purpose : +//======================================================================= +OpenGl_TileSampler::OpenGl_TileSampler() +: mySample (0), + mySizeX (0), + mySizeY (0), + myTilesX (0), + myTilesY (0) +{ + mySampler.initFaure(); +} + +//======================================================================= +//function : GrabVarianceMap +//purpose : +//======================================================================= +void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext) +{ +#if !defined(GL_ES_VERSION_2_0) + std::vector aRawData (NbTiles(), 0); + + theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, &aRawData.front()); + const GLenum anErr = theContext->core11fwd->glGetError(); + if (anErr != GL_NO_ERROR) + { + theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM, + "Error! Failed to fetch visual error map from the GPU"); + } + else + { + for (int aTileIdx = 0, aNbTiles = NbTiles(); aTileIdx < aNbTiles; ++aTileIdx) + { + myVarianceMap[aTileIdx] = aRawData[aTileIdx] * THE_SCALE_FACTOR; + } + + for (int aX = 0; aX < myTilesX; ++aX) + { + for (int aY = 0; aY < myTilesY; ++aY) + { + ChangeTile (aX, aY) *= 1.0f / TileArea (aX, aY); // average error over the tile + + if (aY > 0) + { + ChangeTile (aX, aY) += Tile (aX, aY - 1); + } + } + } + + myMarginalMap.resize (myTilesX); // build marginal distribution + for (int aX = 0; aX < myTilesX; ++aX) + { + myMarginalMap[aX] = Tile (aX, myTilesY - 1); + + if (aX > 0) + myMarginalMap[aX] += myMarginalMap[aX - 1]; + } + } +#else + // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead + (void )theContext; +#endif +} + +//======================================================================= +//function : Sample +//purpose : +//======================================================================= +void OpenGl_TileSampler::Sample (int& theOffsetX, + int& theOffsetY) +{ + int aX = 0; + int aY = 0; + + const float aKsiX = mySampler.sample (0, mySample) * myMarginalMap.back(); + for (; aX < myTilesX - 1; ++aX) + { + if (aKsiX <= myMarginalMap[aX]) + { + break; + } + } + + const float aKsiY = mySampler.sample (1, mySample) * Tile (aX, myTilesY - 1); + for (; aY < myTilesY - 1; ++aY) + { + if (aKsiY <= Tile (aX, aY)) + { + break; + } + } + + theOffsetX = aX * TileSize(); + theOffsetY = aY * TileSize(); + + ++mySample; +} + +//======================================================================= +//function : SetSize +//purpose : +//======================================================================= +void OpenGl_TileSampler::SetSize (const int theSizeX, + const int theSizeY) +{ + if (mySizeX == theSizeX + && mySizeY == theSizeY) + { + return; + } + + mySizeX = theSizeX; + mySizeY = theSizeY; + + myTilesX = static_cast (ceilf (static_cast (mySizeX) / TileSize())); + myTilesY = static_cast (ceilf (static_cast (mySizeY) / TileSize())); + + myVarianceMap.resize (myTilesX * myTilesY); +} + +//======================================================================= +//function : Upload +//purpose : +//======================================================================= +void OpenGl_TileSampler::Upload (const Handle(OpenGl_Context)& theContext, + const Handle(OpenGl_Texture)& theTexture, + bool theAdaptive) +{ + if (theTexture.IsNull()) + { + return; + } + + std::vector aData (myTilesX * myTilesY * 2); + for (int aX = 0; aX < myTilesX; ++aX) + { + for (int aY = 0; aY < myTilesY; ++aY) + { + if (!theAdaptive) + { + aData[(aY * myTilesX + aX) * 2 + 0] = aX * TileSize(); + aData[(aY * myTilesX + aX) * 2 + 1] = aY * TileSize(); + } + else + { + Sample (aData[(aY * myTilesX + aX) * 2 + 0], + aData[(aY * myTilesX + aX) * 2 + 1]); + } + } + } + + theTexture->Bind (theContext); + + theContext->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, GL_RG32I, myTilesX, myTilesY, 0, GL_RG_INTEGER, GL_UNSIGNED_INT, &aData.front()); + const GLenum anErr = theContext->core11fwd->glGetError(); + if (anErr != GL_NO_ERROR) + { + theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM, + "Error! Failed to upload tile offset map on the GPU"); + } +} diff --git a/src/OpenGl/OpenGl_TileSampler.hxx b/src/OpenGl/OpenGl_TileSampler.hxx new file mode 100644 index 0000000000..4f272930b0 --- /dev/null +++ b/src/OpenGl/OpenGl_TileSampler.hxx @@ -0,0 +1,110 @@ +// Created on: 2016-06-16 +// Created by: Denis BOGOLEPOV & Danila ULYANOV +// Copyright (c) 2016 OPEN CASCADE SAS +// +// This file is part of Open CASCADE Technology software library. +// +// This library is free software; you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License version 2.1 as published +// by the Free Software Foundation, with special exception defined in the file +// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT +// distribution for complete text of the license and disclaimer of any warranty. +// +// Alternatively, this file may be used under the terms of Open CASCADE +// commercial license or contractual agreement. + +#ifndef _OpenGl_TileSampler_H +#define _OpenGl_TileSampler_H + +#include +#include + +#include + +//! Tool object used for sampling screen tiles according to estimated pixel variance (used in path tracing engine). +//! To improve GPU thread coherency, render window is split into pixel blocks or tiles. +class OpenGl_TileSampler +{ +public: + + //! Size of individual tile in pixels. + static int TileSize() { return 32; } + +public: + + //! Creates new tile sampler. + Standard_EXPORT OpenGl_TileSampler(); + + //! Returns width of ray-tracing viewport. + int SizeX() const { return mySizeX; } + + //! Returns height of ray-tracing viewport. + int SizeY() const { return mySizeY; } + + //! Returns number of tiles in X dimension. + int NbTilesX() const { return myTilesX; } + + //! Returns number of tiles in Y dimension. + int NbTilesY() const { return myTilesY; } + + //! Returns total number of tiles in viewport. + int NbTiles() const { return myTilesX * myTilesY; } + + //! Specifies size of ray-tracing viewport. + Standard_EXPORT void SetSize (const int theSizeX, + const int theSizeY); + + //! Returns number of pixels in the given tile. + int TileArea (const int theX, + const int theY) const + { + return Min (TileSize(), mySizeX - theX * TileSize()) + * Min (TileSize(), mySizeY - theY * TileSize()); + } + + //! Fetches current error estimation from the GPU and + //! builds 2D discrete distribution for tile sampling. + Standard_EXPORT void GrabVarianceMap (const Handle(OpenGl_Context)& theContext); + + //! Samples tile location according to estimated error. + Standard_EXPORT void Sample (int& theOffsetX, + int& theOffsetY); + + //! Resets tile sampler to initial state. + void Reset() { mySample = 0; } + + //! Uploads offsets of sampled tiles to the given OpenGL texture. + Standard_EXPORT void Upload (const Handle(OpenGl_Context)& theContext, + const Handle(OpenGl_Texture)& theTexture, + bool theAdaptive); + +protected: + + //! Returns tile value (estimated error). + float Tile (const int theX, + const int theY) const + { + return myVarianceMap[theY * myTilesX + theX]; + } + + //! Returns tile value (estimated error). + float& ChangeTile (const int theX, + const int theY) + { + return myVarianceMap[theY * myTilesX + theX]; + } + +protected: + + std::vector myVarianceMap; //!< Estimation of visual error per tile + std::vector myMarginalMap; //!< Marginal distribution of 2D error map + OpenGl_HaltonSampler mySampler; //!< Halton sequence generator + int mySample; //!< Index of generated sample + int mySizeX; //!< Width of ray-tracing viewport + int mySizeY; //!< Height of ray-tracing viewport + int myTilesX; //!< Number of tiles in X dimension + int myTilesY; //!< Number of tiles in Y dimension + +}; + +#endif // _OpenGl_TileSampler_H diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index 514ae9313f..06a805323c 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -513,6 +514,10 @@ protected: Handle(OpenGl_Texture) myTextureEnv; + //! Framebuffers for OpenGL output. + Handle(OpenGl_FrameBuffer) myOpenGlFBO; + Handle(OpenGl_FrameBuffer) myOpenGlFBO2; + protected: //! @name Rendering properties //! Two framebuffers (left and right views) store cached main presentation @@ -569,7 +574,7 @@ protected: //! @name data types related to ray-tracing OpenGl_RT_uDirectLB, OpenGl_RT_uDirectRT, OpenGl_RT_uDirectRB, - OpenGl_RT_uViewMat, + OpenGl_RT_uViewPrMat, OpenGl_RT_uUnviewMat, // 3D scene params @@ -590,6 +595,10 @@ protected: //! @name data types related to ray-tracing OpenGl_RT_uTexSamplersArray, OpenGl_RT_uBlockedRngEnabled, + // size of render window + OpenGl_RT_uWinSizeX, + OpenGl_RT_uWinSizeY, + // sampled frame params OpenGl_RT_uSampleWeight, OpenGl_RT_uFrameRndSeed, @@ -599,10 +608,14 @@ protected: //! @name data types related to ray-tracing OpenGl_RT_uOffsetY, OpenGl_RT_uSamples, + // adaptive path tracing images + OpenGl_RT_uRenderImage, + OpenGl_RT_uOffsetImage, + OpenGl_RT_NbVariables // special field }; - //! Defines texture samplers. + //! Defines OpenGL texture samplers. enum ShaderSamplerNames { OpenGl_RT_SceneNodeInfoTexture = 0, @@ -622,10 +635,17 @@ protected: //! @name data types related to ray-tracing OpenGl_RT_FsaaInputTexture = 11, OpenGl_RT_PrevAccumTexture = 12, - OpenGl_RT_DepthTexture = 13, - OpenGl_RT_OpenGlColorTexture = 14, - OpenGl_RT_OpenGlDepthTexture = 15 + OpenGl_RT_RaytraceDepthTexture = 13 + }; + + //! Defines OpenGL image samplers. + enum ShaderImageNames + { + OpenGl_RT_OutputImageLft = 0, + OpenGl_RT_OutputImageRgh = 1, + OpenGl_RT_VisualErrorImage = 2, + OpenGl_RT_TileOffsetsImage = 3 }; //! Tool class for management of shader sources. @@ -700,13 +720,17 @@ protected: //! @name data types related to ray-tracing //! Enables/disables the use of OpenGL bindless textures. Standard_Boolean UseBindlessTextures; + //! Enables/disables adaptive screen sampling for path tracing. + Standard_Boolean AdaptiveScreenSampling; + //! Creates default compile-time ray-tracing parameters. RaytracingParams() : StackSize (THE_DEFAULT_STACK_SIZE), NbBounces (THE_DEFAULT_NB_BOUNCES), TransparentShadows (Standard_False), GlobalIllumination (Standard_False), - UseBindlessTextures (Standard_False) + UseBindlessTextures (Standard_False), + AdaptiveScreenSampling (Standard_False) { // } @@ -867,25 +891,31 @@ protected: //! @name methods related to ray-tracing void unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext); //! Sets uniform state for the given ray-tracing shader program. - Standard_Boolean setUniformState (const OpenGl_Vec3* theOrigins, - const OpenGl_Vec3* theDirects, - const OpenGl_Mat4& theViewMat, - const OpenGl_Mat4& theUnviewMat, - const Standard_Integer theProgramId, + Standard_Boolean setUniformState (const Standard_Integer theProgramId, + const Standard_Integer theSizeX, + const Standard_Integer theSizeY, const Handle(OpenGl_Context)& theGlContext); //! Runs ray-tracing shader programs. Standard_Boolean runRaytraceShaders (const Standard_Integer theSizeX, const Standard_Integer theSizeY, - const OpenGl_Vec3* theOrigins, - const OpenGl_Vec3* theDirects, - const OpenGl_Mat4& theViewMat, - const OpenGl_Mat4& theUnviewMat, Graphic3d_Camera::Projection theProjection, OpenGl_FrameBuffer* theReadDrawFbo, const Handle(OpenGl_Context)& theGlContext); - //! Redraws the window using OpenGL/GLSL ray-tracing. + //! Runs classical (Whitted-style) ray-tracing kernel. + Standard_Boolean runRaytrace (const Standard_Integer theSizeX, + const Standard_Integer theSizeY, + Graphic3d_Camera::Projection theProjection, + OpenGl_FrameBuffer* theReadDrawFbo, + const Handle(OpenGl_Context)& theGlContext); + + //! Runs path tracing (global illumination) kernel. + Standard_Boolean runPathtrace (const Graphic3d_Camera::Projection theProjection, + OpenGl_FrameBuffer* theReadDrawFbo, + const Handle(OpenGl_Context)& theGlContext); + + //! Redraws the window using OpenGL/GLSL ray-tracing or path tracing. Standard_Boolean raytrace (const Standard_Integer theSizeX, const Standard_Integer theSizeY, Graphic3d_Camera::Projection theProjection, @@ -894,13 +924,13 @@ protected: //! @name methods related to ray-tracing protected: //! @name fields related to ray-tracing - //! Result of shaders initialization. + //! Result of RT/PT shaders initialization. RaytraceInitStatus myRaytraceInitStatus; - //! Is geometry data valid? + //! Is ray-tracing geometry data valid? Standard_Boolean myIsRaytraceDataValid; - //! Warning about missing extension GL_ARB_bindless_texture has been displayed? + //! True if warning about missing extension GL_ARB_bindless_texture has been displayed. Standard_Boolean myIsRaytraceWarnTextures; //! 3D scene geometry data for ray-tracing. @@ -918,11 +948,15 @@ protected: //! @name fields related to ray-tracing ShaderSource myRaytraceShaderSource; //! OpenGL/GLSL source of adaptive-AA fragment shader. ShaderSource myPostFSAAShaderSource; + //! OpenGL/GLSL source of RT/PT display fragment shader. + ShaderSource myOutImageShaderSource; //! OpenGL/GLSL ray-tracing fragment shader. Handle(OpenGl_ShaderObject) myRaytraceShader; //! OpenGL/GLSL adaptive-AA fragment shader. Handle(OpenGl_ShaderObject) myPostFSAAShader; + //! OpenGL/GLSL ray-tracing display fragment shader. + Handle(OpenGl_ShaderObject) myOutImageShader; //! OpenGL/GLSL ray-tracing shader program. Handle(OpenGl_ShaderProgram) myRaytraceProgram; @@ -955,12 +989,22 @@ protected: //! @name fields related to ray-tracing Handle(OpenGl_TextureBufferArb) myRaytraceLightSrcTexture; //! 1st framebuffer (FBO) to perform adaptive FSAA. + //! Used in compatibility mode (no adaptive sampling). Handle(OpenGl_FrameBuffer) myRaytraceFBO1[2]; //! 2nd framebuffer (FBO) to perform adaptive FSAA. + //! Used in compatibility mode (no adaptive sampling). Handle(OpenGl_FrameBuffer) myRaytraceFBO2[2]; - //! Framebuffer (FBO) for preliminary OpenGL output. - Handle(OpenGl_FrameBuffer) myOpenGlFBO; - Handle(OpenGl_FrameBuffer) myOpenGlFBO2; + + //! Output textures (2 textures are used in stereo mode). + //! Used if adaptive screen sampling is activated. + Handle(OpenGl_Texture) myRaytraceOutputTexture[2]; + + //! Texture containing per-tile visual error estimation. + //! Used if adaptive screen sampling is activated. + Handle(OpenGl_Texture) myRaytraceVisualErrorTexture; + //! Texture containing offsets of sampled screen tiles. + //! Used if adaptive screen sampling is activated. + Handle(OpenGl_Texture) myRaytraceTileOffsetsTexture; //! Vertex buffer (VBO) for drawing dummy quad. OpenGl_VertexBuffer myRaytraceScreenQuad; @@ -995,6 +1039,9 @@ protected: //! @name fields related to ray-tracing //! Bullard RNG to produce random sequence. math_BullardGenerator myRNG; + //! Tool object for sampling screen tiles in PT mode. + OpenGl_TileSampler myTileSampler; + public: DEFINE_STANDARD_ALLOC diff --git a/src/OpenGl/OpenGl_View_Raytrace.cxx b/src/OpenGl/OpenGl_View_Raytrace.cxx index 03f6f8de67..df86d0e608 100644 --- a/src/OpenGl/OpenGl_View_Raytrace.cxx +++ b/src/OpenGl/OpenGl_View_Raytrace.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,9 @@ Standard_Boolean OpenGl_View::updateRaytraceGeometry (const RaytraceUpdateMode } if (toRestart) + { myAccumFrames = 0; + } myNonRaytraceStructureIDs = aNonRaytraceIDs; } @@ -393,7 +396,7 @@ OpenGl_RaytraceMaterial OpenGl_View::convertMaterial (const OpenGl_AspectFace* // Handle material textures if (theAspect->Aspect()->ToMapTexture()) { - if (theGlContext->arbTexBindless != NULL) + if (theGlContext->HasRayTracingTextures()) { buildTextureTransform (theAspect->TextureParams(), theMaterial.TextureTransform); @@ -1080,9 +1083,19 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C TCollection_AsciiString ("\n#define MAX_TEX_NUMBER ") + TCollection_AsciiString (OpenGl_RaytraceGeometry::MAX_TEX_NUMBER); } - if (myRaytraceParameters.GlobalIllumination) + if (myRaytraceParameters.GlobalIllumination) // path tracing activated { aPrefixString += TCollection_AsciiString ("\n#define PATH_TRACING"); + + if (myRaytraceParameters.AdaptiveScreenSampling) // adaptive screen sampling requested + { + // to activate the feature we need OpenGL 4.4 and GL_NV_shader_atomic_float extension + if (theGlContext->IsGlGreaterEqual (4, 4) && theGlContext->CheckExtension ("GL_NV_shader_atomic_float")) + { + aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING") + + TCollection_AsciiString ("\n#define BLOCK_SIZE ") + TCollection_AsciiString (OpenGl_TileSampler::TileSize()); + } + } } return aPrefixString; @@ -1155,6 +1168,10 @@ Handle(OpenGl_ShaderObject) OpenGl_View::initShader (const GLenum aShader->Release (theGlContext.operator->()); +#ifdef RAY_TRACE_PRINT_INFO + std::cout << "Shader build log:\n" << aBuildLog << "\n"; +#endif + return Handle(OpenGl_ShaderObject)(); } else if (theGlContext->caps->glslWarnings) @@ -1169,6 +1186,10 @@ Handle(OpenGl_ShaderObject) OpenGl_View::initShader (const GLenum theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW, aMessage); } + +#ifdef RAY_TRACE_PRINT_INFO + std::cout << "Shader build log:\n" << aBuildLog << "\n"; +#endif } return aShader; @@ -1300,15 +1321,32 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context aToRebuildShaders = Standard_True; } + if (myRenderParams.AdaptiveScreenSampling != myRaytraceParameters.AdaptiveScreenSampling) + { + myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling; + if (myRenderParams.AdaptiveScreenSampling) // adaptive sampling was requested + { + if (!theGlContext->HasRayTracingAdaptiveSampling()) + { + // disable the feature if it is not supported + myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling = Standard_False; + theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW, + "Adaptive sampling not supported (OpenGL 4.4 or GL_NV_shader_atomic_float is missing)"); + } + } + + aToRebuildShaders = Standard_True; + } + if (aToRebuildShaders) { // Reject accumulated frames myAccumFrames = 0; - // We need to update environment texture + // Environment map should be updated myToUpdateEnvironmentMap = Standard_True; - TCollection_AsciiString aPrefixString = generateShaderPrefix (theGlContext); + const TCollection_AsciiString aPrefixString = generateShaderPrefix (theGlContext); #ifdef RAY_TRACE_PRINT_INFO std::cout << "GLSL prefix string:" << std::endl << aPrefixString << std::endl; @@ -1316,23 +1354,29 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context myRaytraceShaderSource.SetPrefix (aPrefixString); myPostFSAAShaderSource.SetPrefix (aPrefixString); + myOutImageShaderSource.SetPrefix (aPrefixString); if (!myRaytraceShader->LoadSource (theGlContext, myRaytraceShaderSource.Source()) - || !myPostFSAAShader->LoadSource (theGlContext, myPostFSAAShaderSource.Source())) + || !myPostFSAAShader->LoadSource (theGlContext, myPostFSAAShaderSource.Source()) + || !myOutImageShader->LoadSource (theGlContext, myOutImageShaderSource.Source())) { return safeFailBack ("Failed to load source into ray-tracing fragment shaders", theGlContext); } if (!myRaytraceShader->Compile (theGlContext) - || !myPostFSAAShader->Compile (theGlContext)) + || !myPostFSAAShader->Compile (theGlContext) + || !myOutImageShader->Compile (theGlContext)) { return safeFailBack ("Failed to compile ray-tracing fragment shaders", theGlContext); } myRaytraceProgram->SetAttributeName (theGlContext, Graphic3d_TOA_POS, "occVertex"); myPostFSAAProgram->SetAttributeName (theGlContext, Graphic3d_TOA_POS, "occVertex"); + myOutImageProgram->SetAttributeName (theGlContext, Graphic3d_TOA_POS, "occVertex"); + if (!myRaytraceProgram->Link (theGlContext) - || !myPostFSAAProgram->Link (theGlContext)) + || !myPostFSAAProgram->Link (theGlContext) + || !myOutImageProgram->Link (theGlContext)) { return safeFailBack ("Failed to initialize vertex attributes for ray-tracing program", theGlContext); } @@ -1356,7 +1400,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context myRaytraceParameters.NbBounces = myRenderParams.RaytracingDepth; - TCollection_AsciiString aFolder = Graphic3d_ShaderProgram::ShadersFolder(); + const TCollection_AsciiString aFolder = Graphic3d_ShaderProgram::ShadersFolder(); if (aFolder.IsEmpty()) { @@ -1369,7 +1413,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context myRaytraceGeometry.TopLevelTreeDepth() + myRaytraceGeometry.BotLevelTreeDepth()); } - TCollection_AsciiString aPrefixString = generateShaderPrefix (theGlContext); + const TCollection_AsciiString aPrefixString = generateShaderPrefix (theGlContext); #ifdef RAY_TRACE_PRINT_INFO std::cout << "GLSL prefix string:" << std::endl << aPrefixString << std::endl; @@ -1444,11 +1488,10 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context } { - ShaderSource aDispShaderSrc; TCollection_AsciiString aFiles[] = { aFolder + "/Display.fs", "" }; - if (!aDispShaderSrc.Load (aFiles, aPrefixString)) + if (!myOutImageShaderSource.Load (aFiles, aPrefixString)) { - return safeFailBack (aDispShaderSrc.ErrorDescription(), theGlContext); + return safeFailBack (myOutImageShaderSource.ErrorDescription(), theGlContext); } Handle(OpenGl_ShaderObject) aBasicVertShader = initShader (GL_VERTEX_SHADER, aBasicVertShaderSrc, theGlContext); @@ -1457,17 +1500,17 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context return safeFailBack ("Failed to set vertex shader source", theGlContext); } - Handle(OpenGl_ShaderObject) aDisplayShader = initShader (GL_FRAGMENT_SHADER, aDispShaderSrc, theGlContext); - if (aDisplayShader.IsNull()) + myOutImageShader = initShader (GL_FRAGMENT_SHADER, myOutImageShaderSource, theGlContext); + if (myOutImageShader.IsNull()) { aBasicVertShader->Release (theGlContext.operator->()); return safeFailBack ("Failed to set display fragment shader source", theGlContext); } - myOutImageProgram = initProgram (theGlContext, aBasicVertShader, aDisplayShader); + myOutImageProgram = initProgram (theGlContext, aBasicVertShader, myOutImageShader); if (myOutImageProgram.IsNull()) { - return safeFailBack ("Failed to initialize output shader program", theGlContext); + return safeFailBack ("Failed to initialize display shader program", theGlContext); } } } @@ -1504,11 +1547,6 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context aShaderProgram->SetSampler (theGlContext, "uRaytraceLightSrcTexture", OpenGl_RT_RaytraceLightSrcTexture); - aShaderProgram->SetSampler (theGlContext, - "uOpenGlColorTexture", OpenGl_RT_OpenGlColorTexture); - aShaderProgram->SetSampler (theGlContext, - "uOpenGlDepthTexture", OpenGl_RT_OpenGlDepthTexture); - if (anIndex == 1) { aShaderProgram->SetSampler (theGlContext, @@ -1539,7 +1577,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context aShaderProgram->GetUniformLocation (theGlContext, "uDirectLT"); myUniformLocations[anIndex][OpenGl_RT_uDirectRT] = aShaderProgram->GetUniformLocation (theGlContext, "uDirectRT"); - myUniformLocations[anIndex][OpenGl_RT_uViewMat] = + myUniformLocations[anIndex][OpenGl_RT_uViewPrMat] = aShaderProgram->GetUniformLocation (theGlContext, "uViewMat"); myUniformLocations[anIndex][OpenGl_RT_uUnviewMat] = aShaderProgram->GetUniformLocation (theGlContext, "uUnviewMat"); @@ -1574,11 +1612,21 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context myUniformLocations[anIndex][OpenGl_RT_uBlockedRngEnabled] = aShaderProgram->GetUniformLocation (theGlContext, "uBlockedRngEnabled"); + myUniformLocations[anIndex][OpenGl_RT_uWinSizeX] = + aShaderProgram->GetUniformLocation (theGlContext, "uWinSizeX"); + myUniformLocations[anIndex][OpenGl_RT_uWinSizeY] = + aShaderProgram->GetUniformLocation (theGlContext, "uWinSizeY"); + myUniformLocations[anIndex][OpenGl_RT_uSampleWeight] = aShaderProgram->GetUniformLocation (theGlContext, "uSampleWeight"); myUniformLocations[anIndex][OpenGl_RT_uFrameRndSeed] = aShaderProgram->GetUniformLocation (theGlContext, "uFrameRndSeed"); + myUniformLocations[anIndex][OpenGl_RT_uRenderImage] = + aShaderProgram->GetUniformLocation (theGlContext, "uRenderImage"); + myUniformLocations[anIndex][OpenGl_RT_uOffsetImage] = + aShaderProgram->GetUniformLocation (theGlContext, "uOffsetImage"); + myUniformLocations[anIndex][OpenGl_RT_uBackColorTop] = aShaderProgram->GetUniformLocation (theGlContext, "uBackColorTop"); myUniformLocations[anIndex][OpenGl_RT_uBackColorBot] = @@ -1591,7 +1639,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context "uInputTexture", OpenGl_RT_PrevAccumTexture); myOutImageProgram->SetSampler (theGlContext, - "uDepthTexture", OpenGl_RT_DepthTexture); + "uDepthTexture", OpenGl_RT_RaytraceDepthTexture); theGlContext->BindProgram (NULL); } @@ -1617,11 +1665,10 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context // ======================================================================= // function : nullifyResource -// purpose : +// purpose : Releases OpenGL resource // ======================================================================= template -inline void nullifyResource (const Handle(OpenGl_Context)& theGlContext, - Handle(T)& theResource) +inline void nullifyResource (const Handle(OpenGl_Context)& theGlContext, Handle(T)& theResource) { if (!theResource.IsNull()) { @@ -1641,6 +1688,12 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC myRaytraceFBO2[0]->Release (theGlContext.operator->()); myRaytraceFBO2[1]->Release (theGlContext.operator->()); + nullifyResource (theGlContext, myRaytraceOutputTexture[0]); + nullifyResource (theGlContext, myRaytraceOutputTexture[1]); + + nullifyResource (theGlContext, myRaytraceTileOffsetsTexture); + nullifyResource (theGlContext, myRaytraceVisualErrorTexture); + nullifyResource (theGlContext, myRaytraceShader); nullifyResource (theGlContext, myPostFSAAShader); @@ -1664,7 +1717,9 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC myRaytraceGeometry.ReleaseResources (theGlContext); if (myRaytraceScreenQuad.IsValid()) + { myRaytraceScreenQuad.Release (theGlContext.operator->()); + } } // ======================================================================= @@ -1682,6 +1737,7 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer myRaytraceFBO2[0]->Release (theGlContext.operator->()); myRaytraceFBO1[1]->Release (theGlContext.operator->()); myRaytraceFBO2[1]->Release (theGlContext.operator->()); + return Standard_True; } @@ -1700,6 +1756,53 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer myRaytraceFBO2[1]->Release (theGlContext.operator->()); } + myTileSampler.SetSize (theSizeX, theSizeY); + + if (myRaytraceTileOffsetsTexture.IsNull()) + { + myRaytraceOutputTexture[0] = new OpenGl_Texture(); + myRaytraceOutputTexture[1] = new OpenGl_Texture(); + + myRaytraceTileOffsetsTexture = new OpenGl_Texture(); + myRaytraceVisualErrorTexture = new OpenGl_Texture(); + } + + if (myRaytraceOutputTexture[0]->SizeX() / 3 != theSizeX + || myRaytraceOutputTexture[0]->SizeY() / 2 != theSizeY) + { + // Due to limitations of OpenGL image load-store extension + // atomic operations are supported only for single-channel + // images, so we define GL_R32F image. It is used as array + // of 6D floating point vectors: + // 0 - R color channel + // 1 - G color channel + // 2 - B color channel + // 3 - hit time transformed into OpenGL NDC space + // 4 - luminance accumulated for odd samples only + myRaytraceOutputTexture[0]->InitRectangle (theGlContext, + theSizeX * 3, theSizeY * 2, OpenGl_TextureFormat::Create()); + + myRaytraceVisualErrorTexture->Init (theGlContext, + GL_R32I, GL_RED_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D); + + myRaytraceTileOffsetsTexture->Init (theGlContext, + GL_RG32I, GL_RG_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D); + } + + if (myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo) + { + if (myRaytraceOutputTexture[1]->SizeX() / 3 != theSizeX + || myRaytraceOutputTexture[1]->SizeY() / 2 != theSizeY) + { + myRaytraceOutputTexture[1]->InitRectangle (theGlContext, + theSizeX * 3, theSizeY * 2, OpenGl_TextureFormat::Create()); + } + } + else + { + myRaytraceOutputTexture[1]->Release (theGlContext.operator->()); + } + return Standard_True; } @@ -1711,14 +1814,14 @@ void OpenGl_View::updateCamera (const OpenGl_Mat4& theOrientation, const OpenGl_Mat4& theViewMapping, OpenGl_Vec3* theOrigins, OpenGl_Vec3* theDirects, - OpenGl_Mat4& theView, + OpenGl_Mat4& theViewPr, OpenGl_Mat4& theUnview) { // compute view-projection matrix - theView = theViewMapping * theOrientation; + theViewPr = theViewMapping * theOrientation; // compute inverse view-projection matrix - theView.Inverted (theUnview); + theViewPr.Inverted (theUnview); Standard_Integer aOriginIndex = 0; Standard_Integer aDirectIndex = 0; @@ -1797,7 +1900,7 @@ Standard_Boolean OpenGl_View::uploadRaytraceData (const Handle(OpenGl_Context)& ///////////////////////////////////////////////////////////////////////////// // Create OpenGL BVH buffers - if (mySceneNodeInfoTexture.IsNull()) // create scene BVH buffers + if (mySceneNodeInfoTexture.IsNull()) // create scene BVH buffers { mySceneNodeInfoTexture = new OpenGl_TextureBufferArb; mySceneMinPointTexture = new OpenGl_TextureBufferArb; @@ -1816,7 +1919,7 @@ Standard_Boolean OpenGl_View::uploadRaytraceData (const Handle(OpenGl_Context)& } } - if (myGeometryVertexTexture.IsNull()) // create geometry buffers + if (myGeometryVertexTexture.IsNull()) // create geometry buffers { myGeometryVertexTexture = new OpenGl_TextureBufferArb; myGeometryNormalTexture = new OpenGl_TextureBufferArb; @@ -2184,26 +2287,26 @@ Standard_Boolean OpenGl_View::updateRaytraceEnvironmentMap (const Handle(OpenGl_ return aResult; } + Handle(OpenGl_ShaderProgram) aPrograms[] = { myRaytraceProgram, + myPostFSAAProgram }; + for (Standard_Integer anIdx = 0; anIdx < 2; ++anIdx) { - const Handle(OpenGl_ShaderProgram)& aProgram = - anIdx == 0 ? myRaytraceProgram : myPostFSAAProgram; - - if (!aProgram.IsNull()) + if (!aPrograms[anIdx].IsNull()) { - aResult &= theGlContext->BindProgram (aProgram); + aResult &= theGlContext->BindProgram (aPrograms[anIdx]); if (!myTextureEnv.IsNull()) { myTextureEnv->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_EnvironmentMapTexture); - aResult &= aProgram->SetUniform (theGlContext, + aResult &= aPrograms[anIdx]->SetUniform (theGlContext, myUniformLocations[anIdx][OpenGl_RT_uSphereMapEnabled], 1); } else { - aResult &= aProgram->SetUniform (theGlContext, + aResult &= aPrograms[anIdx]->SetUniform (theGlContext, myUniformLocations[anIdx][OpenGl_RT_uSphereMapEnabled], 0); } } @@ -2220,63 +2323,85 @@ Standard_Boolean OpenGl_View::updateRaytraceEnvironmentMap (const Handle(OpenGl_ // function : setUniformState // purpose : Sets uniform state for the given ray-tracing shader program // ======================================================================= -Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3* theOrigins, - const OpenGl_Vec3* theDirects, - const OpenGl_Mat4& theViewMat, - const OpenGl_Mat4& theUnviewMat, - const Standard_Integer theProgramId, +Standard_Boolean OpenGl_View::setUniformState (const Standard_Integer theProgramId, + const Standard_Integer theWinSizeX, + const Standard_Integer theWinSizeY, const Handle(OpenGl_Context)& theGlContext) { - Handle(OpenGl_ShaderProgram)& theProgram = - theProgramId == 0 ? myRaytraceProgram : myPostFSAAProgram; + // Get projection state + OpenGl_MatrixState& aCntxProjectionState = theGlContext->ProjectionState; + + OpenGl_Mat4 aViewPrjMat; + OpenGl_Mat4 anUnviewMat; + OpenGl_Vec3 aOrigins[4]; + OpenGl_Vec3 aDirects[4]; + + updateCamera (myCamera->OrientationMatrixF(), + aCntxProjectionState.Current(), + aOrigins, + aDirects, + aViewPrjMat, + anUnviewMat); + + Handle(OpenGl_ShaderProgram)& theProgram = theProgramId == 0 + ? myRaytraceProgram + : myPostFSAAProgram; if (theProgram.IsNull()) { return Standard_False; } - const Standard_Integer aLightSourceBufferSize = - static_cast (myRaytraceGeometry.Sources.size()); - // Set camera state theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uOriginLB], theOrigins[0]); + myUniformLocations[theProgramId][OpenGl_RT_uOriginLB], aOrigins[0]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uOriginRB], theOrigins[1]); + myUniformLocations[theProgramId][OpenGl_RT_uOriginRB], aOrigins[1]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uOriginLT], theOrigins[2]); + myUniformLocations[theProgramId][OpenGl_RT_uOriginLT], aOrigins[2]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uOriginRT], theOrigins[3]); + myUniformLocations[theProgramId][OpenGl_RT_uOriginRT], aOrigins[3]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uDirectLB], theDirects[0]); + myUniformLocations[theProgramId][OpenGl_RT_uDirectLB], aDirects[0]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uDirectRB], theDirects[1]); + myUniformLocations[theProgramId][OpenGl_RT_uDirectRB], aDirects[1]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uDirectLT], theDirects[2]); + myUniformLocations[theProgramId][OpenGl_RT_uDirectLT], aDirects[2]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uDirectRT], theDirects[3]); + myUniformLocations[theProgramId][OpenGl_RT_uDirectRT], aDirects[3]); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uViewMat], theViewMat); + myUniformLocations[theProgramId][OpenGl_RT_uViewPrMat], aViewPrjMat); theProgram->SetUniform (theGlContext, - myUniformLocations[theProgramId][OpenGl_RT_uUnviewMat], theUnviewMat); + myUniformLocations[theProgramId][OpenGl_RT_uUnviewMat], anUnviewMat); - // Set scene parameters + // Set ray-tracing intersection parameters theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uSceneRad], myRaytraceSceneRadius); theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uSceneEps], myRaytraceSceneEpsilon); + + const Standard_Integer aLightSourceBufferSize = + static_cast (myRaytraceGeometry.Sources.size()); + + // Set ray-tracing light source parameters theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uLightCount], aLightSourceBufferSize); theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uLightAmbnt], myRaytraceGeometry.Ambient); - // Set run-time rendering options + // Enable/disable run time rendering effects theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uShadowsEnabled], myRenderParams.IsShadowEnabled ? 1 : 0); theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uReflectEnabled], myRenderParams.IsReflectionEnabled ? 1 : 0); - if (myRenderParams.IsGlobalIlluminationEnabled) + // Set screen dimensions + myRaytraceProgram->SetUniform (theGlContext, + myUniformLocations[theProgramId][OpenGl_RT_uWinSizeX], theWinSizeX); + myRaytraceProgram->SetUniform (theGlContext, + myUniformLocations[theProgramId][OpenGl_RT_uWinSizeY], theWinSizeY); + + if (myRenderParams.IsGlobalIlluminationEnabled) // if Monte-Carlo sampling enabled { theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uBlockedRngEnabled], myRenderParams.CoherentPathTracingMode ? 1 : 0); @@ -2288,12 +2413,11 @@ Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3* the const std::vector& aTextures = myRaytraceGeometry.TextureHandles(); theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uTexSamplersArray], - static_cast (aTextures.size()), (OpenGl_Vec2u* )&aTextures.front()); + static_cast (aTextures.size()), reinterpret_cast (&aTextures.front())); } // Set background colors (only gradient background supported) - if (myBgGradientArray != NULL - && myBgGradientArray->IsDefined()) + if (myBgGradientArray != NULL && myBgGradientArray->IsDefined()) { theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uBackColorTop], myBgGradientArray->GradientColor (0)); @@ -2303,6 +2427,7 @@ Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3* the else { const OpenGl_Vec4& aBackColor = myBgColor; + theProgram->SetUniform (theGlContext, myUniformLocations[theProgramId][OpenGl_RT_uBackColorTop], aBackColor); theProgram->SetUniform (theGlContext, @@ -2321,6 +2446,21 @@ Standard_Boolean OpenGl_View::setUniformState (const OpenGl_Vec3* the // ======================================================================= void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext) { + if (myRaytraceParameters.AdaptiveScreenSampling) + { + #if !defined(GL_ES_VERSION_2_0) + theGlContext->core42->glBindImageTexture (OpenGl_RT_OutputImageLft, + myRaytraceOutputTexture[0]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F); + theGlContext->core42->glBindImageTexture (OpenGl_RT_OutputImageRgh, + myRaytraceOutputTexture[1]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F); + + theGlContext->core42->glBindImageTexture (OpenGl_RT_VisualErrorImage, + myRaytraceVisualErrorTexture->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I); + theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage, + myRaytraceTileOffsetsTexture->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I); + #endif + } + mySceneMinPointTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneMinPointTexture); mySceneMaxPointTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneMaxPointTexture); mySceneNodeInfoTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneNodeInfoTexture); @@ -2331,12 +2471,6 @@ void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlConte mySceneTransformTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_SceneTransformTexture); myRaytraceMaterialTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceMaterialTexture); myRaytraceLightSrcTexture->BindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceLightSrcTexture); - - if (!myOpenGlFBO.IsNull()) - { - myOpenGlFBO->ColorTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlColorTexture); - myOpenGlFBO->DepthStencilTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlDepthTexture); - } } // ======================================================================= @@ -2356,12 +2490,6 @@ void OpenGl_View::unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlCon myRaytraceMaterialTexture->UnbindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceMaterialTexture); myRaytraceLightSrcTexture->UnbindTexture (theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceLightSrcTexture); - if (!myOpenGlFBO.IsNull()) - { - myOpenGlFBO->ColorTexture()->Unbind (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlColorTexture); - myOpenGlFBO->DepthStencilTexture()->Unbind (theGlContext, GL_TEXTURE0 + OpenGl_RT_OpenGlDepthTexture); - } - theGlContext->core15fwd->glActiveTexture (GL_TEXTURE0); } @@ -2371,81 +2499,70 @@ void OpenGl_View::unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlCon // ======================================================================= Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer theSizeX, const Standard_Integer theSizeY, - const OpenGl_Vec3* theOrigins, - const OpenGl_Vec3* theDirects, - const OpenGl_Mat4& theViewMat, - const OpenGl_Mat4& theUnviewMat, Graphic3d_Camera::Projection theProjection, OpenGl_FrameBuffer* theReadDrawFbo, const Handle(OpenGl_Context)& theGlContext) { - bindRaytraceTextures (theGlContext); - - Handle(OpenGl_FrameBuffer) aRenderFramebuffer; - Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer; - Handle(OpenGl_FrameBuffer) anAccumFramebuffer; + Standard_Boolean aResult = theGlContext->BindProgram (myRaytraceProgram); - // Choose proper set of framebuffers for stereo rendering - Standard_Boolean isStereo = myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo; - Standard_Boolean isRightEye = theProjection == Graphic3d_Camera::Projection_MonoRightEye; - Standard_Integer aFBOIdx = (isStereo && isRightEye) ? 1 : 0; + aResult &= setUniformState (0, + theSizeX, + theSizeY, + theGlContext); - if (myRaytraceParameters.GlobalIllumination) // if path-tracing is used + if (myRaytraceParameters.GlobalIllumination) // path tracing { - aRenderFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx]; - anAccumFramebuffer = myAccumFrames % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx]; - aDepthSourceFramebuffer = aRenderFramebuffer; - - anAccumFramebuffer->ColorTexture()->Bind ( - theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture); - - aRenderFramebuffer->BindBuffer (theGlContext); + aResult &= runPathtrace (theProjection, theReadDrawFbo, theGlContext); } - else if (myRenderParams.IsAntialiasingEnabled) // if 2-pass ray-tracing is used + else // Whitted-style ray-tracing { - myRaytraceFBO1[aFBOIdx]->BindBuffer (theGlContext); + aResult &= runRaytrace (theSizeX, theSizeY, theProjection, theReadDrawFbo, theGlContext); } - Standard_Boolean aResult = theGlContext->BindProgram (myRaytraceProgram); + return aResult; +} - aResult &= setUniformState (theOrigins, - theDirects, - theViewMat, - theUnviewMat, - 0, // ID of RT program - theGlContext); +// ======================================================================= +// function : runRaytrace +// purpose : Runs Whitted-style ray-tracing +// ======================================================================= +Standard_Boolean OpenGl_View::runRaytrace (const Standard_Integer theSizeX, + const Standard_Integer theSizeY, + Graphic3d_Camera::Projection theProjection, + OpenGl_FrameBuffer* theReadDrawFbo, + const Handle(OpenGl_Context)& theGlContext) +{ + Standard_Boolean aResult = Standard_True; - if (myRaytraceParameters.GlobalIllumination) - { - if (myAccumFrames == 0) - { - myRNG.SetSeed(); - } + bindRaytraceTextures (theGlContext); - // Set frame accumulation weight - myRaytraceProgram->SetUniform (theGlContext, - myUniformLocations[0][OpenGl_RT_uSampleWeight], 1.f / (myAccumFrames + 1)); + Handle(OpenGl_FrameBuffer) aRenderImageFramebuffer; + Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer; - // Set random number generator seed - myRaytraceProgram->SetUniform (theGlContext, - myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast (myRNG.NextInt() >> 2)); + // Choose proper set of frame buffers for stereo rendering + const Standard_Integer aFBOIdx (theProjection == Graphic3d_Camera::Projection_MonoRightEye); + + if (myRenderParams.IsAntialiasingEnabled) // if second FSAA pass is used + { + myRaytraceFBO1[aFBOIdx]->BindBuffer (theGlContext); + + glClear (GL_DEPTH_BUFFER_BIT); // render the image with depth } theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6); - if (myRenderParams.IsAntialiasingEnabled && !myRenderParams.IsGlobalIlluminationEnabled) + if (myRenderParams.IsAntialiasingEnabled) { - glDepthMask (GL_FALSE); + glDisable (GL_DEPTH_TEST); // improve jagged edges without depth buffer + // bind ray-tracing output image as input myRaytraceFBO1[aFBOIdx]->ColorTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_FsaaInputTexture); aResult &= theGlContext->BindProgram (myPostFSAAProgram); - aResult &= setUniformState (theOrigins, - theDirects, - theViewMat, - theUnviewMat, - 1, // ID of FSAA program + aResult &= setUniformState (1 /* FSAA ID */, + theSizeX, + theSizeY, theGlContext); // Perform multi-pass adaptive FSAA using ping-pong technique. @@ -2480,49 +2597,48 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer aResult &= myPostFSAAProgram->SetUniform (theGlContext, myUniformLocations[1][OpenGl_RT_uOffsetY], aOffsetY); - Handle(OpenGl_FrameBuffer)& aFramebuffer = anIt % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx]; + Handle(OpenGl_FrameBuffer)& aFramebuffer = anIt % 2 + ? myRaytraceFBO2[aFBOIdx] + : myRaytraceFBO1[aFBOIdx]; aFramebuffer->BindBuffer (theGlContext); + // perform adaptive FSAA pass theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6); aFramebuffer->ColorTexture()->Bind (theGlContext, GL_TEXTURE0 + OpenGl_RT_FsaaInputTexture); } - aRenderFramebuffer = myRaytraceFBO2[aFBOIdx]; + aRenderImageFramebuffer = myRaytraceFBO2[aFBOIdx]; aDepthSourceFramebuffer = myRaytraceFBO1[aFBOIdx]; - } - if (myRaytraceParameters.GlobalIllumination || myRenderParams.IsAntialiasingEnabled) - { - // Output accumulated image - glDepthMask (GL_TRUE); + glEnable (GL_DEPTH_TEST); + // Display filtered image theGlContext->BindProgram (myOutImageProgram); - myOutImageProgram->SetUniform (theGlContext, "uApplyGamma", static_cast (myRaytraceParameters.GlobalIllumination)); - if (theReadDrawFbo != NULL) { theReadDrawFbo->BindBuffer (theGlContext); } else { - aRenderFramebuffer->UnbindBuffer (theGlContext); + aRenderImageFramebuffer->UnbindBuffer (theGlContext); } - aRenderFramebuffer->ColorTexture()->Bind ( + aRenderImageFramebuffer->ColorTexture()->Bind ( theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture); aDepthSourceFramebuffer->DepthStencilTexture()->Bind ( - theGlContext, GL_TEXTURE0 + OpenGl_RT_DepthTexture); + theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceDepthTexture); + // copy the output image with depth values theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6); aDepthSourceFramebuffer->DepthStencilTexture()->Unbind ( - theGlContext, GL_TEXTURE0 + OpenGl_RT_DepthTexture); + theGlContext, GL_TEXTURE0 + OpenGl_RT_RaytraceDepthTexture); - aRenderFramebuffer->ColorTexture()->Unbind ( + aRenderImageFramebuffer->ColorTexture()->Unbind ( theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture); } @@ -2533,6 +2649,140 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer return aResult; } +// ======================================================================= +// function : runPathtrace +// purpose : Runs path tracing shader +// ======================================================================= +Standard_Boolean OpenGl_View::runPathtrace (const Graphic3d_Camera::Projection theProjection, + OpenGl_FrameBuffer* theReadDrawFbo, + const Handle(OpenGl_Context)& theGlContext) +{ + Standard_Boolean aResult = Standard_True; + + if (myRaytraceParameters.AdaptiveScreenSampling) + { + if (myAccumFrames == 0) + { + myTileSampler.Reset(); // reset tile sampler to its initial state + } + + // We upload tile offset texture each 4 frames in order + // to minimize overhead of additional memory bandwidth. + // Adaptive sampling is starting after first 10 frames. + if (myAccumFrames % 4 == 0) + { + myTileSampler.Upload (theGlContext, myRaytraceTileOffsetsTexture, myAccumFrames > 10); + } + } + + bindRaytraceTextures (theGlContext); + + Handle(OpenGl_FrameBuffer) aRenderImageFramebuffer; + Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer; + Handle(OpenGl_FrameBuffer) anAccumImageFramebuffer; + + // Choose proper set of frame buffers for stereo rendering + const Standard_Integer aFBOIdx (theProjection == Graphic3d_Camera::Projection_MonoRightEye); + + const Standard_Integer anImageId = (aFBOIdx != 0) + ? OpenGl_RT_OutputImageRgh + : OpenGl_RT_OutputImageLft; + + aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx]; + anAccumImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx]; + + aDepthSourceFramebuffer = aRenderImageFramebuffer; + + anAccumImageFramebuffer->ColorTexture()->Bind ( + theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture); + + aRenderImageFramebuffer->BindBuffer (theGlContext); + + if (myAccumFrames == 0) + { + myRNG.SetSeed(); // start RNG from beginning + } + + // Clear adaptive screen sampling images + if (myRaytraceParameters.AdaptiveScreenSampling) + { + #if !defined(GL_ES_VERSION_2_0) + if (myAccumFrames == 0) + { + theGlContext->core44->glClearTexImage (myRaytraceOutputTexture[aFBOIdx]->TextureId(), 0, GL_RED, GL_FLOAT, NULL); + } + + theGlContext->core44->glClearTexImage (myRaytraceVisualErrorTexture->TextureId(), 0, GL_RED_INTEGER, GL_INT, NULL); + #endif + } + + // Set frame accumulation weight + myRaytraceProgram->SetUniform (theGlContext, + myUniformLocations[0][OpenGl_RT_uSampleWeight], 1.f / (myAccumFrames + 1)); + + // Set random number generator seed + myRaytraceProgram->SetUniform (theGlContext, + myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast (myRNG.NextInt() >> 2)); + + // Set image uniforms for render program + myRaytraceProgram->SetUniform (theGlContext, + myUniformLocations[0][OpenGl_RT_uRenderImage], anImageId); + myRaytraceProgram->SetUniform (theGlContext, + myUniformLocations[0][OpenGl_RT_uOffsetImage], OpenGl_RT_TileOffsetsImage); + + glDisable (GL_DEPTH_TEST); + + // Generate for the given RNG seed + theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6); + + // Output accumulated path traced image + theGlContext->BindProgram (myOutImageProgram); + + if (myRaytraceParameters.AdaptiveScreenSampling) + { + // Set uniforms for display program + myOutImageProgram->SetUniform (theGlContext, "uRenderImage", anImageId); + myOutImageProgram->SetUniform (theGlContext, "uAccumFrames", myAccumFrames); + myOutImageProgram->SetUniform (theGlContext, "uVarianceImage", OpenGl_RT_VisualErrorImage); + myOutImageProgram->SetUniform (theGlContext, "uDebugAdaptive", myRenderParams.ShowSamplingTiles ? 1 : 0); + } + + if (theReadDrawFbo != NULL) + { + theReadDrawFbo->BindBuffer (theGlContext); + } + else + { + aRenderImageFramebuffer->UnbindBuffer (theGlContext); + } + + aRenderImageFramebuffer->ColorTexture()->Bind ( + theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture); + + glEnable (GL_DEPTH_TEST); + + // Copy accumulated image with correct depth values + theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6); + + aRenderImageFramebuffer->ColorTexture()->Unbind ( + theGlContext, GL_TEXTURE0 + OpenGl_RT_PrevAccumTexture); + + if (myRaytraceParameters.AdaptiveScreenSampling) + { + myRaytraceVisualErrorTexture->Bind (theGlContext); + + // Download visual error map from the GPU and build + // adjusted tile offsets for optimal image sampling + myTileSampler.GrabVarianceMap (theGlContext); + } + + unbindRaytraceTextures (theGlContext); + + theGlContext->BindProgram (NULL); + + return aResult; +} + // ======================================================================= // function : raytrace // purpose : Redraws the window using OpenGL/GLSL ray-tracing @@ -2558,35 +2808,17 @@ Standard_Boolean OpenGl_View::raytrace (const Standard_Integer theSizeX, return Standard_False; } - // Get model-view and projection matrices - OpenGl_Mat4 aOrientationMatrix = myCamera->OrientationMatrixF(); - OpenGl_Mat4 aViewMappingMatrix = theGlContext->ProjectionState.Current(); + OpenGl_Mat4 aLightSourceMatrix; - OpenGl_Mat4 aInverOrientMatrix; - aOrientationMatrix.Inverted (aInverOrientMatrix); - if (!updateRaytraceLightSources (aInverOrientMatrix, theGlContext)) - { - return Standard_False; - } + // Get inversed model-view matrix for transforming lights + myCamera->OrientationMatrixF().Inverted (aLightSourceMatrix); - OpenGl_Vec3 aOrigins[4]; - OpenGl_Vec3 aDirects[4]; - OpenGl_Mat4 aViewMat; - OpenGl_Mat4 anUnviewMat; - - updateCamera (aOrientationMatrix, - aViewMappingMatrix, - aOrigins, - aDirects, - aViewMat, - anUnviewMat); - - if (theReadDrawFbo != NULL) + if (!updateRaytraceLightSources (aLightSourceMatrix, theGlContext)) { - theReadDrawFbo->BindBuffer (theGlContext); + return Standard_False; } - // Generate ray-traced image + // Generate image using Whitted-style ray-tracing or path tracing if (myIsRaytraceDataValid) { myRaytraceScreenQuad.BindVertexAttrib (theGlContext, Graphic3d_TOA_POS); @@ -2597,29 +2829,13 @@ Standard_Boolean OpenGl_View::raytrace (const Standard_Integer theSizeX, 0, GL_DEBUG_SEVERITY_MEDIUM, "Error: Failed to acquire OpenGL image textures"); } - // Remember the old depth function and mask - GLint aDepthFunc; - theGlContext->core11fwd->glGetIntegerv (GL_DEPTH_FUNC, &aDepthFunc); - - GLboolean aDepthMask; - theGlContext->core11fwd->glGetBooleanv (GL_DEPTH_WRITEMASK, &aDepthMask); - glDisable (GL_BLEND); - glDepthFunc (GL_ALWAYS); - - Standard_Boolean aResult = runRaytraceShaders (theSizeX, - theSizeY, - aOrigins, - aDirects, - aViewMat, - anUnviewMat, - theProjection, - theReadDrawFbo, - theGlContext); - - // Restore depth function and mask - glDepthFunc (aDepthFunc); - glDepthMask (aDepthMask); + + const Standard_Boolean aResult = runRaytraceShaders (theSizeX, + theSizeY, + theProjection, + theReadDrawFbo, + theGlContext); if (!aResult) { diff --git a/src/OpenGl/OpenGl_View_Redraw.cxx b/src/OpenGl/OpenGl_View_Redraw.cxx index dab7ad6bfd..6b76f4f379 100644 --- a/src/OpenGl/OpenGl_View_Redraw.cxx +++ b/src/OpenGl/OpenGl_View_Redraw.cxx @@ -991,20 +991,13 @@ void OpenGl_View::renderStructs (Graphic3d_Camera::Projection theProjection, { if (theReadDrawFbo != NULL) { - theReadDrawFbo->BindReadBuffer (aCtx); + theReadDrawFbo->BindDrawBuffer (aCtx); } else { - aCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, 0); + aCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, 0); } - myOpenGlFBO->BindDrawBuffer (aCtx); - - aCtx->arbFBOBlit->glBlitFramebuffer (0, 0, aSizeX, aSizeY, - 0, 0, aSizeX, aSizeY, - GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, - GL_NEAREST); - // Render non-polygonal elements in default layer myZLayers.Render (myWorkspace, theToDrawImmediate, OpenGl_LF_Default); } diff --git a/src/Shaders/Display.fs b/src/Shaders/Display.fs index d367df8e4b..c4f9faa175 100644 --- a/src/Shaders/Display.fs +++ b/src/Shaders/Display.fs @@ -1,29 +1,114 @@ -//! Input image. -uniform sampler2D uInputTexture; +#ifdef ADAPTIVE_SAMPLING -//! Ray tracing depth image. -uniform sampler2D uDepthTexture; + #extension GL_ARB_shader_image_load_store : require -//! Gamma correction flag. -uniform int uApplyGamma; + //! OpenGL image used for accumulating rendering result. + volatile restrict layout(size1x32) uniform image2D uRenderImage; + + //! OpenGL image storing variance of sampled pixels blocks. + volatile restrict layout(size1x32) uniform iimage2D uVarianceImage; + +#else // ADAPTIVE_SAMPLING + + //! Input image. + uniform sampler2D uInputTexture; + + //! Ray tracing depth image. + uniform sampler2D uDepthTexture; + +#endif // ADAPTIVE_SAMPLING + +//! Number of accumulated frames. +uniform int uAccumFrames; + +//! Is debug mode enabled for importance screen sampling. +uniform int uDebugAdaptive; //! Output pixel color. out vec4 OutColor; +//! RGB weight factors to calculate luminance. +#define LUMA vec3 (0.2126f, 0.7152f, 0.0722f) + +//! Scale factor used to quantize visual error. +#define SCALE_FACTOR 1.0e6f + +// ======================================================================= +// function : main +// purpose : +// ======================================================================= void main (void) { +#ifndef ADAPTIVE_SAMPLING + vec4 aColor = texelFetch (uInputTexture, ivec2 (gl_FragCoord.xy), 0); +#ifdef PATH_TRACING + float aDepth = aColor.w; // path tracing uses averaged depth +#else float aDepth = texelFetch (uDepthTexture, ivec2 (gl_FragCoord.xy), 0).r; +#endif + gl_FragDepth = aDepth; - if (uApplyGamma == 1) +#else // ADAPTIVE_SAMPLING + + ivec2 aPixel = ivec2 (gl_FragCoord.xy); + + vec4 aColor = vec4 (0.0); + + // fetch accumulated color and total number of samples + aColor.x = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 0, + 2 * aPixel.y + 0)).x; + aColor.y = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 1, + 2 * aPixel.y + 0)).x; + aColor.z = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 1, + 2 * aPixel.y + 1)).x; + aColor.w = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 0, + 2 * aPixel.y + 1)).x; + + // calculate normalization factor + float aSampleWeight = 1.f / max (1.0, aColor.w); + + // calculate averaged depth value + gl_FragDepth = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 2, + 2 * aPixel.y + 1)).x * aSampleWeight; + + // calculate averaged radiance for all samples and even samples only + float aHalfRad = imageLoad (uRenderImage, ivec2 (3 * aPixel.x + 2, + 2 * aPixel.y + 0)).x * aSampleWeight * 2.f; + + float aAverRad = dot (aColor.rgb, LUMA) * aSampleWeight; + + // apply our 'tone mapping' operator (gamma correction and clamping) + aHalfRad = min (1.f, sqrt (aHalfRad)); + aAverRad = min (1.f, sqrt (aAverRad)); + + // calculate visual error + float anError = (aAverRad - aHalfRad) * (aAverRad - aHalfRad); + + // accumulate visual error to current block + imageAtomicAdd (uVarianceImage, ivec2 (aPixel / vec2 (BLOCK_SIZE)), int (anError * SCALE_FACTOR)); + + if (uDebugAdaptive == 0) // normal rendering { - // apply gamma correction (we use gamma = 2) - OutColor = vec4 (sqrt (aColor.rgb), aColor.a); + aColor = vec4 (aColor.rgb * aSampleWeight, 1.0); } - else + else // showing number of samples { - OutColor = aColor; + aColor = vec4 (0.5f * aColor.rgb * aSampleWeight + vec3 (0.f, aColor.w / uAccumFrames * 0.35f, 0.f), 1.0); } + +#endif // ADAPTIVE_SAMPLING + +#ifdef PATH_TRACING + + // apply gamma correction (we use gamma = 2) + OutColor = vec4 (sqrt (aColor.rgb), 0.f); + +#else // not PATH_TRACING + + OutColor = aColor; + +#endif } diff --git a/src/Shaders/PathtraceBase.fs b/src/Shaders/PathtraceBase.fs index a9c2e10931..bf778ad5f2 100644 --- a/src/Shaders/PathtraceBase.fs +++ b/src/Shaders/PathtraceBase.fs @@ -512,15 +512,15 @@ float handleDirectLight (in vec3 theInput, in vec3 theToLight, in float theCosMa //======================================================================= // function : sampleLight -// purpose : general sampling function for directional and point lights +// purpose : General sampling function for directional and point lights //======================================================================= -vec3 sampleLight (in vec3 theToLight, in bool isDirectional, in float theSmoothness, inout float thePDF) +vec3 sampleLight (in vec3 theToLight, inout float theDistance, in bool isInfinite, in float theSmoothness, inout float thePDF) { - SLocalSpace aSpace = LocalSpace (theToLight); + SLocalSpace aSpace = LocalSpace (theToLight * (1.f / theDistance)); // for point lights smoothness defines radius - float aCosMax = isDirectional ? theSmoothness : - inversesqrt (1.f + theSmoothness * theSmoothness / dot (theToLight, theToLight)); + float aCosMax = isInfinite ? theSmoothness : + inversesqrt (1.f + theSmoothness * theSmoothness / (theDistance * theDistance)); float aKsi1 = RandFloat(); float aKsi2 = RandFloat(); @@ -613,7 +613,6 @@ vec3 intersectLight (in SRay theRay, in bool isViewRay, in int theBounce, in flo //======================================================================= vec4 PathTrace (in SRay theRay, in vec3 theInverse) { - float anOpenGlDepth = ComputeOpenGlDepth (theRay); float aRaytraceDepth = MAXFLOAT; vec3 aRadiance = ZERO; @@ -648,32 +647,22 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) dot (aInvTransf1, aHit.Normal), dot (aInvTransf2, aHit.Normal))); - // For polygons that are parallel to the screen plane, the depth slope - // is equal to 1, resulting in small polygon offset. For polygons that - // that are at a large angle to the screen, the depth slope tends to 1, - // resulting in a larger polygon offset - float aPolygonOffset = uSceneEpsilon * EPS_SCALE / - max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE); - - if (anOpenGlDepth < aHit.Time + aPolygonOffset) - { - vec4 aSrcColorRGBA = ComputeOpenGlColor(); - - aRadiance += aThroughput.xyz * aSrcColorRGBA.xyz; - - aDepth = INVALID_BOUNCES; // terminate path - } - theRay.Origin += theRay.Direct * aHit.Time; // get new intersection point - // Evaluate depth + // Evaluate depth on first hit if (aDepth == 0) { + // For polygons that are parallel to the screen plane, the depth slope + // is equal to 1, resulting in small polygon offset. For polygons that + // that are at a large angle to the screen, the depth slope tends to 1, + // resulting in a larger polygon offset + float aPolygonOffset = uSceneEpsilon * EPS_SCALE / + max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE); + // Hit point in NDC-space [-1,1] (the polygon offset is applied in the world space) vec4 aNDCPoint = uViewMat * vec4 (theRay.Origin + theRay.Direct * aPolygonOffset, 1.f); - aNDCPoint.xyz *= 1.f / aNDCPoint.w; - aRaytraceDepth = aNDCPoint.z * 0.5f + 0.5f; + aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w) * 0.5f + 0.5f; } // fetch material (BSDF) @@ -701,7 +690,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) vec3 aTexColor = textureLod ( sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f).rgb; - aMaterial.Kd.rgb *= aTexColor; + aMaterial.Kd.rgb *= aTexColor * aTexColor; // de-gamma correction (for gamma = 2) } #endif @@ -732,8 +721,8 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) float aPDF = 1.f / uLightCount, aDistance = length (aLight.xyz); - aLight.xyz = sampleLight (aLight.xyz * (1.f / aDistance), - aLight.w == 0.f /* is infinite */, aParam.w /* angle cosine */, aPDF); + aLight.xyz = sampleLight (aLight.xyz, aDistance, + aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aPDF); vec3 aContrib = (1.f / aPDF) * aParam.rgb /* Le */ * handleMaterial ( aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace)); @@ -787,13 +776,11 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) aHit.Normal * mix (-uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, anInput))), anInput); theInverse = InverseDirection (anInput); - - anOpenGlDepth = MAXFLOAT; // disable combining image with OpenGL output } gl_FragDepth = aRaytraceDepth; - return vec4 (aRadiance, 0.f); + return vec4 (aRadiance, aRaytraceDepth); } #endif diff --git a/src/Shaders/RaytraceBase.fs b/src/Shaders/RaytraceBase.fs index e7a3e3df4f..6c5f5e4692 100644 --- a/src/Shaders/RaytraceBase.fs +++ b/src/Shaders/RaytraceBase.fs @@ -1,3 +1,8 @@ +#ifdef ADAPTIVE_SAMPLING + #extension GL_ARB_shader_image_load_store : require + #extension GL_NV_shader_atomic_float : require +#endif + #ifdef USE_TEXTURES #extension GL_ARB_bindless_texture : require #endif @@ -66,11 +71,6 @@ uniform samplerBuffer uRaytraceLightSrcTexture; //! Environment map texture. uniform sampler2D uEnvironmentMapTexture; -//! Input pre-raytracing image rendered by OpenGL. -uniform sampler2D uOpenGlColorTexture; -//! Input pre-raytracing depth image rendered by OpenGL. -uniform sampler2D uOpenGlDepthTexture; - //! Total number of light sources. uniform int uLightCount; //! Intensity of global ambient light. @@ -95,6 +95,14 @@ uniform float uSceneEpsilon; uniform uvec2 uTextureSamplers[MAX_TEX_NUMBER]; #endif +#ifdef ADAPTIVE_SAMPLING + //! OpenGL image used for accumulating rendering result. + volatile restrict layout(size1x32) uniform image2D uRenderImage; + + //! OpenGL image storing offsets of sampled pixels blocks. + coherent restrict layout(size2x32) uniform iimage2D uOffsetImage; +#endif + //! Top color of gradient background. uniform vec4 uBackColorTop = vec4 (0.0); //! Bottom color of gradient background. @@ -240,7 +248,22 @@ vec3 InverseDirection (in vec3 theInput) //======================================================================= vec4 BackgroundColor() { +#ifdef ADAPTIVE_SAMPLING + + ivec2 aFragCoord = ivec2 (gl_FragCoord.xy); + + ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE, + aFragCoord.y / BLOCK_SIZE)).xy; + + aTileXY.y += aFragCoord.y % min (uWinSizeY - aTileXY.y, BLOCK_SIZE); + + return mix (uBackColorBot, uBackColorTop, float (aTileXY.y) / uWinSizeY); + +#else + return mix (uBackColorBot, uBackColorTop, vPixel.y); + +#endif } ///////////////////////////////////////////////////////////////////////////////////////// @@ -263,39 +286,6 @@ SRay GenerateRay (in vec2 thePixel) return SRay (mix (aP0, aP1, thePixel.y), aDirection); } -// ======================================================================= -// function : ComputeOpenGlDepth -// purpose : -// ======================================================================= -float ComputeOpenGlDepth (in SRay theRay) -{ - // a depth in range [0,1] - float anOpenGlDepth = texelFetch (uOpenGlDepthTexture, ivec2 (gl_FragCoord.xy), 0).r; - // pixel point in NDC-space [-1,1] - vec4 aPoint = vec4 (2.0f * vPixel.x - 1.0f, - 2.0f * vPixel.y - 1.0f, - 2.0f * anOpenGlDepth - 1.0f, - 1.0f); - vec4 aFinal = uUnviewMat * aPoint; - aFinal.xyz *= 1.f / aFinal.w; - - return (anOpenGlDepth < 1.f) ? length (aFinal.xyz - theRay.Origin) : MAXFLOAT; -} - -// ======================================================================= -// function : ComputeOpenGlColor -// purpose : -// ======================================================================= -vec4 ComputeOpenGlColor() -{ - vec4 anOpenGlColor = texelFetch (uOpenGlColorTexture, ivec2 (gl_FragCoord.xy), 0); - // During blending with factors GL_SRC_ALPHA and GL_ONE_MINUS_SRC_ALPHA (for text and markers) - // the alpha channel (written in the color buffer) was squared. - anOpenGlColor.a = 1.f - sqrt (anOpenGlColor.a); - - return anOpenGlColor; -} - // ======================================================================= // function : IntersectSphere // purpose : Computes ray-sphere intersection @@ -725,7 +715,7 @@ float SceneAnyHit (in SRay theRay, in vec3 theInverse, in float theDistance) #endif } - toContinue = (aHead >= 0); + toContinue = (aHead >= 0) && (aFactor > 0.1f); if (aHead == aStop) // go to top-level BVH { @@ -876,7 +866,6 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) int aTrsfId; - float anOpenGlDepth = ComputeOpenGlDepth (theRay); float aRaytraceDepth = MAXFLOAT; for (int aDepth = 0; aDepth < NB_BOUNCES; ++aDepth) @@ -898,8 +887,7 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) } else { - vec4 aGlColor = ComputeOpenGlColor(); - aColor = vec4 (mix (aGlColor.rgb, BackgroundColor().rgb, aGlColor.w), aGlColor.w); + aColor = BackgroundColor(); } aResult += aWeight.xyz * aColor.xyz; aWeight.w *= aColor.w; @@ -915,31 +903,22 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) dot (aInvTransf1, aHit.Normal), dot (aInvTransf2, aHit.Normal))); - // For polygons that are parallel to the screen plane, the depth slope - // is equal to 1, resulting in small polygon offset. For polygons that - // that are at a large angle to the screen, the depth slope tends to 1, - // resulting in a larger polygon offset - float aPolygonOffset = uSceneEpsilon * EPS_SCALE / - max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE); - - if (anOpenGlDepth < aHit.Time + aPolygonOffset) - { - vec4 aGlColor = ComputeOpenGlColor(); - - aResult += aWeight.xyz * aGlColor.xyz; - aWeight *= aGlColor.w; - } - theRay.Origin += theRay.Direct * aHit.Time; // intersection point - // Evaluate depth + // Evaluate depth on first hit if (aDepth == 0) { + // For polygons that are parallel to the screen plane, the depth slope + // is equal to 1, resulting in small polygon offset. For polygons that + // that are at a large angle to the screen, the depth slope tends to 1, + // resulting in a larger polygon offset + float aPolygonOffset = uSceneEpsilon * EPS_SCALE / + max (abs (dot (theRay.Direct, aHit.Normal)), MIN_SLOPE); + // Hit point in NDC-space [-1,1] (the polygon offset is applied in the world space) vec4 aNDCPoint = uViewMat * vec4 (theRay.Origin + theRay.Direct * aPolygonOffset, 1.f); - aNDCPoint.xyz *= 1.f / aNDCPoint.w; - aRaytraceDepth = aNDCPoint.z * 0.5f + 0.5f; + aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w) * 0.5f + 0.5f; } vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex); @@ -1042,10 +1021,6 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) { theRay.Direct = Refract (theRay.Direct, aNormal, aOpacity.z, aOpacity.w); } - else - { - anOpenGlDepth -= aHit.Time + uSceneEpsilon; - } } else { @@ -1074,8 +1049,6 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse) theInverse = 1.0f / max (abs (theRay.Direct), SMALL); theInverse = mix (-theInverse, theInverse, step (ZERO, theRay.Direct)); - - anOpenGlDepth = MAXFLOAT; // disable combining image with OpenGL output } theRay.Origin += theRay.Direct * uSceneEpsilon; diff --git a/src/Shaders/RaytraceRender.fs b/src/Shaders/RaytraceRender.fs index d75e962a78..ae25d4ef95 100644 --- a/src/Shaders/RaytraceRender.fs +++ b/src/Shaders/RaytraceRender.fs @@ -1,19 +1,24 @@ out vec4 OutColor; -// Seed for random number generator +// Seed for random number generator (generated on CPU). uniform int uFrameRndSeed; -// Weight of current frame related to accumulated frames. -uniform float uSampleWeight; +//! Enables/disables using of single RNG seed for 16x16 image +//! blocks. Increases performance up to 4x, but the noise has +//! become structured. Can be used fo final rendering. +uniform int uBlockedRngEnabled; -//! Input accumulated image. -uniform sampler2D uAccumTexture; +#ifndef ADAPTIVE_SAMPLING + //! Weight of current frame related to accumulated samples. + uniform float uSampleWeight; -//! Enabled/disbales using of single RNG seed for image 16x16 blocks. -//! Increases performance up to 4 times, but noise becomes structured. -uniform int uBlockedRngEnabled; + //! Input accumulated image. + uniform sampler2D uAccumTexture; +#endif -#define MAX_RADIANCE vec3 (10.f) +//! Maximum radiance that can be added to the pixel. Decreases noise +//! level, but introduces some bias. +#define MAX_RADIANCE vec3 (25.f) // ======================================================================= // function : main @@ -21,35 +26,82 @@ uniform int uBlockedRngEnabled; // ======================================================================= void main (void) { + SeedRand (uFrameRndSeed, uWinSizeX, uBlockedRngEnabled == 0 ? 1 : 16); + #ifndef PATH_TRACING + SRay aRay = GenerateRay (vPixel); + #else - ivec2 aWinSize = textureSize (uAccumTexture, 0); - SeedRand (uFrameRndSeed, aWinSize.x, uBlockedRngEnabled == 0 ? 1 : 8); + ivec2 aFragCoord = ivec2 (gl_FragCoord.xy); - SRay aRay = GenerateRay (vPixel + - vec2 (RandFloat() + 1.f, RandFloat() + 1.f) / vec2 (aWinSize)); -#endif +#ifdef ADAPTIVE_SAMPLING - vec3 aInvDirect = 1.f / max (abs (aRay.Direct), SMALL); + ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE, + aFragCoord.y / BLOCK_SIZE)).xy; - aInvDirect = vec3 (aRay.Direct.x < 0.f ? -aInvDirect.x : aInvDirect.x, - aRay.Direct.y < 0.f ? -aInvDirect.y : aInvDirect.y, - aRay.Direct.z < 0.f ? -aInvDirect.z : aInvDirect.z); + ivec2 aRealBlockSize = ivec2 (min (uWinSizeX - aTileXY.x, BLOCK_SIZE), + min (uWinSizeY - aTileXY.y, BLOCK_SIZE)); + + aFragCoord.x = aTileXY.x + (aFragCoord.x % aRealBlockSize.x); + aFragCoord.y = aTileXY.y + (aFragCoord.y % aRealBlockSize.y); + +#endif // ADAPTIVE_SAMPLING + + vec2 aPnt = vec2 (aFragCoord.x + RandFloat(), + aFragCoord.y + RandFloat()); + + SRay aRay = GenerateRay (aPnt / vec2 (uWinSizeX, uWinSizeY)); + +#endif // PATH_TRACING + + vec3 aInvDirect = InverseDirection (aRay.Direct); #ifdef PATH_TRACING + vec4 aColor = PathTrace (aRay, aInvDirect); - if (any (isnan (aColor.xyz))) + if (any (isnan (aColor.rgb))) { aColor.rgb = ZERO; } aColor.rgb = min (aColor.rgb, MAX_RADIANCE); +#ifdef ADAPTIVE_SAMPLING + + // accumulate RGB color and depth + imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0, + 2 * aFragCoord.y + 0), aColor.r); + imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1, + 2 * aFragCoord.y + 0), aColor.g); + imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1, + 2 * aFragCoord.y + 1), aColor.b); + imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2, + 2 * aFragCoord.y + 1), aColor.w); + + // accumulate number of samples + float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0, + 2 * aFragCoord.y + 1), 1.0); + + if (int (aNbSamples) % 2 == 0) // accumulate luminance for even samples only + { + imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2, + 2 * aFragCoord.y + 0), dot (LUMA, aColor.rgb)); + } + + discard; // fragment should not be written to frame buffer + +#else + OutColor = mix (texture2D (uAccumTexture, vPixel), aColor, uSampleWeight); + +#endif // ADAPTIVE_SAMPLING + #else + OutColor = clamp (Radiance (aRay, aInvDirect), 0.f, 1.f); -#endif + +#endif // PATH_TRACING } \ No newline at end of file diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index cc7a130368..1aa1699feb 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -8168,7 +8168,7 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, case Graphic3d_RM_RAYTRACING: theDI << "raytrace "; break; } theDI << "\n"; - theDI << "msaa: " << aParams.NbMsaaSamples << "\n"; + theDI << "msaa: " << aParams.NbMsaaSamples << "\n"; theDI << "rayDepth: " << aParams.RaytracingDepth << "\n"; theDI << "fsaa: " << (aParams.IsAntialiasingEnabled ? "on" : "off") << "\n"; theDI << "shadows: " << (aParams.IsShadowEnabled ? "on" : "off") << "\n"; @@ -8176,6 +8176,8 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, theDI << "gleam: " << (aParams.IsTransparentShadowEnabled ? "on" : "off") << "\n"; theDI << "GI: " << (aParams.IsGlobalIlluminationEnabled ? "on" : "off") << "\n"; theDI << "blocked RNG: " << (aParams.CoherentPathTracingMode ? "on" : "off") << "\n"; + theDI << "iss: " << (aParams.AdaptiveScreenSampling ? "on" : "off") << "\n"; + theDI << "iss debug: " << (aParams.ShowSamplingTiles ? "on" : "off") << "\n"; theDI << "shadingModel: "; switch (aView->ShadingModel()) { @@ -8401,6 +8403,38 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, } aParams.CoherentPathTracingMode = toEnable; } + else if (aFlag == "-iss") + { + if (toPrint) + { + theDI << (aParams.AdaptiveScreenSampling ? "on" : "off") << " "; + continue; + } + + Standard_Boolean toEnable = Standard_True; + if (++anArgIter < theArgNb + && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable)) + { + --anArgIter; + } + aParams.AdaptiveScreenSampling = toEnable; + } + else if (aFlag == "-issd") + { + if (toPrint) + { + theDI << (aParams.ShowSamplingTiles ? "on" : "off") << " "; + continue; + } + + Standard_Boolean toEnable = Standard_True; + if (++anArgIter < theArgNb + && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable)) + { + --anArgIter; + } + aParams.ShowSamplingTiles = toEnable; + } else if (aFlag == "-env") { if (toPrint) @@ -9500,6 +9534,8 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands) "\n '-gi on|off' Enables/disables global illumination effects" "\n '-brng on|off' Enables/disables blocked RNG (fast coherent PT)" "\n '-env on|off' Enables/disables environment map background" + "\n '-iss on|off' Enables/disables adaptive screen sampling (PT mode)" + "\n '-issd on|off' Shows screen sampling distribution in ISS mode" "\n '-shadingModel model' Controls shading model from enumeration" "\n color, flat, gouraud, phong" "\n '-resolution value' Sets a new pixels density (PPI), defines scaling factor for parameters like text size" diff --git a/tests/bugs/vis/bug27083 b/tests/bugs/vis/bug27083 index 9311674ed1..bb30320f58 100644 --- a/tests/bugs/vis/bug27083 +++ b/tests/bugs/vis/bug27083 @@ -1,20 +1,18 @@ puts "========" puts "OCC27083" +puts "Visualization, Ray Tracing - shape with visible face boundaries disappears after turning the ray-tracing on" puts "========" puts "" -################################################################## -puts "Visualization, Ray Tracing - shape with visible face boundaries disappears after turning the ray-tracing on" -################################################################## pload VISUALIZATION MODELING box b 1 1 1 -vinit +vclear +vinit View1 -vdisplay b +vdisplay -noupdate -dispMode 1 b vfit -vsetdispmode 1 vshowfaceboundary b 1 255 0 0 3 vraytrace 1 @@ -22,7 +20,7 @@ if {[vreadpixel 295 255 name] != "GOLDENROD4 0"} { puts "ERROR: the box with boundary aspect set is not shown in ray-tracing mode!" } -if {[vreadpixel 105 58 name] != "RED 0"} { +if {[vreadpixel 105 58 name] != "RED 1"} { puts "ERROR: the box's boundaries are not shown in ray-tracing mode!" }