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 "vtkOpenGLError.h"
21 #include "vtkOpenGLFramebufferObject.h"
22 #include "vtkOpenGLRenderWindow.h"
23 #include "vtkOpenGLShaderCache.h"
24 #include "vtkOpenGLState.h"
25 #include "vtkOpenGLVertexArrayObject.h"
26 #include "vtkRenderState.h"
27 #include "vtkRenderer.h"
28 #include "vtkShaderProgram.h"
29 #include "vtkTextureObject.h"
30 
31 #include "vtkOpenGLHelper.h"
32 
33 #include "vtkSimpleMotionBlurPassFS.h"
34 #include "vtkTextureObjectVS.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 << "): setting SubFrames to "
95                   << 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(
134       &this->ViewportWidth, &this->ViewportHeight, &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(this->ViewportWidth, this->ViewportHeight, 4, VTK_UNSIGNED_CHAR);
160   }
161   this->ColorTexture->Resize(this->ViewportWidth, this->ViewportHeight);
162 
163   for (int i = 0; i < 2; i++)
164   {
165     this->AccumulationTexture[i]->SetContext(renWin);
166     if (!this->AccumulationTexture[i]->GetHandle())
167     {
168       this->AccumulationTexture[i]->SetInternalFormat(GL_RGBA16F);
169       this->AccumulationTexture[i]->SetDataType(GL_FLOAT);
170       this->AccumulationTexture[i]->Allocate2D(
171         this->ViewportWidth, this->ViewportHeight, 4, VTK_UNSIGNED_CHAR);
172     }
173     this->AccumulationTexture[i]->Resize(this->ViewportWidth, this->ViewportHeight);
174   }
175   // Depth texture
176   this->DepthTexture->SetContext(renWin);
177   if (!this->DepthTexture->GetHandle())
178   {
179     this->DepthTexture->AllocateDepth(this->ViewportWidth, this->ViewportHeight, this->DepthFormat);
180   }
181   this->DepthTexture->Resize(this->ViewportWidth, this->ViewportHeight);
182 
183   if (this->FrameBufferObject == nullptr)
184   {
185     this->FrameBufferObject = vtkOpenGLFramebufferObject::New();
186     this->FrameBufferObject->SetContext(renWin);
187   }
188 
189   renWin->GetState()->PushFramebufferBindings();
190   this->RenderDelegate(s, this->ViewportWidth, this->ViewportHeight, this->ViewportWidth,
191     this->ViewportHeight, this->FrameBufferObject, this->ColorTexture, this->DepthTexture);
192 
193   // has something changed that would require us to recreate the shader?
194   if (!this->BlendProgram)
195   {
196     this->BlendProgram = new vtkOpenGLHelper;
197     // build the shader source code
198     std::string VSSource = vtkTextureObjectVS;
199     std::string FSSource = vtkSimpleMotionBlurPassFS;
200     std::string GSSource;
201 
202     // compile and bind it if needed
203     vtkShaderProgram* newShader = renWin->GetShaderCache()->ReadyShaderProgram(
204       VSSource.c_str(), FSSource.c_str(), GSSource.c_str());
205 
206     // if the shader changed reinitialize the VAO
207     if (newShader != this->BlendProgram->Program)
208     {
209       this->BlendProgram->Program = newShader;
210       this->BlendProgram->VAO->ShaderProgramChanged(); // reset the VAO as the shader has changed
211     }
212 
213     this->BlendProgram->ShaderSourceTime.Modified();
214   }
215   else
216   {
217     renWin->GetShaderCache()->ReadyShaderProgram(this->BlendProgram->Program);
218   }
219 
220   this->FrameBufferObject->AddColorAttachment(
221     0, this->AccumulationTexture[this->ActiveAccumulationTexture]);
222 
223   ostate->vtkglViewport(0, 0, this->ViewportWidth, this->ViewportHeight);
224   ostate->vtkglScissor(0, 0, this->ViewportWidth, this->ViewportHeight);
225 
226   // clear the accumulator on 0
227   if (this->CurrentSubFrame == 0)
228   {
229     ostate->vtkglClearColor(0.0, 0.0, 0.0, 0.0);
230     ostate->vtkglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
231     ostate->vtkglClear(GL_COLOR_BUFFER_BIT);
232   }
233 
234   this->ColorTexture->Activate();
235   int sourceId = this->ColorTexture->GetTextureUnit();
236   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
237   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
238   this->BlendProgram->Program->SetUniformi("source", sourceId);
239   this->BlendProgram->Program->SetUniformf("blendScale", 1.0 / this->SubFrames);
240   ostate->vtkglDisable(GL_DEPTH_TEST);
241 
242   // save off current state of src / dst blend functions
243   // local scope for bfsaver
244   {
245     vtkOpenGLState::ScopedglBlendFuncSeparate bfsaver(ostate);
246     ostate->vtkglBlendFunc(GL_ONE, GL_ONE);
247     this->FrameBufferObject->RenderQuad(0, this->ViewportWidth - 1, 0, this->ViewportHeight - 1,
248       this->BlendProgram->Program, this->BlendProgram->VAO);
249     this->ColorTexture->Deactivate();
250     // restore blend func on scope exit
251   }
252 
253   // blit either the last or the current FO
254   this->CurrentSubFrame++;
255   if (this->CurrentSubFrame < this->SubFrames)
256   {
257     this->FrameBufferObject->AddColorAttachment(
258       0, this->AccumulationTexture[this->ActiveAccumulationTexture == 0 ? 1 : 0]);
259   }
260   else
261   {
262     this->CurrentSubFrame = 0;
263     this->ActiveAccumulationTexture = (this->ActiveAccumulationTexture == 0 ? 1 : 0);
264   }
265 
266   renWin->GetState()->PopFramebufferBindings();
267 
268   // now copy the result to the outer FO
269   renWin->GetState()->PushReadFramebufferBinding();
270   this->FrameBufferObject->Bind(vtkOpenGLFramebufferObject::GetReadMode());
271 
272   ostate->vtkglViewport(
273     this->ViewportX, this->ViewportY, this->ViewportWidth, this->ViewportHeight);
274   ostate->vtkglScissor(this->ViewportX, this->ViewportY, this->ViewportWidth, this->ViewportHeight);
275 
276   ostate->vtkglBlitFramebuffer(0, 0, this->ViewportWidth, this->ViewportHeight, this->ViewportX,
277     this->ViewportY, this->ViewportX + this->ViewportWidth, this->ViewportY + this->ViewportHeight,
278     GL_COLOR_BUFFER_BIT, GL_LINEAR);
279 
280   renWin->GetState()->PopReadFramebufferBinding();
281 
282   vtkOpenGLCheckErrorMacro("failed after Render");
283 }
284 
285 //------------------------------------------------------------------------------
286 // Description:
287 // Release graphics resources and ask components to release their own
288 // resources.
289 // \pre w_exists: w!=0
ReleaseGraphicsResources(vtkWindow * w)290 void vtkSimpleMotionBlurPass::ReleaseGraphicsResources(vtkWindow* w)
291 {
292   assert("pre: w_exists" && w != nullptr);
293 
294   this->Superclass::ReleaseGraphicsResources(w);
295 
296   if (this->FrameBufferObject != nullptr)
297   {
298     this->FrameBufferObject->Delete();
299     this->FrameBufferObject = nullptr;
300   }
301   if (this->ColorTexture != nullptr)
302   {
303     this->ColorTexture->ReleaseGraphicsResources(w);
304   }
305   if (this->DepthTexture != nullptr)
306   {
307     this->DepthTexture->ReleaseGraphicsResources(w);
308   }
309   if (this->AccumulationTexture[0] != nullptr)
310   {
311     this->AccumulationTexture[0]->ReleaseGraphicsResources(w);
312   }
313   if (this->AccumulationTexture[1] != nullptr)
314   {
315     this->AccumulationTexture[1]->ReleaseGraphicsResources(w);
316   }
317   if (this->BlendProgram != nullptr)
318   {
319     this->BlendProgram->ReleaseGraphicsResources(w);
320     delete this->BlendProgram;
321     this->BlendProgram = nullptr;
322   }
323 }
324