Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
RenderTarget.cpp
Go to the documentation of this file.
2
3#include "Constants.h"
4#include "Log.h"
5
6namespace mnd
7{
8
9namespace
10{
11const char *FramebufferStatusName(GLenum status)
12{
13 switch (status)
14 {
15 case GL_FRAMEBUFFER_COMPLETE:
16 return "complete";
17 case GL_FRAMEBUFFER_UNDEFINED:
18 return "undefined";
19 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
20 return "incomplete attachment";
21 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
22 return "missing attachment";
23 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
24 return "incomplete draw buffer";
25 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
26 return "incomplete read buffer";
27 case GL_FRAMEBUFFER_UNSUPPORTED:
28 return "unsupported";
29 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
30 return "incomplete multisample";
31 case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
32 return "incomplete layer targets";
33 default:
34 return "unknown";
35 }
36}
37} // namespace
38
39bool RenderTarget::Create(int w, int h)
40{
41 if (w <= 0 || h <= 0)
42 {
43 LOG_ERROR("RenderTarget::Create called with invalid size %dx%d", w, h);
44 return false;
45 }
46 Destroy();
47
48 glGenFramebuffers(1, &m_fbo);
49 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
50
51 // ---- Colour attachment 0: shaded RGBA8 ------------------------------
52 glGenTextures(1, &m_color);
53 glBindTexture(GL_TEXTURE_2D, m_color);
54 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
55 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
56 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
57 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
58 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
59 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_color, 0);
60
61 // ---- Colour attachment 1: view-space normals (N*0.5+0.5) ------------
62 glGenTextures(1, &m_normal);
63 glBindTexture(GL_TEXTURE_2D, m_normal);
64 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
65 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
66 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
67 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
68 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
69 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_normal, 0);
70
71 // ---- Depth attachment: sampleable 24-bit texture --------------------
72 glGenTextures(1, &m_depth);
73 glBindTexture(GL_TEXTURE_2D, m_depth);
74 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, w, h, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
75 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
76 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
77 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
78 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
79 // Sampling depth as a normal float, not a shadow comparison.
80 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
81 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depth, 0);
82
83 const GLenum bufs[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
84 glDrawBuffers(2, bufs);
85
86 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
87 glBindFramebuffer(GL_FRAMEBUFFER, 0);
88
89 if (status != GL_FRAMEBUFFER_COMPLETE)
90 {
91 LOG_ERROR("RenderTarget FBO incomplete (%s, 0x%x) at %dx%d",
92 FramebufferStatusName(status),
93 status,
94 w,
95 h);
96 Destroy();
97 return false;
98 }
99
100 m_w = w;
101 m_h = h;
102 return true;
103}
104
105void RenderTarget::Resize(int w, int h)
106{
107 if (w == m_w && h == m_h && m_fbo != 0)
108 {
109 return;
110 }
111 Create(w, h);
112}
113
115{
116 if (m_color != 0) glDeleteTextures(1, &m_color);
117 if (m_normal != 0) glDeleteTextures(1, &m_normal);
118 if (m_depth != 0) glDeleteTextures(1, &m_depth);
119 if (m_fbo != 0) glDeleteFramebuffers(1, &m_fbo);
120 m_color = m_normal = m_depth = m_fbo = 0;
121 m_w = m_h = 0;
122}
123
125{
126 if (!IsValid())
127 {
128 LOG_ERROR("RenderTarget::Bind called before a valid FBO was created");
129 return;
130 }
131
132 glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
133 // Tell GL we're writing to both attachments. Shaders that only declare
134 // one fragment output (basic/lab/psx etc.) leave attachment 1 at the
135 // clear value — that's fine, those objects just won't generate normal
136 // edges in the post pass.
137 const GLenum bufs[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
138 glDrawBuffers(2, bufs);
139 glViewport(0, 0, m_w, m_h);
140}
141
142void RenderTarget::BindDefault(int winW, int winH)
143{
144 glBindFramebuffer(GL_FRAMEBUFFER, 0);
145 glViewport(0, 0, winW, winH);
146}
147
148// ---- Nearest blit (vertex-less fullscreen triangle) -------------------
149
150namespace
151{
152GLuint gBlitProg = 0;
153GLuint gBlitVao = 0;
154GLint gBlitModeLoc = -1;
155GLint gBlitTexLoc = -1;
156
157const char *kBlitVS = R"(#version 330 core
158out vec2 vUV;
159void main()
160{
161 vec2 p = vec2((gl_VertexID == 1) ? 3.0 : -1.0,
162 (gl_VertexID == 2) ? 3.0 : -1.0);
163 vUV = (p + 1.0) * 0.5;
164 gl_Position = vec4(p, 0.0, 1.0);
165}
166)";
167
168const char *kBlitFS = R"(#version 330 core
169in vec2 vUV;
170out vec4 oColor;
171uniform sampler2D uTex;
172uniform int uMode; // 0 = rgb, 1 = splat .r (depth), 2 = packed normal
173void main()
174{
175 vec4 s = texture(uTex, vUV);
176 if (uMode == 1)
177 {
178 // Push depth contrast — raw depth is near-1.0 for most scenes
179 // and looks flat-white. Bias/scale gives a usable preview.
180 float d = s.r;
181 float v = clamp((d - 0.95) * 20.0, 0.0, 1.0);
182 oColor = vec4(v, v, v, 1.0);
183 }
184 else if (uMode == 2)
185 {
186 oColor = vec4(s.rgb, 1.0);
187 }
188 else
189 {
190 oColor = vec4(s.rgb, 1.0);
191 }
192}
193)";
194
195GLuint CompileStage(GLenum stage, const char *src)
196{
197 GLuint s = glCreateShader(stage);
198 glShaderSource(s, 1, &src, nullptr);
199 glCompileShader(s);
200 GLint ok = 0;
201 glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
202 if (ok == 0)
203 {
204 char log[kShaderInfoLogSize];
205 glGetShaderInfoLog(s, sizeof(log), nullptr, log);
206 LOG_ERROR("Blit shader compile failed: %s", log);
207 }
208 return s;
209}
210
211void EnsureBlitResources()
212{
213 if (gBlitProg != 0)
214 {
215 return;
216 }
217 GLuint vs = CompileStage(GL_VERTEX_SHADER, kBlitVS);
218 GLuint fs = CompileStage(GL_FRAGMENT_SHADER, kBlitFS);
219 gBlitProg = glCreateProgram();
220 glAttachShader(gBlitProg, vs);
221 glAttachShader(gBlitProg, fs);
222 glLinkProgram(gBlitProg);
223 GLint ok = 0;
224 glGetProgramiv(gBlitProg, GL_LINK_STATUS, &ok);
225 if (ok == 0)
226 {
227 char log[kShaderInfoLogSize];
228 glGetProgramInfoLog(gBlitProg, sizeof(log), nullptr, log);
229 LOG_ERROR("Blit program link failed: %s", log);
230 }
231 glDeleteShader(vs);
232 glDeleteShader(fs);
233
234 gBlitTexLoc = glGetUniformLocation(gBlitProg, "uTex");
235 gBlitModeLoc = glGetUniformLocation(gBlitProg, "uMode");
236
237 glGenVertexArrays(1, &gBlitVao);
238}
239} // namespace
240
241void BlitNearest(GLuint srcTexture, int dstW, int dstH, BlitMode mode)
242{
243 EnsureBlitResources();
244 if (gBlitProg == 0)
245 {
246 return;
247 }
248
249 glViewport(0, 0, dstW, dstH);
250 glDisable(GL_DEPTH_TEST);
251 glDisable(GL_CULL_FACE);
252 glDisable(GL_BLEND);
253
254 glUseProgram(gBlitProg);
255 glActiveTexture(GL_TEXTURE0);
256 glBindTexture(GL_TEXTURE_2D, srcTexture);
257 if (gBlitTexLoc >= 0) glUniform1i(gBlitTexLoc, 0);
258 if (gBlitModeLoc >= 0) glUniform1i(gBlitModeLoc, static_cast<int>(mode));
259
260 glBindVertexArray(gBlitVao);
261 glDrawArrays(GL_TRIANGLES, 0, 3);
262 glBindVertexArray(0);
263
264 glEnable(GL_DEPTH_TEST);
265}
266
267} // namespace mnd
Engine-wide compile-time tunables and ANSI colour codes.
constexpr mnd::i32 kShaderInfoLogSize
Definition Constants.h:57
int GLint
Definition GLForward.h:13
unsigned int GLenum
Definition GLForward.h:12
unsigned int GLuint
Definition GLForward.h:11
Console logging macros for all engine and game code.
#define LOG_ERROR(fmt,...)
Definition Log.h:81
Offscreen FBO with colour, view-normal and depth attachments.
static void BindDefault(int winW, int winH)
bool IsValid() const
bool Create(int w, int h)
void Resize(int w, int h)
BlitMode
Blit modes for BlitNearest.
void BlitNearest(GLuint srcTexture, int dstW, int dstH, BlitMode mode)