1 /**
2  * Copyright:   Copyright (C) 2018 Gabriel Gheorghe, All Rights Reserved
3  * Authors:     $(Gabriel Gheorghe)
4  * License:     $(LINK2 https://www.gnu.org/licenses/gpl-3.0.txt, GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007)
5  * Source:      $(LINK2 https://github.com/GabyForceQ/LibertyEngine/blob/master/source/liberty/graphics/postprocessing.d, _postprocessing.d)
6  * Documentation:
7  * Coverage:
8  */
9 module liberty.graphics.postprocessing;
10 version (nonde) :
11 version (__OpenGL__) :
12 import liberty.graphics.opengl;
13 /// Value less = priority bigger
14 enum PostProcessFxFlag : long {
15 	///
16 	Default = 0x00,
17 	///
18 	Sepia = 0x01,
19 	///
20     Aqua = 0x02, // TODO.
21 	///
22 	BlackAndWhite = 0x04
23 }
24 ///
25 class Postprocessing {
26 	private immutable defaultVER = "
27 		#version 450 core
28 	";
29 	private immutable defaultVS = "
30 		#if VERTEX_SHADER
31             in vec3 position;
32             in vec2 coordinates;
33             out vec2 fragmentUV;
34             void main()
35             {
36                 gl_Position = vec4(position, 1.0);
37                 fragmentUV = coordinates;
38             }
39         #endif
40 	";
41 	private immutable defaultFS_begin = "
42 		#if FRAGMENT_SHADER
43             in vec2 fragmentUV;
44             uniform sampler2D fbTexture;
45             out vec4 color;
46             void main() {
47                 vec3 base = texture(fbTexture, fragmentUV).rgb;
48 	";
49 	private immutable defaultFS_end = "
50 		        color = vec4(base, 1.0);
51             }
52         #endif
53 	";
54 	private immutable bwFS = "
55                 float luminance = base.r * 0.299 + base.g * 0.578 + base.b * 0.114;
56                 base = vec3(luminance);
57 	";
58 	private immutable sepiaFS = "
59 				vec3 sepia = vec3(
60 					clamp(base.r * 0.393 + base.g * 0.769 + base.b * 0.189, 0.0, 1.0),
61 					clamp(base.r * 0.349 + base.g * 0.686 + base.b * 0.168, 0.0, 1.0),
62 					clamp(base.r * 0.272 + base.g * 0.534 + base.b * 0.131, 0.0, 1.0)
63 				);
64 				base = mix(base, sepia, clamp(1.0, 0.0, 1.0));
65     ";
66 	///
67     this(GLBackend gl, int screenWidth, int screenHeight, PostProcessFxFlag flag) {
68         _screenBuf = new GLTexture2D();
69         _screenBuf.minFilter = GL_LINEAR_MIPMAP_LINEAR;
70         _screenBuf.magFilter = GL_LINEAR;
71         _screenBuf.wrapS = GL_CLAMP_TO_EDGE;
72         _screenBuf.wrapT = GL_CLAMP_TO_EDGE;
73         _screenBuf.setImage(0, GL_RGBA, screenWidth, screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
74         _screenBuf.generateMipmap();
75         _fbo = new GLFrameBufferObject();
76         _fbo.use();
77         _fbo.color(0).attach(_screenBuf);
78         _fbo.unuse();
79 
80         // create a shader program made of a single fragment shader
81         string postprocProgramSource = defaultVER ~ defaultVS ~ defaultFS_begin;
82         if (flag & PostProcessFxFlag.Sepia) {
83             postprocProgramSource ~= sepiaFS;
84         }
85         if (flag & PostProcessFxFlag.BlackAndWhite) {
86             postprocProgramSource ~= bwFS;
87         }
88         postprocProgramSource ~= defaultFS_end;
89             
90         _program = new GLProgram(postprocProgramSource);
91     }
92 
93     ~this()
94     {
95         _program.destroy();
96         _fbo.destroy();
97         _screenBuf.destroy();
98     }
99 
100     void bindFBO()
101     {
102         _fbo.use();
103     }
104 
105     // Post-processing pass
106     void pass(void delegate() drawGeometry) {
107         _fbo.unuse();
108         _screenBuf.generateMipmap();
109 
110         int texUnit = 1;
111         _screenBuf.use(texUnit);
112 
113         _program.uniform("fbTexture").set(texUnit);
114         _program.uniform("sharpen").set(true);
115         _program.use();
116 
117         drawGeometry();
118     }
119 private:
120     GLFrameBufferObject _fbo;
121     GLTexture2D _screenBuf;
122     GLProgram _program;
123 }