1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkPBRIrradianceTexture.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkPBRIrradianceTexture.h"
16 #include "vtkObjectFactory.h"
17 #include "vtkOpenGLFramebufferObject.h"
18 #include "vtkOpenGLQuadHelper.h"
19 #include "vtkOpenGLRenderUtilities.h"
20 #include "vtkOpenGLRenderWindow.h"
21 #include "vtkOpenGLState.h"
22 #include "vtkRenderer.h"
23 #include "vtkShaderProgram.h"
24 #include "vtkTextureObject.h"
25 
26 #include "vtk_glew.h"
27 
28 #include <sstream>
29 
30 vtkStandardNewMacro(vtkPBRIrradianceTexture);
31 
32 vtkCxxSetObjectMacro(vtkPBRIrradianceTexture, InputTexture, vtkOpenGLTexture);
33 
34 //------------------------------------------------------------------------------
~vtkPBRIrradianceTexture()35 vtkPBRIrradianceTexture::~vtkPBRIrradianceTexture()
36 {
37   if (this->InputTexture)
38   {
39     this->InputTexture->Delete();
40   }
41 }
42 
43 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)44 void vtkPBRIrradianceTexture::PrintSelf(ostream& os, vtkIndent indent)
45 {
46   this->Superclass::PrintSelf(os, indent);
47   os << indent << "IrradianceStep: " << this->IrradianceStep << "\n";
48   os << indent << "IrradianceSize: " << this->IrradianceSize << endl;
49 }
50 
51 //------------------------------------------------------------------------------
52 // Release the graphics resources used by this texture.
ReleaseGraphicsResources(vtkWindow * win)53 void vtkPBRIrradianceTexture::ReleaseGraphicsResources(vtkWindow* win)
54 {
55   if (this->InputTexture)
56   {
57     this->InputTexture->ReleaseGraphicsResources(win);
58   }
59   this->Superclass::ReleaseGraphicsResources(win);
60 }
61 
62 //------------------------------------------------------------------------------
Load(vtkRenderer * ren)63 void vtkPBRIrradianceTexture::Load(vtkRenderer* ren)
64 {
65   vtkOpenGLRenderWindow* renWin = vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow());
66   if (!renWin)
67   {
68     vtkErrorMacro("No render window.");
69   }
70 
71   if (!this->InputTexture)
72   {
73     vtkErrorMacro("No input cubemap specified.");
74   }
75 
76   this->InputTexture->Render(ren);
77 
78   if (this->GetMTime() > this->LoadTime.GetMTime() ||
79     this->InputTexture->GetMTime() > this->LoadTime.GetMTime())
80   {
81     if (this->TextureObject == nullptr)
82     {
83       this->TextureObject = vtkTextureObject::New();
84     }
85     this->TextureObject->SetContext(renWin);
86     this->TextureObject->SetFormat(GL_RGB);
87     this->TextureObject->SetInternalFormat(GL_RGB16F);
88     this->TextureObject->SetDataType(GL_FLOAT);
89     this->TextureObject->SetWrapS(vtkTextureObject::ClampToEdge);
90     this->TextureObject->SetWrapT(vtkTextureObject::ClampToEdge);
91     this->TextureObject->SetWrapR(vtkTextureObject::ClampToEdge);
92     this->TextureObject->SetMinificationFilter(vtkTextureObject::Linear);
93     this->TextureObject->SetMagnificationFilter(vtkTextureObject::Linear);
94     this->TextureObject->CreateCubeFromRaw(
95       this->IrradianceSize, this->IrradianceSize, 3, VTK_FLOAT, nullptr);
96 
97     this->RenderWindow = renWin;
98 
99     vtkOpenGLState* state = renWin->GetState();
100     vtkOpenGLState::ScopedglViewport svp(state);
101     vtkOpenGLState::ScopedglEnableDisable sdepth(state, GL_DEPTH_TEST);
102     vtkOpenGLState::ScopedglEnableDisable sblend(state, GL_BLEND);
103     vtkOpenGLState::ScopedglEnableDisable sscissor(state, GL_SCISSOR_TEST);
104 
105     std::string FSSource = vtkOpenGLRenderUtilities::GetFullScreenQuadFragmentShaderTemplate();
106 
107     vtkShaderProgram::Substitute(FSSource, "//VTK::FSQ::Decl",
108       "//VTK::TEXTUREINPUT::Decl\n"
109       "uniform vec3 shift;\n"
110       "uniform vec3 contribX;\n"
111       "uniform vec3 contribY;\n"
112       "const float PI = 3.14159265359;\n"
113       "vec3 GetSampleColor(vec3 dir)\n"
114       "{\n"
115       "  //VTK::SAMPLING::Decl\n"
116       "  //VTK::COLORSPACE::Decl\n"
117       "}\n");
118 
119     if (this->ConvertToLinear)
120     {
121       vtkShaderProgram::Substitute(
122         FSSource, "//VTK::COLORSPACE::Decl", "return pow(col, vec3(2.2));");
123     }
124     else
125     {
126       vtkShaderProgram::Substitute(FSSource, "//VTK::COLORSPACE::Decl", "return col;");
127     }
128 
129     if (this->InputTexture->GetCubeMap())
130     {
131       vtkShaderProgram::Substitute(
132         FSSource, "//VTK::TEXTUREINPUT::Decl", "uniform samplerCube inputTex;");
133 
134       vtkShaderProgram::Substitute(
135         FSSource, "//VTK::SAMPLING::Decl", "vec3 col = texture(inputTex, dir).rgb;");
136     }
137     else
138     {
139       vtkShaderProgram::Substitute(
140         FSSource, "//VTK::TEXTUREINPUT::Decl", "uniform sampler2D inputTex;");
141 
142       vtkShaderProgram::Substitute(FSSource, "//VTK::SAMPLING::Decl",
143         "  dir = normalize(dir);\n"
144         "  float theta = atan(dir.z, dir.x);\n"
145         "  float phi = asin(dir.y);\n"
146         "  vec2 p = vec2(theta * 0.1591 + 0.5, phi * 0.3183 + 0.5);\n"
147         "  vec3 col = texture(inputTex, p).rgb;\n");
148     }
149 
150     std::stringstream fsImpl;
151     fsImpl
152       << "  const vec3 x = vec3(1.0, 0.0, 0.0);\n"
153          "  const vec3 y = vec3(0.0, 1.0, 0.0);\n"
154          "  vec3 n = normalize(vec3(shift.x + contribX.x * texCoord.x + contribY.x * texCoord.y,\n"
155          "    shift.y + contribX.y * texCoord.x + contribY.y * texCoord.y,\n"
156          "    shift.z + contribX.z * texCoord.x + contribY.z * texCoord.y));\n"
157          "  vec3 t = normalize(cross(n, y));\n"
158          "  mat3 m = mat3(t, cross(n, t), n);\n"
159          "  vec3 acc = vec3(0.0);\n"
160          "  float nSamples = 0.0;\n"
161          "  for (float phi = 0.0; phi < 2.0 * PI; phi += "
162       << this->IrradianceStep
163       << ")\n"
164          "  {\n"
165          "    for (float theta = 0.0; theta < 0.5 * PI; theta += "
166       << this->IrradianceStep
167       << ")\n"
168          "    {\n"
169          "      vec3 sample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));\n"
170          "      float factor = cos(theta) * sin(theta);\n"
171          "      acc += GetSampleColor(m * sample) * factor;\n"
172          "      nSamples = nSamples + 1.0;\n"
173          "    }\n"
174          "  }\n"
175          "  gl_FragData[0] = vec4(acc * (PI / nSamples), 1.0);\n";
176 
177     vtkShaderProgram::Substitute(FSSource, "//VTK::FSQ::Impl", fsImpl.str());
178 
179     vtkOpenGLQuadHelper quadHelper(renWin,
180       vtkOpenGLRenderUtilities::GetFullScreenQuadVertexShader().c_str(), FSSource.c_str(), "");
181 
182     vtkNew<vtkOpenGLFramebufferObject> fbo;
183     fbo->SetContext(renWin);
184     renWin->GetState()->PushFramebufferBindings();
185     fbo->Bind();
186 
187     if (!quadHelper.Program || !quadHelper.Program->GetCompiled())
188     {
189       vtkErrorMacro("Couldn't build the shader program for irradiance.");
190     }
191     else
192     {
193       this->InputTexture->GetTextureObject()->Activate();
194       quadHelper.Program->SetUniformi("inputTex", this->InputTexture->GetTextureUnit());
195 
196       float shift[6][3] = { { 1.f, 1.f, 1.f }, { -1.f, 1.f, -1.f }, { -1.f, 1.f, -1.f },
197         { -1.f, -1.f, 1.f }, { -1.f, 1.f, 1.f }, { 1.f, 1.f, -1.f } };
198       float contribX[6][3] = { { 0.f, 0.f, -2.f }, { 0.f, 0.f, 2.f }, { 2.f, 0.f, 0.f },
199         { 2.f, 0.f, 0.f }, { 2.f, 0.f, 0.f }, { -2.f, 0.f, 0.f } };
200       float contribY[6][3] = { { 0.f, -2.f, 0.f }, { 0.f, -2.f, 0.f }, { 0.f, 0.f, 2.f },
201         { 0.f, 0.f, -2.f }, { 0.f, -2.f, 0.f }, { 0.f, -2.f, 0.f } };
202 
203       for (int i = 0; i < 6; i++)
204       {
205         fbo->AddColorAttachment(0, this->TextureObject, 0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i);
206         fbo->ActivateDrawBuffers(1);
207         fbo->Start(this->IrradianceSize, this->IrradianceSize);
208 
209         quadHelper.Program->SetUniform3f("shift", shift[i]);
210         quadHelper.Program->SetUniform3f("contribX", contribX[i]);
211         quadHelper.Program->SetUniform3f("contribY", contribY[i]);
212         quadHelper.Render();
213         fbo->RemoveColorAttachment(0);
214 
215         // Computing irradiance can be long depending on the GPU.
216         // On Windows 7, a computation longer than 2 seconds triggers GPU timeout.
217         // The following call do a glFlush() that inform the OS that the computation is finished
218         // thus avoids the trigger of the GPU timeout.
219         renWin->WaitForCompletion();
220       }
221       this->InputTexture->GetTextureObject()->Deactivate();
222     }
223     renWin->GetState()->PopFramebufferBindings();
224     this->LoadTime.Modified();
225   }
226 
227   this->TextureObject->Activate();
228 }
229