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.algorithm;
32 import std.math;
33 
34 import dlib.math.vector;
35 import dlib.image.color;
36 
37 import dagon.core.libs;
38 import dagon.core.ownership;
39 import dagon.graphics.gbuffer;
40 
41 abstract class RenderTarget: Owner
42 {
43     uint width;
44     uint height;
45     
46     this(uint w, uint h, Owner o)
47     {
48         super(o);
49         width = w;
50         height = h;
51     }
52     
53     void bind();
54     void unbind();
55     void clear(Color4f clearColor);
56 }
57 
58 /*
59  * TODO: the whole thing should be rewritten in more reasonable way.
60  * E.g., a chain of specialized buffers extending abstract interface.
61  */
62 class Framebuffer: RenderTarget
63 {
64     GBuffer gbuffer;
65 
66     GLuint fbo;
67     GLuint depthTexture = 0;
68     GLuint colorTexture1 = 0;
69     GLuint colorTexture2 = 0;
70     GLuint currColorTexture = 0;
71     GLuint lumaTexture = 0;
72     GLuint velocityTexture = 0;
73 
74     Vector2f[4] vertices;
75     Vector2f[4] texcoords;
76     uint[3][2] indices;
77 
78     GLuint vao = 0;
79     GLuint vbo = 0;
80     GLuint tbo = 0;
81     GLuint eao = 0;
82 
83     bool isFloating = false;
84 
85     bool isHDRSceneBuffer = false;
86 
87     this(GBuffer gbuffer, uint w, uint h, bool floating, bool isHDRSceneBuffer, Owner o)
88     {
89         super(w, h, o);
90 
91         this.isHDRSceneBuffer = isHDRSceneBuffer;
92 
93         this.gbuffer = gbuffer;
94 
95         glActiveTexture(GL_TEXTURE0);
96 
97         glGenTextures(1, &depthTexture);
98         glBindTexture(GL_TEXTURE_2D, depthTexture);
99         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
100         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
101         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
102         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
103         glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, null);
104         glBindTexture(GL_TEXTURE_2D, 0);
105 
106         isFloating = floating;
107 
108         glGenTextures(1, &colorTexture1);
109         glBindTexture(GL_TEXTURE_2D, colorTexture1);
110         if (!floating)
111         {
112             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
113             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
114             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
115         }
116         else
117         {
118             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null);
119             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
120             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
121         }
122         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
123         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
124         glBindTexture(GL_TEXTURE_2D, 0);
125         
126         glGenTextures(1, &colorTexture2);
127         glBindTexture(GL_TEXTURE_2D, colorTexture2);
128         if (!floating)
129         {
130             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
131             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
132             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
133         }
134         else
135         {
136             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null);
137             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
138             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
139         }
140         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
141         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
142         glBindTexture(GL_TEXTURE_2D, 0);
143         
144         currColorTexture = colorTexture1;
145         
146         if (isHDRSceneBuffer)
147         {
148             glGenTextures(1, &lumaTexture);
149             glBindTexture(GL_TEXTURE_2D, lumaTexture);
150             glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, width, height, 0, GL_RED, GL_FLOAT, null);
151             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
152             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
153             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
154             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
155             glBindTexture(GL_TEXTURE_2D, 0);
156         }
157 
158         glGenFramebuffers(1, &fbo);
159         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
160         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currColorTexture, 0);
161         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
162 
163         if (isHDRSceneBuffer)
164         {
165             velocityTexture = gbuffer.velocityTexture;
166 
167             // TODO: use the same layout as geometry pass?
168             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, lumaTexture, 0);
169             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, velocityTexture, 0);
170 
171             GLenum[3] bufs = [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2];
172             glDrawBuffers(3, bufs.ptr);
173         }
174         else
175         {
176             glDrawBuffer(GL_COLOR_ATTACHMENT0);
177         }
178 
179         GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
180         if (status != GL_FRAMEBUFFER_COMPLETE)
181             writeln(status);
182 
183         glBindFramebuffer(GL_FRAMEBUFFER, 0);
184 
185         vertices[0] = Vector2f(0, 0);
186         vertices[1] = Vector2f(0, 1);
187         vertices[2] = Vector2f(1, 0);
188         vertices[3] = Vector2f(1, 1);
189 
190         texcoords[0] = Vector2f(0, 1);
191         texcoords[1] = Vector2f(0, 0);
192         texcoords[2] = Vector2f(1, 1);
193         texcoords[3] = Vector2f(1, 0);
194 
195         indices[0][0] = 0;
196         indices[0][1] = 1;
197         indices[0][2] = 2;
198 
199         indices[1][0] = 2;
200         indices[1][1] = 1;
201         indices[1][2] = 3;
202 
203         glGenBuffers(1, &vbo);
204         glBindBuffer(GL_ARRAY_BUFFER, vbo);
205         glBufferData(GL_ARRAY_BUFFER, vertices.length * float.sizeof * 2, vertices.ptr, GL_STATIC_DRAW);
206 
207         glGenBuffers(1, &tbo);
208         glBindBuffer(GL_ARRAY_BUFFER, tbo);
209         glBufferData(GL_ARRAY_BUFFER, texcoords.length * float.sizeof * 2, texcoords.ptr, GL_STATIC_DRAW);
210 
211         glGenBuffers(1, &eao);
212         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
213         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.length * uint.sizeof * 3, indices.ptr, GL_STATIC_DRAW);
214 
215         glGenVertexArrays(1, &vao);
216         glBindVertexArray(vao);
217         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
218 
219         glEnableVertexAttribArray(0);
220         glBindBuffer(GL_ARRAY_BUFFER, vbo);
221         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null);
222 
223         glEnableVertexAttribArray(1);
224         glBindBuffer(GL_ARRAY_BUFFER, tbo);
225         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, null);
226 
227         glBindVertexArray(0);
228 
229         maxMipmap = cast(int)log2(cast(float)max(width, height));
230     }
231 
232     int maxMipmap;
233 
234     ~this()
235     {
236         glBindFramebuffer(GL_FRAMEBUFFER, 0);
237         glDeleteFramebuffers(1, &fbo);
238 
239         if (glIsTexture(depthTexture))
240             glDeleteTextures(1, &depthTexture);
241         if (glIsTexture(colorTexture1))
242             glDeleteTextures(1, &colorTexture1);
243         if (glIsTexture(colorTexture2))
244             glDeleteTextures(1, &colorTexture2);
245         if (glIsTexture(lumaTexture))
246             glDeleteTextures(1, &lumaTexture);
247 
248         glDeleteVertexArrays(1, &vao);
249         glDeleteBuffers(1, &vbo);
250         glDeleteBuffers(1, &tbo);
251         glDeleteBuffers(1, &eao);
252     }
253 
254     void genLuminanceMipmaps()
255     {
256         glBindTexture(GL_TEXTURE_2D, lumaTexture);
257         glGenerateMipmap(GL_TEXTURE_2D);
258         glBindTexture(GL_TEXTURE_2D, 0);
259     }
260 
261     float averageLuminance()
262     {
263         glBindTexture(GL_TEXTURE_2D, lumaTexture);
264         float luma = 0.0f;
265         if (!isHDRSceneBuffer)
266             return 0.0f;
267         else
268             glGetTexImage(GL_TEXTURE_2D, maxMipmap, GL_RED, GL_FLOAT, &luma);
269         glBindTexture(GL_TEXTURE_2D, 0);
270         return luma;
271     }
272 
273     override void bind()
274     {
275         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
276     }
277 
278     override void unbind()
279     {
280         glBindFramebuffer(GL_FRAMEBUFFER, 0);
281     }
282 
283     void render()
284     {
285         glDepthMask(0);
286         glDisable(GL_DEPTH_TEST);
287         glBindVertexArray(vao);
288         glDrawElements(GL_TRIANGLES, cast(uint)indices.length * 3, GL_UNSIGNED_INT, cast(void*)0);
289         glBindVertexArray(0);
290         glEnable(GL_DEPTH_TEST);
291         glDepthMask(1);
292     }
293 
294     override void clear(Color4f clearColor)
295     {
296         glClear(GL_DEPTH_BUFFER_BIT);
297         Color4f zero = Color4f(0, 0, 0, 0);
298         glClearBufferfv(GL_COLOR, 0, clearColor.arrayof.ptr);
299         if (isHDRSceneBuffer)
300         {
301             glClearBufferfv(GL_COLOR, 1, zero.arrayof.ptr);
302             //glClearBufferfv(GL_COLOR, 2, zero.arrayof.ptr);
303         }
304     }
305     
306     void swapColorTextureAttachments()
307     {
308         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
309         if (currColorTexture == colorTexture1)
310             currColorTexture = colorTexture2;
311         else
312             currColorTexture = colorTexture1;
313         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, currColorTexture, 0);
314         glBindFramebuffer(GL_FRAMEBUFFER, 0);
315     }
316     
317     GLuint currentColorTexture() @property
318     {
319         return currColorTexture;
320     }
321     
322     GLuint previousColorTexture() @property
323     {
324         if (currColorTexture == colorTexture1)
325             return colorTexture2;
326         else
327             return colorTexture1;
328     }
329 }