RenderTarget.cpp
1// Copyright 2019 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
4
5#include <smk/Color.hpp>
6#include <smk/Drawable.hpp>
7#include <smk/RenderTarget.hpp>
8#include <smk/Texture.hpp>
9
10namespace smk {
11bool g_invalidate_textures = false; // NOLINT
12namespace {
13
14RenderTarget* render_target = nullptr; // NOLINT
15RenderState cached_render_state_; // NOLINT
16
17const Texture& WhiteTexture() {
18 static const smk::Texture white_texture = [] {
19 static const uint8_t data[4] = {255, 255, 255, 255}; // NOLINT
20 return smk::Texture(data, 1, 1); // NOLINT
21 }();
22
23 return white_texture;
24}
25
26} // namespace
27
28void RenderTarget::Bind(RenderTarget* target) {
29 if (render_target == target) {
30 return;
31 }
32 render_target = target;
33 glBindFramebuffer(GL_FRAMEBUFFER, render_target->frame_buffer_);
34 glViewport(0, 0, render_target->width_, render_target->height_);
35}
36
37/// @brief Build an invalid RenderTarget.
38/// It can be replaced later by using the move operator.
40
41/// @brief Constructor from temporary.
43 operator=(std::move(other));
44}
45
46/// @brief Move operator.
48 std::swap(width_, other.width_);
49 std::swap(height_, other.height_);
50 std::swap(projection_matrix_, other.projection_matrix_);
51 std::swap(view_, other.view_);
52 std::swap(vertex_shader_2d_, other.vertex_shader_2d_);
53 std::swap(fragment_shader_2d_, other.fragment_shader_2d_);
54 std::swap(shader_program_2d_, other.shader_program_2d_);
55 std::swap(vertex_shader_3d_, other.vertex_shader_3d_);
56 std::swap(fragment_shader_3d_, other.fragment_shader_3d_);
57 std::swap(shader_program_3d_, other.shader_program_3d_);
58 std::swap(shader_program_, other.shader_program_);
59 std::swap(frame_buffer_, other.frame_buffer_);
60 return *this;
61}
62
63/// @brief Clear the surface with a single color.
64/// @param color: An opaque color to fill the surface.
65void RenderTarget::Clear(const glm::vec4& color) {
66 Bind(this);
67 glClearColor(color.r, color.g, color.b, color.a); // NOLINT
68 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
69 glDisable(GL_DEPTH_TEST);
70 glDisable(GL_CULL_FACE);
71}
72
73/// @brief Set the View to use.
74/// @param view: The view to use.
75void RenderTarget::SetView(const View& view) {
76 view_ = view;
77 float z_x = +2.F / view.width_; // NOLINT [0, width] -> [-1,1]
78 float z_y = -2.F / view.height_; // NOLINT [0, height] -> [-1,1]
79 float t_x = -view.x_ * z_x;
80 float t_y = -view.y_ * z_y;
81 SetView(glm::mat4(z_x, 0.F, 0.F, 0.F, //
82 0.F, z_y, 0.F, 0.F, //
83 0.F, 0.F, 1.F, 0.F, //
84 t_x, t_y, 0.F, 1.F)); //
85}
86
87/// @brief Set the View to use.
88/// @param mat: The matrix to transform from/to the OpenGL space [-1,1]^3 to the
89/// screen space.
90void RenderTarget::SetView(const glm::mat4& mat) {
91 projection_matrix_ = mat;
92}
93
94/// @brief Return the View currently assigned to this RenderTarget.
95const View& RenderTarget::view() const {
96 return view_;
97}
98
99/// @brief Set the ShaderProgram to be used.
100/// @param shader_program: The ShaderProgram to be used.
101///
102/// ## Example:
103///
104/// ~~~cpp
105/// auto shader_vertex = Shader::FromString(R"(
106/// layout(location = 0) in vec2 space_position;
107/// layout(location = 1) in vec2 texture_position;
108///
109/// uniform mat4 projection;
110/// uniform mat4 view;
111///
112/// out vec2 f_texture_position;
113///
114/// void main() {
115/// f_texture_position = texture_position;
116/// gl_Position = projection * view * vec4(space_position, 0.F, 1.F);
117/// }
118/// )", GL_VERTEX_SHADER);
119///
120/// fragment_shader_2d_ = Shader::FromString(R"(
121/// in vec2 f_texture_position;
122/// uniform sampler2D texture_0;
123/// uniform vec4 color;
124/// out vec4 out_color;
125///
126/// void main() {
127/// vec4 inverted_color = vec4(1.F) - color;
128/// out_color = texture(texture_0, f_texture_position) * inverted_color.
129/// }
130/// )";
131///
132/// auto shader_program = smk::ShaderProgram>();
133/// shader_program.AddShader(vertex_shader_2d_);
134/// shader_program.AddShader(fragment_shader_2d_);
135/// shader_program.Link();
136///
137/// window.SetShaderProgram(shader_program);
138/// ~~~
139/// {
141 shader_program_ = shader_program;
142 shader_program_.Use();
143 shader_program_.SetUniform("texture_0", 0);
144 shader_program_.SetUniform("color", glm::vec4(1.F, 1.F, 1.F, 1.F));
145 shader_program_.SetUniform("projection", glm::mat4(1.F));
146 shader_program_.SetUniform("view", glm::mat4(1.F));
147}
148
149/// @brief Return the default predefined 2D shader program. It is bound by
150/// default.
152 return shader_program_2d_;
153};
154
155/// @brief Return the default predefined 3D shader program.
157 return shader_program_3d_;
158};
159
160/// @brief Draw on the surface
161/// @param drawable: The object to be drawn on the surface.
162void RenderTarget::Draw(const Drawable& drawable) {
163 Bind(this);
164 RenderState state;
165 state.shader_program = shader_program_;
166 state.view = glm::mat4(1.F);
167 state.color = smk::Color::White;
169 drawable.Draw(*this, state);
170}
171
172/// @brief Draw on the surface
173/// @param state: The RenderState to be usd for drawing.
175 // Vertex Array
176 if (cached_render_state_.vertex_array != state.vertex_array) {
177 cached_render_state_.vertex_array = state.vertex_array;
178 state.vertex_array.Bind();
179 }
180
181 // Shader
182 if (cached_render_state_.shader_program != state.shader_program) {
183 cached_render_state_.shader_program = state.shader_program;
184 cached_render_state_.shader_program.Use();
185 }
186
187 // Color
188 if (cached_render_state_.color != state.color) {
189 cached_render_state_.color = state.color;
190 cached_render_state_.shader_program.SetUniform("color", state.color);
191 }
192
193 // View (not cached)
194 state.shader_program.SetUniform("projection", projection_matrix_);
195 state.shader_program.SetUniform("view", state.view);
196
197 // Texture
198 const auto& texture = state.texture.id() ? state.texture : WhiteTexture();
199 if (cached_render_state_.texture != texture || g_invalidate_textures) {
200 cached_render_state_.texture = texture;
201 texture.Bind();
202 g_invalidate_textures = false;
203 }
204
205 if (cached_render_state_.blend_mode != state.blend_mode) {
206 cached_render_state_.blend_mode = state.blend_mode;
207 glEnable(GL_BLEND);
208 glBlendEquationSeparate(state.blend_mode.equation_rgb,
209 state.blend_mode.equation_alpha);
210 glBlendFuncSeparate(state.blend_mode.src_rgb, state.blend_mode.dst_rgb,
211 state.blend_mode.src_alpha, state.blend_mode.dst_alpha);
212 }
213
214 glDrawArrays(GL_TRIANGLES, 0, GLsizei(state.vertex_array.size()));
215}
216
217/// @brief the dimension (width, height) of the drawing area.
218/// @return the dimensions in (pixels, pixels) of the surface.
219glm::vec2 RenderTarget::dimensions() const {
220 return glm::vec2(width_, height_);
221}
222
223/// @brief the width of the surface.
224/// @return the height of the surface.
226 return width_;
227}
228
229/// @brief the height of the surface.
230/// @return the height in pixels of the surface.
232 return height_;
233}
234
235void RenderTarget::InitRenderTarget() {
236 View default_view;
237 default_view.SetCenter(float(width_) / 2.F, float(height_) / 2.F); // NOLINT
238 default_view.SetSize(float(width_), float(height_));
239 SetView(default_view);
240
241 vertex_shader_2d_ = Shader::FromString(R"(
242 layout(location = 0) in vec2 space_position;
243 layout(location = 1) in vec2 texture_position;
244
245 uniform mat4 projection;
246 uniform mat4 view;
247
248 out vec2 f_texture_position;
249
250 void main() {
251 f_texture_position = texture_position;
252 gl_Position = projection * view * vec4(space_position, 0.F, 1.F);
253 }
254 )",
255 GL_VERTEX_SHADER);
256
257 fragment_shader_2d_ = Shader::FromString(R"(
258 in vec2 f_texture_position;
259 uniform sampler2D texture_0;
260 uniform vec4 color;
261 out vec4 out_color;
262
263 void main() {
264 out_color = texture(texture_0, f_texture_position) * color;
265 }
266 )",
267 GL_FRAGMENT_SHADER);
268
269 shader_program_2d_.AddShader(vertex_shader_2d_);
270 shader_program_2d_.AddShader(fragment_shader_2d_);
271 shader_program_2d_.Link();
272
273 vertex_shader_3d_ = Shader::FromString(R"(
274 layout(location = 0) in vec3 space_position;
275 layout(location = 1) in vec3 normal;
276 layout(location = 2) in vec2 texture_position;
277
278 uniform mat4 projection;
279 uniform mat4 view;
280
281 out vec4 fPosition;
282 out vec2 fTexture;
283 out vec3 fNormal;
284
285 void main() {
286 fTexture = texture_position;
287 fPosition = view * vec4(space_position,1.F);
288 fNormal = vec3(view * vec4(normal,0.F));
289
290 gl_Position = projection * fPosition;
291 }
292 )",
293 GL_VERTEX_SHADER);
294
295 fragment_shader_3d_ = Shader::FromString(R"(
296 uniform sampler2D texture_0;
297 uniform vec4 color;
298
299 uniform vec4 light_position;
300 uniform float ambient;
301 uniform float diffuse;
302 uniform float specular;
303 uniform float specular_power;
304
305 in vec4 fPosition;
306 in vec2 fTexture;
307 in vec3 fNormal;
308
309 out vec4 out_color;
310
311 void main(void)
312 {
313 vec3 object_dir =-normalize(fPosition.xyz);
314 vec3 normal_dir = normalize(fNormal);
315 vec3 light_dir = normalize(light_position.xyz-fPosition.xyz);
316 vec3 reflect_dir = -reflect(object_dir,normal_dir);
317
318 float diffuse_strength = max(0.F, dot(normal_dir, light_dir));
319 float specular_strength = pow(max(0.F, dot(reflect_dir, light_dir)),
320 specular_power);
321
322 out_color = texture(texture_0, fTexture);
323 out_color.rgb *= ambient +
324 diffuse * diffuse_strength +
325 specular * specular_strength;
326 out_color *= color;
327 }
328 )",
329 GL_FRAGMENT_SHADER);
330
331 shader_program_3d_.AddShader(vertex_shader_3d_);
332 shader_program_3d_.AddShader(fragment_shader_3d_);
333 shader_program_3d_.Link();
334
335 shader_program_3d_.Use();
336 constexpr auto default_light_position = glm::vec4(0.F, 5.F, 0.F, 1.F);
337 constexpr auto default_ambient = 0.3F;
338 constexpr auto default_diffuse = 0.5F;
339 constexpr auto default_specular = 0.5F;
340 constexpr auto default_specular_power = 4.F;
341 shader_program_3d_.SetUniform("light_position", default_light_position);
342 shader_program_3d_.SetUniform("ambient", default_ambient);
343 shader_program_3d_.SetUniform("diffuse", default_diffuse);
344 shader_program_3d_.SetUniform("specular", default_specular);
345 shader_program_3d_.SetUniform("specular_power", default_specular_power);
346
347 SetShaderProgram(shader_program_2d_);
348}
349
350} // namespace smk
Interface for class that can be draw to a smk::RenderTarget.
Definition: Drawable.hpp:14
virtual void Draw(RenderTarget &target, RenderState state) const =0
RenderTarget & operator=(RenderTarget &&other) noexcept
Move operator.
void SetView(const View &view)
Set the View to use.
void SetShaderProgram(ShaderProgram &shader_program)
Set the ShaderProgram to be used.
ShaderProgram & shader_program_2d()
Return the default predefined 2D shader program. It is bound by default.
const View & view() const
Return the View currently assigned to this RenderTarget.
int height() const
the height of the surface.
virtual void Draw(const Drawable &drawable)
Draw on the surface.
ShaderProgram & shader_program_3d()
Return the default predefined 3D shader program.
RenderTarget()
Build an invalid RenderTarget. It can be replaced later by using the move operator.
glm::vec2 dimensions() const
the dimension (width, height) of the drawing area.
void Clear(const glm::vec4 &color)
Clear the surface with a single color.
int width() const
the width of the surface.
A shader program is a set of shader (for instance vertex shader + pixel shader) defining the renderin...
Definition: Shader.hpp:114
void SetUniform(const std::string &name, float x, float y, float z)
Assign shader vec3 uniform.
Definition: Shader.cpp:377
void AddShader(const Shader &shader)
Add a Shader to the program list. This must called multiple time for each shader components before ca...
Definition: Shader.cpp:224
void Use() const
Bind the ShaderProgram. Future draw will use it. This unbind any previously bound ShaderProgram.
Definition: Shader.cpp:429
void Link() const
Add a Shader to the program list.
Definition: Shader.cpp:236
static Shader FromString(const std::string &content, GLenum type)
Load a shader from a std::string.
Definition: Shader.cpp:60
size_t size() const
The size of the GPU array.
Definition: VertexArray.cpp:92
float x_
<
Definition: View.hpp:27
void SetCenter(float x, float y)
Set the center position of the in-game view. param x The center of the view along the horizontal axis...
Definition: View.cpp:12
void SetSize(float width, float height)
Set the size of the in-game view. param width The size of the view along the horizontal axis....
Definition: View.cpp:26
const glm::vec4 White
White.
Definition: Color.cpp:29
static const BlendMode Alpha
destination = source * source.a + destination * (1 - souce.a)
Definition: BlendMode.hpp:68
Contain all the data needed to draw.
Definition: RenderState.hpp:17
glm::mat4 view
The "view" transformation.
Definition: RenderState.hpp:21
glm::vec4 color
The masking color.
Definition: RenderState.hpp:22
Texture texture
The texture 0 bound.
Definition: RenderState.hpp:19
BlendMode blend_mode
The OpenGL BlendMode.
Definition: RenderState.hpp:23
VertexArray vertex_array
The shape to to be drawn.
Definition: RenderState.hpp:20
ShaderProgram shader_program
The shader used.
Definition: RenderState.hpp:18
GLuint id() const
Access the ID of the texture.
Definition: Texture.cpp:207