1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkSimpleMotionBlurPass.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 
16 #include "vtkSimpleMotionBlurPass.h"
17 #include "vtkObjectFactory.h"
18 #include <cassert>
19 
20 #include "vtkRenderState.h"
21 #include "vtkRenderer.h"
22 #include "vtkOpenGLFramebufferObject.h"
23 #include "vtkTextureObject.h"
24 #include "vtkOpenGLRenderWindow.h"
25 #include "vtkOpenGLState.h"
26 #include "vtkOpenGLError.h"
27 #include "vtkShaderProgram.h"
28 #include "vtkOpenGLShaderCache.h"
29 #include "vtkOpenGLVertexArrayObject.h"
30 
31 #include "vtkOpenGLHelper.h"
32 
33 #include "vtkTextureObjectVS.h"
34 #include "vtkSimpleMotionBlurPassFS.h"
35 
36 vtkStandardNewMacro(vtkSimpleMotionBlurPass);
37 
38 // ----------------------------------------------------------------------------
vtkSimpleMotionBlurPass()39 vtkSimpleMotionBlurPass::vtkSimpleMotionBlurPass()
40 {
41   this->SubFrames = 30;
42   this->CurrentSubFrame = 0;
43   this->BlendProgram = nullptr;
44 
45   this->FrameBufferObject=nullptr;
46   this->AccumulationTexture[0] = vtkTextureObject::New();
47   this->AccumulationTexture[1] = vtkTextureObject::New();
48   this->ActiveAccumulationTexture = 0;
49   this->ColorTexture = vtkTextureObject::New();
50   this->DepthTexture = vtkTextureObject::New();
51   this->DepthFormat = vtkTextureObject::Float32;
52   this->ColorFormat = vtkTextureObject::Fixed8;
53 }
54 
55 // ----------------------------------------------------------------------------
~vtkSimpleMotionBlurPass()56 vtkSimpleMotionBlurPass::~vtkSimpleMotionBlurPass()
57 {
58   if(this->FrameBufferObject!=nullptr)
59   {
60     vtkErrorMacro(<<"FrameBufferObject should have been deleted in ReleaseGraphicsResources().");
61   }
62   if(this->AccumulationTexture[0] != nullptr)
63   {
64     this->AccumulationTexture[0]->Delete();
65     this->AccumulationTexture[0] = nullptr;
66   }
67   if(this->AccumulationTexture[1] != nullptr)
68   {
69     this->AccumulationTexture[1]->Delete();
70     this->AccumulationTexture[1] = nullptr;
71   }
72   if(this->ColorTexture!=nullptr)
73   {
74     this->ColorTexture->Delete();
75     this->ColorTexture = nullptr;
76   }
77   if(this->DepthTexture !=nullptr)
78   {
79     this->DepthTexture->Delete();
80     this->DepthTexture = nullptr;
81   }
82 }
83 
84 //----------------------------------------------------------------------------
SetSubFrames(int subFrames)85 void vtkSimpleMotionBlurPass::SetSubFrames(int subFrames)
86 {
87   if (this->SubFrames != subFrames)
88   {
89     this->SubFrames = subFrames;
90     if (this->CurrentSubFrame >= this->SubFrames)
91     {
92       this->CurrentSubFrame = 0;
93     }
94     vtkDebugMacro(<< this->GetClassName() << " (" << this
95       << "): setting SubFrames to " << subFrames);
96     this->Modified();
97   }
98 }
99 
100 // ----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)101 void vtkSimpleMotionBlurPass::PrintSelf(ostream& os, vtkIndent indent)
102 {
103   os << indent << "SubFrames: " << this->SubFrames << "\n";
104   this->Superclass::PrintSelf(os,indent);
105 }
106 
107 // ----------------------------------------------------------------------------
108 // Description:
109 // Perform rendering according to a render state \p s.
110 // \pre s_exists: s!=0
Render(const vtkRenderState * s)111 void vtkSimpleMotionBlurPass::Render(const vtkRenderState *s)
112 {
113   assert("pre: s_exists" && s!=nullptr);
114 
115   vtkOpenGLClearErrorMacro();
116 
117   this->NumberOfRenderedProps=0;
118 
119   vtkRenderer *r=s->GetRenderer();
120   vtkOpenGLRenderWindow *renWin = static_cast<vtkOpenGLRenderWindow *>(r->GetRenderWindow());
121   vtkOpenGLState *ostate = renWin->GetState();
122 
123   if(this->DelegatePass == nullptr)
124   {
125     vtkWarningMacro(<<" no delegate.");
126     return;
127   }
128 
129   // 1. Create a new render state with an FO.
130   if(s->GetFrameBuffer()==nullptr)
131   {
132     // get the viewport dimensions
133     r->GetTiledSizeAndOrigin(&this->ViewportWidth,&this->ViewportHeight,
134                              &this->ViewportX,&this->ViewportY);
135   }
136   else
137   {
138     int size[2];
139     s->GetWindowSize(size);
140     this->ViewportWidth=size[0];
141     this->ViewportHeight=size[1];
142     this->ViewportX=0;
143     this->ViewportY=0;
144   }
145 
146   this->ColorTexture->SetContext(renWin);
147   if (!this->ColorTexture->GetHandle())
148   {
149     if (this->ColorFormat == vtkTextureObject::Float16)
150     {
151       this->ColorTexture->SetInternalFormat(GL_RGBA16F);
152       this->ColorTexture->SetDataType(GL_FLOAT);
153     }
154     if (this->ColorFormat == vtkTextureObject::Float32)
155     {
156       this->ColorTexture->SetInternalFormat(GL_RGBA32F);
157       this->ColorTexture->SetDataType(GL_FLOAT);
158     }
159     this->ColorTexture->Allocate2D(
160       this->ViewportWidth, this->ViewportHeight, 4,
161       VTK_UNSIGNED_CHAR);
162   }
163   this->ColorTexture->Resize(this->ViewportWidth, this->ViewportHeight);
164 
165   for (int i = 0; i < 2; i++)
166   {
167     this->AccumulationTexture[i]->SetContext(renWin);
168     if (!this->AccumulationTexture[i]->GetHandle())
169     {
170       this->AccumulationTexture[i]->SetInternalFormat(GL_RGBA16F);
171       this->AccumulationTexture[i]->SetDataType(GL_FLOAT);
172       this->AccumulationTexture[i]->Allocate2D(
173         this->ViewportWidth, this->ViewportHeight, 4,
174         VTK_UNSIGNED_CHAR);
175     }
176     this->AccumulationTexture[i]->Resize(this->ViewportWidth, this->ViewportHeight);
177   }
178   // Depth texture
179   this->DepthTexture->SetContext(renWin);
180   if (!this->DepthTexture->GetHandle())
181   {
182     this->DepthTexture->AllocateDepth(
183       this->ViewportWidth, this->ViewportHeight, this->DepthFormat);
184   }
185   this->DepthTexture->Resize(this->ViewportWidth, this->ViewportHeight);
186 
187   if(this->FrameBufferObject==nullptr)
188   {
189     this->FrameBufferObject=vtkOpenGLFramebufferObject::New();
190     this->FrameBufferObject->SetContext(renWin);
191   }
192 
193   this->FrameBufferObject->SaveCurrentBindingsAndBuffers();
194   this->RenderDelegate(s,
195     this->ViewportWidth, this->ViewportHeight,
196     this->ViewportWidth, this->ViewportHeight,
197     this->FrameBufferObject,
198     this->ColorTexture, this->DepthTexture);
199 
200   // has something changed that would require us to recreate the shader?
201   if (!this->BlendProgram)
202   {
203     this->BlendProgram = new vtkOpenGLHelper;
204     // build the shader source code
205     std::string VSSource = vtkTextureObjectVS;
206     std::string FSSource = vtkSimpleMotionBlurPassFS;
207     std::string GSSource;
208 
209     // compile and bind it if needed
210     vtkShaderProgram *newShader =
211       renWin->GetShaderCache()->ReadyShaderProgram(
212         VSSource.c_str(),
213         FSSource.c_str(),
214         GSSource.c_str());
215 
216     // if the shader changed reinitialize the VAO
217     if (newShader != this->BlendProgram->Program)
218     {
219       this->BlendProgram->Program = newShader;
220       this->BlendProgram->VAO->ShaderProgramChanged(); // reset the VAO as the shader has changed
221     }
222 
223     this->BlendProgram->ShaderSourceTime.Modified();
224   }
225   else
226   {
227     renWin->GetShaderCache()->ReadyShaderProgram(this->BlendProgram->Program);
228   }
229 
230   this->FrameBufferObject->AddColorAttachment(
231     this->FrameBufferObject->GetBothMode(), 0,
232     this->AccumulationTexture[this->ActiveAccumulationTexture]);
233 
234   ostate->vtkglViewport(0, 0,
235     this->ViewportWidth, this->ViewportHeight);
236   ostate->vtkglScissor(0, 0,
237     this->ViewportWidth, this->ViewportHeight);
238 
239   // clear the accumulator on 0
240   if (this->CurrentSubFrame == 0)
241   {
242     ostate->vtkglClearColor(0.0,0.0,0.0,0.0);
243     ostate->vtkglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
244     ostate->vtkglClear(GL_COLOR_BUFFER_BIT);
245   }
246 
247   this->ColorTexture->Activate();
248   int sourceId = this->ColorTexture->GetTextureUnit();
249   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
250   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
251   this->BlendProgram->Program->SetUniformi("source",sourceId);
252   this->BlendProgram->Program->SetUniformf("blendScale",1.0/this->SubFrames);
253   ostate->vtkglDisable(GL_DEPTH_TEST);
254 
255   // save off current state of src / dst blend functions
256   // local scope for bfsaver
257   {
258     vtkOpenGLState::ScopedglBlendFuncSeparate bfsaver(ostate);
259     ostate->vtkglBlendFunc( GL_ONE, GL_ONE);
260     this->FrameBufferObject->RenderQuad(
261       0, this->ViewportWidth - 1,
262       0, this->ViewportHeight - 1,
263       this->BlendProgram->Program, this->BlendProgram->VAO);
264     this->ColorTexture->Deactivate();
265     // restore blend func on scope exit
266   }
267 
268   // blit either the last or the current FO
269   this->CurrentSubFrame++;
270   if (this->CurrentSubFrame < this->SubFrames)
271   {
272     this->FrameBufferObject->AddColorAttachment(
273       this->FrameBufferObject->GetBothMode(), 0,
274       this->AccumulationTexture[
275         this->ActiveAccumulationTexture == 0 ? 1 : 0]);
276   }
277   else
278   {
279     this->CurrentSubFrame = 0;
280     this->ActiveAccumulationTexture =
281       (this->ActiveAccumulationTexture == 0 ? 1 : 0);
282   }
283 
284   this->FrameBufferObject->RestorePreviousBindingsAndBuffers();
285 
286   // now copy the result to the outer FO
287   this->FrameBufferObject->SaveCurrentBindingsAndBuffers(
288     this->FrameBufferObject->GetReadMode());
289   this->FrameBufferObject->Bind(
290     this->FrameBufferObject->GetReadMode());
291 
292   ostate->vtkglViewport(this->ViewportX, this->ViewportY,
293     this->ViewportWidth, this->ViewportHeight);
294   ostate->vtkglScissor(this->ViewportX, this->ViewportY,
295     this->ViewportWidth, this->ViewportHeight);
296 
297   glBlitFramebuffer(
298     0, 0, this->ViewportWidth, this->ViewportHeight,
299     this->ViewportX, this->ViewportY,
300     this->ViewportX + this->ViewportWidth,
301     this->ViewportY + this->ViewportHeight,
302     GL_COLOR_BUFFER_BIT,
303     GL_LINEAR);
304 
305   this->FrameBufferObject->RestorePreviousBindingsAndBuffers(
306     this->FrameBufferObject->GetReadMode());
307 
308   vtkOpenGLCheckErrorMacro("failed after Render");
309 }
310 
311 // ----------------------------------------------------------------------------
312 // Description:
313 // Release graphics resources and ask components to release their own
314 // resources.
315 // \pre w_exists: w!=0
ReleaseGraphicsResources(vtkWindow * w)316 void vtkSimpleMotionBlurPass::ReleaseGraphicsResources(vtkWindow *w)
317 {
318   assert("pre: w_exists" && w!=nullptr);
319 
320   this->Superclass::ReleaseGraphicsResources(w);
321 
322   if(this->FrameBufferObject!=nullptr)
323   {
324     this->FrameBufferObject->Delete();
325     this->FrameBufferObject=nullptr;
326   }
327   if(this->ColorTexture!=nullptr)
328   {
329     this->ColorTexture->ReleaseGraphicsResources(w);
330   }
331   if(this->DepthTexture!=nullptr)
332   {
333     this->DepthTexture->ReleaseGraphicsResources(w);
334   }
335   if(this->AccumulationTexture[0] !=nullptr)
336   {
337     this->AccumulationTexture[0]->ReleaseGraphicsResources(w);
338   }
339   if(this->AccumulationTexture[1] !=nullptr)
340   {
341     this->AccumulationTexture[1]->ReleaseGraphicsResources(w);
342   }
343   if (this->BlendProgram !=nullptr)
344   {
345     this->BlendProgram->ReleaseGraphicsResources(w);
346     delete this->BlendProgram;
347     this->BlendProgram = nullptr;
348   }
349 }
350