1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.framebuffer;
29 
30 import std.stdio;
31 import std.math;
32 
33 import dlib.math.vector;
34 import dlib.image.color;
35 
36 import dagon.core.libs;
37 import dagon.core.ownership;
38 import dagon.graphics.gbuffer;
39 
40 abstract class RenderTarget: Owner
41 {
42     uint width;
43     uint height;
44     
45     this(uint w, uint h, Owner o)
46     {
47         super(o);
48         width = w;
49         height = h;
50     }
51     
52     void bind();
53     void unbind();
54     void clear(Color4f clearColor);
55 }
56 
57 /*
58  * TODO: the whole thing should be rewritten in more reasonable way.
59  * E.g., a chain of specialized buffers extending abstract interface.
60  */
61 class Framebuffer: RenderTarget
62 {
63     GBuffer gbuffer;
64 
65     GLuint fbo;
66     GLuint depthTexture = 0;
67     GLuint colorTexture1 = 0;
68     GLuint colorTexture2 = 0;
69     GLuint currColorTexture = 0;
70     GLuint lumaTexture = 0;
71     GLuint velocityTexture = 0;
72 
73     Vector2f[4] vertices;
74     Vector2f[4] texcoords;
75     uint[3][2] indices;
76 
77     GLuint vao = 0;
78     GLuint vbo = 0;
79     GLuint tbo = 0;
80     GLuint eao = 0;
81 
82     bool isFloating = false;
83 
84     bool isHDRSceneBuffer = false;
85 
86     this(GBuffer gbuffer, uint w, uint h, bool floating, bool isHDRSceneBuffer, Owner o)
87     {
88         super(w, h, o);
89 
90         this.isHDRSceneBuffer = isHDRSceneBuffer;
91 
92         this.gbuffer = gbuffer;
93 
94         glActiveTexture(GL_TEXTURE0);
95 
96         glGenTextures(1, &depthTexture);
97         glBindTexture(GL_TEXTURE_2D, depthTexture);
98         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
99         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
100         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
101         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
102         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
103         glBindTexture(GL_TEXTURE_2D, 0);
104 
105         isFloating = floating;
106 
107         glGenTextures(1, &colorTexture1);
108         glBindTexture(GL_TEXTURE_2D, colorTexture1);
109         if (!floating)
110         {
111             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
112             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
113             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
114         }
115         else
116         {
117             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null);
118             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
119             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
120         }
121         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
122         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
123         glBindTexture(GL_TEXTURE_2D, 0);
124         
125         glGenTextures(1, &colorTexture2);
126         glBindTexture(GL_TEXTURE_2D, colorTexture2);
127         if (!floating)
128         {
129             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
130             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
131             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
132         }
133         else
134         {
135             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null);
136             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
137             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
138         }
139         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
140         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
141         glBindTexture(GL_TEXTURE_2D, 0);
142         
143         currColorTexture = colorTexture1;
144         
145         if (isHDRSceneBuffer)
146         {
147             glGenTextures(1, &lumaTexture);
148             glBindTexture(GL_TEXTURE_2D, lumaTexture);
149             glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, width, height, 0, GL_RED, GL_FLOAT, null);
150             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
151             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
152             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
153             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
154             glBindTexture(GL_TEXTURE_2D, 0);
155         }
156 
157         glGenFramebuffers(1, &fbo);
158         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
159         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currColorTexture, 0);
160         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
161 
162         if (isHDRSceneBuffer)
163         {
164             velocityTexture = gbuffer.velocityTexture;
165 
166             // TODO: use the same layout as geometry pass?
167             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, lumaTexture, 0);
168             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, velocityTexture, 0);
169 
170             GLenum[3] bufs = [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2];
171             glDrawBuffers(3, bufs.ptr);
172         }
173         else
174         {
175             glDrawBuffer(GL_COLOR_ATTACHMENT0);
176         }
177 
178         GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
179         if (status != GL_FRAMEBUFFER_COMPLETE)
180             writeln(status);
181 
182         glBindFramebuffer(GL_FRAMEBUFFER, 0);
183 
184         vertices[0] = Vector2f(0, 0);
185         vertices[1] = Vector2f(0, 1);
186         vertices[2] = Vector2f(1, 0);
187         vertices[3] = Vector2f(1, 1);
188 
189         texcoords[0] = Vector2f(0, 1);
190         texcoords[1] = Vector2f(0, 0);
191         texcoords[2] = Vector2f(1, 1);
192         texcoords[3] = Vector2f(1, 0);
193 
194         indices[0][0] = 0;
195         indices[0][1] = 1;
196         indices[0][2] = 2;
197 
198         indices[1][0] = 2;
199         indices[1][1] = 1;
200         indices[1][2] = 3;
201 
202         glGenBuffers(1, &vbo);
203         glBindBuffer(GL_ARRAY_BUFFER, vbo);
204         glBufferData(GL_ARRAY_BUFFER, vertices.length * float.sizeof * 2, vertices.ptr, GL_STATIC_DRAW);
205 
206         glGenBuffers(1, &tbo);
207         glBindBuffer(GL_ARRAY_BUFFER, tbo);
208         glBufferData(GL_ARRAY_BUFFER, texcoords.length * float.sizeof * 2, texcoords.ptr, GL_STATIC_DRAW);
209 
210         glGenBuffers(1, &eao);
211         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
212         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof * 3, indices.ptr, GL_STATIC_DRAW);
213 
214         glGenVertexArrays(1, &vao);
215         glBindVertexArray(vao);
216         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
217 
218         glEnableVertexAttribArray(0);
219         glBindBuffer(GL_ARRAY_BUFFER, vbo);
220         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null);
221 
222         glEnableVertexAttribArray(1);
223         glBindBuffer(GL_ARRAY_BUFFER, tbo);
224         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null);
225 
226         glBindVertexArray(0);
227 
228         maxMipmap = cast(int)log2(fmax(width, height));
229     }
230 
231     int maxMipmap;
232 
233     ~this()
234     {
235         glBindFramebuffer(GL_FRAMEBUFFER, 0);
236         glDeleteFramebuffers(1, &fbo);
237 
238         if (glIsTexture(depthTexture))
239             glDeleteTextures(1, &depthTexture);
240         if (glIsTexture(colorTexture1))
241             glDeleteTextures(1, &colorTexture1);
242         if (glIsTexture(colorTexture2))
243             glDeleteTextures(1, &colorTexture2);
244         if (glIsTexture(lumaTexture))
245             glDeleteTextures(1, &lumaTexture);
246 
247         glDeleteVertexArrays(1, &vao);
248         glDeleteBuffers(1, &vbo);
249         glDeleteBuffers(1, &tbo);
250         glDeleteBuffers(1, &eao);
251     }
252 
253     void genLuminanceMipmaps()
254     {
255         glBindTexture(GL_TEXTURE_2D, lumaTexture);
256         glGenerateMipmap(GL_TEXTURE_2D);
257         glBindTexture(GL_TEXTURE_2D, 0);
258     }
259 
260     float averageLuminance()
261     {
262         glBindTexture(GL_TEXTURE_2D, lumaTexture);
263         float luma = 0.0f;
264         if (!isHDRSceneBuffer)
265             return 0.0f;
266         else
267             glGetTexImage(GL_TEXTURE_2D, maxMipmap, GL_RED, GL_FLOAT, &luma);
268         glBindTexture(GL_TEXTURE_2D, 0);
269         return luma;
270     }
271 
272     override void bind()
273     {
274         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
275     }
276 
277     override void unbind()
278     {
279         glBindFramebuffer(GL_FRAMEBUFFER, 0);
280     }
281 
282     void render()
283     {
284         glDepthMask(0);
285         glDisable(GL_DEPTH_TEST);
286         glBindVertexArray(vao);
287         glDrawElements(GL_TRIANGLES, cast(uint)indices.length * 3, GL_UNSIGNED_INT, cast(void*)0);
288         glBindVertexArray(0);
289         glEnable(GL_DEPTH_TEST);
290         glDepthMask(1);
291     }
292 
293     override void clear(Color4f clearColor)
294     {
295         glClear(GL_DEPTH_BUFFER_BIT);
296         Color4f zero = Color4f(0, 0, 0, 0);
297         glClearBufferfv(GL_COLOR, 0, clearColor.arrayof.ptr);
298         if (isHDRSceneBuffer)
299         {
300             glClearBufferfv(GL_COLOR, 1, zero.arrayof.ptr);
301             //glClearBufferfv(GL_COLOR, 2, zero.arrayof.ptr);
302         }
303     }
304     
305     void swapColorTextureAttachments()
306     {
307         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
308         if (currColorTexture == colorTexture1)
309             currColorTexture = colorTexture2;
310         else
311             currColorTexture = colorTexture1;
312         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currColorTexture, 0);
313         glBindFramebuffer(GL_FRAMEBUFFER, 0);
314     }
315     
316     GLuint currentColorTexture() @property
317     {
318         return currColorTexture;
319     }
320     
321     GLuint previousColorTexture() @property
322     {
323         if (currColorTexture == colorTexture1)
324             return colorTexture2;
325         else
326             return colorTexture1;
327     }
328 }