0027607: Visualization - Implement adaptive screen space sampling in path tracing
[occt.git] / src / OpenGl / OpenGl_TileSampler.cxx
1 // Created on: 2016-06-16
2 // Created by: Denis BOGOLEPOV & Danila ULYANOV
3 // Copyright (c) 2016 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <OpenGl_Context.hxx>
17 #include <OpenGl_TileSampler.hxx>
18 #include <TCollection_ExtendedString.hxx>
19
20 namespace
21 {
22   //! Scale factor for estimating visual error.
23   static const float THE_SCALE_FACTOR = 1.0f / 1e6f;
24 }
25
26 //=======================================================================
27 //function : OpenGl_TileSampler
28 //purpose  :
29 //=======================================================================
30 OpenGl_TileSampler::OpenGl_TileSampler()
31 : mySample (0),
32   mySizeX  (0),
33   mySizeY  (0),
34   myTilesX (0),
35   myTilesY (0)
36 {
37   mySampler.initFaure();
38 }
39
40 //=======================================================================
41 //function : GrabVarianceMap
42 //purpose  :
43 //=======================================================================
44 void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext)
45 {
46 #if !defined(GL_ES_VERSION_2_0)
47   std::vector<GLint> aRawData (NbTiles(), 0);
48
49   theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, &aRawData.front());
50   const GLenum anErr = theContext->core11fwd->glGetError();
51   if (anErr != GL_NO_ERROR)
52   {
53     theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
54                              "Error! Failed to fetch visual error map from the GPU");
55   }
56   else
57   {
58     for (int aTileIdx = 0, aNbTiles = NbTiles(); aTileIdx < aNbTiles; ++aTileIdx)
59     {
60       myVarianceMap[aTileIdx] = aRawData[aTileIdx] * THE_SCALE_FACTOR;
61     }
62
63     for (int aX = 0; aX < myTilesX; ++aX)
64     {
65       for (int aY = 0; aY < myTilesY; ++aY)
66       {
67         ChangeTile (aX, aY) *= 1.0f / TileArea (aX, aY); // average error over the tile
68
69         if (aY > 0)
70         {
71           ChangeTile (aX, aY) += Tile (aX, aY - 1);
72         }
73       }
74     }
75
76     myMarginalMap.resize (myTilesX); // build marginal distribution
77     for (int aX = 0; aX < myTilesX; ++aX)
78     {
79       myMarginalMap[aX] = Tile (aX, myTilesY - 1);
80
81       if (aX > 0)
82         myMarginalMap[aX] += myMarginalMap[aX - 1];
83     }
84   }
85 #else
86   // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
87   (void )theContext;
88 #endif
89 }
90
91 //=======================================================================
92 //function : Sample
93 //purpose  :
94 //=======================================================================
95 void OpenGl_TileSampler::Sample (int& theOffsetX,
96                                  int& theOffsetY)
97 {
98   int aX = 0;
99   int aY = 0;
100
101   const float aKsiX = mySampler.sample (0, mySample) * myMarginalMap.back();
102   for (; aX < myTilesX - 1; ++aX)
103   {
104     if (aKsiX <= myMarginalMap[aX])
105     {
106       break;
107     }
108   }
109
110   const float aKsiY = mySampler.sample (1, mySample) * Tile (aX, myTilesY - 1);
111   for (; aY < myTilesY - 1; ++aY)
112   {
113     if (aKsiY <= Tile (aX, aY))
114     {
115       break;
116     }
117   }
118
119   theOffsetX = aX * TileSize();
120   theOffsetY = aY * TileSize();
121
122   ++mySample;
123 }
124
125 //=======================================================================
126 //function : SetSize
127 //purpose  :
128 //=======================================================================
129 void OpenGl_TileSampler::SetSize (const int theSizeX,
130                                   const int theSizeY)
131 {
132   if (mySizeX == theSizeX
133    && mySizeY == theSizeY)
134   {
135     return;
136   }
137
138   mySizeX = theSizeX;
139   mySizeY = theSizeY;
140
141   myTilesX = static_cast<int> (ceilf (static_cast<float> (mySizeX) / TileSize()));
142   myTilesY = static_cast<int> (ceilf (static_cast<float> (mySizeY) / TileSize()));
143
144   myVarianceMap.resize (myTilesX * myTilesY);
145 }
146
147 //=======================================================================
148 //function : Upload
149 //purpose  :
150 //=======================================================================
151 void OpenGl_TileSampler::Upload (const Handle(OpenGl_Context)& theContext,
152                                  const Handle(OpenGl_Texture)& theTexture,
153                                  bool theAdaptive)
154 {
155   if (theTexture.IsNull())
156   {
157     return;
158   }
159
160   std::vector<GLint> aData (myTilesX * myTilesY * 2);
161   for (int aX = 0; aX < myTilesX; ++aX)
162   {
163     for (int aY = 0; aY < myTilesY; ++aY)
164     {
165       if (!theAdaptive)
166       {
167         aData[(aY * myTilesX + aX) * 2 + 0] = aX * TileSize();
168         aData[(aY * myTilesX + aX) * 2 + 1] = aY * TileSize();
169       }
170       else
171       {
172         Sample (aData[(aY * myTilesX + aX) * 2 + 0],
173                 aData[(aY * myTilesX + aX) * 2 + 1]);
174       }
175     }
176   }
177
178   theTexture->Bind (theContext);
179
180   theContext->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, GL_RG32I, myTilesX, myTilesY, 0, GL_RG_INTEGER, GL_UNSIGNED_INT, &aData.front());
181   const GLenum anErr = theContext->core11fwd->glGetError();
182   if (anErr != GL_NO_ERROR)
183   {
184     theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
185                              "Error! Failed to upload tile offset map on the GPU");
186   }
187 }