diff --git a/Vivid2D/Vivid2D.cpp b/Vivid2D/Vivid2D.cpp new file mode 100644 index 0000000..151a96e --- /dev/null +++ b/Vivid2D/Vivid2D.cpp @@ -0,0 +1,37 @@ +// Vivid2D.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 +// + +// 包含GLM主头文件 +#include +// 包含vec2定义 +#include +#include + +int main() { + // 创建一个二维浮点向量 (Vector2f) + glm::vec2 position(100.0f, 200.0f); + + // 创建另一个向量代表速度 + glm::vec2 velocity(5.0f, -2.0f); + + // 向量加法,更新位置 + position += velocity; + + // 输出新的位置 + // GLM的向量可以直接用std::cout输出,但需要包含 + // 这里为了简单,我们手动输出 + std::cout << "New Position: (" << position.x << ", " << position.y << ")" << std::endl; + + return 0; +} + +// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单 +// 调试程序: F5 或调试 >“开始调试”菜单 + +// 入门使用技巧: +// 1. 使用解决方案资源管理器窗口添加/管理文件 +// 2. 使用团队资源管理器窗口连接到源代码管理 +// 3. 使用输出窗口查看生成输出和其他消息 +// 4. 使用错误列表窗口查看错误 +// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目 +// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件 diff --git a/Vivid2D/Vivid2D.vcxproj b/Vivid2D/Vivid2D.vcxproj new file mode 100644 index 0000000..40e49cf --- /dev/null +++ b/Vivid2D/Vivid2D.vcxproj @@ -0,0 +1,134 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {4b88f09b-523c-4678-adcc-e1421a7dc39e} + Vivid2D + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + \ No newline at end of file diff --git a/Vivid2D/Vivid2D.vcxproj.filters b/Vivid2D/Vivid2D.vcxproj.filters new file mode 100644 index 0000000..8c40458 --- /dev/null +++ b/Vivid2D/Vivid2D.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 源文件 + + + \ No newline at end of file diff --git a/Vivid2DRenderer.sln b/Vivid2DRenderer.sln new file mode 100644 index 0000000..ced1df4 --- /dev/null +++ b/Vivid2DRenderer.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35027.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Vivid2DRenderer", "Vivid2DRenderer\Vivid2DRenderer.vcxproj", "{CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Vivid2D", "Vivid2D\Vivid2D.vcxproj", "{4B88F09B-523C-4678-ADCC-E1421A7DC39E}" + ProjectSection(ProjectDependencies) = postProject + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68} = {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Debug|x64.ActiveCfg = Debug|x64 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Debug|x64.Build.0 = Debug|x64 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Debug|x86.ActiveCfg = Debug|Win32 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Debug|x86.Build.0 = Debug|Win32 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Release|x64.ActiveCfg = Release|x64 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Release|x64.Build.0 = Release|x64 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Release|x86.ActiveCfg = Release|Win32 + {CDFA4C34-767D-49C0-AFE1-EE6C49A9DA68}.Release|x86.Build.0 = Release|Win32 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Debug|x64.ActiveCfg = Debug|x64 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Debug|x64.Build.0 = Debug|x64 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Debug|x86.ActiveCfg = Debug|Win32 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Debug|x86.Build.0 = Debug|Win32 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Release|x64.ActiveCfg = Release|x64 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Release|x64.Build.0 = Release|x64 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Release|x86.ActiveCfg = Release|Win32 + {4B88F09B-523C-4678-ADCC-E1421A7DC39E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CD348B38-5FCC-4BCC-9826-2060B6862A5C} + EndGlobalSection +EndGlobal diff --git a/Vivid2DRenderer/Vivid2DRenderer.vcxproj b/Vivid2DRenderer/Vivid2DRenderer.vcxproj new file mode 100644 index 0000000..58fecc4 --- /dev/null +++ b/Vivid2DRenderer/Vivid2DRenderer.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {cdfa4c34-767d-49c0-afe1-ee6c49a9da68} + Vivid2DRenderer + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;VIVID2DRENDERER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;VIVID2DRENDERER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;VIVID2DRENDERER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;VIVID2DRENDERER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + /utf-8 %(AdditionalOptions) + stdcpp20 + 4828 + + + Windows + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters b/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters new file mode 100644 index 0000000..20a379b --- /dev/null +++ b/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters @@ -0,0 +1,93 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + + + \ No newline at end of file diff --git a/Vivid2DRenderer/dllmain.cpp b/Vivid2DRenderer/dllmain.cpp new file mode 100644 index 0000000..daed8c8 --- /dev/null +++ b/Vivid2DRenderer/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : 定义 DLL 应用程序的入口点。 +#include "pch.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/Vivid2DRenderer/framework.h b/Vivid2DRenderer/framework.h new file mode 100644 index 0000000..80cbbc9 --- /dev/null +++ b/Vivid2DRenderer/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容 +// Windows 头文件 +#include diff --git a/Vivid2DRenderer/pch.cpp b/Vivid2DRenderer/pch.cpp new file mode 100644 index 0000000..b6fb8f4 --- /dev/null +++ b/Vivid2DRenderer/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: 与预编译标头对应的源文件 + +#include "pch.h" + +// 当使用预编译的头时,需要使用此源文件,编译才能成功。 diff --git a/Vivid2DRenderer/pch.h b/Vivid2DRenderer/pch.h new file mode 100644 index 0000000..9660927 --- /dev/null +++ b/Vivid2DRenderer/pch.h @@ -0,0 +1,13 @@ +// pch.h: 这是预编译标头文件。 +// 下方列出的文件仅编译一次,提高了将来生成的生成性能。 +// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。 +// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。 +// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。 + +#ifndef PCH_H +#define PCH_H + +// 添加要在此处预编译的标头 +#include "framework.h" + +#endif //PCH_H diff --git a/Vivid2DRenderer/systems/RenderSystem.cpp b/Vivid2DRenderer/systems/RenderSystem.cpp new file mode 100644 index 0000000..a7131f8 --- /dev/null +++ b/Vivid2DRenderer/systems/RenderSystem.cpp @@ -0,0 +1,474 @@ +#include "pch.h" +#include "RenderSystem.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// ================== ̬Աʼ ================== +std::thread::id RenderSystem::s_RenderThreadId; +std::atomic RenderSystem::s_IsInInit = false; +std::queue> RenderSystem::s_RenderQueue; +std::mutex RenderSystem::s_RenderQueueMutex; +std::atomic RenderSystem::s_IsReplayingQueue = false; +std::stack RenderSystem::s_StateStack; +int RenderSystem::s_ViewportWidth = 800; +int RenderSystem::s_ViewportHeight = 600; +glm::vec4 RenderSystem::s_ClearColorValue = { 0.0f, 0.0f, 0.0f, 1.0f }; +std::shared_ptr RenderSystem::s_Logger; + +// ================== ־ϵͳ ================== +void RenderSystem::InitializeLogging() { + std::vector sinks; + + auto console_sink = std::make_shared(); + console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [thread %t] %v"); + sinks.push_back(console_sink); + + try { + auto file_sink = std::make_shared("logs/Vivid2D.log", true); + file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [thread %t] %v"); + sinks.push_back(file_sink); + } + catch (const spdlog::spdlog_ex& ex) { + std::cerr << "Log file creation failed: " << ex.what() << std::endl; + } + + s_Logger = std::make_shared("RenderSystem", begin(sinks), end(sinks)); + spdlog::register_logger(s_Logger); + s_Logger->set_level(spdlog::level::trace); + s_Logger->flush_on(spdlog::level::trace); + + s_Logger->info("Logging system initialized."); +} + +void RenderSystem::ShutdownLogging() { + s_Logger->info("Logging system shutting down."); + s_Logger.reset(); + spdlog::shutdown(); +} + +// ================== ʼ̹߳ ================== +void RenderSystem::initRenderThread() { + if (s_RenderThreadId == std::thread::id()) { + s_RenderThreadId = std::this_thread::get_id(); + s_Logger->info("Render thread initialized on thread ID: {}", std::hash{}(s_RenderThreadId)); + } + else if (s_RenderThreadId != std::this_thread::get_id()) { + s_Logger->critical("Render thread already initialized by another thread!"); + throw std::runtime_error("Render thread already initialized by another thread"); + } +} +void RenderSystem::beginInitialization() { s_IsInInit = true; } +void RenderSystem::finishInitialization() { + s_IsInInit = false; + if (getQueueSize() > 0) { replayQueue(); } +} +bool RenderSystem::isOnRenderThread() { return std::this_thread::get_id() == s_RenderThreadId; } +bool RenderSystem::isInInitPhase() { return s_IsInInit; } +void RenderSystem::assertOnRenderThread() { if (!isOnRenderThread()) { throw std::runtime_error("RenderSystem called from wrong thread!"); } } +void RenderSystem::assertOnRenderThreadOrInit() { if (!isInInitPhase() && !isOnRenderThread()) { throw std::runtime_error("RenderSystem called from wrong thread!"); } } + +// ================== Ⱦ ================== +void RenderSystem::recordRenderCall(std::function&& renderCall) { + std::lock_guard lock(s_RenderQueueMutex); + s_RenderQueue.push(std::move(renderCall)); +} +void RenderSystem::replayQueue() { + s_IsReplayingQueue = true; + std::queue> localQueue; + { + std::lock_guard lock(s_RenderQueueMutex); + s_RenderQueue.swap(localQueue); + } + while (!localQueue.empty()) { + auto& call = localQueue.front(); + if (call) { call(); } + localQueue.pop(); + } + s_IsReplayingQueue = false; +} + +// ================== OpenGL ״̬װ ================== +#define RENDER_SYSTEM_QUEUE_CALL(FunctionName, ...) \ + if (!isOnRenderThread()) { \ + recordRenderCall([=] { FunctionName(__VA_ARGS__); }); \ + } else { \ + FunctionName(__VA_ARGS__); \ + } + +#define RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(FunctionName) \ + if (!isOnRenderThread()) { \ + recordRenderCall([=] { FunctionName(); }); \ + } else { \ + FunctionName(); \ + } + +void RenderSystem::enable(GLenum capability) { RENDER_SYSTEM_QUEUE_CALL(_enable, capability); } +void RenderSystem::_enable(GLenum c) { assertOnRenderThread(); glEnable(c); } + +void RenderSystem::disable(GLenum capability) { RENDER_SYSTEM_QUEUE_CALL(_disable, capability); } +void RenderSystem::_disable(GLenum c) { assertOnRenderThread(); glDisable(c); } + +void RenderSystem::pushState() { RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(_pushState); } +void RenderSystem::_pushState() { + assertOnRenderThread(); + _checkGLError("before pushState"); + s_StateStack.push(RenderState()); + _checkGLError("after pushState"); +} + +void RenderSystem::popState() { RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(_popState); } +void RenderSystem::_popState() { + assertOnRenderThread(); + if (!s_StateStack.empty()) { + s_StateStack.top().restore(); + s_StateStack.pop(); + _checkGLError("after popState"); + } + else { + s_Logger->warn("popState called with empty state stack."); + } +} +int RenderSystem::getStateStackSize() { return s_StateStack.size(); } + +void RenderSystem::clearColor(float r, float g, float b, float a) { RENDER_SYSTEM_QUEUE_CALL(_clearColor, r, g, b, a); } +void RenderSystem::_clearColor(float r, float g, float b, float a) { + assertOnRenderThread(); + glClearColor(r, g, b, a); + s_ClearColorValue = { r, g, b, a }; +} + +void RenderSystem::clear(GLbitfield mask) { RENDER_SYSTEM_QUEUE_CALL(_clear, mask); } +void RenderSystem::_clear(GLbitfield m) { assertOnRenderThread(); glClear(m); } + +void RenderSystem::viewport(int x, int y, int width, int height) { RENDER_SYSTEM_QUEUE_CALL(_viewport, x, y, width, height); } +void RenderSystem::_viewport(int x, int y, int w, int h) { + assertOnRenderThread(); + glViewport(x, y, w, h); + s_ViewportWidth = w; + s_ViewportHeight = h; +} + +// ================== VAO / VBO / EBO ================== +GLuint RenderSystem::GenVertexArrays() { assertOnRenderThread(); GLuint vao = 0; glGenVertexArrays(1, &vao); return vao; } +void RenderSystem::DeleteVertexArrays(GLuint vao) { RENDER_SYSTEM_QUEUE_CALL(_glDeleteVertexArrays, vao); } +void RenderSystem::_glDeleteVertexArrays(GLuint v) { assertOnRenderThread(); glDeleteVertexArrays(1, &v); } +void RenderSystem::BindVertexArray(GLuint vao) { RENDER_SYSTEM_QUEUE_CALL(_glBindVertexArray, vao); } +void RenderSystem::_glBindVertexArray(GLuint v) { assertOnRenderThread(); glBindVertexArray(v); } +GLuint RenderSystem::GenBuffers() { assertOnRenderThread(); GLuint buf; glGenBuffers(1, &buf); return buf; } +void RenderSystem::DeleteBuffers(GLuint buffer) { RENDER_SYSTEM_QUEUE_CALL(_glDeleteBuffers, buffer); } +void RenderSystem::_glDeleteBuffers(GLuint b) { assertOnRenderThread(); glDeleteBuffers(1, &b); } +void RenderSystem::BindBuffer(GLenum target, GLuint buffer) { RENDER_SYSTEM_QUEUE_CALL(_glBindBuffer, target, buffer); } +void RenderSystem::_glBindBuffer(GLenum t, GLuint b) { assertOnRenderThread(); glBindBuffer(t, b); } +void RenderSystem::BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) { + assertOnRenderThread(); + glBufferData(target, size, data, usage); +} + +// [] _glBufferData Ķ +void RenderSystem::_glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) { + assertOnRenderThread(); + glBufferData(target, size, data, usage); +} + +// ================== Uniform ================== +void RenderSystem::uniform1i(GLint location, int value) { RENDER_SYSTEM_QUEUE_CALL(_uniform1i, location, value); } +void RenderSystem::_uniform1i(GLint l, int v) { assertOnRenderThread(); glUniform1i(l, v); } +void RenderSystem::uniform1f(GLint location, float value) { RENDER_SYSTEM_QUEUE_CALL(_uniform1f, location, value); } +void RenderSystem::_uniform1f(GLint l, float v) { assertOnRenderThread(); glUniform1f(l, v); } +void RenderSystem::uniform2f(GLint location, float x, float y) { RENDER_SYSTEM_QUEUE_CALL(_uniform2f, location, x, y); } +void RenderSystem::_uniform2f(GLint l, float x, float y) { assertOnRenderThread(); glUniform2f(l, x, y); } +void RenderSystem::uniform3f(GLint location, float x, float y, float z) { RENDER_SYSTEM_QUEUE_CALL(_uniform3f, location, x, y, z); } +void RenderSystem::_uniform3f(GLint l, float x, float y, float z) { assertOnRenderThread(); glUniform3f(l, x, y, z); } +void RenderSystem::uniform4f(GLint location, float x, float y, float z, float w) { RENDER_SYSTEM_QUEUE_CALL(_uniform4f, location, x, y, z, w); } +void RenderSystem::_uniform4f(GLint l, float x, float y, float z, float w) { assertOnRenderThread(); glUniform4f(l, x, y, z, w); } +void RenderSystem::uniformMatrix3fv(GLint l, GLsizei c, GLboolean t, const GLfloat* v) { assertOnRenderThread(); glUniformMatrix3fv(l, c, t, v); } +void RenderSystem::uniformMatrix4fv(GLint l, GLsizei c, GLboolean t, const GLfloat* v) { assertOnRenderThread(); glUniformMatrix4fv(l, c, t, v); } + +// [] _uniformMatrix3fv Ķ +void RenderSystem::_uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { + assertOnRenderThread(); + glUniformMatrix3fv(location, count, transpose, value); +} + +// [] _uniformMatrix4fv Ķ +void RenderSystem::_uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { + assertOnRenderThread(); + glUniformMatrix4fv(location, count, transpose, value); +} + +void RenderSystem::uniform2f(GLint location, const glm::vec2& vec) { uniform2f(location, vec.x, vec.y); } +void RenderSystem::uniform3f(GLint location, const glm::vec3& vec) { uniform3f(location, vec.x, vec.y, vec.z); } +void RenderSystem::uniform4f(GLint location, const glm::vec4& vec) { uniform4f(location, vec.x, vec.y, vec.z, vec.w); } +void RenderSystem::uniformMatrix3(GLint location, const glm::mat3& matrix, bool transpose) { + if (!isOnRenderThread()) { recordRenderCall([=] { _uniformMatrix3fv(location, 1, transpose, glm::value_ptr(matrix)); }); } + else { _uniformMatrix3fv(location, 1, transpose, glm::value_ptr(matrix)); } +} +void RenderSystem::uniformMatrix4(GLint location, const glm::mat4& matrix, bool transpose) { + if (!isOnRenderThread()) { recordRenderCall([=] { _uniformMatrix4fv(location, 1, transpose, glm::value_ptr(matrix)); }); } + else { _uniformMatrix4fv(location, 1, transpose, glm::value_ptr(matrix)); } +} +GLint RenderSystem::getUniformLocation(GLuint program, const std::string& name) { assertOnRenderThread(); return glGetUniformLocation(program, name.c_str()); } + +// ================== ================== +void RenderSystem::lineWidth(float width) { RENDER_SYSTEM_QUEUE_CALL(_lineWidth, width); } +void RenderSystem::_lineWidth(float w) { assertOnRenderThread(); glLineWidth(w); } +void RenderSystem::drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { RENDER_SYSTEM_QUEUE_CALL(_drawElements, mode, count, type, indices); } +void RenderSystem::_drawElements(GLenum m, GLsizei c, GLenum t, const void* i) { assertOnRenderThread(); glDrawElements(m, c, t, i); } +void RenderSystem::drawArrays(GLenum mode, GLint first, GLsizei count) { RENDER_SYSTEM_QUEUE_CALL(_drawArrays, mode, first, count); } +void RenderSystem::_drawArrays(GLenum m, GLint f, GLsizei c) { assertOnRenderThread(); glDrawArrays(m, f, c); } + +// ================== ģʽ ================== +void RenderSystem::enableBlend() { RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(_enableBlend); } +void RenderSystem::_enableBlend() { assertOnRenderThread(); glEnable(GL_BLEND); } +void RenderSystem::disableBlend() { RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(_disableBlend); } +void RenderSystem::_disableBlend() { assertOnRenderThread(); glDisable(GL_BLEND); } +void RenderSystem::blendFunc(GLenum sfactor, GLenum dfactor) { RENDER_SYSTEM_QUEUE_CALL(_blendFunc, sfactor, dfactor); } +void RenderSystem::_blendFunc(GLenum s, GLenum d) { assertOnRenderThread(); glBlendFunc(s, d); } +void RenderSystem::defaultBlendFunc() { blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } + +// ================== ɫ ================== +GLuint RenderSystem::createProgram() { assertOnRenderThread(); return glCreateProgram(); } +GLint RenderSystem::getCurrentProgram() { assertOnRenderThread(); GLint p; glGetIntegerv(GL_CURRENT_PROGRAM, &p); return p; } +void RenderSystem::attachShader(GLuint p, GLuint s) { RENDER_SYSTEM_QUEUE_CALL(_attachShader, p, s); } +void RenderSystem::_attachShader(GLuint p, GLuint s) { assertOnRenderThread(); glAttachShader(p, s); } +void RenderSystem::linkProgram(GLuint p) { RENDER_SYSTEM_QUEUE_CALL(_linkProgram, p); } +void RenderSystem::_linkProgram(GLuint p) { assertOnRenderThread(); glLinkProgram(p); } +GLint RenderSystem::getProgrami(GLuint p, GLenum n) { assertOnRenderThread(); GLint i; glGetProgramiv(p, n, &i); return i; } +std::string RenderSystem::getProgramInfoLog(GLuint program) { + assertOnRenderThread(); + GLint len = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len); + if (len > 1) { + std::vector log(len); + glGetProgramInfoLog(program, len, nullptr, log.data()); + return std::string(log.begin(), log.end()); + } + return ""; +} +void RenderSystem::detachShader(GLuint p, GLuint s) { RENDER_SYSTEM_QUEUE_CALL(_detachShader, p, s); } +void RenderSystem::_detachShader(GLuint p, GLuint s) { assertOnRenderThread(); glDetachShader(p, s); } +void RenderSystem::deleteProgram(GLuint p) { RENDER_SYSTEM_QUEUE_CALL(_deleteProgram, p); } +void RenderSystem::_deleteProgram(GLuint p) { assertOnRenderThread(); glDeleteProgram(p); } +GLuint RenderSystem::linkProgram(GLuint vertexShader, GLuint fragmentShader) { + assertOnRenderThread(); + GLuint program = createProgram(); + _attachShader(program, vertexShader); + _attachShader(program, fragmentShader); + _linkProgram(program); + if (getProgrami(program, GL_LINK_STATUS) != GL_TRUE) { + std::string log = getProgramInfoLog(program); + s_Logger->error("Program link failed: {}", log); + _deleteProgram(program); + throw std::runtime_error("Program link failed: " + log); + } + _detachShader(program, vertexShader); + _detachShader(program, fragmentShader); + return program; +} +GLuint RenderSystem::linkProgram(GLuint v, GLuint g, GLuint f) { + assertOnRenderThread(); + GLuint p = createProgram(); + _attachShader(p, v); + if (g != 0) _attachShader(p, g); + _attachShader(p, f); + _linkProgram(p); + if (getProgrami(p, GL_LINK_STATUS) != GL_TRUE) { + std::string log = getProgramInfoLog(p); + s_Logger->error("Program link failed: {}", log); + _deleteProgram(p); + throw std::runtime_error("Program link failed: " + log); + } + _detachShader(p, v); + if (g != 0) _detachShader(p, g); + _detachShader(p, f); + return p; +} +void RenderSystem::useProgram(GLuint p) { RENDER_SYSTEM_QUEUE_CALL(_useProgram, p); } +void RenderSystem::_useProgram(GLuint p) { assertOnRenderThread(); glUseProgram(p); } + +// ================== ɫ ================== +GLuint RenderSystem::createShader(GLenum type) { assertOnRenderThread(); return glCreateShader(type); } +void RenderSystem::shaderSource(GLuint shader, const std::string& source) { + if (!isOnRenderThread()) { recordRenderCall([=] { _shaderSource(shader, source); }); } + else { _shaderSource(shader, source); } +} +void RenderSystem::_shaderSource(GLuint s, const std::string& src) { + assertOnRenderThread(); + const char* c_str = src.c_str(); + glShaderSource(s, 1, &c_str, nullptr); +} +void RenderSystem::compileShader(GLuint s) { RENDER_SYSTEM_QUEUE_CALL(_compileShader, s); } +void RenderSystem::_compileShader(GLuint s) { assertOnRenderThread(); glCompileShader(s); } +GLint RenderSystem::getShaderi(GLuint s, GLenum p) { assertOnRenderThread(); GLint i; glGetShaderiv(s, p, &i); return i; } +std::string RenderSystem::getShaderInfoLog(GLuint shader) { + assertOnRenderThread(); + GLint len = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); + if (len > 1) { + std::vector log(len); + glGetShaderInfoLog(shader, len, nullptr, log.data()); + return std::string(log.begin(), log.end()); + } + return ""; +} +void RenderSystem::deleteShader(GLuint s) { RENDER_SYSTEM_QUEUE_CALL(_deleteShader, s); } +void RenderSystem::_deleteShader(GLuint s) { assertOnRenderThread(); glDeleteShader(s); } +GLuint RenderSystem::compileShader(GLenum type, const std::string& source) { + assertOnRenderThread(); + GLuint shader = createShader(type); + _shaderSource(shader, source); + _compileShader(shader); + if (getShaderi(shader, GL_COMPILE_STATUS) != GL_TRUE) { + std::string log = getShaderInfoLog(shader); + s_Logger->error("Shader compilation failed: {}", log); + _deleteShader(shader); + throw std::runtime_error("Shader compilation failed: " + log); + } + return shader; +} + +// ================== Ȳ ================== +void RenderSystem::depthFunc(GLenum func) { RENDER_SYSTEM_QUEUE_CALL(_depthFunc, func); } +void RenderSystem::_depthFunc(GLenum f) { assertOnRenderThread(); glDepthFunc(f); } +void RenderSystem::depthMask(bool flag) { RENDER_SYSTEM_QUEUE_CALL(_depthMask, flag); } +void RenderSystem::_depthMask(bool f) { assertOnRenderThread(); glDepthMask(f ? GL_TRUE : GL_FALSE); } +void RenderSystem::clearDepth(double depth) { RENDER_SYSTEM_QUEUE_CALL(_clearDepth, depth); } +void RenderSystem::_clearDepth(double d) { assertOnRenderThread(); glClearDepth(d); } +void RenderSystem::enableDepthTest() { RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(_enableDepthTest); } +void RenderSystem::_enableDepthTest() { assertOnRenderThread(); glEnable(GL_DEPTH_TEST); } +void RenderSystem::disableDepthTest() { RENDER_SYSTEM_QUEUE_CALL_NO_ARGS(_disableDepthTest); } +void RenderSystem::_disableDepthTest() { assertOnRenderThread(); glDisable(GL_DEPTH_TEST); } + +// ================== ================== +void RenderSystem::bindTexture(GLuint texture) { RENDER_SYSTEM_QUEUE_CALL(_bindTexture, texture); } +void RenderSystem::_bindTexture(GLuint t) { assertOnRenderThread(); glBindTexture(GL_TEXTURE_2D, t); } +void RenderSystem::getTexImage(GLenum t, GLint l, GLenum f, GLenum ty, void* p) { assertOnRenderThread(); glGetTexImage(t, l, f, ty, p); } + +// [] _getTexImage Ķ +void RenderSystem::_getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels) { + assertOnRenderThread(); + glGetTexImage(target, level, format, type, pixels); +} + +void RenderSystem::activeTexture(GLenum texture) { RENDER_SYSTEM_QUEUE_CALL(_activeTexture, texture); } +void RenderSystem::_activeTexture(GLenum t) { assertOnRenderThread(); glActiveTexture(t); } +GLuint RenderSystem::genTextures() { assertOnRenderThread(); GLuint t; glGenTextures(1, &t); return t; } +void RenderSystem::deleteTextures(GLuint texture) { RENDER_SYSTEM_QUEUE_CALL(_deleteTextures, texture); } +void RenderSystem::_deleteTextures(GLuint t) { assertOnRenderThread(); glDeleteTextures(1, &t); } +void RenderSystem::texImage2D(GLenum t, GLint l, GLint iF, GLsizei w, GLsizei h, GLint b, GLenum f, GLenum ty, const void* p) { assertOnRenderThread(); glTexImage2D(t, l, iF, w, h, b, f, ty, p); } + +// [] _texImage2D Ķ +void RenderSystem::_texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) { + assertOnRenderThread(); + glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels); +} + +void RenderSystem::texParameteri(GLenum target, GLenum pname, GLint param) { RENDER_SYSTEM_QUEUE_CALL(_texParameteri, target, pname, param); } +void RenderSystem::_texParameteri(GLenum t, GLenum p, GLint v) { assertOnRenderThread(); glTexParameteri(t, p, v); } +void RenderSystem::setTextureMinFilter(GLenum filter) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); } +void RenderSystem::setTextureMagFilter(GLenum filter) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); } +void RenderSystem::setTextureWrapS(GLenum wrap) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); } +void RenderSystem::setTextureWrapT(GLenum wrap) { texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); } +GLuint RenderSystem::createDefaultTexture() { + assertOnRenderThread(); + GLuint textureId = genTextures(); + _bindTexture(textureId); + unsigned char whitePixel[] = { 255, 255, 255, 255 }; + texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, whitePixel); + _texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + _texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + _bindTexture(0); + return textureId; +} + +// ================== ================== +void RenderSystem::enableVertexAttribArray(GLuint i) { RENDER_SYSTEM_QUEUE_CALL(_enableVertexAttribArray, i); } +void RenderSystem::_enableVertexAttribArray(GLuint i) { assertOnRenderThread(); glEnableVertexAttribArray(i); } +void RenderSystem::disableVertexAttribArray(GLuint i) { RENDER_SYSTEM_QUEUE_CALL(_disableVertexAttribArray, i); } +void RenderSystem::_disableVertexAttribArray(GLuint i) { assertOnRenderThread(); glDisableVertexAttribArray(i); } +void RenderSystem::vertexAttribPointer(GLuint i, GLint s, GLenum t, GLboolean n, GLsizei st, const void* p) { RENDER_SYSTEM_QUEUE_CALL(_vertexAttribPointer, i, s, t, n, st, p); } +void RenderSystem::_vertexAttribPointer(GLuint i, GLint s, GLenum t, GLboolean n, GLsizei st, const void* p) { assertOnRenderThread(); glVertexAttribPointer(i, s, t, n, st, p); } + +// ================== ߷ ================== +void RenderSystem::readPixels(int x, int y, int w, int h, GLenum f, GLenum t, void* p) { assertOnRenderThread(); glReadPixels(x, y, w, h, f, t, p); } + +// [] _readPixels Ķ +void RenderSystem::_readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels) { + assertOnRenderThread(); + glReadPixels(x, y, width, height, format, type, pixels); +} + +bool RenderSystem::isExtensionSupported(const std::string& ext) { + assertOnRenderThread(); + const char* extensions = (const char*)glGetString(GL_EXTENSIONS); + return strstr(extensions, ext.c_str()) != nullptr; +} +void RenderSystem::pixelStore(GLenum pname, GLint param) { RENDER_SYSTEM_QUEUE_CALL(_pixelStore, pname, param); } +void RenderSystem::_pixelStore(GLenum p, GLint v) { assertOnRenderThread(); glPixelStorei(p, v); } +std::string RenderSystem::getVendor() { assertOnRenderThread(); return (const char*)glGetString(GL_VENDOR); } +std::string RenderSystem::getRenderer() { assertOnRenderThread(); return (const char*)glGetString(GL_RENDERER); } +std::string RenderSystem::getOpenGLVersion() { assertOnRenderThread(); return (const char*)glGetString(GL_VERSION); } +std::string RenderSystem::getGLSLVersion() { assertOnRenderThread(); return (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); } +void RenderSystem::logDetailedGLInfo() { + assertOnRenderThread(); + s_Logger->info("=== OpenGL System Information ==="); + s_Logger->info("Vendor: {}", getVendor()); + s_Logger->info("Renderer: {}", getRenderer()); + s_Logger->info("OpenGL Version: {}", getOpenGLVersion()); + s_Logger->info("GLSL Version: {}", getGLSLVersion()); + GLint val; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &val); + s_Logger->info("Max Texture Size: {}", val); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &val); + s_Logger->info("Max Vertex Attributes: {}", val); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &val); + s_Logger->info("Max Texture Units: {}", val); +} +void RenderSystem::setupDefaultState() { + beginInitialization(); + clearColor(0.0f, 0.0f, 0.0f, 1.0f); + viewport(0, 0, s_ViewportWidth, s_ViewportHeight); + enableBlend(); + defaultBlendFunc(); + disableDepthTest(); + finishInitialization(); +} +void RenderSystem::checkGLError(const std::string& operation) { RENDER_SYSTEM_QUEUE_CALL(_checkGLError, operation); } +void RenderSystem::_checkGLError(const std::string& op) { + assertOnRenderThread(); + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + s_Logger->error("OpenGL error during [{}]: {} ({})", op, getGLErrorString(error), error); + } +} +std::string RenderSystem::getGLErrorString(GLenum error) { + switch (error) { + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; + default: return "Unknown (" + std::to_string(error) + ")"; + } +} + +// ================== ȡ״̬ ================== +int RenderSystem::getViewportWidth() { return s_ViewportWidth; } +int RenderSystem::getViewportHeight() { return s_ViewportHeight; } +glm::vec4 RenderSystem::getClearColor() { return s_ClearColorValue; } +int RenderSystem::getQueueSize() { + std::lock_guard lock(s_RenderQueueMutex); + return s_RenderQueue.size(); +} \ No newline at end of file diff --git a/Vivid2DRenderer/systems/RenderSystem.h b/Vivid2DRenderer/systems/RenderSystem.h new file mode 100644 index 0000000..fbc0975 --- /dev/null +++ b/Vivid2DRenderer/systems/RenderSystem.h @@ -0,0 +1,276 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// ǰ spdlog::logger Աͷļа +namespace spdlog { + class logger; +} + +class RenderSystem { +public: + RenderSystem() = delete; + ~RenderSystem() = delete; + RenderSystem(const RenderSystem&) = delete; + RenderSystem& operator=(const RenderSystem&) = delete; + + + // ================== ־ϵͳ ================== + /** + * @brief ʼ־ϵͳκ־֮ǰʼʱá + */ + static void InitializeLogging(); + + /** + * @brief ر־ϵͳӦʱȷ־ˢ¡ + */ + static void ShutdownLogging(); + + // ================== ʼ̹߳ ================== + static void initRenderThread(); + static void beginInitialization(); + static void finishInitialization(); + static bool isOnRenderThread(); + static bool isInInitPhase(); + static void assertOnRenderThread(); + static void assertOnRenderThreadOrInit(); + + // ================== Ⱦ ================== + static void recordRenderCall(std::function&& renderCall); + static void replayQueue(); + + // ================== OpenGL ״̬װ ================== + static void enable(GLenum capability); + static void disable(GLenum capability); + static void pushState(); + static void popState(); + static int getStateStackSize(); + static void clearColor(float r, float g, float b, float a); + static void clear(GLbitfield mask); + static void viewport(int x, int y, int width, int height); + + // ================== VAO / VBO / EBO ================== + static GLuint GenVertexArrays(); + static void DeleteVertexArrays(GLuint vao); + static void BindVertexArray(GLuint vao); + static GLuint GenBuffers(); + static void DeleteBuffers(GLuint buffer); + static void BindBuffer(GLenum target, GLuint buffer); + static void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + + // ================== Uniform ================== + static void uniform1i(GLint location, int value); + static void uniform1f(GLint location, float value); + static void uniform2f(GLint location, float x, float y); + static void uniform3f(GLint location, float x, float y, float z); + static void uniform4f(GLint location, float x, float y, float z, float w); + static void uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + static void uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + static void uniform2f(GLint location, const glm::vec2& vec); + static void uniform3f(GLint location, const glm::vec3& vec); + static void uniform4f(GLint location, const glm::vec4& vec); + static void uniformMatrix3(GLint location, const glm::mat3& matrix, bool transpose = false); + static void uniformMatrix4(GLint location, const glm::mat4& matrix, bool transpose = false); + static GLint getUniformLocation(GLuint program, const std::string& name); + + // ================== ================== + static void lineWidth(float width); + static void drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices); + static void drawArrays(GLenum mode, GLint first, GLsizei count); + + // ================== ģʽ ================== + static void enableBlend(); + static void disableBlend(); + static void blendFunc(GLenum sfactor, GLenum dfactor); + static void defaultBlendFunc(); + + // ================== ɫ ================== + static GLuint createProgram(); + static GLint getCurrentProgram(); + static void attachShader(GLuint program, GLuint shader); + static void linkProgram(GLuint program); + static GLint getProgrami(GLuint program, GLenum pname); + static std::string getProgramInfoLog(GLuint program); + static void detachShader(GLuint program, GLuint shader); + static void deleteProgram(GLuint program); + static GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader); + static GLuint linkProgram(GLuint vertexShader, GLuint geometryShader, GLuint fragmentShader); + static void useProgram(GLuint program); + + // ================== ɫ ================== + static GLuint createShader(GLenum type); + static void shaderSource(GLuint shader, const std::string& source); + static void compileShader(GLuint shader); + static GLint getShaderi(GLuint shader, GLenum pname); + static std::string getShaderInfoLog(GLuint shader); + static void deleteShader(GLuint shader); + static GLuint compileShader(GLenum type, const std::string& source); + + // ================== Ȳ ================== + static void depthFunc(GLenum func); + static void depthMask(bool flag); + static void clearDepth(double depth); + static void enableDepthTest(); + static void disableDepthTest(); + + // ================== ================== + static void bindTexture(GLuint texture); + static void getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); + static void activeTexture(GLenum texture); + static GLuint genTextures(); + static void deleteTextures(GLuint texture); + static void texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels); + static void texParameteri(GLenum target, GLenum pname, GLint param); + static void setTextureMinFilter(GLenum filter); + static void setTextureMagFilter(GLenum filter); + static void setTextureWrapS(GLenum wrap); + static void setTextureWrapT(GLenum wrap); + static GLuint createDefaultTexture(); + + // ================== ================== + static void enableVertexAttribArray(GLuint index); + static void disableVertexAttribArray(GLuint index); + static void vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + + // ================== ߷ ================== + static void readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels); + static bool isExtensionSupported(const std::string& extension); + static void pixelStore(GLenum pname, GLint param); + static std::string getVendor(); + static std::string getRenderer(); + static std::string getOpenGLVersion(); + static std::string getGLSLVersion(); + static void logDetailedGLInfo(); + static void setupDefaultState(); + static void checkGLError(const std::string& operation); + + // ================== ȡ״̬ ================== + static int getViewportWidth(); + static int getViewportHeight(); + static glm::vec4 getClearColor(); + static int getQueueSize(); + +private: + struct RenderState; + // ˽еġֱִGLõİ汾 + static void _enable(GLenum capability); + static void _disable(GLenum capability); + static void _pushState(); + static void _popState(); + static void _clearColor(float r, float g, float b, float a); + static void _clear(GLbitfield mask); + static void _viewport(int x, int y, int width, int height); + static void _glDeleteVertexArrays(GLuint vao); + static void _uniform1i(GLint location, int value); + static void _uniform1f(GLint location, float value); + static void _uniform2f(GLint location, float x, float y); + static void _uniform3f(GLint location, float x, float y, float z); + static void _uniform4f(GLint location, float x, float y, float z, float w); + static void _uniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + static void _uniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); + static void _lineWidth(float width); + static void _drawElements(GLenum mode, GLsizei count, GLenum type, const void* indices); + static void _drawArrays(GLenum mode, GLint first, GLsizei count); + static void _glBindVertexArray(GLuint vao); + static void _enableBlend(); + static void _disableBlend(); + static void _blendFunc(GLenum sfactor, GLenum dfactor); + static void _attachShader(GLuint program, GLuint shader); + static void _linkProgram(GLuint program); + static void _detachShader(GLuint program, GLuint shader); + static void _deleteProgram(GLuint program); + static void _useProgram(GLuint program); + static void _shaderSource(GLuint shader, const std::string& source); + static void _compileShader(GLuint shader); + static void _deleteShader(GLuint shader); + static void _depthFunc(GLenum func); + static void _depthMask(bool flag); + static void _clearDepth(double depth); + static void _enableDepthTest(); + static void _disableDepthTest(); + static void _bindTexture(GLuint texture); + static void _getTexImage(GLenum target, GLint level, GLenum format, GLenum type, void* pixels); + static void _activeTexture(GLenum texture); + static void _deleteTextures(GLuint texture); + static void _texImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels); + static void _texParameteri(GLenum target, GLenum pname, GLint param); + static void _glBindBuffer(GLenum target, GLuint buffer); + static void _glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + static void _glDeleteBuffers(GLuint buffer); + static void _enableVertexAttribArray(GLuint index); + static void _disableVertexAttribArray(GLuint index); + static void _vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + static void _readPixels(int x, int y, int width, int height, GLenum format, GLenum type, void* pixels); + static void _pixelStore(GLenum pname, GLint param); + static void _checkGLError(const std::string& operation); + + + static std::string getGLErrorString(GLenum error); + + // ̬Ա + static std::thread::id s_RenderThreadId; + static std::atomic s_IsInInit; + static std::queue> s_RenderQueue; + static std::mutex s_RenderQueueMutex; + static std::atomic s_IsReplayingQueue; + + // ================== RenderState ṹʵ ================== + struct RenderSystem::RenderState { + GLint currentProgram = 0; + GLboolean blendEnabled = GL_FALSE; + GLboolean depthTestEnabled = GL_FALSE; + GLint blendSrcFactor = GL_SRC_ALPHA; + GLint blendDstFactor = GL_ONE_MINUS_SRC_ALPHA; + GLint activeTexture = GL_TEXTURE0; + GLint boundTexture = 0; + glm::vec4 clearColor{ 0.0f, 0.0f, 0.0f, 1.0f }; + glm::ivec4 viewport{ 0, 0, 800, 600 }; + + + RenderState() { + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram); + blendEnabled = glIsEnabled(GL_BLEND); + depthTestEnabled = glIsEnabled(GL_DEPTH_TEST); + glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcFactor); + glGetIntegerv(GL_BLEND_DST_RGB, &blendDstFactor); + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); + glGetFloatv(GL_COLOR_CLEAR_VALUE, &clearColor[0]); + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + while (glGetError() != GL_NO_ERROR) {} + } + + void restore() { + glViewport(viewport.x, viewport.y, viewport.z, viewport.w); + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + glUseProgram(currentProgram); + glActiveTexture(activeTexture); + glBindTexture(GL_TEXTURE_2D, boundTexture); + if (blendEnabled) glEnable(GL_BLEND); else glDisable(GL_BLEND); + glBlendFunc(blendSrcFactor, blendDstFactor); + if (depthTestEnabled) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + while (glGetError() != GL_NO_ERROR) {} + } + + }; + + static std::stack s_StateStack; + + static int s_ViewportWidth; + static int s_ViewportHeight; + static glm::vec4 s_ClearColorValue; + + static std::shared_ptr s_Logger; + +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/buffer/BufferBuilder.cpp b/Vivid2DRenderer/systems/buffer/BufferBuilder.cpp new file mode 100644 index 0000000..faf6f3e --- /dev/null +++ b/Vivid2DRenderer/systems/buffer/BufferBuilder.cpp @@ -0,0 +1,182 @@ +#include "pch.h" + +#include "BufferUploader.h" +#include "BufferBuilder.h" +#include +#include +#include + +namespace Buffer { + + // ========================================================== + // RenderState ʵ + // ========================================================== + + void RenderState::saveCurrentState() { + GLint currentProgram = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram); + this->shaderProgram = static_cast(currentProgram); + + GLint boundTexture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTexture); + this->textureId = static_cast(boundTexture); + + GLint activeTextureUnit = 0; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureUnit); + this->textureUnit = activeTextureUnit - GL_TEXTURE0; + + this->depthTest = glIsEnabled(GL_DEPTH_TEST); + this->blending = glIsEnabled(GL_BLEND); + + while (glGetError() != GL_NO_ERROR) {} + } + + void RenderState::applyState() const { + // ָ Program + if (shaderProgram != 0) { + glUseProgram(shaderProgram); + } + + // ָ Texture + if (textureId != 0) { + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_2D, textureId); + } + + // ָ Blending/Depth + if (this->blending) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } + + if (this->depthTest) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + } + + + // ========================================================== + // BufferBuilder ʵ + // ========================================================== + + // ĬϹ캯 (256 floats -> 64 ) + BufferBuilder::BufferBuilder() : BufferBuilder(256) {} + + // 캯 + BufferBuilder::BufferBuilder(int initialFloatCapacity) + : array(std::max(16, initialFloatCapacity)), size(0), vertexCount(0) + { + // Ĭϻģʽ + this->mode = GL_TRIANGLES; + // ȷ array ʼǿյ + this->array.clear(); + } + + void BufferBuilder::begin(int glMode, int estimatedVertexCount) { + this->mode = glMode; + this->size = 0; + this->vertexCount = 0; + this->array.clear(); + + // 浱ǰȾ״̬ + if (!stateSaved) { + renderState.saveCurrentState(); + stateSaved = true; + } + + if (estimatedVertexCount > 0) { + this->array.reserve(estimatedVertexCount * COMPONENTS_PER_VERTEX); + } + } + + void BufferBuilder::setTexture(unsigned int textureId, int textureUnit) { + this->renderState.textureId = textureId; + this->renderState.textureUnit = textureUnit; + } + + void BufferBuilder::setShader(unsigned int programId) { + this->renderState.shaderProgram = programId; + } + + void BufferBuilder::setColor(const glm::vec4& color) { + this->renderState.color = color; + } + + void BufferBuilder::setOpacity(float opacity) { + this->renderState.opacity = opacity; + } + + void BufferBuilder::vertex(float x, float y, float u, float v) { + this->array.push_back(x); + this->array.push_back(y); + this->array.push_back(u); + this->array.push_back(v); + this->size = this->array.size(); + this->vertexCount++; + } + + std::unique_ptr BufferBuilder::end() { + if (vertexCount == 0) return nullptr; + + RenderSystem::assertOnRenderThread(); + + // 1. Ͱ GPU Դ + unsigned int vao = RenderSystem::GenVertexArrays(); + unsigned int vbo = RenderSystem::GenBuffers(); + + RenderSystem::BindVertexArray(vao); + RenderSystem::BindBuffer(GL_ARRAY_BUFFER, vbo); + + // 2. ϴ + // RenderSystem Ѿ GL_ARRAY_BUFFER GL_DYNAMIC_DRAW + RenderSystem::BufferData(GL_ARRAY_BUFFER, this->array.size() * sizeof(float), + this->array.data(), GL_DYNAMIC_DRAW); + + // 3. ö (Location 0: Position, Location 1: TexCoord) + // Position (location 0): 2 floats, offset 0 + RenderSystem::enableVertexAttribArray(0); + RenderSystem::vertexAttribPointer(0, 2, GL_FLOAT, false, + COMPONENTS_PER_VERTEX * sizeof(float), 0); + + // Texture Coords (location 1): 2 floats, offset 2 * sizeof(float) + RenderSystem::enableVertexAttribArray(1); + RenderSystem::vertexAttribPointer(1, 2, GL_FLOAT, false, + COMPONENTS_PER_VERTEX * sizeof(float), + reinterpret_cast(2 * sizeof(float))); + + // 4. + RenderSystem::BindBuffer(GL_ARRAY_BUFFER, 0); + RenderSystem::BindVertexArray(0); + + // 5. BuiltBuffer + RenderState stateCopy = renderState.copy(); + stateSaved = false; + + return std::make_unique(vao, vbo, vertexCount, mode, stateCopy); + } + + void BufferBuilder::endImmediate() { + std::unique_ptr buffer = end(); + if (buffer != nullptr) { + BufferUploader::drawWithShader(std::move(buffer)); + } + } + + void BufferBuilder::dispose(const BuiltBuffer* buffer) { + if (buffer != nullptr) { + RenderSystem::DeleteVertexArrays(buffer->vao); + RenderSystem::DeleteBuffers(buffer->vbo); + } + } + + void BufferBuilder::clear() { + this->array.clear(); + this->size = 0; + this->vertexCount = 0; + } +} // namespace Buffer \ No newline at end of file diff --git a/Vivid2DRenderer/systems/buffer/BufferBuilder.h b/Vivid2DRenderer/systems/buffer/BufferBuilder.h new file mode 100644 index 0000000..4c7afcf --- /dev/null +++ b/Vivid2DRenderer/systems/buffer/BufferBuilder.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include +#include + +// ЩͷļĿд +#include "../../systems/RenderSystem.h" + +namespace Buffer { + + /** + * @brief Ⱦ״̬ṹ + */ + struct RenderState { + // Ⱦ״̬ (Ӧ Java е RenderState) + unsigned int textureId = 0; + int textureUnit = 0; + unsigned int shaderProgram = 0; + glm::vec4 color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + float opacity = 1.0f; + int blendMode = 0; + bool depthTest = false; + bool blending = true; + + /** + * @brief 浱ǰ OpenGL ״̬ (Program, Texture, Blending/Depth) + */ + void saveCurrentState(); + + /** + * @brief Ӧñ OpenGL ״̬ ( Program Texture GL ״̬) + */ + void applyState() const; + + /** + * @brief ״̬ + */ + RenderState copy() const { + RenderState copy; + copy.textureId = this->textureId; + copy.textureUnit = this->textureUnit; + copy.shaderProgram = this->shaderProgram; + copy.color = this->color; // glm::vec4 Ĭֵ֧ + copy.opacity = this->opacity; + copy.blendMode = this->blendMode; + copy.depthTest = this->depthTest; + copy.blending = this->blending; + return copy; + } + }; + + /** + * @brief ɵĻݽṹ + */ + struct BuiltBuffer { + unsigned int vao; + unsigned int vbo; + int vertexCount; + int mode; // GL_TRIANGLES, GL_LINE_LOOP + RenderState renderState; + + BuiltBuffer(unsigned int vao, unsigned int vbo, int vertexCount, int mode, const RenderState& state) + : vao(vao), vbo(vbo), vertexCount(vertexCount), mode(mode), renderState(state) {} + }; + + + /** + * @brief 򻯰 BufferBuilderڰһԻƼ塣 + * ÿʽ float x, float y, float u, float v 4 float + */ + class BufferBuilder { + private: + static constexpr int COMPONENTS_PER_VERTEX = 4; // x,y,u,v + std::vector array; + int size; // ʵʴ洢 float (array.size()) + int vertexCount; + int mode; // GL mode + RenderState renderState; + bool stateSaved = false; + public: + BufferBuilder(); + explicit BufferBuilder(int initialFloatCapacity); + + /** + * @brief ʼҪƵ GL ģʽ ( GL_LINE_LOOP) + */ + void begin(int glMode, int estimatedVertexCount); + + void setTexture(unsigned int textureId, int textureUnit); + void setShader(unsigned int programId); + void setColor(const glm::vec4& color); + void setOpacity(float opacity); + + /** + * @brief Ӷ㣺x,y,u,v + */ + void vertex(float x, float y, float u, float v); + + /** + * @brief BuiltBufferƣ߸ dispose + */ + std::unique_ptr end(); + + /** + * @brief ϴƣͷ GPU Դ + */ + void endImmediate(); + + /** + * @brief GPU Դ + */ + static void dispose(const BuiltBuffer* buffer); + + /** + * @brief ״̬Ա + */ + void clear(); + + int getVertexCount() const { + return vertexCount; + } + + bool isEmpty() const { + return vertexCount == 0; + } + }; + +} // namespace Buffer \ No newline at end of file diff --git a/Vivid2DRenderer/systems/buffer/BufferUploader.cpp b/Vivid2DRenderer/systems/buffer/BufferUploader.cpp new file mode 100644 index 0000000..e174d06 --- /dev/null +++ b/Vivid2DRenderer/systems/buffer/BufferUploader.cpp @@ -0,0 +1,107 @@ +#include "pch.h" + +#include "BufferUploader.h" + +#include +#include +#include + +namespace Buffer { + + void BufferUploader::applyRenderState(const RenderState& state) { + // 1. Ӧ + if (state.textureId != 0) { + RenderSystem::activeTexture(GL_TEXTURE0 + state.textureUnit); + RenderSystem::bindTexture(state.textureId); + } + + // 2. Ӧɫ + unsigned int currentProgram = 0; + if (state.shaderProgram != 0) { + currentProgram = state.shaderProgram; + RenderSystem::useProgram(state.shaderProgram); + } + else { + // ״̬ûָɫԻȡǰ󶨵ɫ + GLint programId = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, &programId); + currentProgram = static_cast(programId); + } + + // 3. uColor Uniform ( Program ʱ) + if (currentProgram != 0) { + GLint colorLoc = RenderSystem::getUniformLocation(currentProgram, "uColor"); + + if (colorLoc != -1) { + // ʹ RenderSystem::uniform4f ɫ + RenderSystem::uniform4f(colorLoc, + state.color.x, state.color.y, state.color.z, state.color.w); + } + } + else { + spdlog::debug("BufferUploader::applyRenderState: No shader program available for color setting."); + } + + // 4. Ӧûģʽ + if (state.blending) { + RenderSystem::enableBlend(); + // RenderSystem::defaultBlendFunc() װ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + RenderSystem::defaultBlendFunc(); + } + else { + RenderSystem::disableBlend(); + } + + // 5. ӦȲ + if (state.depthTest) { + RenderSystem::enableDepthTest(); + } + else { + RenderSystem::disableDepthTest(); + } + } + + void BufferUploader::restoreRenderState(const RenderState& previousState) { + // ָ֮ǰ״̬ RenderState::applyState() ʵ֡ + previousState.applyState(); + } + + + // ========================================================== + // ߼ʵ + // ========================================================== + + void BufferUploader::drawWithShader(std::unique_ptr buffer) { + + // Ϊգֱӷ + if (!buffer) return; + + RenderSystem::assertOnRenderThread(); + RenderState currentState; + currentState.saveCurrentState(); + + try { + // ʹ -> unique_ptr ָ BuiltBuffer Ա + const RenderState& bufferState = buffer->renderState; + applyRenderState(bufferState); + + if (buffer->vao != 0) { + RenderSystem::BindVertexArray(buffer->vao); + } + if (buffer->vertexCount > 0) { + RenderSystem::drawArrays(buffer->mode, 0, buffer->vertexCount); + } + + // VAO + if (buffer->vao != 0) { + RenderSystem::BindVertexArray(0); + } + } + catch (const std::exception& e) { + spdlog::error("BufferUploader drawing failed: {}", e.what()); + } + restoreRenderState(currentState); + BufferBuilder::dispose(buffer.get()); + } + +} // namespace Buffer \ No newline at end of file diff --git a/Vivid2DRenderer/systems/buffer/BufferUploader.h b/Vivid2DRenderer/systems/buffer/BufferUploader.h new file mode 100644 index 0000000..bd72d2d --- /dev/null +++ b/Vivid2DRenderer/systems/buffer/BufferUploader.h @@ -0,0 +1,38 @@ +#pragma once + +#include "BufferBuilder.h" +#include "../../systems/RenderSystem.h" + +namespace Buffer { + + /** + * @brief ϴӦȾ״̬ VAO ִлơ + */ + class BufferUploader { + public: + /** + * @brief ʹɫѹĻӹȨ + * @param buffer BuiltBuffer unique_ptr + */ + static void drawWithShader(std::unique_ptr buffer); + + /** + * @brief ѹĻ drawWithShader ͬ + */ + static void draw(std::unique_ptr buffer) { + drawWithShader(std::move(buffer)); + } + + private: + /** + * @brief ӦָȾ״̬ + */ + static void applyRenderState(const RenderState& state); + + /** + * @brief ָ֮ǰȾ״̬ + */ + static void restoreRenderState(const RenderState& previousState); + }; + +} // namespace Buffer \ No newline at end of file diff --git a/Vivid2DRenderer/systems/buffer/Tesselator.cpp b/Vivid2DRenderer/systems/buffer/Tesselator.cpp new file mode 100644 index 0000000..1ba6aca --- /dev/null +++ b/Vivid2DRenderer/systems/buffer/Tesselator.cpp @@ -0,0 +1,33 @@ +#include "pch.h" +#include "Tesselator.h" + +#include "BufferBuilder.h" +#include "BufferUploader.h" + +namespace Buffer { + Tesselator::Tesselator(int bufferSize) { + // ʼ BufferBuilderײ + this->builder = std::make_unique(bufferSize); + } + + Tesselator::Tesselator() : Tesselator(DEFAULT_BUFFER_SIZE) {} + + Tesselator& Tesselator::getInstance() { + RenderSystem::assertOnRenderThreadOrInit(); + static Tesselator INSTANCE; + return INSTANCE; + } + + void Tesselator::end() { + RenderSystem::assertOnRenderThread(); + std::unique_ptr builtBuffer = this->builder->end(); + if (builtBuffer) { + BufferUploader::drawWithShader(std::move(builtBuffer)); + } + } + + BufferBuilder& Tesselator::getBuilder() { + return *this->builder; + } + +} // namespace Buffer \ No newline at end of file diff --git a/Vivid2DRenderer/systems/buffer/Tesselator.h b/Vivid2DRenderer/systems/buffer/Tesselator.h new file mode 100644 index 0000000..e670d65 --- /dev/null +++ b/Vivid2DRenderer/systems/buffer/Tesselator.h @@ -0,0 +1,119 @@ +#pragma once + +#include + +#include "BufferBuilder.h" +#include "../../systems/RenderSystem.h" + +/** + * @file BufferSystem.h + * @brief Vivid2D ȾϵͳļʱģʽImmediate Modeϵͳ + * + * Ŀ꣺ṩһ򵥡ЧĽӿڣʱ̬ݣ + * ʹһ Draw Call ύ GPU ȾȻͷ GPU Դ + * + * ========================================================== + * + * ========================================================== + * + * 1. Tesselator () + * ---------------------------- + * - ãϵͳڵ㣬һ BufferBuilder ʵ + * - ӿڣTesselator::getInstance() ȡTesselator::getBuilder() ȡ + * - ķTesselator::end()ô˷ᴥ (Builder -> Uploader) + * + * 2. BufferBuilder (ݹ) + * ---------------------------- + * - ã CPU ڴռݣx, y, u, v͵ǰȾ״̬ (RenderState) + * - ӿڣbegin() vertex() Ӷ㣻setTexture/setColor ״̬ + * - end() һ BuiltBuffer ʵ + * + * 3. BuiltBuffer () + * -------------------------- + * - ãһݽṹװбҪ GPU Դ (VAO/VBO)GL ģʽԼʱ RenderState + * - ص㣺Ȩͨ std::unique_ptr תƣȷԴֻ Uploader ƺһΡ + * + * 4. BufferUploader (ƺ) + * -------------------------------- + * - ã BuiltBufferм¼ RenderState Ӧõ GL ģ VAOִ glDrawArrays ƣ GPU Դ + * - ķBufferUploader::drawWithShader(std::unique_ptr buffer) + * + * ========================================================== + * ͹ (һı) + * ========================================================== + * + * 1. ʼͻȡ + * Tesselator& t = Tesselator::getInstance(); + * BufferBuilder& builder = t.getBuilder(); + * + * 2. ʼ + * builder.begin(GL_QUADS, 4); // Ԥƻһ 4 ı + * + * 3. ״̬ + * builder.setShader(myProgramId); + * builder.setTexture(myTextureId, 0); + * builder.setColor(glm::vec4(1.0f, 1.0f, 1.0f, 0.5f)); // ͸ɫ + * + * 4. Ӷ㣨x, y, u, v + * builder.vertex(10.0f, 10.0f, 0.0f, 0.0f); + * builder.vertex(110.0f, 10.0f, 1.0f, 0.0f); + * builder.vertex(110.0f, 110.0f, 1.0f, 1.0f); + * builder.vertex(10.0f, 110.0f, 0.0f, 1.0f); + * + * 5. ύƺ + * t.end(); + * // ʱϴɣGPU ϵ VAO/VBO Դѱͷš + * + */ +namespace Buffer { + + class BufferBuilder; + + /** + * @brief Tesselator ࣺΪ BufferBuilder ĵ + * * ְ + * 1. ṩʡ + * 2. װ BufferBuilderṩһµļ幹ӿڡ + * 3. end() ʱ BufferUploader ơ + */ + class Tesselator { + private: + static constexpr int DEFAULT_BUFFER_SIZE = 2097152; // 2MB floats + + std::unique_ptr builder; // BufferBuilder ʵ + + /** + * @brief ˽й캯ȷ + */ + explicit Tesselator(int bufferSize); + + /** + * @brief ˽ĬϹ캯 + */ + Tesselator(); + + public: + Tesselator(const Tesselator&) = delete; + Tesselator& operator=(const Tesselator&) = delete; + + /** + * @brief ȡ Tesselator ĵʵ̰߳ȫӳٳʼ + * * Ⱦ߳ϵûڳʼ׶εá + * @return Tesselator& ʵá + */ + static Tesselator& getInstance(); + + /** + * @brief ϴ GPUơ + * * Ⱦ߳ϵá + */ + void end(); + + /** + * @brief ȡײ BufferBuilder Ӷ״̬ + * @return BufferBuilder& + */ + BufferBuilder& getBuilder(); + }; + +} // namespace Buffer \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/CompleteShader.h b/Vivid2DRenderer/systems/sources/CompleteShader.h new file mode 100644 index 0000000..6786656 --- /dev/null +++ b/Vivid2DRenderer/systems/sources/CompleteShader.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Shader.h" +#include +#include + +class ShaderProgram; + +/** + * @brief ɫ (Ӧ Java CompleteShader ӿ) + * װһɫĶƬɫ壬ԼԪݺͳʼ߼ + */ +class CompleteShader { +public: + // ǻʵ + virtual ~CompleteShader() = default; + + /** + * @brief ȡɫ塣 + * @return ɫ + */ + virtual const Shader& getVertexShader() const = 0; + + /** + * @brief ȡƬɫ塣 + * @return Ƭɫ + */ + virtual const Shader& getFragmentShader() const = 0; + + /** + * @brief ȡɫơ + * @return ַ + */ + virtual std::string getShaderName() const = 0; + + /** + * @brief ǷĬɫ + * @return Ĭɫ true + */ + virtual bool isDefaultShader() const = 0; + + /** + * @brief óɺĬͳһ + * Ӧ Java ӿе default + * @param program ӵ ShaderProgram ָ롣 + */ + virtual void setDefaultUniforms(ShaderProgram* program) const {} +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/Shader.h b/Vivid2DRenderer/systems/sources/Shader.h new file mode 100644 index 0000000..0469443 --- /dev/null +++ b/Vivid2DRenderer/systems/sources/Shader.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +/** + * @brief ɫ (Ӧ Java Shader ӿ) + * װɫ綥ƬɫĴơ + */ +class Shader { +public: + // ǻʵ + virtual ~Shader() = default; + + /** + * @brief ȡɫ GLSL 롣 + * @return GLSL ַ + */ + virtual std::string getShaderCode() const = 0; + + /** + * @brief ȡɫƣ־͵ԣ + * @return ɫַ + */ + virtual std::string getShaderName() const = 0; +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/ShaderManagement.cpp b/Vivid2DRenderer/systems/sources/ShaderManagement.cpp new file mode 100644 index 0000000..1deb3be --- /dev/null +++ b/Vivid2DRenderer/systems/sources/ShaderManagement.cpp @@ -0,0 +1,251 @@ +#include "pch.h" + +#include "ShaderManagement.h" + +#include +#include +#include +#include + +#include "../../systems/RenderSystem.h" +#include "../../systems/sources/ShaderProgram.h" +#include "../../systems/sources/Shader.h" +#include "../../systems/sources/CompleteShader.h" +#include "def/TextShader.h" +#include "def/SolidColorShader.h" +#include "def/Shader2D.h" + +std::map> ShaderManagement::s_shaderMap; +ShaderProgram* ShaderManagement::s_defaultProgram = nullptr; + +/** + * @brief ʹ helper ƹ std::initializer_list Ŀơ + * * * @return std::vector> + */ +static std::vector> create_shader_list() +{ + std::vector> list; + list.push_back(std::make_unique()); + list.push_back(std::make_unique()); + list.push_back(std::make_unique()); + return list; +} + +std::vector> ShaderManagement::s_shaderList = create_shader_list(); + +void ShaderManagement::compileAllShaders() +{ + RenderSystem::assertOnRenderThread(); + + for (const auto& completeShader : s_shaderList) { + compileShaderProgram(completeShader.get()); + } + + if (s_defaultProgram == nullptr && !s_shaderMap.empty()) { + s_defaultProgram = s_shaderMap.begin()->second.get(); + } +} + +void ShaderManagement::compileShaderProgram(const CompleteShader* completeShader) +{ + const std::string& shaderName = completeShader->getShaderName(); + + try { + const Shader& vertexShaderRef = completeShader->getVertexShader(); + GLuint vsId = compileShader(GL_VERTEX_SHADER, vertexShaderRef.getShaderCode(), + vertexShaderRef.getShaderName()); + + const Shader& fragmentShaderRef = completeShader->getFragmentShader(); + GLuint fsId = compileShader(GL_FRAGMENT_SHADER, fragmentShaderRef.getShaderCode(), + fragmentShaderRef.getShaderName()); + + GLuint programId = linkProgram(vsId, fsId, shaderName); + std::unique_ptr shaderProgram = std::make_unique(programId); + + if (completeShader->isDefaultShader()) { + s_defaultProgram = shaderProgram.get(); + setupDefaultUniforms(s_defaultProgram); + } + else { + completeShader->setDefaultUniforms(shaderProgram.get()); + } + + s_shaderMap[shaderName] = std::move(shaderProgram); + RenderSystem::deleteShader(vsId); + RenderSystem::deleteShader(fsId); + + spdlog::info("ɹɫ: {}", shaderName); + + } + catch (const std::runtime_error& e) { + spdlog::error("ɫʧ: {}", shaderName); + std::cerr << "Shader compilation failed: " << shaderName << "\nError: " << e.what() << std::endl; + throw; + } +} + +void ShaderManagement::setupDefaultUniforms(ShaderProgram* program) +{ + if (!program) return; + program->use(); + setUniformInt(program, "uTexture", 0); + setUniformFloat(program, "uOpacity", 1.0f); + setUniformVec4(program, "uColor", glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); + setUniformInt(program, "uBlendMode", 0); + setUniformInt(program, "uDebugMode", 0); + setUniformInt(program, "uLightCount", 0); + program->stop(); + RenderSystem::checkGLError("setupDefaultUniforms"); +} + +GLuint ShaderManagement::compileShader(GLenum type, const std::string& source, const std::string& shaderName) +{ + GLuint shaderId = RenderSystem::createShader(type); + RenderSystem::shaderSource(shaderId, source); + RenderSystem::compileShader(shaderId); + if (RenderSystem::getShaderi(shaderId, GL_COMPILE_STATUS) != GL_TRUE) { + std::string log = RenderSystem::getShaderInfoLog(shaderId); + RenderSystem::deleteShader(shaderId); + throw std::runtime_error("ɫʧ [" + shaderName + "]:\n" + log); + } + return shaderId; +} + +GLuint ShaderManagement::linkProgram(GLuint vertexShaderId, GLuint fragmentShaderId, const std::string& programName) +{ + GLuint programId = RenderSystem::createProgram(); + RenderSystem::attachShader(programId, vertexShaderId); + RenderSystem::attachShader(programId, fragmentShaderId); + RenderSystem::linkProgram(programId); + if (RenderSystem::getProgrami(programId, GL_LINK_STATUS) != GL_TRUE) { + std::string log = RenderSystem::getProgramInfoLog(programId); + RenderSystem::deleteProgram(programId); + throw std::runtime_error("ɫʧ [" + programName + "]:\n" + log); + } + validateProgram(programId, programName); + return programId; +} + +void ShaderManagement::validateProgram(GLuint programId, const std::string& programName) +{ + GLint validateStatus = RenderSystem::getProgrami(programId, GL_VALIDATE_STATUS); + + if (validateStatus != GL_TRUE) { + std::string log = RenderSystem::getProgramInfoLog(programId); + spdlog::warn("ɫ֤ [{}]: {}", programName, log); + } +} + +ShaderProgram* ShaderManagement::getDefaultProgram() +{ + return s_defaultProgram; +} + +ShaderProgram* ShaderManagement::getShaderProgram(const std::string& name) +{ + auto it = s_shaderMap.find(name); + if (it != s_shaderMap.end()) { + return it->second.get(); + } + return nullptr; +} + + +void ShaderManagement::cleanup() +{ + RenderSystem::assertOnRenderThread(); + s_shaderMap.clear(); + s_shaderList.clear(); + s_defaultProgram = nullptr; + spdlog::info("ShaderManagement cleanup completed."); +} + +void ShaderManagement::setUniformInt(ShaderProgram* program, const std::string& name, GLint value) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform1i(location, value); + } +} + +void ShaderManagement::setUniformFloat(ShaderProgram* program, const std::string& name, GLfloat value) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform1f(location, value); + } +} + +void ShaderManagement::setUniformVec2(ShaderProgram* program, const std::string& name, GLfloat x, GLfloat y) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform2f(location, x, y); + } +} + +void ShaderManagement::setUniformVec2(ShaderProgram* program, const std::string& name, const glm::vec2& vec) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform2f(location, vec); + } +} + +void ShaderManagement::setUniformVec3(ShaderProgram* program, const std::string& name, GLfloat x, GLfloat y, GLfloat z) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform3f(location, x, y, z); + } +} + +void ShaderManagement::setUniformVec3(ShaderProgram* program, const std::string& name, const glm::vec3& vec) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform3f(location, vec); + } +} + +void ShaderManagement::setUniformVec4(ShaderProgram* program, const std::string& name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform4f(location, x, y, z, w); + } +} + +void ShaderManagement::setUniformVec4(ShaderProgram* program, const std::string& name, const glm::vec4& vec) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniform4f(location, vec); + } +} + +void ShaderManagement::setUniformMat3(ShaderProgram* program, const std::string& name, const glm::mat3& matrix) +{ + if (!program) return; + program->use(); + GLint location = program->getUniformLocation(name); + if (location != -1) { + RenderSystem::uniformMatrix3(location, matrix, false); + } +} \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/ShaderManagement.h b/Vivid2DRenderer/systems/sources/ShaderManagement.h new file mode 100644 index 0000000..3026497 --- /dev/null +++ b/Vivid2DRenderer/systems/sources/ShaderManagement.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class ShaderProgram; +class CompleteShader; +class RenderSystem; + +class Shader2D; +class SolidColorShader; +class TextShader; + +/** + * @brief ɫ - ɫı롢Ӻ͹ (C++ ̬ʵ) + */ +class ShaderManagement +{ +private: + // ֹʵ + ShaderManagement() = delete; + ShaderManagement(const ShaderManagement&) = delete; + ShaderManagement& operator=(const ShaderManagement&) = delete; + +public: + /** + * @brief ɫ򻺴ӳ䣬ƴ洢ѱɫ + * ʹ std::unique_ptr ShaderProgram + */ + static std::map> s_shaderMap; + + /** + * @brief ɫб˳洢ɫԴ붨 + * ע⣺ C++ У̬ʼбͨ .cpp ļС + * ʹ std::vector> ̬ + */ + static std::vector> s_shaderList; + + /** + * @brief Ĭɫ + */ + static ShaderProgram* s_defaultProgram; + + // --- Ĺ --- + + /** + * @brief עɫ + */ + static void compileAllShaders(); + + /** + * @brief ɫԴ + */ + static void cleanup(); + + // --- Getter --- + + /** + * @brief ȡĬɫ + */ + static ShaderProgram* getDefaultProgram(); + + /** + * @brief ƻȡɫ + */ + static ShaderProgram* getShaderProgram(const std::string& name); + + static void setUniformInt(ShaderProgram* program, const std::string& name, GLint value); + static void setUniformFloat(ShaderProgram* program, const std::string& name, GLfloat value); + + // Vec2/Vec3/Vec4 (ʹ GLM) + static void setUniformVec2(ShaderProgram* program, const std::string& name, GLfloat x, GLfloat y); + static void setUniformVec2(ShaderProgram* program, const std::string& name, const glm::vec2& vec); + static void setUniformVec3(ShaderProgram* program, const std::string& name, GLfloat x, GLfloat y, GLfloat z); + static void setUniformVec3(ShaderProgram* program, const std::string& name, const glm::vec3& vec); + static void setUniformVec4(ShaderProgram* program, const std::string& name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + static void setUniformVec4(ShaderProgram* program, const std::string& name, const glm::vec4& vec); + + // Mat3 (ʹ GLM) + static void setUniformMat3(ShaderProgram* program, const std::string& name, const glm::mat3& matrix); + +private: + /** + * @brief 뵥ɫ + */ + static void compileShaderProgram(const CompleteShader* completeShader); + + /** + * @brief Ĭɫuniformֵ + */ + static void setupDefaultUniforms(ShaderProgram* program); + + /** + * @brief ɫ + */ + static GLuint compileShader(GLenum type, const std::string& source, const std::string& shaderName); + + /** + * @brief ɫ + */ + static GLuint linkProgram(GLuint vertexShaderId, GLuint fragmentShaderId, const std::string& programName); + + /** + * @brief Զ֤ + */ + static void validateProgram(GLuint programId, const std::string& programName); +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/ShaderProgram.cpp b/Vivid2DRenderer/systems/sources/ShaderProgram.cpp new file mode 100644 index 0000000..97d249d --- /dev/null +++ b/Vivid2DRenderer/systems/sources/ShaderProgram.cpp @@ -0,0 +1,120 @@ +#include "pch.h" + +#include "ShaderProgram.h" +#include + +ShaderProgram::ShaderProgram(GLuint programId) + : m_programId(programId) +{ + spdlog::info("ShaderProgram created with ID: {}", programId); +} + +// +ShaderProgram::~ShaderProgram() +{ + deleteProgram(); + spdlog::info("ShaderProgram {} destroyed.", m_programId); +} + +void ShaderProgram::use() const +{ + glUseProgram(m_programId); +} + +void ShaderProgram::stop() const +{ + glUseProgram(0); +} + +GLint ShaderProgram::getUniformLocation(const std::string& name) +{ + if (auto it = m_uniformCache.find(name); it != m_uniformCache.end()) { + return it->second; + } + GLint location = glGetUniformLocation(m_programId, name.c_str()); + m_uniformCache[name] = location; + if (location == -1) { + spdlog::warn("Uniform '{}' not found in program ID: {}", name, m_programId); + } + return location; +} + +// --- uniform ÷ (ʹ name) --- + +void ShaderProgram::setUniform1i(const std::string& name, GLint value) +{ + GLint location = getUniformLocation(name); + setUniform1i(location, value); +} + +void ShaderProgram::setUniform1f(const std::string& name, GLfloat value) +{ + GLint location = getUniformLocation(name); + setUniform1f(location, value); +} + +void ShaderProgram::setUniform2f(const std::string& name, GLfloat x, GLfloat y) +{ + GLint location = getUniformLocation(name); + if (location != -1) { + glUniform2f(location, x, y); + } +} + +void ShaderProgram::setUniform3f(const std::string& name, GLfloat x, GLfloat y, GLfloat z) +{ + GLint location = getUniformLocation(name); + if (location != -1) { + glUniform3f(location, x, y, z); + } +} + +void ShaderProgram::setUniform4f(const std::string& name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + GLint location = getUniformLocation(name); + setUniform4f(location, x, y, z, w); +} + +void ShaderProgram::setUniformMatrix3(const std::string& name, const glm::mat3& matrix) +{ + GLint location = getUniformLocation(name); + setUniformMatrix3(location, matrix); +} + +// --- uniform ÷ (ʹ location) --- + +void ShaderProgram::setUniform1i(GLint location, GLint value) +{ + if (location != -1) { + glUniform1i(location, value); + } +} + +void ShaderProgram::setUniform1f(GLint location, GLfloat value) +{ + if (location != -1) { + glUniform1f(location, value); + } +} + +void ShaderProgram::setUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) +{ + if (location != -1) { + glUniform4f(location, x, y, z, w); + } +} + +void ShaderProgram::setUniformMatrix3(GLint location, const glm::mat3& matrix) +{ + if (location != -1) { + glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); + } +} + +void ShaderProgram::deleteProgram() +{ + if (glIsProgram(m_programId)) { + glDeleteProgram(m_programId); + m_uniformCache.clear(); + } +} \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/ShaderProgram.h b/Vivid2DRenderer/systems/sources/ShaderProgram.h new file mode 100644 index 0000000..80ee4ab --- /dev/null +++ b/Vivid2DRenderer/systems/sources/ShaderProgram.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include + +/** + * @brief OpenGLɫװࡣ + * * һ࣬ڹOpenGLɫ򣬰ͳһ(uniform)úͻ档 + * ǵ̳ԣΪ麯 + */ +class ShaderProgram +{ +public: + // 캯 + explicit ShaderProgram(GLuint programId); + + // ȷȷԴ + virtual ~ShaderProgram(); + + // ɫ + void use() const; + + // ֹͣʹɫ + void stop() const; + + // ȡͳһ(uniform)λã + GLint getUniformLocation(const std::string& name); + + // --- uniform ÷ (ʹ name) --- + void setUniform1i(const std::string& name, GLint value); + void setUniform1f(const std::string& name, GLfloat value); + void setUniform2f(const std::string& name, GLfloat x, GLfloat y); + void setUniform3f(const std::string& name, GLfloat x, GLfloat y, GLfloat z); + void setUniform4f(const std::string& name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + + // ʹ glm::mat3 + void setUniformMatrix3(const std::string& name, const glm::mat3& matrix); + + // --- uniform ÷ (ʹ location) --- + void setUniform1i(GLint location, GLint value); + void setUniform1f(GLint location, GLfloat value); + void setUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void setUniformMatrix3(GLint location, const glm::mat3& matrix); + + // OpenGLԴ + void deleteProgram(); + + // ȡID + GLuint getProgramId() const { return m_programId; } + +protected: + const GLuint m_programId; + std::map m_uniformCache; +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/def/Shader2D.cpp b/Vivid2DRenderer/systems/sources/def/Shader2D.cpp new file mode 100644 index 0000000..72f5b94 --- /dev/null +++ b/Vivid2DRenderer/systems/sources/def/Shader2D.cpp @@ -0,0 +1,163 @@ +#include "pch.h" + +#include "Shader2D.h" + + +// ɫ GLSL (Ӧ VertexShaders.java) +static const std::string VERTEX_SHADER_SRC = R"""( +#version 330 core +layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec2 aTexCoord; +out vec2 vTexCoord; +out vec2 vWorldPos; + +uniform mat3 uModelMatrix; +uniform mat3 uViewMatrix; +uniform mat3 uProjectionMatrix; + +void main() { + // ʹ 3x3 Ļλ + vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0); + gl_Position = vec4(p.xy, 0.0, 1.0); + vTexCoord = aTexCoord; + // world-space λù fragment shader ʹã xy + vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy; +} +)"""; + +// Ƭɫ GLSL (Ӧ FragmentShaders.java) +static const std::string FRAGMENT_SHADER_SRC = R"""( +#version 330 core +in vec2 vTexCoord; +in vec2 vWorldPos; +out vec4 FragColor; + +uniform sampler2D uTexture; +uniform vec4 uColor; +uniform float uOpacity; +uniform int uBlendMode; +uniform int uDebugMode; + +#define MAX_LIGHTS 8 +uniform vec2 uLightsPos[MAX_LIGHTS]; +uniform vec3 uLightsColor[MAX_LIGHTS]; +uniform float uLightsIntensity[MAX_LIGHTS]; +uniform int uLightsIsAmbient[MAX_LIGHTS]; +uniform int uLightCount; + +// ˥ϵ +const float ATT_CONST = 1.0; +const float ATT_LINEAR = 0.09; +const float ATT_QUAD = 0.032; + +void main() { + // 1. ͼ͸ + vec4 tex = texture(uTexture, vTexCoord); + float alpha = tex.a * uOpacity; + if (alpha <= 0.001) discard; + + // 2. ޹ԴŻ· + if (uLightCount == 0) { + vec3 base = tex.rgb * uColor.rgb; + base = clamp(base, 0.0, 1.0); + FragColor = vec4(base, alpha); + return; + } + + // 3. ռ· + vec3 baseColor = tex.rgb * uColor.rgb; + vec3 ambient = vec3(0.06); // Сⲹ + vec3 lighting = vec3(0.0); + vec3 specularAccum = vec3(0.0); + + // ۻ + for (int i = 0; i < uLightCount; ++i) { + if (uLightsIsAmbient[i] == 1) { + lighting += uLightsColor[i] * uLightsIntensity[i]; + } + } + lighting += ambient; + + // Դ/ǻ + for (int i = 0; i < uLightCount; ++i) { + if (uLightsIsAmbient[i] == 1) continue; + + vec2 toLight = uLightsPos[i] - vWorldPos; + float dist = length(toLight); + // ׼˥ + float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist); + + float radiance = uLightsIntensity[i] * attenuation; + + // (ھ) + float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); + vec3 diff = uLightsColor[i] * radiance * diffuseFactor; + lighting += diff; + + // 򵥸߹ + vec3 lightDir3 = normalize(vec3(toLight, 0.0)); + vec3 viewDir = vec3(0.0, 0.0, 1.0); + vec3 normal = vec3(0.0, 0.0, 1.0); + vec3 reflectDir = reflect(-lightDir3, normal); + float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); + float specIntensity = 0.2; + specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity; + } + + // Ӧù + vec3 totalLighting = min(lighting + specularAccum, vec3(2.0)); + vec3 finalColor = baseColor * totalLighting; + + // 4. 򵥻ģʽ + if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb; + else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb; + else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb); + + // 5. + finalColor = clamp(finalColor, 0.0, 1.0); + FragColor = vec4(finalColor, alpha); +} +)"""; + +class Shader2D::VertexShaders final : public Shader { +public: + virtual ~VertexShaders() = default; + std::string getShaderCode() const override { + return VERTEX_SHADER_SRC; + } + std::string getShaderName() const override { + return "Vertex Shaders"; + } +}; + +class Shader2D::FragmentShaders final : public Shader { +public: + virtual ~FragmentShaders() = default; + std::string getShaderCode() const override { + return FRAGMENT_SHADER_SRC; + } + std::string getShaderName() const override { + return "Fragment shaders"; + } +}; + +const Shader2D::VertexShaders Shader2D::s_vertexShader; +const Shader2D::FragmentShaders Shader2D::s_fragmentShader; + +Shader2D::Shader2D() +{ +} + +const Shader& Shader2D::getVertexShader() const +{ + return s_vertexShader; +} + +const Shader& Shader2D::getFragmentShader() const +{ + return s_fragmentShader; +} + +void Shader2D::setDefaultUniforms(ShaderProgram* program) const +{ +} \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/def/Shader2D.h b/Vivid2DRenderer/systems/sources/def/Shader2D.h new file mode 100644 index 0000000..05233a9 --- /dev/null +++ b/Vivid2DRenderer/systems/sources/def/Shader2D.h @@ -0,0 +1,33 @@ +#pragma once + +// +#include "../CompleteShader.h" +#include "../ShaderProgram.h" +#include + +/** + * @brief Ĭɫʵ (Shader2D) + * ̳ CompleteShader 2D ȾĬɫ + */ +class Shader2D final : public CompleteShader { +public: + Shader2D(); + virtual ~Shader2D() = default; + const Shader& getVertexShader() const override; + const Shader& getFragmentShader() const override; + std::string getShaderName() const override { + return "Vivid2d Shader"; + } + bool isDefaultShader() const override { + return true; + } + void setDefaultUniforms(ShaderProgram* program) const override; +private: + // ڲɫ ( .cpp ļУֻǰ) + class VertexShaders; + class FragmentShaders; + + // ̬Ա洢ɫʵȷðȫ + static const VertexShaders s_vertexShader; + static const FragmentShaders s_fragmentShader; +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/def/SolidColorShader.cpp b/Vivid2DRenderer/systems/sources/def/SolidColorShader.cpp new file mode 100644 index 0000000..1faeeab --- /dev/null +++ b/Vivid2DRenderer/systems/sources/def/SolidColorShader.cpp @@ -0,0 +1,123 @@ +#include "pch.h" + +#include "SolidColorShader.h" + +#include + +static const std::string SOLID_COLOR_VERTEX_SHADER_SRC = R"""( +#version 330 core +layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec2 aTexCoord; + +uniform mat3 uModelMatrix; +uniform mat3 uViewMatrix; +uniform mat3 uProjectionMatrix; + +void main() { + // ʹ 3x3 Ļλ + vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0); + gl_Position = vec4(p.xy, 0.0, 1.0); +} +)"""; + +static const std::string SOLID_COLOR_FRAGMENT_SHADER_SRC = R"""( +#version 330 core +out vec4 FragColor; + +uniform vec4 uColor; +uniform float uOpacity; + +void main() { + // ֱʹɫ + vec4 finalColor = uColor; + finalColor.a *= uOpacity; + + // ͸̫Ƭ + if (finalColor.a <= 0.001) discard; + + FragColor = finalColor; +} +)"""; + +class SolidColorShader::SolidColorVertexShader final : public Shader { +public: + virtual ~SolidColorVertexShader() = default; + std::string getShaderCode() const override { + return SOLID_COLOR_VERTEX_SHADER_SRC; + } + std::string getShaderName() const override { + return "Solid Color Vertex Shader"; + } +}; + +class SolidColorShader::SolidColorFragmentShader final : public Shader { +public: + virtual ~SolidColorFragmentShader() = default; + std::string getShaderCode() const override { + return SOLID_COLOR_FRAGMENT_SHADER_SRC; + } + std::string getShaderName() const override { + return "Solid Color Fragment Shader"; + } +}; + +const SolidColorShader::SolidColorVertexShader SolidColorShader::s_vertexShader; +const SolidColorShader::SolidColorFragmentShader SolidColorShader::s_fragmentShader; + +SolidColorShader::SolidColorShader() +{ +} + +const Shader& SolidColorShader::getVertexShader() const +{ + return s_vertexShader; +} + +const Shader& SolidColorShader::getFragmentShader() const +{ + return s_fragmentShader; +} + +void SolidColorShader::setDefaultUniforms(ShaderProgram* program) const +{ + if (!program) return; + program->use(); + int colorLoc = program->getUniformLocation("uColor"); + if (colorLoc != -1) { + program->setUniform4f(colorLoc, 1.0f, 1.0f, 1.0f, 1.0f); + } + + int opacityLoc = program->getUniformLocation("uOpacity"); + if (opacityLoc != -1) { + program->setUniform1f(opacityLoc, 1.0f); + } + + program->stop(); +} + +void SolidColorShader::setColor(ShaderProgram* program, float r, float g, float b, float a) +{ + if (!program) return; + program->use(); + + int colorLoc = program->getUniformLocation("uColor"); + if (colorLoc != -1) { + program->setUniform4f(colorLoc, r, g, b, a); + } + + program->stop(); +} + +void SolidColorShader::setOpacity(ShaderProgram* program, float opacity) +{ + if (!program) return; + + program->use(); + + int opacityLoc = program->getUniformLocation("uOpacity"); + if (opacityLoc != -1) { + program->setUniform1f(opacityLoc, opacity); + } + + program->stop(); +} \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/def/SolidColorShader.h b/Vivid2DRenderer/systems/sources/def/SolidColorShader.h new file mode 100644 index 0000000..68f0c0c --- /dev/null +++ b/Vivid2DRenderer/systems/sources/def/SolidColorShader.h @@ -0,0 +1,59 @@ +#pragma once + +#include "../CompleteShader.h" +#include "../ShaderProgram.h" +#include + +/** + * @brief ɫɫ (SolidColorShader) + * רڻƴɫ壬ѡп򡢵ͼεȡ + */ +class SolidColorShader final : public CompleteShader { +public: + SolidColorShader(); + virtual ~SolidColorShader() = default; + + // --- CompleteShader ӿʵ --- + + // 뷵 const + const Shader& getVertexShader() const override; + + // 뷵 const + const Shader& getFragmentShader() const override; + + std::string getShaderName() const override { + return "Solid Color Shader"; + } + + bool isDefaultShader() const override { + return false; + } + + // const Ա + void setDefaultUniforms(ShaderProgram* program) const override; + + // --- ɫ/͸ȿƷ --- + + /** + * @brief ɫɫ + * @param program ǰʹõɫ + * @param r, g, b, a ɫ + */ + void setColor(ShaderProgram* program, float r, float g, float b, float a); + + /** + * @brief ɫ͸ + * @param program ǰʹõɫ + * @param opacity ͸ֵ + */ + void setOpacity(ShaderProgram* program, float opacity); + +private: + // ڲɫ ( .cpp ļУֻǰ) + class SolidColorVertexShader; + class SolidColorFragmentShader; + + // ̬Ա洢ɫʵ + static const SolidColorVertexShader s_vertexShader; + static const SolidColorFragmentShader s_fragmentShader; +}; \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/def/TextShader.cpp b/Vivid2DRenderer/systems/sources/def/TextShader.cpp new file mode 100644 index 0000000..ce2e71f --- /dev/null +++ b/Vivid2DRenderer/systems/sources/def/TextShader.cpp @@ -0,0 +1,78 @@ +#include "pch.h" + +#include "TextShader.h" + +#include "../ShaderProgram.h" + +static const std::string TEXT_VERTEX_SHADER_CODE = R"""( +#version 330 core +layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec2 aTexCoord; + +uniform mat3 uProjectionMatrix; + +out vec2 vTexCoord; + +void main() { + vec3 p = uProjectionMatrix * vec3(aPosition, 1.0); + gl_Position = vec4(p.xy, 0.0, 1.0); + vTexCoord = aTexCoord; +} +)"""; + +static const std::string TEXT_FRAGMENT_SHADER_CODE = R"""( +#version 330 core +in vec2 vTexCoord; +out vec4 FragColor; + +uniform sampler2D uTexture; +uniform vec4 uColor; + +void main() { + // ʹ .r ͨȡͨ (λͼ) + float alpha = texture(uTexture, vTexCoord).r; + FragColor = vec4(uColor.rgb, uColor.a * alpha); +} +)"""; + +class TextShader::TextVertexShader : public Shader { +public: + virtual ~TextVertexShader() = default; + std::string getShaderCode() const override { return TEXT_VERTEX_SHADER_CODE; } + std::string getShaderName() const override { return "TextVertexShader"; } +}; +class TextShader::TextFragmentShader : public Shader { +public: + virtual ~TextFragmentShader() = default; + std::string getShaderCode() const override { return TEXT_FRAGMENT_SHADER_CODE; } + std::string getShaderName() const override { return "TextFragmentShader"; } +}; +const TextShader::TextVertexShader TextShader::s_vertexShader; +const TextShader::TextFragmentShader TextShader::s_fragmentShader; +TextShader::TextShader() + : m_color(1.0f, 1.0f, 1.0f, 1.0f) +{} + +const Shader& TextShader::getVertexShader() const +{ + return s_vertexShader; +} + +const Shader& TextShader::getFragmentShader() const +{ + return s_fragmentShader; +} + +void TextShader::setColor(const glm::vec4& color) +{ + this->m_color = color; +} + +void TextShader::setDefaultUniforms(ShaderProgram* program) +{ + if (!program) return; + program->use(); + program->setUniform4f("uColor", m_color.r, m_color.g, m_color.b, m_color.a); + program->setUniform1i("uTexture", 0); + program->stop(); +} \ No newline at end of file diff --git a/Vivid2DRenderer/systems/sources/def/TextShader.h b/Vivid2DRenderer/systems/sources/def/TextShader.h new file mode 100644 index 0000000..e735a9f --- /dev/null +++ b/Vivid2DRenderer/systems/sources/def/TextShader.h @@ -0,0 +1,58 @@ +#pragma once + +// TextShader.h .../systems/sources/def +#include "../CompleteShader.h" +#include "../Shader.h" +#include + +class ShaderProgram; + +/** + * @brief ıɫ (TextShader) + * ̳ CompleteShaderṩıȾɫͳʼá + */ +class TextShader : public CompleteShader { +public: + TextShader(); + virtual ~TextShader() = default; + + /** + * @brief ȡɫơ + */ + std::string getShaderName() const override { return "TextShader"; } + + /** + * @brief ȡɫ塣 + */ + const Shader& getVertexShader() const override; + + /** + * @brief ȡƬɫ塣 + */ + const Shader& getFragmentShader() const override; + + /** + * @brief óɺĬͳһ + */ + void setDefaultUniforms(ShaderProgram* program); + + /** + * @brief ıɫ + */ + void setColor(const glm::vec4& color); + + /** + * @brief ǷΪĬɫ + */ + bool isDefaultShader() const override { return false; } + +private: + glm::vec4 m_color; + + // ̬ɫڷشƣ + class TextVertexShader; + class TextFragmentShader; + + static const TextVertexShader s_vertexShader; + static const TextFragmentShader s_fragmentShader; +}; \ No newline at end of file diff --git a/Vivid2DRenderer/vcpkg.json b/Vivid2DRenderer/vcpkg.json new file mode 100644 index 0000000..4de7ac8 --- /dev/null +++ b/Vivid2DRenderer/vcpkg.json @@ -0,0 +1,7 @@ +{ + "name": "my-opengl-project", + "version-string": "0.1.0", + "dependencies": [ + "stb" + ] +} \ No newline at end of file