1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-04 18:06:22 +03:00

0030483: Visualization, Path Tracing - make Tile Size configurable

OpenGl_TileSampler has been refactored to better describe its logic:
- Offset image now defines tile index instead of offset to tile origin.
- Added 2D array defining the number of times to sample tile for straight-forward debugging.

Graphic3d_RenderingParams has been extended with property
RayTracingTileSize for testing various tile configurations.
Default behavior is the following:
- Target number of tiles (e.g. upper limit per frame): 256
- Tile size: 32x32.

OpenGl_View::runPathtrace() has been split into two methods per rendering stage.
OpenGl_Texture::Init() now returns FALSE immediately on 0 input dimensions.

Added Image_PixMapTypedData template class allowing to work with image data of known pixel format.
This commit is contained in:
kgv 2019-02-06 19:21:23 +03:00 committed by apn
parent e607bd3e6b
commit 66d1cdc65d
17 changed files with 569 additions and 482 deletions

View File

@ -113,6 +113,7 @@ public:
TwoSidedBsdfModels (Standard_False),
RadianceClampingValue (30.0),
RebuildRayTracingShaders (Standard_False),
RayTracingTileSize (32),
NbRayTracingTiles (16 * 16),
CameraApertureRadius (0.0f),
CameraFocalPlaneDist (1.0f),
@ -187,7 +188,10 @@ 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)
Standard_Integer RayTracingTileSize; //!< screen tile size, 32 by default (adaptive sampling mode of path tracing);
Standard_Integer NbRayTracingTiles; //!< maximum number of screen tiles per frame, 256 by default (adaptive sampling mode of path tracing);
//! this parameter limits the number of tiles to be rendered per redraw, increasing Viewer interactivity,
//! but also increasing the time for achieving a good quality; -1 means no limit
Standard_ShortReal CameraApertureRadius; //!< aperture radius of perspective camera used for depth-of-field, 0.0 by default (no DOF) (path tracing only)
Standard_ShortReal CameraFocalPlaneDist; //!< focal distance of perspective camera used for depth-of field, 1.0 by default (path tracing only)
FrustumCulling FrustumCullingState; //!< state of frustum culling optimization; FrustumCulling_On by default

View File

@ -7,5 +7,6 @@ Image_Format.hxx
Image_PixMap.cxx
Image_PixMap.hxx
Image_PixMapData.hxx
Image_PixMapTypedData.hxx
Image_VideoRecorder.cxx
Image_VideoRecorder.hxx

View File

@ -38,7 +38,7 @@ public:
}
//! Initializer.
void Init (const Handle(NCollection_BaseAllocator)& theAlloc,
bool Init (const Handle(NCollection_BaseAllocator)& theAlloc,
const Standard_Size theSizeBPP,
const Standard_Size theSizeX,
const Standard_Size theSizeY,
@ -59,6 +59,16 @@ public:
Allocate (mySize);
}
SetTopDown (TopToDown == 1);
return !IsEmpty();
}
//! Reset all values to zeros.
void ZeroData()
{
if (myData != NULL)
{
memset (myData, 0, mySize);
}
}
//! @return data pointer to requested row (first column).

View File

@ -0,0 +1,64 @@
// Copyright (c) 2019 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 _Image_PixMapTypedData_Header
#define _Image_PixMapTypedData_Header
#include <Image_PixMapData.hxx>
//! Structure to manage image buffer with predefined pixel type.
template<typename PixelType_t>
class Image_PixMapTypedData : public Image_PixMapData
{
public:
//! Empty constructor.
Image_PixMapTypedData() {}
//! Initializer.
bool Init (const Handle(NCollection_BaseAllocator)& theAlloc,
Standard_Size theSizeX,
Standard_Size theSizeY,
Standard_Size theSizeRowBytes = 0,
Standard_Byte* theDataPtr = 0)
{
const Standard_Size aSizeBPP = sizeof(PixelType_t);
return Image_PixMapData::Init (theAlloc, aSizeBPP, theSizeX, theSizeY, theSizeRowBytes, theDataPtr);
}
//! Reset all values to specified one.
void Init (const PixelType_t& theValue)
{
for (Standard_Size aRowIter = 0; aRowIter < SizeY; ++aRowIter)
{
for (Standard_Size aColIter = 0; aColIter < SizeX; ++aColIter)
{
ChangeValue (aRowIter, aColIter) = theValue;
}
}
}
//! @return data pointer to requested row (first column).
const PixelType_t* Row (Standard_Size theRow) const { return (const PixelType_t* )Image_PixMapData::Row (theRow); }
//! @return data pointer to requested row (first column).
PixelType_t* ChangeRow (Standard_Size theRow) { return (PixelType_t* )Image_PixMapData::ChangeRow (theRow); }
//! @return data pointer to requested position.
const PixelType_t& Value (Standard_Size theRow, Standard_Size theCol) const { return *(const PixelType_t* )Image_PixMapData::Value (theRow, theCol); }
//! @return data pointer to requested position.
PixelType_t& ChangeValue (Standard_Size theRow, Standard_Size theCol) { return *(PixelType_t* )Image_PixMapData::ChangeValue (theRow, theCol); }
};
#endif // _Image_PixMapTypedData_Header

View File

@ -405,6 +405,15 @@ bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
const Graphic3d_TypeOfTexture theType,
const Image_PixMap* theImage)
{
if (theSizeX < 1
|| theSizeY < 1)
{
theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
"Error: texture of 0 size cannot be created.");
Release (theCtx.operator->());
return false;
}
#if !defined(GL_ES_VERSION_2_0)
const GLenum aTarget = theType == Graphic3d_TOT_1D
? GL_TEXTURE_1D

View File

@ -15,24 +15,18 @@
#include <OpenGl_Context.hxx>
#include <OpenGl_TileSampler.hxx>
#include <Graphic3d_RenderingParams.hxx>
#include <TCollection_ExtendedString.hxx>
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)
: myLastSample (0),
myScaleFactor(1.0f),
myTileSize (0),
myViewSize (0, 0)
{
mySampler.initFaure();
}
@ -41,156 +35,211 @@ OpenGl_TileSampler::OpenGl_TileSampler()
//function : GrabVarianceMap
//purpose :
//=======================================================================
void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext)
{
#if !defined(GL_ES_VERSION_2_0)
std::vector<GLint> 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<int> (ceilf (static_cast<float> (mySizeX) / TileSize()));
myTilesY = static_cast<int> (ceilf (static_cast<float> (mySizeY) / TileSize()));
myVarianceMap.resize (myTilesX * myTilesY);
}
//=======================================================================
//function : Upload
//purpose :
//=======================================================================
void OpenGl_TileSampler::Upload (const Handle(OpenGl_Context)& theContext,
const Handle(OpenGl_Texture)& theTexture,
const int theNbTilesX,
const int theNbTilesY,
const bool theAdaptive)
void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
const Handle(OpenGl_Texture)& theTexture)
{
if (theTexture.IsNull())
{
return;
}
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<GLint> aData (aNbTilesX * aNbTilesY * 2);
for (int aX = 0; aX < aNbTilesX; ++aX)
myVarianceRaw.Init (0);
#if !defined(GL_ES_VERSION_2_0)
theTexture->Bind (theContext);
theContext->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, 1);
theContext->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0);
theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, myVarianceRaw.ChangeData());
const GLenum anErr = theContext->core11fwd->glGetError();
theTexture->Unbind (theContext);
if (anErr != GL_NO_ERROR)
{
for (int aY = 0; aY < aNbTilesY; ++aY)
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");
return;
}
#else
// glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
(void )theContext;
#endif
const float aFactor = 1.0f / myScaleFactor;
for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
{
for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
{
if (!theAdaptive)
const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
float& aTile = myVarianceMap.ChangeValue (aRowIter, aColIter);
aTile = aFactor * float(aRawValue);
aTile *= 1.0f / tileArea ((int )aColIter, (int )aRowIter); // average error over the tile
if (aRowIter != 0)
{
aData[(aY * aNbTilesX + aX) * 2 + 0] = aX * TileSize();
aData[(aY * aNbTilesX + aX) * 2 + 1] = aY * TileSize();
}
else
{
Sample (aData[(aY * aNbTilesX + aX) * 2 + 0],
aData[(aY * aNbTilesX + aX) * 2 + 1]);
aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
}
}
}
theTexture->Bind (theContext);
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)
// build marginal distribution
for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
{
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");
myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
if (aX != 0)
{
myMarginalMap[aX] += myMarginalMap[aX - 1];
}
}
}
//=======================================================================
//function : nextTileToSample
//purpose :
//=======================================================================
Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
{
Graphic3d_Vec2i aTile (0, 0);
const float aKsiX = mySampler.sample (0, myLastSample) * myMarginalMap.back();
for (; (size_t )aTile.x() < myMarginalMap.size() - 1; ++aTile.x())
{
if (aKsiX <= myMarginalMap[aTile.x()])
{
break;
}
}
const float aKsiY = mySampler.sample (1, myLastSample) * myVarianceMap.Value (myVarianceMap.SizeY - 1, aTile.x());
for (; (size_t )aTile.y() < myVarianceMap.SizeY - 1; ++aTile.y())
{
if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
{
break;
}
}
++myLastSample;
return aTile;
}
//=======================================================================
//function : SetSize
//purpose :
//=======================================================================
void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
const Graphic3d_Vec2i& theSize)
{
if (theSize.x() <= 0
|| theSize.y() <= 0)
{
return;
}
myViewSize = theSize;
const int aTileSize = Max (theParams.RayTracingTileSize, 1);
const int aNbTilesX = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.x()) / aTileSize)));
const int aNbTilesY = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.y()) / aTileSize)));
if (myTileSize != aTileSize
|| (int )myTiles.SizeX != aNbTilesX
|| (int )myTiles.SizeY != aNbTilesY)
{
myTileSize = aTileSize;
myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
myTiles.SetTopDown (true);
myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
myTiles.Init (1);
myVarianceMap.SetTopDown (true);
myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
myVarianceMap.Init (0.0f);
myVarianceRaw.SetTopDown (true);
myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
myVarianceRaw.Init (0);
myOffsets.SetTopDown (true);
myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
myOffsets.Init (Graphic3d_Vec2i (-1, -1));
myMarginalMap.resize (myTiles.SizeX);
myMarginalMap.assign (myMarginalMap.size(), 0.0f);
}
// calculate a size of compact offsets texture optimal for rendering reduced number of tiles
Standard_Integer aNbShunkTilesX = (int )myTiles.SizeX, aNbShunkTilesY = (int )myTiles.SizeY;
if (theParams.NbRayTracingTiles > 0)
{
aNbShunkTilesX = 8;
aNbShunkTilesY = 8;
for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
{
(anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
}
}
if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
|| (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
{
myOffsetsShrunk.SetTopDown (true);
myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
}
}
//=======================================================================
//function : UploadOffsets
//purpose :
//=======================================================================
bool OpenGl_TileSampler::UploadOffsets (const Handle(OpenGl_Context)& theContext,
const Handle(OpenGl_Texture)& theOffsetsTexture,
const bool theAdaptive)
{
if (myTiles.IsEmpty())
{
return false;
}
myTiles.Init (0);
Image_PixMapTypedData<Graphic3d_Vec2i>& anOffsets = theAdaptive ? myOffsetsShrunk : myOffsets;
anOffsets.Init (Graphic3d_Vec2i (-1, -1));
for (Standard_Size aRowIter = 0; aRowIter < anOffsets.SizeY; ++aRowIter)
{
for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
{
Graphic3d_Vec2i& aRedirectTile = anOffsets.ChangeValue (aRowIter, aColIter);
aRedirectTile = theAdaptive ? nextTileToSample() : Graphic3d_Vec2i ((int )aColIter, (int )aRowIter);
myTiles.ChangeValue (aRedirectTile.y(), aRedirectTile.x()) += 1;
}
}
bool hasErrors = false;
if (!theOffsetsTexture.IsNull())
{
if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
|| theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
|| !theOffsetsTexture->IsValid())
{
theOffsetsTexture->Release (theContext.operator->());
if (!theOffsetsTexture->Init (theContext, GL_RG32I, GL_RG_INTEGER, GL_INT,
(int )anOffsets.SizeX, (int )anOffsets.SizeY, Graphic3d_TOT_2D))
{
hasErrors = true;
}
}
if (theOffsetsTexture->IsValid())
{
theOffsetsTexture->Bind (theContext);
theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
#if !defined(GL_ES_VERSION_2_0)
theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
#endif
theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )anOffsets.SizeX, (int )anOffsets.SizeY, GL_RG_INTEGER, GL_INT, anOffsets.Data());
if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
{
hasErrors = true;
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");
}
theOffsetsTexture->Unbind (theContext);
}
}
return !hasErrors;
}

View File

@ -19,8 +19,12 @@
#include <OpenGl_Texture.hxx>
#include <OpenGl_HaltonSampler.hxx>
#include <Image_PixMapTypedData.hxx>
#include <vector>
class Graphic3d_RenderingParams;
//! Tool object used for sampling screen tiles according to estimated pixel variance (used in path tracing engine).
//! 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
@ -30,87 +34,89 @@
//! final final image is the same for both cases.
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; }
//! Size of individual tile in pixels.
Graphic3d_Vec2i TileSize() const { return Graphic3d_Vec2i (myTileSize, myTileSize); }
//! Returns height of ray-tracing viewport.
int SizeY() const { return mySizeY; }
//! Scale factor for quantization of visual error (float) into signed integer.
float VarianceScaleFactor() const { return myScaleFactor; }
//! Returns number of tiles in X dimension.
int NbTilesX() const { return myTilesX; }
int NbTilesX() const { return (int)myTiles.SizeX; }
//! Returns number of tiles in Y dimension.
int NbTilesY() const { return myTilesY; }
int NbTilesY() const { return (int)myTiles.SizeY; }
//! Returns total number of tiles in viewport.
int NbTiles() const { return myTilesX * myTilesY; }
int NbTiles() const { return int(myTiles.SizeX * myTiles.SizeY); }
//! Specifies size of ray-tracing viewport.
Standard_EXPORT void SetSize (const int theSizeX,
const int theSizeY);
//! Returns ray-tracing viewport.
const Graphic3d_Vec2i& ViewSize() const { return myViewSize; }
//! Returns number of pixels in the given tile.
int TileArea (const int theX,
const int theY) const
//! Number of tiles within offsets texture.
Graphic3d_Vec2i NbOffsetTiles (bool theAdaptive) const
{
return Min (TileSize(), mySizeX - theX * TileSize())
* Min (TileSize(), mySizeY - theY * TileSize());
return theAdaptive
? Graphic3d_Vec2i ((int )myOffsetsShrunk.SizeX, (int )myOffsetsShrunk.SizeY)
: Graphic3d_Vec2i ((int )myOffsets.SizeX, (int )myOffsets.SizeY);
}
//! Maximum number of tiles within offsets texture.
Graphic3d_Vec2i NbOffsetTilesMax() const { return NbOffsetTiles (true).cwiseMax (NbOffsetTiles (false)); }
//! Viewport for rendering using offsets texture.
Graphic3d_Vec2i OffsetTilesViewport (bool theAdaptive) const { return NbOffsetTiles (theAdaptive) * myTileSize; }
//! Maximum viewport for rendering using offsets texture.
Graphic3d_Vec2i OffsetTilesViewportMax() const { return NbOffsetTilesMax() * myTileSize; }
//! Specifies size of ray-tracing viewport and recomputes tile size.
Standard_EXPORT void SetSize (const Graphic3d_RenderingParams& theParams,
const Graphic3d_Vec2i& theSize);
//! Fetches current error estimation from the GPU and
//! builds 2D discrete distribution for tile sampling.
Standard_EXPORT void GrabVarianceMap (const Handle(OpenGl_Context)& theContext);
Standard_EXPORT void GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
const Handle(OpenGl_Texture)& theTexture);
//! 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; }
//! Resets (restart) tile sampler to initial state.
void Reset() { myLastSample = 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,
const int theNbTilesX,
const int theNbTilesY,
const bool theAdaptive);
Standard_EXPORT bool UploadOffsets (const Handle(OpenGl_Context)& theContext,
const Handle(OpenGl_Texture)& theOffsetsTexture,
const bool theAdaptive);
protected:
//! Returns tile value (estimated error).
float Tile (const int theX,
const int theY) const
//! Returns number of pixels in the given tile.
int tileArea (int theX, int theY) const
{
return myVarianceMap[theY * myTilesX + theX];
const int aSizeX = Min (myTileSize, myViewSize.x() - theX * myTileSize);
const int aSizeY = Min (myTileSize, myViewSize.y() - theY * myTileSize);
return aSizeX * aSizeY;
}
//! Returns tile value (estimated error).
float& ChangeTile (const int theX,
const int theY)
{
return myVarianceMap[theY * myTilesX + theX];
}
//! Samples tile location according to estimated error.
Standard_EXPORT Graphic3d_Vec2i nextTileToSample();
protected:
std::vector<float> myVarianceMap; //!< Estimation of visual error per tile
std::vector<float> 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
Image_PixMapTypedData<unsigned int> myTiles; //!< number of samples per tile (initially all 1)
Image_PixMapTypedData<float> myVarianceMap; //!< Estimation of visual error per tile
Image_PixMapTypedData<int> myVarianceRaw; //!< Estimation of visual error per tile (raw data)
Image_PixMapTypedData<Graphic3d_Vec2i> myOffsets; //!< 2D array of tiles redirecting to another tile
Image_PixMapTypedData<Graphic3d_Vec2i> myOffsetsShrunk; //!< 2D array of tiles redirecting to another tile (shrunk)
std::vector<float> myMarginalMap; //!< Marginal distribution of 2D error map
OpenGl_HaltonSampler mySampler; //!< Halton sequence generator
unsigned int myLastSample; //!< Index of generated sample
float myScaleFactor; //!< scale factor for quantization of visual error (float) into signed integer
int myTileSize; //!< tile size
Graphic3d_Vec2i myViewSize; //!< ray-tracing viewport
};

View File

@ -593,6 +593,8 @@ protected: //! @name data types related to ray-tracing
// images used by ISS mode
OpenGl_RT_uRenderImage,
OpenGl_RT_uOffsetImage,
OpenGl_RT_uTileSize,
OpenGl_RT_uVarianceScaleFactor,
// maximum radiance value
OpenGl_RT_uMaxRadiance,
@ -603,12 +605,9 @@ protected: //! @name data types related to ray-tracing
//! Defines OpenGL image samplers.
enum ShaderImageNames
{
OpenGl_RT_OutputImageLft = 0,
OpenGl_RT_OutputImageRgh = 1,
OpenGl_RT_VisualErrorImageLft = 2,
OpenGl_RT_VisualErrorImageRgh = 3,
OpenGl_RT_TileOffsetsImageLft = 4,
OpenGl_RT_TileOffsetsImageRgh = 5
OpenGl_RT_OutputImage = 0,
OpenGl_RT_VisualErrorImage = 1,
OpenGl_RT_TileOffsetsImage = 2,
};
//! Tool class for management of shader sources.
@ -697,12 +696,6 @@ 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;
//! Enables/disables depth-of-field effect (path tracing, perspective camera).
Standard_Boolean DepthOfField;
@ -721,8 +714,6 @@ protected: //! @name data types related to ray-tracing
AdaptiveScreenSampling (Standard_False),
UseEnvMapForBackground (Standard_False),
RadianceClampingValue (30.0),
NbTilesX (16),
NbTilesY (16),
DepthOfField (Standard_False),
ToneMappingMethod (Graphic3d_ToneMappingMethod_Disabled) { }
};
@ -854,7 +845,9 @@ protected: //! @name methods related to ray-tracing
const Handle(OpenGl_ShaderObject)& theFragShader);
//! Initializes OpenGL/GLSL shader programs.
Standard_Boolean initRaytraceResources (const Handle(OpenGl_Context)& theGlContext);
Standard_Boolean initRaytraceResources (const Standard_Integer theSizeX,
const Standard_Integer theSizeY,
const Handle(OpenGl_Context)& theGlContext);
//! Releases OpenGL/GLSL shader programs.
void releaseRaytraceResources (const Handle(OpenGl_Context)& theGlContext,
@ -884,7 +877,8 @@ protected: //! @name methods related to ray-tracing
const int theWinSizeY);
//! Binds ray-trace textures to corresponding texture units.
void bindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext);
void bindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext,
int theStereoView);
//! Unbinds ray-trace textures from corresponding texture unit.
void unbindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext);
@ -914,9 +908,13 @@ protected: //! @name methods related to ray-tracing
Standard_Boolean runPathtrace (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 runPathtraceOut (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,

View File

@ -1139,8 +1139,7 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C
// 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());
aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING");
}
}
@ -1328,7 +1327,9 @@ Handle(OpenGl_ShaderProgram) OpenGl_View::initProgram (const Handle(OpenGl_Conte
// function : initRaytraceResources
// purpose : Initializes OpenGL/GLSL shader programs
// =======================================================================
Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context)& theGlContext)
Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theSizeX,
const Standard_Integer theSizeY,
const Handle(OpenGl_Context)& theGlContext)
{
if (myRaytraceInitStatus == OpenGl_RT_FAIL)
{
@ -1372,40 +1373,17 @@ 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
|| aNbTilesX != myRaytraceParameters.NbTilesX
|| aNbTilesY != myRaytraceParameters.NbTilesY)
|| myRaytraceGeometry.HasTextures() != myRaytraceParameters.UseBindlessTextures)
{
myRaytraceParameters.NbBounces = myRenderParams.RaytracingDepth;
myRaytraceParameters.TransparentShadows = myRenderParams.IsTransparentShadowEnabled;
myRaytraceParameters.GlobalIllumination = myRenderParams.IsGlobalIlluminationEnabled;
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;
}
@ -1425,6 +1403,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
aToRebuildShaders = Standard_True;
}
myTileSampler.SetSize (myRenderParams, myRaytraceParameters.AdaptiveScreenSampling ? Graphic3d_Vec2i (theSizeX, theSizeY) : Graphic3d_Vec2i (0, 0));
const bool toEnableDof = !myCamera->IsOrthographic() && myRaytraceParameters.GlobalIllumination;
if (myRaytraceParameters.DepthOfField != toEnableDof)
@ -1749,6 +1728,10 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
aShaderProgram->GetUniformLocation (theGlContext, "uRenderImage");
myUniformLocations[anIndex][OpenGl_RT_uOffsetImage] =
aShaderProgram->GetUniformLocation (theGlContext, "uOffsetImage");
myUniformLocations[anIndex][OpenGl_RT_uTileSize] =
aShaderProgram->GetUniformLocation (theGlContext, "uTileSize");
myUniformLocations[anIndex][OpenGl_RT_uVarianceScaleFactor] =
aShaderProgram->GetUniformLocation (theGlContext, "uVarianceScaleFactor");
myUniformLocations[anIndex][OpenGl_RT_uBackColorTop] =
aShaderProgram->GetUniformLocation (theGlContext, "uBackColorTop");
@ -1875,107 +1858,76 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
if (myRaytraceParameters.AdaptiveScreenSampling)
{
const Standard_Integer aSizeX = std::max (myRaytraceParameters.NbTilesX * 64, theSizeX);
const Standard_Integer aSizeY = std::max (myRaytraceParameters.NbTilesY * 64, theSizeY);
myRaytraceFBO1[0]->InitLazy (theGlContext, aSizeX, aSizeY, GL_RGBA32F, myFboDepthFormat);
myRaytraceFBO2[0]->InitLazy (theGlContext, aSizeX, aSizeY, GL_RGBA32F, myFboDepthFormat);
Graphic3d_Vec2i aMaxViewport = myTileSampler.OffsetTilesViewportMax().cwiseMax (Graphic3d_Vec2i (theSizeX, theSizeY));
myRaytraceFBO1[0]->InitLazy (theGlContext, aMaxViewport.x(), aMaxViewport.y(), GL_RGBA32F, myFboDepthFormat);
myRaytraceFBO2[0]->InitLazy (theGlContext, aMaxViewport.x(), aMaxViewport.y(), GL_RGBA32F, myFboDepthFormat);
if (myRaytraceFBO1[1]->IsValid()) // second FBO not needed
{
myRaytraceFBO1[1]->Release (theGlContext.operator->());
myRaytraceFBO2[1]->Release (theGlContext.operator->());
}
}
else // non-adaptive mode
for (int aViewIter = 0; aViewIter < 2; ++aViewIter)
{
if (myRaytraceFBO1[0]->GetSizeX() != theSizeX
|| myRaytraceFBO1[0]->GetSizeY() != theSizeY)
if (myRaytraceTileOffsetsTexture[aViewIter].IsNull())
{
myAccumFrames = 0; // accumulation should be restarted
myRaytraceOutputTexture[aViewIter] = new OpenGl_Texture();
myRaytraceVisualErrorTexture[aViewIter] = new OpenGl_Texture();
myRaytraceTileOffsetsTexture[aViewIter] = new OpenGl_Texture();
}
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
if (aViewIter == 1
&& myCamera->ProjectionType() != Graphic3d_Camera::Projection_Stereo)
{
myRaytraceFBO1[1]->Release (theGlContext.operator->());
myRaytraceFBO2[1]->Release (theGlContext.operator->());
}
}
myTileSampler.SetSize (theSizeX, theSizeY);
if (myRaytraceTileOffsetsTexture[0].IsNull()
|| myRaytraceTileOffsetsTexture[1].IsNull())
{
myRaytraceOutputTexture[0] = new OpenGl_Texture();
myRaytraceOutputTexture[1] = new OpenGl_Texture();
myRaytraceTileOffsetsTexture[0] = new OpenGl_Texture();
myRaytraceTileOffsetsTexture[1] = new OpenGl_Texture();
myRaytraceVisualErrorTexture[0] = new OpenGl_Texture();
myRaytraceVisualErrorTexture[1] = new OpenGl_Texture();
}
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
// 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<GLfloat, 1>());
// workaround for some NVIDIA drivers
myRaytraceVisualErrorTexture[0]->Release (theGlContext.operator->());
myRaytraceTileOffsetsTexture[0]->Release (theGlContext.operator->());
myRaytraceVisualErrorTexture[0]->Init (theGlContext,
GL_R32I, GL_RED_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
myRaytraceTileOffsetsTexture[0]->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<GLfloat, 1>());
myRaytraceOutputTexture[1]->Release (theGlContext.operator->());
myRaytraceVisualErrorTexture[1]->Release (theGlContext.operator->());
myRaytraceTileOffsetsTexture[1]->Release (theGlContext.operator->());
continue;
}
myRaytraceVisualErrorTexture[1]->Init (theGlContext,
GL_R32I, GL_RED_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
if (myRaytraceParameters.AdaptiveScreenSampling)
{
if (myRaytraceOutputTexture[aViewIter]->SizeX() / 3 == theSizeX
&& myRaytraceOutputTexture[aViewIter]->SizeY() / 2 == theSizeY
&& myRaytraceVisualErrorTexture[aViewIter]->SizeX() == myTileSampler.NbTilesX()
&& myRaytraceVisualErrorTexture[aViewIter]->SizeY() == myTileSampler.NbTilesY())
{
continue;
}
myRaytraceTileOffsetsTexture[1]->Init (theGlContext,
GL_RG32I, GL_RG_INTEGER, GL_INT, myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
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
// 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[aViewIter]->InitRectangle (theGlContext, theSizeX * 3, theSizeY * 2, OpenGl_TextureFormat::Create<GLfloat, 1>());
// workaround for some NVIDIA drivers
myRaytraceVisualErrorTexture[aViewIter]->Release (theGlContext.operator->());
myRaytraceVisualErrorTexture[aViewIter]->Init (theGlContext, GL_R32I, GL_RED_INTEGER, GL_INT,
myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
}
else // non-adaptive mode
{
if (myRaytraceFBO1[aViewIter]->GetSizeX() != theSizeX
|| myRaytraceFBO1[aViewIter]->GetSizeY() != theSizeY)
{
myAccumFrames = 0; // accumulation should be restarted
}
myRaytraceFBO1[aViewIter]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat);
myRaytraceFBO2[aViewIter]->InitLazy (theGlContext, theSizeX, theSizeY, GL_RGBA32F, myFboDepthFormat);
}
}
else
{
myRaytraceOutputTexture[1]->Release (theGlContext.operator->());
}
return Standard_True;
}
@ -2721,25 +2673,22 @@ Standard_Boolean OpenGl_View::setUniformState (const Standard_Integer the
// function : bindRaytraceTextures
// purpose : Binds ray-trace textures to corresponding texture units
// =======================================================================
void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext)
void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlContext,
int theStereoView)
{
if (myRaytraceParameters.AdaptiveScreenSampling)
if (myRaytraceParameters.AdaptiveScreenSampling
&& myRaytraceParameters.GlobalIllumination)
{
#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_VisualErrorImageLft,
myRaytraceVisualErrorTexture[0]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
theGlContext->core42->glBindImageTexture (OpenGl_RT_VisualErrorImageRgh,
myRaytraceVisualErrorTexture[1]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImageLft,
myRaytraceTileOffsetsTexture[0]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImageRgh,
myRaytraceTileOffsetsTexture[1]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
#endif
theGlContext->core42->glBindImageTexture (OpenGl_RT_OutputImage,
myRaytraceOutputTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
theGlContext->core42->glBindImageTexture (OpenGl_RT_VisualErrorImage,
myRaytraceVisualErrorTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage,
myRaytraceTileOffsetsTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
#else
(void )theStereoView;
#endif
}
if (!myTextureEnv.IsNull()
@ -2801,7 +2750,8 @@ Standard_Boolean OpenGl_View::runRaytraceShaders (const Standard_Integer
if (myRaytraceParameters.GlobalIllumination) // path tracing
{
aResult &= runPathtrace (theSizeX, theSizeY, theProjection, theReadDrawFbo, theGlContext);
aResult &= runPathtrace (theSizeX, theSizeY, theProjection, theGlContext);
aResult &= runPathtraceOut (theProjection, theReadDrawFbo, theGlContext);
}
else // Whitted-style ray-tracing
{
@ -2823,13 +2773,9 @@ Standard_Boolean OpenGl_View::runRaytrace (const Standard_Integer theSize
{
Standard_Boolean aResult = Standard_True;
bindRaytraceTextures (theGlContext);
Handle(OpenGl_FrameBuffer) aRenderImageFramebuffer;
Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer;
// Choose proper set of frame buffers for stereo rendering
const Standard_Integer aFBOIdx (theProjection == Graphic3d_Camera::Projection_MonoRightEye);
const Standard_Integer aFBOIdx = (theProjection == Graphic3d_Camera::Projection_MonoRightEye) ? 1 : 0;
bindRaytraceTextures (theGlContext, aFBOIdx);
if (myRenderParams.IsAntialiasingEnabled) // if second FSAA pass is used
{
@ -2899,8 +2845,8 @@ Standard_Boolean OpenGl_View::runRaytrace (const Standard_Integer theSize
aFramebuffer->ColorTexture()->Bind (theGlContext, OpenGl_RT_FsaaInputTexture);
}
aRenderImageFramebuffer = myRaytraceFBO2[aFBOIdx];
aDepthSourceFramebuffer = myRaytraceFBO1[aFBOIdx];
const Handle(OpenGl_FrameBuffer)& aRenderImageFramebuffer = myRaytraceFBO2[aFBOIdx];
const Handle(OpenGl_FrameBuffer)& aDepthSourceFramebuffer = myRaytraceFBO1[aFBOIdx];
glEnable (GL_DEPTH_TEST);
@ -2940,11 +2886,8 @@ Standard_Boolean OpenGl_View::runRaytrace (const Standard_Integer theSize
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)
{
Standard_Boolean aResult = Standard_True;
if (myToUpdateEnvironmentMap) // check whether the map was changed
{
myAccumFrames = myToUpdateEnvironmentMap = 0;
@ -2953,15 +2896,13 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
if (myRenderParams.CameraApertureRadius != myPrevCameraApertureRadius
|| myRenderParams.CameraFocalPlaneDist != myPrevCameraFocalPlaneDist)
{
myPrevCameraApertureRadius = myRenderParams.CameraApertureRadius;
myPrevCameraFocalPlaneDist = myRenderParams.CameraFocalPlaneDist;
myAccumFrames = 0;
}
// Choose proper set of frame buffers for stereo rendering
const Standard_Integer aFBOIdx (theProjection == Graphic3d_Camera::Projection_MonoRightEye);
const Standard_Integer aFBOIdx = (theProjection == Graphic3d_Camera::Projection_MonoRightEye) ? 1 : 0;
if (myRaytraceParameters.AdaptiveScreenSampling)
{
@ -2970,106 +2911,88 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
myTileSampler.Reset(); // reset tile sampler to its initial state
// Adaptive sampling is starting at the second frame
myTileSampler.Upload (theGlContext,
myRaytraceTileOffsetsTexture[aFBOIdx],
myRaytraceParameters.NbTilesX,
myRaytraceParameters.NbTilesY,
false);
}
}
myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], false);
bindRaytraceTextures (theGlContext);
Handle(OpenGl_FrameBuffer) aRenderImageFramebuffer;
Handle(OpenGl_FrameBuffer) aDepthSourceFramebuffer;
Handle(OpenGl_FrameBuffer) anAccumImageFramebuffer;
const Standard_Integer anImageId = (aFBOIdx != 0)
? OpenGl_RT_OutputImageRgh
: OpenGl_RT_OutputImageLft;
const Standard_Integer anErrorId = (aFBOIdx != 0)
? OpenGl_RT_VisualErrorImageRgh
: OpenGl_RT_VisualErrorImageLft;
const Standard_Integer anOffsetId = (aFBOIdx != 0)
? OpenGl_RT_TileOffsetsImageRgh
: OpenGl_RT_TileOffsetsImageLft;
aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
anAccumImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx];
aDepthSourceFramebuffer = aRenderImageFramebuffer;
anAccumImageFramebuffer->ColorTexture()->Bind (theGlContext, 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 || (myAccumFrames == 1 && myCamera->IsStereo()))
{
#if !defined(GL_ES_VERSION_2_0)
theGlContext->core44->glClearTexImage (myRaytraceOutputTexture[aFBOIdx]->TextureId(), 0, GL_RED, GL_FLOAT, NULL);
#endif
}
// Clear adaptive screen sampling images
#if !defined(GL_ES_VERSION_2_0)
theGlContext->core44->glClearTexImage (myRaytraceVisualErrorTexture[aFBOIdx]->TextureId(), 0, GL_RED_INTEGER, GL_INT, NULL);
#endif
}
bindRaytraceTextures (theGlContext, aFBOIdx);
const Handle(OpenGl_FrameBuffer)& anAccumImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO2[aFBOIdx] : myRaytraceFBO1[aFBOIdx];
anAccumImageFramebuffer->ColorTexture()->Bind (theGlContext, OpenGl_RT_PrevAccumTexture);
// Set frame accumulation weight
myRaytraceProgram->SetUniform (theGlContext,
myUniformLocations[0][OpenGl_RT_uAccumSamples], myAccumFrames);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uAccumSamples], myAccumFrames);
// Set random number generator seed
myRaytraceProgram->SetUniform (theGlContext,
myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
if (myAccumFrames == 0)
{
myRNG.SetSeed(); // start RNG from beginning
}
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (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], anOffsetId);
glDisable (GL_DEPTH_TEST);
if (myRaytraceParameters.AdaptiveScreenSampling
&& ((myAccumFrames > 0 && !myCamera->IsStereo()) || myAccumFrames > 1))
if (myRaytraceParameters.AdaptiveScreenSampling)
{
glViewport (0,
0,
myTileSampler.TileSize() * myRaytraceParameters.NbTilesX,
myTileSampler.TileSize() * myRaytraceParameters.NbTilesY);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uRenderImage], OpenGl_RT_OutputImage);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uOffsetImage], OpenGl_RT_TileOffsetsImage);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uTileSize], myTileSampler.TileSize());
}
const Handle(OpenGl_FrameBuffer)& aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
aRenderImageFramebuffer->BindBuffer (theGlContext);
if (myRaytraceParameters.AdaptiveScreenSampling)
{
// extend viewport here, so that tiles at boundaries (cut tile size by target rendering viewport)
// redirected to inner tiles (full tile size) are drawn entirely
const Graphic3d_Vec2i anOffsetViewport = myTileSampler.OffsetTilesViewport (myAccumFrames > 1); // shrunk offsets texture will be uploaded since 3rd frame
glViewport (0, 0, anOffsetViewport.x(), anOffsetViewport.y());
}
// Generate for the given RNG seed
glDisable (GL_DEPTH_TEST);
theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
if (myRaytraceParameters.AdaptiveScreenSampling
&& ((myAccumFrames > 0 && !myCamera->IsStereo()) || myAccumFrames > 1))
{
glViewport (0,
0,
theSizeX,
theSizeY);
}
aRenderImageFramebuffer->UnbindBuffer (theGlContext);
if (myRaytraceParameters.AdaptiveScreenSampling)
{
glViewport (0, 0, theSizeX, theSizeY);
}
return true;
}
// =======================================================================
// function : runPathtraceOut
// purpose :
// =======================================================================
Standard_Boolean OpenGl_View::runPathtraceOut (const Graphic3d_Camera::Projection theProjection,
OpenGl_FrameBuffer* theReadDrawFbo,
const Handle(OpenGl_Context)& theGlContext)
{
// Output accumulated path traced image
theGlContext->BindProgram (myOutImageProgram);
// Choose proper set of frame buffers for stereo rendering
const Standard_Integer aFBOIdx = (theProjection == Graphic3d_Camera::Projection_MonoRightEye) ? 1 : 0;
if (myRaytraceParameters.AdaptiveScreenSampling)
{
// Set uniforms for display program
myOutImageProgram->SetUniform (theGlContext, "uRenderImage", anImageId);
myOutImageProgram->SetUniform (theGlContext, "uRenderImage", OpenGl_RT_OutputImage);
myOutImageProgram->SetUniform (theGlContext, "uAccumFrames", myAccumFrames);
myOutImageProgram->SetUniform (theGlContext, "uVarianceImage", anErrorId);
myOutImageProgram->SetUniform (theGlContext, "uVarianceImage", OpenGl_RT_VisualErrorImage);
myOutImageProgram->SetUniform (theGlContext, "uDebugAdaptive", myRenderParams.ShowSamplingTiles ? 1 : 0);
myOutImageProgram->SetUniform (theGlContext, "uTileSize", myTileSampler.TileSize());
myOutImageProgram->SetUniform (theGlContext, "uVarianceScaleFactor", myTileSampler.VarianceScaleFactor());
}
if (myRaytraceParameters.GlobalIllumination)
@ -3089,40 +3012,26 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
{
theReadDrawFbo->BindBuffer (theGlContext);
}
else
{
aRenderImageFramebuffer->UnbindBuffer (theGlContext);
}
const Handle(OpenGl_FrameBuffer)& aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
aRenderImageFramebuffer->ColorTexture()->Bind (theGlContext, OpenGl_RT_PrevAccumTexture);
glEnable (GL_DEPTH_TEST);
// Copy accumulated image with correct depth values
glEnable (GL_DEPTH_TEST);
theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
aRenderImageFramebuffer->ColorTexture()->Unbind (theGlContext, OpenGl_RT_PrevAccumTexture);
if (myRaytraceParameters.AdaptiveScreenSampling)
{
myRaytraceVisualErrorTexture[aFBOIdx]->Bind (theGlContext);
// Download visual error map from the GPU and build
// adjusted tile offsets for optimal image sampling
myTileSampler.GrabVarianceMap (theGlContext);
myTileSampler.Upload (theGlContext,
myRaytraceTileOffsetsTexture[aFBOIdx],
myRaytraceParameters.NbTilesX,
myRaytraceParameters.NbTilesY,
myAccumFrames > 0);
// Download visual error map from the GPU and build adjusted tile offsets for optimal image sampling
myTileSampler.GrabVarianceMap (theGlContext, myRaytraceVisualErrorTexture[aFBOIdx]);
myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], myAccumFrames != 0);
}
unbindRaytraceTextures (theGlContext);
theGlContext->BindProgram (NULL);
return aResult;
return true;
}
// =======================================================================
@ -3135,7 +3044,7 @@ Standard_Boolean OpenGl_View::raytrace (const Standard_Integer theSizeX,
OpenGl_FrameBuffer* theReadDrawFbo,
const Handle(OpenGl_Context)& theGlContext)
{
if (!initRaytraceResources (theGlContext))
if (!initRaytraceResources (theSizeX, theSizeY, theGlContext))
{
return Standard_False;
}

View File

@ -1092,15 +1092,16 @@ void OpenGl_View::renderStructs (Graphic3d_Camera::Projection theProjection,
if (!toRenderGL)
{
toRenderGL = !initRaytraceResources (aCtx) ||
!updateRaytraceGeometry (OpenGl_GUM_CHECK, myId, aCtx);
const Standard_Integer aSizeX = theReadDrawFbo != NULL ? theReadDrawFbo->GetVPSizeX() : myWindow->Width();
const Standard_Integer aSizeY = theReadDrawFbo != NULL ? theReadDrawFbo->GetVPSizeY() : myWindow->Height();
toRenderGL = !initRaytraceResources (aSizeX, aSizeY, aCtx)
|| !updateRaytraceGeometry (OpenGl_GUM_CHECK, myId, aCtx);
toRenderGL |= !myIsRaytraceDataValid; // if no ray-trace data use OpenGL
if (!toRenderGL)
{
const Standard_Integer aSizeX = theReadDrawFbo != NULL ? theReadDrawFbo->GetVPSizeX() : myWindow->Width();
const Standard_Integer aSizeY = theReadDrawFbo != NULL ? theReadDrawFbo->GetVPSizeY() : myWindow->Height();
myOpenGlFBO ->InitLazy (aCtx, aSizeX, aSizeY, myFboColorFormat, myFboDepthFormat, 0);
if (theReadDrawFbo != NULL)

View File

@ -10,6 +10,12 @@
//! OpenGL image storing variance of sampled pixels blocks.
volatile restrict layout(size1x32) uniform iimage2D uVarianceImage;
//! Scale factor used to quantize visual error (float) into signed integer.
uniform float uVarianceScaleFactor;
//! Screen space tile size.
uniform ivec2 uTileSize;
#else // ADAPTIVE_SAMPLING
//! Input image.
@ -42,9 +48,6 @@ 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 : ToneMappingFilmic
// purpose :
@ -113,7 +116,8 @@ void main (void)
// 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)));
imageAtomicAdd (uVarianceImage, aPixel / uTileSize,
int (mix (uVarianceScaleFactor, anError * uVarianceScaleFactor, aColor.w > 40.f)));
if (uDebugAdaptive == 0) // normal rendering
{

View File

@ -101,6 +101,9 @@ uniform float uSceneEpsilon;
//! OpenGL image storing offsets of sampled pixels blocks.
coherent restrict layout(size2x32) uniform iimage2D uOffsetImage;
//! Screen space tile size.
uniform ivec2 uTileSize;
#endif
//! Top color of gradient background.
@ -275,10 +278,9 @@ vec4 BackgroundColor()
ivec2 aFragCoord = ivec2 (gl_FragCoord.xy);
ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE,
aFragCoord.y / BLOCK_SIZE)).xy;
ivec2 aTileXY = imageLoad (uOffsetImage, aFragCoord / uTileSize).xy * uTileSize;
aTileXY.y += aFragCoord.y % min (uWinSizeY - aTileXY.y, BLOCK_SIZE);
aTileXY.y += aFragCoord.y % min (uWinSizeY - aTileXY.y, uTileSize.y);
return mix (uBackColorBot, uBackColorTop, float (aTileXY.y) / uWinSizeY);

View File

@ -38,11 +38,11 @@ void main (void)
#ifdef ADAPTIVE_SAMPLING
ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE,
aFragCoord.y / BLOCK_SIZE)).xy;
ivec2 aTileXY = imageLoad (uOffsetImage, aFragCoord / uTileSize).xy * uTileSize;
if (aTileXY.x < 0) { discard; }
ivec2 aRealBlockSize = ivec2 (min (uWinSizeX - aTileXY.x, BLOCK_SIZE),
min (uWinSizeY - aTileXY.y, BLOCK_SIZE));
ivec2 aRealBlockSize = ivec2 (min (uWinSizeX - aTileXY.x, uTileSize.x),
min (uWinSizeY - aTileXY.y, uTileSize.y));
aFragCoord.x = aTileXY.x + (aFragCoord.x % aRealBlockSize.x);
aFragCoord.y = aTileXY.y + (aFragCoord.y % aRealBlockSize.y);

View File

@ -13,6 +13,12 @@ static const char Shaders_Display_fs[] =
" //! OpenGL image storing variance of sampled pixels blocks.\n"
" volatile restrict layout(size1x32) uniform iimage2D uVarianceImage;\n"
"\n"
" //! Scale factor used to quantize visual error (float) into signed integer.\n"
" uniform float uVarianceScaleFactor;\n"
"\n"
" //! Screen space tile size.\n"
" uniform ivec2 uTileSize;\n"
"\n"
"#else // ADAPTIVE_SAMPLING\n"
"\n"
" //! Input image.\n"
@ -45,9 +51,6 @@ static const char Shaders_Display_fs[] =
"//! RGB weight factors to calculate luminance.\n"
"#define LUMA vec3 (0.2126f, 0.7152f, 0.0722f)\n"
"\n"
"//! Scale factor used to quantize visual error.\n"
"#define SCALE_FACTOR 1.0e6f\n"
"\n"
"// =======================================================================\n"
"// function : ToneMappingFilmic\n"
"// purpose :\n"
@ -116,7 +119,8 @@ static const char Shaders_Display_fs[] =
"\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"
" imageAtomicAdd (uVarianceImage, aPixel / uTileSize,\n"
" int (mix (uVarianceScaleFactor, anError * uVarianceScaleFactor, aColor.w > 40.f)));\n"
"\n"
" if (uDebugAdaptive == 0) // normal rendering\n"
" {\n"

View File

@ -104,6 +104,9 @@ static const char Shaders_RaytraceBase_fs[] =
"\n"
" //! OpenGL image storing offsets of sampled pixels blocks.\n"
" coherent restrict layout(size2x32) uniform iimage2D uOffsetImage;\n"
"\n"
" //! Screen space tile size.\n"
" uniform ivec2 uTileSize;\n"
"#endif\n"
"\n"
"//! Top color of gradient background.\n"
@ -278,10 +281,9 @@ static const char Shaders_RaytraceBase_fs[] =
"\n"
" ivec2 aFragCoord = ivec2 (gl_FragCoord.xy);\n"
"\n"
" ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE,\n"
" aFragCoord.y / BLOCK_SIZE)).xy;\n"
" ivec2 aTileXY = imageLoad (uOffsetImage, aFragCoord / uTileSize).xy * uTileSize;\n"
"\n"
" aTileXY.y += aFragCoord.y % min (uWinSizeY - aTileXY.y, BLOCK_SIZE);\n"
" aTileXY.y += aFragCoord.y % min (uWinSizeY - aTileXY.y, uTileSize.y);\n"
"\n"
" return mix (uBackColorBot, uBackColorTop, float (aTileXY.y) / uWinSizeY);\n"
"\n"

View File

@ -41,11 +41,11 @@ static const char Shaders_RaytraceRender_fs[] =
"\n"
"#ifdef ADAPTIVE_SAMPLING\n"
"\n"
" ivec2 aTileXY = imageLoad (uOffsetImage, ivec2 (aFragCoord.x / BLOCK_SIZE,\n"
" aFragCoord.y / BLOCK_SIZE)).xy;\n"
" ivec2 aTileXY = imageLoad (uOffsetImage, aFragCoord / uTileSize).xy * uTileSize;\n"
" if (aTileXY.x < 0) { discard; }\n"
"\n"
" ivec2 aRealBlockSize = ivec2 (min (uWinSizeX - aTileXY.x, BLOCK_SIZE),\n"
" min (uWinSizeY - aTileXY.y, BLOCK_SIZE));\n"
" ivec2 aRealBlockSize = ivec2 (min (uWinSizeX - aTileXY.x, uTileSize.x),\n"
" min (uWinSizeY - aTileXY.y, uTileSize.y));\n"
"\n"
" aFragCoord.x = aTileXY.x + (aFragCoord.x % aRealBlockSize.x);\n"
" aFragCoord.y = aTileXY.y + (aFragCoord.y % aRealBlockSize.y);\n"

View File

@ -10371,6 +10371,7 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
theDI << "two-sided BSDF: " << (aParams.TwoSidedBsdfModels ? "on" : "off") << "\n";
theDI << "max radiance: " << aParams.RadianceClampingValue << "\n";
theDI << "nb tiles (iss): " << aParams.NbRayTracingTiles << "\n";
theDI << "tile size (iss):" << aParams.RayTracingTileSize << "x" << aParams.RayTracingTileSize << "\n";
theDI << "shadingModel: ";
switch (aView->ShadingModel())
{
@ -10812,6 +10813,27 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
}
aParams.ShowSamplingTiles = toEnable;
}
else if (aFlag == "-tilesize")
{
if (toPrint)
{
theDI << aParams.RayTracingTileSize << " ";
continue;
}
else if (++anArgIter >= theArgNb)
{
std::cerr << "Error: wrong syntax at argument '" << anArg << "'\n";
return 1;
}
const Standard_Integer aTileSize = Draw::Atoi (theArgVec[anArgIter]);
if (aTileSize < 1)
{
std::cerr << "Error: invalid size of ISS tile " << aTileSize << ".\n";
return 1;
}
aParams.RayTracingTileSize = aTileSize;
}
else if (aFlag == "-nbtiles")
{
if (toPrint)
@ -10826,17 +10848,18 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
}
const Standard_Integer aNbTiles = Draw::Atoi (theArgVec[anArgIter]);
if (aNbTiles < 64)
if (aNbTiles < -1)
{
std::cerr << "Error: invalid number of ISS tiles " << aNbTiles << ".\n";
std::cerr << "Specify value in range [64, 1024].\n";
return 1;
}
else
else if (aNbTiles > 0
&& (aNbTiles < 64
|| aNbTiles > 1024))
{
aParams.NbRayTracingTiles = aNbTiles;
std::cerr << "Warning: suboptimal number of ISS tiles " << aNbTiles << ". Recommended range: [64, 1024].\n";
}
aParams.NbRayTracingTiles = aNbTiles;
}
else if (aFlag == "-env")
{
@ -12826,7 +12849,8 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
"\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 '-tileSize 1..4096' Specifies size of screen tiles in ISS mode (32 by default)"
"\n '-nbtiles 64..1024' Specifies number of screen tiles per Redraw in ISS mode (256 by default)"
"\n '-rebuildGlsl on|off' Rebuild Ray-Tracing GLSL programs (for debugging)"
"\n '-shadingModel model' Controls shading model from enumeration"
"\n color, flat, gouraud, phong"