From 4eaaf9d812e8f827c184b82ef9540cdff1b372c5 Mon Sep 17 00:00:00 2001 From: dbp Date: Thu, 26 Jan 2017 12:56:13 +0300 Subject: [PATCH] 0028129: Visualization, Path Tracing - Improve interactivity in "steady" rendering mode Re-basing the patch on current master. --- src/Graphic3d/Graphic3d_RenderingParams.hxx | 2 + src/OpenGl/OpenGl_TileSampler.cxx | 31 ++++-- src/OpenGl/OpenGl_TileSampler.hxx | 11 +- src/OpenGl/OpenGl_View.hxx | 18 +++- src/OpenGl/OpenGl_View_Raytrace.cxx | 106 ++++++++++++++----- src/Shaders/Display.fs | 15 ++- src/Shaders/PathtraceBase.fs | 23 ++-- src/Shaders/RaytraceRender.fs | 22 ++-- src/Shaders/Shaders_Display_fs.pxx | 15 ++- src/Shaders/Shaders_PathtraceBase_fs.pxx | 23 ++-- src/Shaders/Shaders_RaytraceRender_fs.pxx | 22 ++-- src/ViewerTest/ViewerTest_ViewerCommands.cxx | 66 ++++++++---- tests/v3d/raytrace/sample_ball_iss | 20 ++++ 13 files changed, 278 insertions(+), 96 deletions(-) create mode 100644 tests/v3d/raytrace/sample_ball_iss diff --git a/src/Graphic3d/Graphic3d_RenderingParams.hxx b/src/Graphic3d/Graphic3d_RenderingParams.hxx index b264d44361..33ca9e3ea3 100644 --- a/src/Graphic3d/Graphic3d_RenderingParams.hxx +++ b/src/Graphic3d/Graphic3d_RenderingParams.hxx @@ -62,6 +62,7 @@ public: TwoSidedBsdfModels (Standard_False), RadianceClampingValue (30.0), RebuildRayTracingShaders (Standard_False), + NbRayTracingTiles (16 * 16), // stereoscopic parameters StereoMode (Graphic3d_StereoMode_QuadBuffer), AnaglyphFilter (Anaglyph_RedCyan_Optimized), @@ -105,6 +106,7 @@ public: Standard_Boolean TwoSidedBsdfModels; //!< forces path tracing to use two-sided versions of original one-sided scattering models Standard_ShortReal RadianceClampingValue; //!< maximum radiance value used for clamping radiance estimation. Standard_Boolean RebuildRayTracingShaders; //!< forces rebuilding ray tracing shaders at the next frame + Standard_Integer NbRayTracingTiles; //!< total number of screen tiles used in adaptive sampling mode (PT only) 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/OpenGl/OpenGl_TileSampler.cxx b/src/OpenGl/OpenGl_TileSampler.cxx index 6e8a1dc393..c9b9b88e0f 100644 --- a/src/OpenGl/OpenGl_TileSampler.cxx +++ b/src/OpenGl/OpenGl_TileSampler.cxx @@ -150,36 +150,45 @@ void OpenGl_TileSampler::SetSize (const int theSizeX, //======================================================================= void OpenGl_TileSampler::Upload (const Handle(OpenGl_Context)& theContext, const Handle(OpenGl_Texture)& theTexture, - bool theAdaptive) + const int theNbTilesX, + const int theNbTilesY, + const bool theAdaptive) { if (theTexture.IsNull()) { return; } - std::vector aData (myTilesX * myTilesY * 2); - for (int aX = 0; aX < myTilesX; ++aX) + const int aNbTilesX = theAdaptive ? theNbTilesX : myTilesX; + const int aNbTilesY = theAdaptive ? theNbTilesY : myTilesY; + + Standard_ASSERT_RAISE (aNbTilesX * aNbTilesY > 0, + "Error! Number of sampling tiles should be positive"); + + std::vector aData (aNbTilesX * aNbTilesY * 2); + + for (int aX = 0; aX < aNbTilesX; ++aX) { - for (int aY = 0; aY < myTilesY; ++aY) + for (int aY = 0; aY < aNbTilesY; ++aY) { if (!theAdaptive) { - aData[(aY * myTilesX + aX) * 2 + 0] = aX * TileSize(); - aData[(aY * myTilesX + aX) * 2 + 1] = aY * TileSize(); + aData[(aY * aNbTilesX + aX) * 2 + 0] = aX * TileSize(); + aData[(aY * aNbTilesX + aX) * 2 + 1] = aY * TileSize(); } else { - Sample (aData[(aY * myTilesX + aX) * 2 + 0], - aData[(aY * myTilesX + aX) * 2 + 1]); + Sample (aData[(aY * aNbTilesX + aX) * 2 + 0], + aData[(aY * aNbTilesX + 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->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, GL_RG32I, aNbTilesX, aNbTilesY, 0, GL_RG_INTEGER, GL_UNSIGNED_INT, &aData.front()); + + if (theContext->core11fwd->glGetError() != 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 index 4f272930b0..9fc975358d 100644 --- a/src/OpenGl/OpenGl_TileSampler.hxx +++ b/src/OpenGl/OpenGl_TileSampler.hxx @@ -22,7 +22,12 @@ #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. +//! To improve GPU thread coherency, rendering window is split into pixel blocks or tiles. The important feature of +//! this approach is that it is possible to keep the same number of tiles for any screen resolution (e.g. 256 tiles +//! can be used for both 512 x 512 window and 1920 x 1080 window). So, a smaller number of tiles allows to increase +//! interactivity (FPS), but at the cost of higher per-frame variance ('noise'). On the contrary a larger number of +//! tiles decrease interactivity, but leads to lower per-frame variance. Note that the total time needed to produce +//! final final image is the same for both cases. class OpenGl_TileSampler { public: @@ -76,7 +81,9 @@ public: //! 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); + const int theNbTilesX, + const int theNbTilesY, + const bool theAdaptive); protected: diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index 45f44cc5fe..7eaadd2b67 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -711,6 +711,12 @@ protected: //! @name data types related to ray-tracing //! Maximum radiance value used for clamping radiance estimation. Standard_ShortReal RadianceClampingValue; + //! Number of tiles in X dimension (in adaptive sampling mode). + Standard_Integer NbTilesX; + + //! Number of tiles in Y dimension (in adaptive sampling mode). + Standard_Integer NbTilesY; + //! Creates default compile-time ray-tracing parameters. RaytracingParams() : StackSize (THE_DEFAULT_STACK_SIZE), @@ -721,7 +727,9 @@ protected: //! @name data types related to ray-tracing TwoSidedBsdfModels (Standard_False), AdaptiveScreenSampling (Standard_False), UseEnvMapForBackground (Standard_False), - RadianceClampingValue (30.0) { } + RadianceClampingValue (30.0), + NbTilesX (16), + NbTilesY (16) { } }; //! Describes state of OpenGL structure. @@ -897,9 +905,11 @@ protected: //! @name methods related to ray-tracing 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); + Standard_Boolean runPathtrace (const Standard_Integer theSizeX, + const Standard_Integer theSizeY, + 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, diff --git a/src/OpenGl/OpenGl_View_Raytrace.cxx b/src/OpenGl/OpenGl_View_Raytrace.cxx index 54494ef0d1..c078c546d5 100644 --- a/src/OpenGl/OpenGl_View_Raytrace.cxx +++ b/src/OpenGl/OpenGl_View_Raytrace.cxx @@ -1359,11 +1359,21 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context } } + Standard_Integer aNbTilesX = 8; + Standard_Integer aNbTilesY = 8; + + for (Standard_Integer anIdx = 0; aNbTilesX * aNbTilesY < myRenderParams.NbRayTracingTiles; ++anIdx) + { + (anIdx % 2 == 0 ? aNbTilesX : aNbTilesY) <<= 1; + } + if (myRenderParams.RaytracingDepth != myRaytraceParameters.NbBounces || myRenderParams.IsTransparentShadowEnabled != myRaytraceParameters.TransparentShadows || myRenderParams.IsGlobalIlluminationEnabled != myRaytraceParameters.GlobalIllumination || myRenderParams.TwoSidedBsdfModels != myRaytraceParameters.TwoSidedBsdfModels - || myRaytraceGeometry.HasTextures() != myRaytraceParameters.UseBindlessTextures) + || myRaytraceGeometry.HasTextures() != myRaytraceParameters.UseBindlessTextures + || aNbTilesX != myRaytraceParameters.NbTilesX + || aNbTilesY != myRaytraceParameters.NbTilesY) { myRaytraceParameters.NbBounces = myRenderParams.RaytracingDepth; myRaytraceParameters.TransparentShadows = myRenderParams.IsTransparentShadowEnabled; @@ -1371,6 +1381,18 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context myRaytraceParameters.TwoSidedBsdfModels = myRenderParams.TwoSidedBsdfModels; myRaytraceParameters.UseBindlessTextures = myRaytraceGeometry.HasTextures(); +#ifdef RAY_TRACE_PRINT_INFO + if (aNbTilesX != myRaytraceParameters.NbTilesX + || aNbTilesY != myRaytraceParameters.NbTilesY) + { + std::cout << "Number of tiles X: " << aNbTilesX << "\n"; + std::cout << "Number of tiles Y: " << aNbTilesY << "\n"; + } +#endif + + myRaytraceParameters.NbTilesX = aNbTilesX; + myRaytraceParameters.NbTilesY = aNbTilesY; + aToRebuildShaders = Standard_True; } @@ -1848,25 +1870,42 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer return Standard_True; } - if (myRaytraceFBO1[0]->GetSizeX() != theSizeX - || myRaytraceFBO1[0]->GetSizeY() != theSizeY) + if (myRaytraceParameters.AdaptiveScreenSampling) { - myAccumFrames = 0; - } + const Standard_Integer aSizeX = std::max (myRaytraceParameters.NbTilesX * 64, theSizeX); + const Standard_Integer aSizeY = std::max (myRaytraceParameters.NbTilesY * 64, theSizeY); - myRaytraceFBO1[0]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); - myRaytraceFBO2[0]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); + myRaytraceFBO1[0]->InitLazy (theGlContext, aSizeX, aSizeY, GL_RGBA32F, myFboDepthFormat); + myRaytraceFBO2[0]->InitLazy (theGlContext, aSizeX, aSizeY, GL_RGBA32F, myFboDepthFormat); - // Init second set of buffers for stereographic rendering. - if (myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo) - { - myRaytraceFBO1[1]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); - myRaytraceFBO2[1]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); + if (myRaytraceFBO1[1]->IsValid()) // second FBO not needed + { + myRaytraceFBO1[1]->Release (theGlContext.operator->()); + myRaytraceFBO2[1]->Release (theGlContext.operator->()); + } } - else + else // non-adaptive mode { - myRaytraceFBO1[1]->Release (theGlContext.operator->()); - myRaytraceFBO2[1]->Release (theGlContext.operator->()); + if (myRaytraceFBO1[0]->GetSizeX() != theSizeX + || myRaytraceFBO1[0]->GetSizeY() != theSizeY) + { + myAccumFrames = 0; // accumulation should be restarted + } + + myRaytraceFBO1[0]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); + myRaytraceFBO2[0]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); + + // Init second set of buffers for stereographic rendering + if (myCamera->ProjectionType() == Graphic3d_Camera::Projection_Stereo) + { + myRaytraceFBO1[1]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); + myRaytraceFBO2[1]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat); + } + else if (myRaytraceFBO1[1]->IsValid()) // second FBO not needed + { + myRaytraceFBO1[1]->Release (theGlContext.operator->()); + myRaytraceFBO2[1]->Release (theGlContext.operator->()); + } } myTileSampler.SetSize (theSizeX, theSizeY); @@ -1883,6 +1922,8 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer if (myRaytraceOutputTexture[0]->SizeX() / 3 != theSizeX || myRaytraceOutputTexture[0]->SizeY() / 2 != theSizeY) { + myAccumFrames = 0; + // 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 @@ -2634,7 +2675,7 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer if (myRaytraceParameters.GlobalIllumination) // path tracing { - aResult &= runPathtrace (theProjection, theReadDrawFbo, theGlContext); + aResult &= runPathtrace (theSizeX, theSizeY, theProjection, theReadDrawFbo, theGlContext); } else // Whitted-style ray-tracing { @@ -2775,7 +2816,9 @@ Standard_Boolean OpenGl_View::runRaytrace (const Standard_Integer theSize // function : runPathtrace // purpose : Runs path tracing shader // ======================================================================= -Standard_Boolean OpenGl_View::runPathtrace (const Graphic3d_Camera::Projection theProjection, +Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer theSizeX, + const Standard_Integer theSizeY, + const Graphic3d_Camera::Projection theProjection, OpenGl_FrameBuffer* theReadDrawFbo, const Handle(OpenGl_Context)& theGlContext) { @@ -2793,13 +2836,12 @@ Standard_Boolean OpenGl_View::runPathtrace (const Graphic3d_Camera::Projection 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 30 frames. - if (myAccumFrames % 4 == 0) - { - myTileSampler.Upload (theGlContext, myRaytraceTileOffsetsTexture, myAccumFrames > 30); - } + // Adaptive sampling is starting at the second frame + myTileSampler.Upload (theGlContext, + myRaytraceTileOffsetsTexture, + myRaytraceParameters.NbTilesX, + myRaytraceParameters.NbTilesY, + myAccumFrames > 0); } bindRaytraceTextures (theGlContext); @@ -2859,9 +2901,25 @@ Standard_Boolean OpenGl_View::runPathtrace (const Graphic3d_Camera::Projection glDisable (GL_DEPTH_TEST); + if (myRaytraceParameters.AdaptiveScreenSampling && myAccumFrames > 0) + { + glViewport (0, + 0, + myTileSampler.TileSize() * myRaytraceParameters.NbTilesX, + myTileSampler.TileSize() * myRaytraceParameters.NbTilesY); + } + // Generate for the given RNG seed theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6); + if (myRaytraceParameters.AdaptiveScreenSampling && myAccumFrames > 0) + { + glViewport (0, + 0, + theSizeX, + theSizeY); + } + // Output accumulated path traced image theGlContext->BindProgram (myOutImageProgram); diff --git a/src/Shaders/Display.fs b/src/Shaders/Display.fs index c4f9faa175..6aff9c1331 100644 --- a/src/Shaders/Display.fs +++ b/src/Shaders/Display.fs @@ -2,6 +2,8 @@ #extension GL_ARB_shader_image_load_store : require + #extension GL_ARB_shader_image_size : enable + //! OpenGL image used for accumulating rendering result. volatile restrict layout(size1x32) uniform image2D uRenderImage; @@ -87,8 +89,9 @@ void main (void) // 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)); + // accumulate visual error to current block; estimated error is written only + // after the first 40 samples and path length has reached 10 bounces or more + imageAtomicAdd (uVarianceImage, ivec2 (aPixel / vec2 (BLOCK_SIZE)), int (mix (SCALE_FACTOR, anError * SCALE_FACTOR, aColor.w > 40.f))); if (uDebugAdaptive == 0) // normal rendering { @@ -96,7 +99,13 @@ void main (void) } else // showing number of samples { - aColor = vec4 (0.5f * aColor.rgb * aSampleWeight + vec3 (0.f, aColor.w / uAccumFrames * 0.35f, 0.f), 1.0); + vec2 aRatio = vec2 (1.f, 1.f); + +#ifdef GL_ARB_shader_image_size + aRatio = vec2 (imageSize (uRenderImage)) / vec2 (3.f * 512.f, 2.f * 512.f); +#endif + + aColor = vec4 (0.5f * aColor.rgb * aSampleWeight + vec3 (0.f, sqrt (aRatio.x * aRatio.y) * aColor.w / uAccumFrames * 0.35f, 0.f), 1.0); } #endif // ADAPTIVE_SAMPLING diff --git a/src/Shaders/PathtraceBase.fs b/src/Shaders/PathtraceBase.fs index 76b44e86f9..7088826599 100644 --- a/src/Shaders/PathtraceBase.fs +++ b/src/Shaders/PathtraceBase.fs @@ -12,9 +12,6 @@ #ifdef PATH_TRACING -//! Number of previously rendered frames. -uniform int uAccumSamples; - /////////////////////////////////////////////////////////////////////////////////////// // Specific data types @@ -700,17 +697,26 @@ vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, o #define MATERIAL_FRESNEL(index) (18 * index + 16) #define MATERIAL_ABSORPT(index) (18 * index + 17) -// Enables expiremental russian roulette sampling +//! Enables experimental russian roulette sampling path termination. +//! In most cases, it provides faster image convergence with minimal +//! bias, so it is enabled by default. #define RUSSIAN_ROULETTE -//! Frame step to increase number of bounces -#define FRAME_STEP 5 +//! Frame step to increase number of bounces. This mode is used +//! for interaction with the model, when path length is limited +//! for the first samples, and gradually increasing when camera +//! is stabilizing. +#ifdef ADAPTIVE_SAMPLING + #define FRAME_STEP 4 +#else + #define FRAME_STEP 5 +#endif //======================================================================= // function : PathTrace // purpose : Calculates radiance along the given ray //======================================================================= -vec4 PathTrace (in SRay theRay, in vec3 theInverse) +vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples) { float aRaytraceDepth = MAXFLOAT; @@ -867,7 +873,8 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse) aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f); #endif - if (RandFloat() > aSurvive || all (lessThan (aThroughput, MIN_THROUGHPUT)) || aDepth >= uAccumSamples / FRAME_STEP + step (1.f / M_PI, aImpPDF)) + // here, we additionally increase path length for non-diffuse bounces + if (RandFloat() > aSurvive || all (lessThan (aThroughput, MIN_THROUGHPUT)) || aDepth >= theNbSamples / FRAME_STEP + step (1.f / M_PI, aImpPDF)) { aDepth = INVALID_BOUNCES; // terminate path } diff --git a/src/Shaders/RaytraceRender.fs b/src/Shaders/RaytraceRender.fs index 15ed8d8f50..e3937ff992 100644 --- a/src/Shaders/RaytraceRender.fs +++ b/src/Shaders/RaytraceRender.fs @@ -8,6 +8,9 @@ uniform int uFrameRndSeed; //! become structured. Can be used fo final rendering. uniform int uBlockedRngEnabled; +//! Number of previously rendered frames (used in non-ISS mode). +uniform int uAccumSamples; + #ifndef ADAPTIVE_SAMPLING //! Input image with previously accumulated samples. uniform sampler2D uAccumTexture; @@ -57,7 +60,18 @@ void main (void) #ifdef PATH_TRACING - vec4 aColor = PathTrace (aRay, aInvDirect); +#ifndef ADAPTIVE_SAMPLING + + vec4 aColor = PathTrace (aRay, aInvDirect, uAccumSamples); + +#else + + float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0, + 2 * aFragCoord.y + 1), 1.0); + + vec4 aColor = PathTrace (aRay, aInvDirect, int (aNbSamples)); + +#endif if (any (isnan (aColor.rgb))) { @@ -78,18 +92,12 @@ void main (void) 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 if (uAccumSamples == 0) diff --git a/src/Shaders/Shaders_Display_fs.pxx b/src/Shaders/Shaders_Display_fs.pxx index 2685179e4a..80e1cba1c8 100644 --- a/src/Shaders/Shaders_Display_fs.pxx +++ b/src/Shaders/Shaders_Display_fs.pxx @@ -5,6 +5,8 @@ static const char Shaders_Display_fs[] = "\n" " #extension GL_ARB_shader_image_load_store : require\n" "\n" + " #extension GL_ARB_shader_image_size : enable\n" + "\n" " //! OpenGL image used for accumulating rendering result.\n" " volatile restrict layout(size1x32) uniform image2D uRenderImage;\n" "\n" @@ -90,8 +92,9 @@ static const char Shaders_Display_fs[] = " // calculate visual error\n" " float anError = (aAverRad - aHalfRad) * (aAverRad - aHalfRad);\n" "\n" - " // accumulate visual error to current block\n" - " imageAtomicAdd (uVarianceImage, ivec2 (aPixel / vec2 (BLOCK_SIZE)), int (anError * SCALE_FACTOR));\n" + " // accumulate visual error to current block; estimated error is written only\n" + " // after the first 40 samples and path length has reached 10 bounces or more\n" + " imageAtomicAdd (uVarianceImage, ivec2 (aPixel / vec2 (BLOCK_SIZE)), int (mix (SCALE_FACTOR, anError * SCALE_FACTOR, aColor.w > 40.f)));\n" "\n" " if (uDebugAdaptive == 0) // normal rendering\n" " {\n" @@ -99,7 +102,13 @@ static const char Shaders_Display_fs[] = " }\n" " else // showing number of samples\n" " {\n" - " aColor = vec4 (0.5f * aColor.rgb * aSampleWeight + vec3 (0.f, aColor.w / uAccumFrames * 0.35f, 0.f), 1.0);\n" + " vec2 aRatio = vec2 (1.f, 1.f);\n" + "\n" + "#ifdef GL_ARB_shader_image_size\n" + " aRatio = vec2 (imageSize (uRenderImage)) / vec2 (3.f * 512.f, 2.f * 512.f);\n" + "#endif\n" + "\n" + " aColor = vec4 (0.5f * aColor.rgb * aSampleWeight + vec3 (0.f, sqrt (aRatio.x * aRatio.y) * aColor.w / uAccumFrames * 0.35f, 0.f), 1.0);\n" " }\n" "\n" "#endif // ADAPTIVE_SAMPLING\n" diff --git a/src/Shaders/Shaders_PathtraceBase_fs.pxx b/src/Shaders/Shaders_PathtraceBase_fs.pxx index fb1746cff0..04cd95aa08 100644 --- a/src/Shaders/Shaders_PathtraceBase_fs.pxx +++ b/src/Shaders/Shaders_PathtraceBase_fs.pxx @@ -15,9 +15,6 @@ static const char Shaders_PathtraceBase_fs[] = "\n" "#ifdef PATH_TRACING\n" "\n" - "//! Number of previously rendered frames.\n" - "uniform int uAccumSamples;\n" - "\n" "///////////////////////////////////////////////////////////////////////////////////////\n" "// Specific data types\n" "\n" @@ -703,17 +700,26 @@ static const char Shaders_PathtraceBase_fs[] = "#define MATERIAL_FRESNEL(index) (18 * index + 16)\n" "#define MATERIAL_ABSORPT(index) (18 * index + 17)\n" "\n" - "// Enables expiremental russian roulette sampling\n" + "//! Enables experimental russian roulette sampling path termination.\n" + "//! In most cases, it provides faster image convergence with minimal\n" + "//! bias, so it is enabled by default.\n" "#define RUSSIAN_ROULETTE\n" "\n" - "//! Frame step to increase number of bounces\n" - "#define FRAME_STEP 5\n" + "//! Frame step to increase number of bounces. This mode is used\n" + "//! for interaction with the model, when path length is limited\n" + "//! for the first samples, and gradually increasing when camera\n" + "//! is stabilizing.\n" + "#ifdef ADAPTIVE_SAMPLING\n" + " #define FRAME_STEP 4\n" + "#else\n" + " #define FRAME_STEP 5\n" + "#endif\n" "\n" "//=======================================================================\n" "// function : PathTrace\n" "// purpose : Calculates radiance along the given ray\n" "//=======================================================================\n" - "vec4 PathTrace (in SRay theRay, in vec3 theInverse)\n" + "vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)\n" "{\n" " float aRaytraceDepth = MAXFLOAT;\n" "\n" @@ -870,7 +876,8 @@ static const char Shaders_PathtraceBase_fs[] = " aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f);\n" "#endif\n" "\n" - " if (RandFloat() > aSurvive || all (lessThan (aThroughput, MIN_THROUGHPUT)) || aDepth >= uAccumSamples / FRAME_STEP + step (1.f / M_PI, aImpPDF))\n" + " // here, we additionally increase path length for non-diffuse bounces\n" + " if (RandFloat() > aSurvive || all (lessThan (aThroughput, MIN_THROUGHPUT)) || aDepth >= theNbSamples / FRAME_STEP + step (1.f / M_PI, aImpPDF))\n" " {\n" " aDepth = INVALID_BOUNCES; // terminate path\n" " }\n" diff --git a/src/Shaders/Shaders_RaytraceRender_fs.pxx b/src/Shaders/Shaders_RaytraceRender_fs.pxx index 46afbe0731..1d6e5bda15 100644 --- a/src/Shaders/Shaders_RaytraceRender_fs.pxx +++ b/src/Shaders/Shaders_RaytraceRender_fs.pxx @@ -11,6 +11,9 @@ static const char Shaders_RaytraceRender_fs[] = "//! become structured. Can be used fo final rendering.\n" "uniform int uBlockedRngEnabled;\n" "\n" + "//! Number of previously rendered frames (used in non-ISS mode).\n" + "uniform int uAccumSamples;\n" + "\n" "#ifndef ADAPTIVE_SAMPLING\n" " //! Input image with previously accumulated samples.\n" " uniform sampler2D uAccumTexture;\n" @@ -60,7 +63,18 @@ static const char Shaders_RaytraceRender_fs[] = "\n" "#ifdef PATH_TRACING\n" "\n" - " vec4 aColor = PathTrace (aRay, aInvDirect);\n" + "#ifndef ADAPTIVE_SAMPLING\n" + "\n" + " vec4 aColor = PathTrace (aRay, aInvDirect, uAccumSamples);\n" + "\n" + "#else\n" + "\n" + " float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,\n" + " 2 * aFragCoord.y + 1), 1.0);\n" + "\n" + " vec4 aColor = PathTrace (aRay, aInvDirect, int (aNbSamples));\n" + "\n" + "#endif\n" "\n" " if (any (isnan (aColor.rgb)))\n" " {\n" @@ -81,18 +95,12 @@ static const char Shaders_RaytraceRender_fs[] = " imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,\n" " 2 * aFragCoord.y + 1), aColor.w);\n" "\n" - " // accumulate number of samples\n" - " float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,\n" - " 2 * aFragCoord.y + 1), 1.0);\n" - "\n" " if (int (aNbSamples) % 2 == 0) // accumulate luminance for even samples only\n" " {\n" " imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,\n" " 2 * aFragCoord.y + 0), dot (LUMA, aColor.rgb));\n" " }\n" "\n" - " discard; // fragment should not be written to frame buffer\n" - "\n" "#else\n" "\n" " if (uAccumSamples == 0)\n" diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index ce91b42a55..5572280097 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -9076,6 +9076,7 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, theDI << "iss debug: " << (aParams.ShowSamplingTiles ? "on" : "off") << "\n"; theDI << "two-sided BSDF: " << (aParams.TwoSidedBsdfModels ? "on" : "off") << "\n"; theDI << "max radiance: " << aParams.RadianceClampingValue << "\n"; + theDI << "nb tiles (iss): " << aParams.NbRayTracingTiles << "\n"; theDI << "shadingModel: "; switch (aView->ShadingModel()) { @@ -9364,6 +9365,32 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI, } aParams.ShowSamplingTiles = toEnable; } + else if (aFlag == "-nbtiles") + { + if (toPrint) + { + theDI << aParams.NbRayTracingTiles << " "; + continue; + } + else if (++anArgIter >= theArgNb) + { + std::cerr << "Error: wrong syntax at argument '" << anArg << "'\n"; + return 1; + } + + const Standard_Integer aNbTiles = Draw::Atoi (theArgVec[anArgIter]); + + if (aNbTiles < 64) + { + std::cerr << "Error: invalid number of ISS tiles " << aNbTiles << ".\n"; + std::cerr << "Specify value in range [64, 1024].\n"; + return 1; + } + else + { + aParams.NbRayTracingTiles = aNbTiles; + } + } else if (aFlag == "-env") { if (toPrint) @@ -10924,25 +10951,26 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands) __FILE__, VRenderParams, group); theCommands.Add("vrenderparams", "\n Manages rendering parameters: " - "\n '-raster' Disables GPU ray-tracing" - "\n '-msaa 0..4' Specifies number of samples for MSAA" - "\n '-rayTrace' Enables GPU ray-tracing" - "\n '-rayDepth 0..10' Defines maximum ray-tracing depth" - "\n '-shadows on|off' Enables/disables shadows rendering" - "\n '-reflections on|off' Enables/disables specular reflections" - "\n '-fsaa on|off' Enables/disables adaptive anti-aliasing" - "\n '-gleam on|off' Enables/disables transparency shadow effects" - "\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 '-twoside on|off' Enables/disables two-sided BSDF models (PT mode)" - "\n '-iss on|off' Enables/disables adaptive screen sampling (PT mode)" - "\n '-issd on|off' Shows screen sampling distribution in ISS mode" - "\n '-maxrad > 0.0' Value used for clamping radiance estimation (PT mode)" - "\n '-rebuildGlsl on|off' Rebuild Ray-Tracing GLSL programs (for debugging)" - "\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" + "\n '-raster' Disables GPU ray-tracing" + "\n '-msaa 0..4' Specifies number of samples for MSAA" + "\n '-rayTrace' Enables GPU ray-tracing" + "\n '-rayDepth 0..10' Defines maximum ray-tracing depth" + "\n '-shadows on|off' Enables/disables shadows rendering" + "\n '-reflections on|off' Enables/disables specular reflections" + "\n '-fsaa on|off' Enables/disables adaptive anti-aliasing" + "\n '-gleam on|off' Enables/disables transparency shadow effects" + "\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 '-twoside on|off' Enables/disables two-sided BSDF models (PT mode)" + "\n '-iss on|off' Enables/disables adaptive screen sampling (PT mode)" + "\n '-issd on|off' Shows screen sampling distribution in ISS mode" + "\n '-maxrad > 0.0' Value used for clamping radiance estimation (PT mode)" + "\n '-nbtiles 64..1024' Specifies number of screen tiles in ISS mode" + "\n '-rebuildGlsl on|off' Rebuild Ray-Tracing GLSL programs (for debugging)" + "\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" "\n Unlike vcaps, these parameters dramatically change visual properties." "\n Command is intended to control presentation quality depending on" "\n hardware capabilities and performance.", diff --git a/tests/v3d/raytrace/sample_ball_iss b/tests/v3d/raytrace/sample_ball_iss new file mode 100644 index 0000000000..32b9d31f1c --- /dev/null +++ b/tests/v3d/raytrace/sample_ball_iss @@ -0,0 +1,20 @@ +puts "============" +puts "Visualization - Path Tracing, Ball sample (ISS mode)" +puts "============" +puts "" + +cpulimit 1000 + +source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_ball.tcl + +vrenderparams -iss -nbtiles 64 +vfps 1024 +vdump $imagedir/${casename}_iss_64.png + +vrenderparams -iss -nbtiles 256 +vfps 256 +vdump $imagedir/${casename}_iss_256.png + +vrenderparams -iss -nbtiles 1024 +vfps 64 +vdump $imagedir/${casename}_iss_1024.png