1 module armos.graphics.fbo;
2 
3 import derelict.opengl3.gl;
4 import armos.graphics;
5 import armos.types;
6 import armos.math.vector;
7 import armos.utils.scoped;
8 
9 /++
10     Frame Buffer Objectを表すclassです.
11 +/
12 class Fbo{
13     public{
14         /++
15         +/
16         this(){
17             import armos.app;
18             this(currentWindow.size);
19         }
20 
21         /++
22         +/
23         this(V2)(in V2 size){
24             this(size[0], size[1]);
25         }
26 
27         /++
28         +/
29         this(T)(in T width, in T height){
30             _size = Vector2i(width, height);
31             Vector2i textureSize = _size * _samples;
32             int x = textureSize.x;
33             int y = textureSize.y;
34             
35             glGenFramebuffers(1, cast(uint*)&_id);
36 
37             _colorTexture = (new Texture).allocate(x, y, armos.graphics.ColorFormat.RGBA)
38                                          .minMagFilter(TextureMinFilter.Nearest, TextureMagFilter.Nearest);
39 
40             _depthTexture = (new Texture).allocate(x, y, armos.graphics.ColorFormat.Depth)
41                                          .minMagFilter(TextureMinFilter.Nearest, TextureMagFilter.Nearest);
42             
43             _colorTextureTmp = (new Texture).allocate(x, y, armos.graphics.ColorFormat.RGBA)
44                                             .minMagFilter(TextureMinFilter.Nearest, TextureMagFilter.Nearest);
45             
46             _depthTextureTmp = (new Texture).allocate(x, y, armos.graphics.ColorFormat.Depth)
47                                             .minMagFilter(TextureMinFilter.Nearest, TextureMagFilter.Nearest);
48 
49             _rect = new Mesh;
50 
51             _rect.texCoords0 = [
52                 Vector4f(0f, 0f, 0.0, 1.0f),
53                 Vector4f(1f, 0,  0.0, 1.0f),
54                 Vector4f(1f, 1f, 0.0, 1.0f),
55                 Vector4f(0,  1f, 0.0, 1.0f),
56             ];
57             // isFlip(true);
58             
59             _rect.vertices = [
60                 Vector4f(0.0,   0.0,    0.0, 1.0f),
61                 Vector4f(_size.x, 0.0,    0.0, 1.0f),
62                 Vector4f(_size.x, _size.y, 0.0, 1.0f),
63                 Vector4f(0.0,   _size.y, 0.0, 1.0f),
64             ];
65 
66             _rect.indices = [
67                 0, 1, 2,
68                 2, 3, 0,
69             ];
70 
71             glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_savedId);
72             glBindFramebuffer(GL_FRAMEBUFFER, _id);
73 
74             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _colorTexture.id, 0);
75             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_TEXTURE_2D, _depthTexture.id, 0);
76             glBindFramebuffer(GL_FRAMEBUFFER, _savedId);
77             
78             _material = (new FboMaterial).texture("colorTexture", _colorTexture)
79                                          .texture("depthTexture", _depthTexture);
80         }
81 
82         /++
83             FBOへの描画処理を開始します.
84         +/
85         Fbo begin(in bool setScreenPerspective = true){
86             glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_savedId);
87             glBindFramebuffer(GL_FRAMEBUFFER, _id);
88             Vector2i textureSize = _size*_samples;
89             viewport(Vector2i.zero, textureSize);
90             
91             pushProjectionMatrix;
92             import armos.app:windowSize;
93             if(setScreenPerspective) loadProjectionMatrix(screenPerspectiveMatrix(_size));
94             return this;
95         }
96 
97         /++
98             FBOへの描画処理を終了します.
99         +/
100         Fbo end(){
101             popProjectionMatrix;
102             glBindFramebuffer(GL_FRAMEBUFFER, _savedId);
103             return this;
104         }
105 
106         /++
107             FBOのIDを返します.
108         +/
109         int id()const{
110             return _id;
111         }
112 
113         /++
114             FBOを描画します.
115         +/
116         Fbo draw(){
117             _material.begin;
118             _rect.drawFill;
119             _material.end;
120             return this;
121         }
122 
123         /++
124             FBOをリサイズします.
125             Params:
126             size = リサイズ後のサイズ
127         +/
128         Fbo resize(in armos.math.Vector2i size){
129             _size = size;
130             _rect.vertices[1][0] = _size[0];
131             _rect.vertices[2][0] = _size[0];
132             _rect.vertices[2][1] = _size[1];
133             _rect.vertices[3][1] = _size[1];
134             resizeTextures;
135             return this;
136         }
137         
138         ///
139         bool isFlip()const{return _isFlip;}
140         
141         ///
142         Fbo isFlip(in bool f){
143             _isFlip = f;
144             if(_isFlip){
145                 _rect.texCoords0 = [
146                     Vector4f(0f, 1f, 0.0, 1.0f),
147                     Vector4f(1f, 1,  0.0, 1.0f),
148                     Vector4f(1f, 0f, 0.0, 1.0f),
149                     Vector4f(0,  0f, 0.0, 1.0f),
150                 ];
151             }else{
152                 _rect.texCoords0 = [
153                     Vector4f(0f, 0f, 0.0, 1.0f),
154                     Vector4f(1f, 0,  0.0, 1.0f),
155                     Vector4f(1f, 1f, 0.0, 1.0f),
156                     Vector4f(0,  1f, 0.0, 1.0f),
157                 ];
158             }
159             return this;
160         }
161         
162         ///
163         Fbo samples(in int s){
164             _samples = s;
165             resizeTextures;
166             return this;
167         }
168         
169         ///
170         int samples()const{
171             return _samples;
172         }
173 
174         ///
175         Fbo minFilter(in TextureMinFilter filter){
176             _colorTexture.minFilter(filter);
177             _colorTextureTmp.minFilter(filter);
178             _depthTexture.minFilter(filter);
179             _depthTextureTmp.minFilter(filter);
180             return this;
181         }
182 
183         ///
184         Fbo magFilter(in TextureMagFilter filter){
185             _colorTexture.magFilter(filter);
186             _colorTextureTmp.magFilter(filter);
187             _depthTexture.magFilter(filter);
188             _depthTextureTmp.magFilter(filter);
189             return this;
190         }
191         
192         ///
193         Fbo filteredBy(Material material){
194             {
195                 auto scopedFbo = scoped(this);
196                 {
197                     auto scopedColor = scoped(_colorTextureTmp);
198                     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _size.x, _size.y);
199                 }
200                 {
201                     auto scopedDepth= scoped(_depthTextureTmp);
202                     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _size.x, _size.y);
203                 }
204             }
205             
206             material.texture("colorTexture", _colorTextureTmp)
207                     .texture("depthTexture", _depthTextureTmp);
208             
209             auto scopedFbo = scoped(this);
210             auto scopedMaterial = scoped(material);
211             _rect.drawFill;
212             
213             return this;
214         }
215         
216     }//public
217 
218     private{
219         int _savedId = 0;
220         int _id = 0;
221 
222         Texture _colorTexture;
223         Texture _colorTextureTmp;
224         Texture _depthTexture;
225         Texture _depthTextureTmp;
226 
227         Mesh _rect = new Mesh;
228         Material _material;
229         int _samples = 1;
230         bool _isFlip = false;
231         Vector2i _size;
232         
233         void resizeTextures(){
234             begin;
235             _colorTexture.resize(_size*_samples);
236             _depthTexture.resize(_size*_samples);
237             _colorTextureTmp.resize(_size*_samples);
238             _depthTextureTmp.resize(_size*_samples);
239             end;
240         }
241     }//private
242 }
243 
244 /++
245 +/
246 class FboMaterial : Material{
247     mixin MaterialImpl;
248     
249     ///
250     this(){
251         _shader = new Shader;
252         _shader.loadSources(fboVertexShaderSource, "", fboFragmentShaderSource);
253     }
254     
255     private{
256     }
257 }//class FboMaterial
258 
259 private immutable string fboVertexShaderSource = q{
260 #version 330
261 
262 uniform mat4 modelViewMatrix;
263 uniform mat4 projectionMatrix;
264 uniform mat4 modelViewProjectionMatrix;
265 uniform mat4 textureMatrix;
266 
267 in vec4 vertex;
268 in vec3 normal;
269 in vec3 tangent;
270 in vec4 texCoord0;
271 in vec4 texCoord1;
272 in vec4 color;
273 
274 out vec4 f_color;
275 out vec2 outtexCoord0;
276 out vec2 outtexCoord1;
277 
278 void main(void) {
279     gl_Position = modelViewProjectionMatrix * vertex;
280     f_color = color;
281     outtexCoord0 = texCoord0.xy;
282     outtexCoord1 = texCoord1.xy;
283 }
284 };
285 
286 private immutable string fboFragmentShaderSource = q{
287 #version 330
288     
289 in vec4 f_color;
290 in vec2 outtexCoord0;
291 in vec2 outtexCoord1;
292 
293 out vec4 fragColor;
294 
295 uniform sampler2D colorTexture;
296 uniform sampler2D depthTexture;
297 
298 void main(void) {
299     fragColor = texture(colorTexture, outtexCoord0);
300 }
301 };