Multi-Sample-Anti-Alias.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 2m4s

This commit is contained in:
2024-10-09 22:22:24 -04:00
parent 0417c37460
commit 97573e28a9
7 changed files with 184 additions and 27 deletions

View File

@@ -82,8 +82,12 @@ namespace JGL {
/// Draws a filled rectangle with rounded corners on the screen.
void FillRoundedRect(const Color4& color, const Vector2& pos, const Vector2& size, float radius = 5, unsigned int subdivisions = 8);
void DrawRenderTargetAsSprite(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
/// Draws a render target to the screen.
void DrawRenderTarget(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
void DrawSprite(const RenderTarget& render_target, const Vector2& position, float rad_rotation = 0, const Vector2& origin = Vector2(0 , 0),
const Vector2& scale = Vector2(1, 1), const Color4& color = Colors::White, Direction inversion = Direction::None);
/// Draws a sprite to the screen by passing a G̶L̶u̶i̶n̶t̶ JGL Texture that represents a handle to a loaded texture.
/// @param texture
/// @param position

View File

@@ -17,7 +17,7 @@ namespace JGL {
return (u8)a & (u8)b;
}
static std::string to_string(JGL::Direction direction) {
static std::string to_string(const JGL::Direction& direction) {
switch (direction) {
case JGL::Direction::None:
return "None";
@@ -33,4 +33,40 @@ namespace JGL {
return "Unknown";
}
}
enum class MSAA_SAMPLE_RATE : u8 {
MSAA_NONE = 0,
MSAA_2X = 1,
MSAA_4X = 2,
MSAA_8X = 3
};
static std::string to_string(const JGL::MSAA_SAMPLE_RATE& sample_rate) {
switch (sample_rate) {
case MSAA_SAMPLE_RATE::MSAA_NONE:
return "No MSAA";
case MSAA_SAMPLE_RATE::MSAA_2X:
return "MSAA 2x";
case MSAA_SAMPLE_RATE::MSAA_4X:
return "MSAA 4x";
case MSAA_SAMPLE_RATE::MSAA_8X:
return "MSAA 8x";
default:
return "Unknown";
}
}
static int to_int(const JGL::MSAA_SAMPLE_RATE& sample_rate) {
switch (sample_rate) {
case MSAA_SAMPLE_RATE::MSAA_NONE:
return 0;
case MSAA_SAMPLE_RATE::MSAA_2X:
return 2;
case MSAA_SAMPLE_RATE::MSAA_4X:
return 4;
case MSAA_SAMPLE_RATE::MSAA_8X:
return 8;
default:
return 0;
}
}
}

View File

@@ -3,12 +3,14 @@
#include <JGL/types/Texture.h>
#include <Color4.hpp>
#include <Colors.hpp>
#include <JGL/types/Enums.h>
namespace JGL {
class RenderTarget;
}
//TODO copy constructor for this. Copying this as it is and then that copy going out of scope will crash the program as it sits.
//If you do copy it you're doing it wrong. But still.
class JGL::RenderTarget {
private:
@@ -20,19 +22,30 @@ private:
GLuint framebuffer_object = 0;
GLuint depth_buffer = 0;
Texture* texture = nullptr;
MSAA_SAMPLE_RATE msaa_sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE;
GLuint msaa_framebuffer_object = 0;
GLuint msaa_depth_buffer = 0;
GLuint msaa_render_buffer = 0;
void Erase();
public:
static GLuint GetActiveGLFramebufferHandle();
static void SetActiveGLRenderTarget(const RenderTarget& render_target);
/** Change the size of the renderable area of the Render Target.
* If the new size is larger than the non-renderable area the texture will be resized to fit. */
/** Change the size of the renderable area of the Render Target. **/
/// @param new_size new size in px.
/// @param clear whether or not the data currently in the RenderTarget is to be kept.
/// @note Clearing the FBO on Resize is *much* faster than keeping the data because we don't have to read back.
void Resize(const Vector2& new_size);
void SetMSAAEnabled(MSAA_SAMPLE_RATE sample_rate);
/// Blits the MSAA FBO onto the regular FBO if MSAA is enabled.
/// Does nothing is MSAA is not enabled or if the color attachment is a texture not created by the RenderTarget.
void Blit() const;
[[nodiscard]] bool TextureCreatedByRenderTarget() const;
public:
[[nodiscard]] Vector2 GetDimensions() const;
[[nodiscard]] MSAA_SAMPLE_RATE GetMSAASampleRate() const;
/// Returns whether or not MSAA is enabled, If it is and you're not using J2D || J3D Begin / End,
/// You need to run "Blit()" after rendering to your FBO before you show it.
/// @note Also, If the texture wasn't made by the RenderTarget you don't want this. It would destroy the texture.
[[nodiscard]] bool MSAAEnabled() const;
[[nodiscard]] Texture* GetJGLTexture() const;
[[nodiscard]] GLuint GetGLTextureHandle() const;
[[nodiscard]] GLuint GetGLFramebufferObjectHandle() const;
@@ -44,6 +57,6 @@ public:
/// Create a render target for a texture that already exists. For adding to an existing texture.
explicit RenderTarget(Texture* texture, const Color4& clear_color = Colors::Black);
/// Create a Render Target with a brand new texture. Want to render JGL elements onto a texture and display it as a sprite?
explicit RenderTarget(const Vector2& size, const Color4& clear_color = Colors::Black, bool use_depth = false);
explicit RenderTarget(const Vector2& size, const Color4& clear_color = Colors::Black, bool use_depth = false, MSAA_SAMPLE_RATE sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE);
~RenderTarget();
};

View File

@@ -37,7 +37,7 @@ namespace JGL {
void load(SoftwareTexture* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
public:
/// Load a texture from a file,
Texture(const std::string& file, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE, const TextureFlag& flags = TextureFlag::INVERT_Y);
explicit Texture(const std::string& file, TextureFilteringMode filtering_mode = TextureFilteringMode::BILINEAR, TextureWrappingMode wrapping_mode = TextureWrappingMode::CLAMP_TO_EDGE, const TextureFlag& flags = TextureFlag::INVERT_Y);
Texture(SoftwareTexture* software_texture, const Vector2& size, const TextureFormat& format, TextureFilteringMode filtering_mode, TextureWrappingMode wrapping_mode);
/* Initialize a texture filled with trash data
this is primarily for the RenderTarget */

View File

@@ -118,14 +118,14 @@ public:
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
image = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::BILINEAR, JGL::TextureWrappingMode::CLAMP_TO_EDGE);
j2d_render_target = new RenderTarget({540, 540}, {0,0,0,0});
image2 = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::NEAREST);
image = new Texture("assets/sprites/Re3D.png", TextureFilteringMode::BILINEAR);
j2d_render_target = new RenderTarget({540, 540}, {0,0,0,0}, false, MSAA_SAMPLE_RATE::MSAA_8X);
image2 = new Texture("assets/sprites/Re3D.png",TextureFilteringMode::BILINEAR);
image2_render_target = new RenderTarget(image2);
J2D::Begin(image2_render_target);
J2D::FillRect(Colors::Red, {0,0}, {4,4});
J2D::DrawString(Colors::Red, "TEST", 0, 16, 1, 16, Jupiteroid);
J2D::DrawString(Colors::Red, "TEST", 0, 16, 1, 16, FreeSans);
J2D::End();
}
@@ -166,7 +166,7 @@ public:
J2D::Begin(j2d_render_target, true);
J2D::FillRect(Colors::Blue, {0,52}, {100,100});
J2D::DrawSprite(*image2, {300, 300}, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawSprite(*image2, {300, 300}, 0, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawMirrorSprite(*image, {400, 300}, Direction::Horizontal | Direction::Vertical, sprite_radians, {0.5,0.5}, {1, 1}, Colors::White);
J2D::DrawPartialSprite(*image, {225, 300}, image->GetDimensions() * 0.25, image->GetDimensions() * 0.75, sprite_radians, {0.5, 0.5}, {1,1}, Colors::White);
J2D::FillRect(Colors::Pinks::HotPink, {68, 120}, {32, 32});
@@ -204,8 +204,8 @@ public:
//Draw the Render Target that we just drew all that stuff onto.
J2D::Begin();
J2D::DrawRenderTargetAsSprite(*j2d_render_target, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawRenderTargetAsSprite(*image2_render_target, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawSprite(*j2d_render_target, {0, 0}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::DrawSprite(*image2_render_target, {300, 500}, 0, {0.5, 0.5}, {1,1}, Colors::White);
J2D::End();
}
@@ -253,7 +253,7 @@ int main(int argc, char** argv) {
window->Open();
window->initGL();
window->setResizable(true);
window->setVsyncEnabled(true);
window->setVsyncEnabled(false);
while (window->isAlive()) {
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();

View File

@@ -127,7 +127,7 @@ namespace JGL {
if (!inJ3D)
inJ2D = true;
else { Logger::Error("Attempt to Begin J2D inside of J3D context."); }
else { Logger::Error("Beginning J2D context inside of J3D context?"); }
if (rt != nullptr && clear_buffers) {
glClearColor(rt->GetClearColor().R(), rt->GetClearColor().G(), rt->GetClearColor().B(), rt->GetClearColor().A());
@@ -170,6 +170,7 @@ namespace JGL {
glColor4fv(oldColor);
if (render_target != nullptr) {
render_target->Blit();
render_target = nullptr;
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
@@ -299,8 +300,7 @@ namespace JGL {
J2D::FillCircle(color, {pos.x + size.x - radius, pos.y + size.y - radius}, radius, subdivisions);
}
void
J2D::DrawRenderTargetAsSprite(const JGL::RenderTarget& rt, const Vector2& position, float rad_rotation, const Vector2& origin,
void J2D::DrawSprite(const JGL::RenderTarget& rt, const Vector2& position, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
//Correct for the render-target being upside-down.
@@ -319,9 +319,13 @@ namespace JGL {
}
//Change the blending mode such that the alpha doesn't get multiplied again.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
J2D::DrawPartialSprite(*rt.GetJGLTexture(), position, {0, 0}, rt.GetDimensions(), rad_rotation, origin, scale, color, d);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (rt.TextureCreatedByRenderTarget())
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
J2D::DrawPartialSprite(*rt.GetJGLTexture(), position, {0, 0}, rt.GetDimensions(), rad_rotation, origin, scale, color, d);
if (rt.TextureCreatedByRenderTarget())
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void J2D::DrawSprite(const Texture& texture, const Vector2& pos, float rad_rotation, const Vector2& origin,
const Vector2& scale, const Color4& color, Direction inversion) {
@@ -769,7 +773,7 @@ namespace JGL {
if (!inJ2D)
inJ3D = true;
else
Logger::Error("Can't begin J3D context inside J2D context.");
Logger::Error("Beginning J3D context inside of J2D context?");
}
void J3D::End() {

View File

@@ -1,6 +1,6 @@
#include <JGL/types/RenderTarget.h>
#include <ReTexture/Texture.h>
#include <jlog/Logger.hpp>
#include <JGL/logger/logger.h>
#include <stdexcept>
JGL::Texture* JGL::RenderTarget::GetJGLTexture() const {
@@ -26,7 +26,7 @@ GLuint JGL::RenderTarget::GetActiveGLFramebufferHandle() {
}
void JGL::RenderTarget::SetActiveGLRenderTarget(const RenderTarget& render_target) {
glBindFramebuffer(GL_FRAMEBUFFER, render_target.GetGLFramebufferObjectHandle());
glBindFramebuffer(GL_FRAMEBUFFER, render_target.MSAAEnabled() ? render_target.msaa_framebuffer_object : render_target.GetGLFramebufferObjectHandle());
glViewport(0,0, render_target.GetDimensions().x, render_target.GetDimensions().y);
}
@@ -36,12 +36,15 @@ Vector2 JGL::RenderTarget::GetDimensions() const {
void JGL::RenderTarget::Erase() {
if (GetActiveGLFramebufferHandle() == framebuffer_object)
jlog::Warning("Deleting the framebuffer that's currently in use?");
Logger::Warning("Deleting the framebuffer that's currently in use?");
if (using_depth)
glDeleteRenderbuffers(1, &depth_buffer);
glDeleteFramebuffers(1, &framebuffer_object);
if (MSAAEnabled())
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
}
Color4 JGL::RenderTarget::GetClearColor() const {
@@ -70,7 +73,7 @@ JGL::RenderTarget::RenderTarget(JGL::Texture* texture, const Color4& clear_color
texture_created_by_us = false;
}
JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color, bool use_depth) {
JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color, bool use_depth, MSAA_SAMPLE_RATE sample_rate) {
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLint viewport[4] = {0, 0, 0, 0};
glGetIntegerv(GL_VIEWPORT, viewport);
@@ -111,6 +114,9 @@ JGL::RenderTarget::RenderTarget(const Vector2& size, const Color4& clear_color,
this->size = size;
texture_created_by_us = true;
this->texture->SetFlags(INVERT_Y);
if (sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE)
SetMSAAEnabled(sample_rate);
}
std::vector<GLfloat> JGL::RenderTarget::GetData() const {
@@ -125,6 +131,9 @@ std::vector<GLfloat> JGL::RenderTarget::GetData() const {
}
void JGL::RenderTarget::Resize(const Vector2& new_size) {
if (!texture_created_by_us)
Logger::Fatal("Resizing a texture that already existed?");
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLfloat old_clear_color[4];
GLint old_viewport[4] = {0, 0, 0, 0};
@@ -182,3 +191,94 @@ JGL::RenderTarget::~RenderTarget() {
if (texture_created_by_us)
delete texture;
}
bool JGL::RenderTarget::TextureCreatedByRenderTarget() const {
return texture_created_by_us;
}
JGL::MSAA_SAMPLE_RATE JGL::RenderTarget::GetMSAASampleRate() const {
return msaa_sample_rate;
}
bool JGL::RenderTarget::MSAAEnabled() const {
return msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE;
}
void JGL::RenderTarget::SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE sample_rate) {
// If we'd be setting the same sample_rate we already have.
if (sample_rate == msaa_sample_rate)
return;
// If we'd be rendering onto a texture and not a plain render target we don't want this.
if (!TextureCreatedByRenderTarget())
return;
// Remove it if they request no msaa or if what they requested is different than what they already have.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE || msaa_sample_rate != MSAA_SAMPLE_RATE::MSAA_NONE) {
if(using_depth)
glDeleteRenderbuffers(1, &msaa_depth_buffer);
glDeleteRenderbuffers(1, &msaa_render_buffer);
glDeleteFramebuffers(1, &msaa_framebuffer_object);
msaa_framebuffer_object = 0;
msaa_depth_buffer = 0;
msaa_render_buffer = 0;
msaa_sample_rate = MSAA_SAMPLE_RATE::MSAA_NONE;
// Only return here if they specifically requested no MSAA. else continue to change mode.
if (sample_rate == MSAA_SAMPLE_RATE::MSAA_NONE)
return;
}
GLuint current_fbo = GetActiveGLFramebufferHandle();
glGenFramebuffers(1, &msaa_framebuffer_object);
glBindFramebuffer(GL_FRAMEBUFFER, msaa_framebuffer_object);
GLint current_renderbuffer = 0;
glGetIntegerv(GL_RENDERBUFFER_BINDING, &current_renderbuffer);
glGenRenderbuffers(1, &msaa_render_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, msaa_render_buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, JGL::to_int(sample_rate), GL_RGBA, size.x, size.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaa_render_buffer);
if (using_depth) {
glGenRenderbuffers(1, &msaa_depth_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, msaa_depth_buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, JGL::to_int(sample_rate), GL_DEPTH_COMPONENT, size.x, size.y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, msaa_depth_buffer);
}
bool failure = false;
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
failure = true,
Logger::Fatal("A new MSAA " + std::to_string(to_int(sample_rate)) + "x framebuffer couldn't be allocated.");
glBindRenderbuffer(GL_RENDERBUFFER, current_renderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
msaa_sample_rate = sample_rate;
if (failure)
SetMSAAEnabled(MSAA_SAMPLE_RATE::MSAA_NONE);
}
void JGL::RenderTarget::Blit() const {
if (!MSAAEnabled() || !TextureCreatedByRenderTarget())
return;
// Save the GL state.
GLuint current_fbo = GetActiveGLFramebufferHandle();
GLint current_draw_fbo = 0;
GLint current_read_fbo = 0;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &current_read_fbo);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_draw_fbo);
// Draw the contents of one into the other.
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaa_framebuffer_object);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_object);
glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Put the GL state back.
glBindFramebuffer(GL_READ_FRAMEBUFFER, current_read_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_draw_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
}