diff --git a/examples/ExampleApps/LabelDrawableExampleApp.cpp b/examples/ExampleApps/LabelDrawableExampleApp.cpp index 72271d8..a076eaf 100644 --- a/examples/ExampleApps/LabelDrawableExampleApp.cpp +++ b/examples/ExampleApps/LabelDrawableExampleApp.cpp @@ -24,7 +24,7 @@ #include "Base/EngineConfiguration.hpp" #include "Controller/FreeCamCameraController.hpp" #include "Image/ImageLoaderPng.hpp" -#include "Scene/FontAtlasGenerator.hpp" +#include "Scene/SdfFontAtlasGenerator.hpp" #include "Scene/IFontAtlasGenerator.hpp" #include diff --git a/examples/ExampleApps/TextExampleApp.cpp b/examples/ExampleApps/TextExampleApp.cpp index f6d15c4..1a762fe 100644 --- a/examples/ExampleApps/TextExampleApp.cpp +++ b/examples/ExampleApps/TextExampleApp.cpp @@ -23,7 +23,7 @@ #include "Base/EngineConfiguration.hpp" #include "Controller/FreeCamCameraController.hpp" #include "Image/ImageLoaderPng.hpp" -#include "Scene/FontAtlasGenerator.hpp" +#include "Scene/SdfFontAtlasGenerator.hpp" #include "Scene/IFontAtlasGenerator.hpp" #include "Scene/BitmapFontAtlasGenerator.hpp" #include @@ -39,8 +39,8 @@ namespace OpenVulkano using namespace Math; namespace fs = std::filesystem; - //#define CREATE_NEW_ATLAS 1 - #define CREATE_BITMAP_ATLAS 0 + constexpr int CREATE_BITMAP_ATLAS = 0; + constexpr int CREATE_NEW_ATLAS = 0; class TextExampleAppImpl final : public TextExampleApp { @@ -71,14 +71,15 @@ namespace OpenVulkano m_nodesPool.resize(N * 3); m_drawablesPool.resize(N * 3); -#if CREATE_BITMAP_ATLAS - std::set s = BitmapFontAtlasGenerator::LoadAllGlyphs(fontPath); - BitmapFontAtlasGenerator generator; - generator.GenerateAtlas(fontPath, s); - generator.SaveAtlasMetadataInfo("bitmap_atlas"); -#endif + if constexpr (CREATE_BITMAP_ATLAS) + { + std::set s = BitmapFontAtlasGenerator::LoadAllGlyphs(fontPath); + BitmapFontAtlasGenerator generator; + generator.GenerateAtlas(fontPath, s); + generator.SaveAtlasMetadataInfo("bitmap_atlas"); + } -#if defined(MSDFGEN_AVAILABLE) && defined(CREATE_NEW_ATLAS) +#if defined(MSDFGEN_AVAILABLE) && CREATE_NEW_ATLAS std::set s = SdfFontAtlasGenerator::LoadAllGlyphs(fontPath); msdf_atlas::Charset charset; for (uint32_t c : s) @@ -99,7 +100,7 @@ namespace OpenVulkano { int textIdx = i % texts.size(); TextDrawable* t = nullptr; -#if defined(MSDFGEN_AVAILABLE) && defined(CREATE_NEW_ATLAS) +#if defined(MSDFGEN_AVAILABLE) && CREATE_NEW_ATLAS if (i < texts.size()) { t = new TextDrawable(m_atlasGenerator.GetAtlasData(), texts[textIdx].second); @@ -180,7 +181,7 @@ namespace OpenVulkano PerspectiveCamera m_cam; OpenVulkano::FreeCamCameraController m_camController; #ifdef MSDFGEN_AVAILABLE - SdfFontAtlasGenerator m_atlasGenerator; + SdfFontAtlasGeneratorT m_atlasGenerator; MsdfFontAtlasGenerator m_msdfAtlasGenerator; #endif std::vector m_drawablesPool; diff --git a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp index 533dd80..723dc99 100644 --- a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp @@ -31,17 +31,15 @@ namespace OpenVulkano::Scene return; } - m_atlasData.reset(new AtlasData); - const std::string sourceName = (std::holds_alternative(source) ? std::get<0>(source) : "Binary array"); + m_atlasData = std::make_shared(); const auto& [lib, face] = FontAtlasGeneratorBase::InitFreetype(source); - FT_FaceRec* pFace = face.get(); // TODO: add flexibility to set your own size const Math::Vector2ui cellSize = { 24, 24 }; // set pixel width/height lower than glyph size above, otherwise some glyphs will be cropped or some overlapping will be present - FT_Set_Pixel_Sizes(pFace, 0, cellSize.y - cellSize.y / 3); + FT_Set_Pixel_Sizes(face.get(), 0, cellSize.y - cellSize.y / 3); const double sq = std::sqrt(chset.size()); - const size_t glyphsPerRow = static_cast(sq) + (sq - static_cast(sq) != 0); + const size_t glyphsPerRow = (static_cast(sq)) + (sq - static_cast(sq) != 0); const size_t rows = (chset.size() / glyphsPerRow) + (chset.size() % glyphsPerRow != 0); const Math::Vector2ui atlasResolution = { glyphsPerRow * cellSize.x, rows * cellSize.y }; @@ -49,8 +47,8 @@ namespace OpenVulkano::Scene // TODO: probably also support keeping coordinates as the integer values native to the font file // but since some algorithms have already been implemented for EM_NORMALIZED mode, currently there is no support for default font metrics (ints) // The coordinates will be normalized to the em size, i.e. 1 = 1 em - const double scaleFactor = (1. / pFace->units_per_EM); - SetupAtlasData(atlasResolution, pFace->height * scaleFactor, FontAtlasType::BITMAP); + const double scaleFactor = (1. / face->units_per_EM); + SetupAtlasData(atlasResolution, face->height * scaleFactor, FontAtlasType::BITMAP); size_t loadedGlyphs = 0; FT_Error error = 0; @@ -59,10 +57,10 @@ namespace OpenVulkano::Scene Math::Vector2ui gridPos = { 0, 0 }; for (uint32_t codepoint : chset) { - error = FT_Load_Char(pFace, codepoint, FT_LOAD_RENDER); + error = FT_Load_Char(face.get(), codepoint, FT_LOAD_RENDER); if (error) { - Logger::APP->error("FT_Load_Char for codepoint {} failed while reading from source {}", codepoint, sourceName); + Logger::APP->error("FT_Load_Char for codepoint {} has failed. {}", codepoint, GetFreetypeErrorDescription(error)); continue; } diff --git a/openVulkanoCpp/Scene/FontAtlasGeneratorBase.cpp b/openVulkanoCpp/Scene/FontAtlasGeneratorBase.cpp index e9528fd..3fba677 100644 --- a/openVulkanoCpp/Scene/FontAtlasGeneratorBase.cpp +++ b/openVulkanoCpp/Scene/FontAtlasGeneratorBase.cpp @@ -9,20 +9,19 @@ #define STBI_MSC_SECURE_CRT #define STB_IMAGE_WRITE_IMPLEMENTATION #include -#include #include #include namespace OpenVulkano::Scene { - std::pair + std::pair FontAtlasGeneratorBase::InitFreetype(const std::variant>& source) { FT_Library library; auto error = FT_Init_FreeType(&library); if (error) { - throw std::runtime_error("Could not initalize freetype library\n"); + throw std::runtime_error(fmt::format("Could not initalize freetype library. {}", GetFreetypeErrorDescription(error))); } FT_Face face; if (std::holds_alternative(source)) @@ -40,7 +39,7 @@ namespace OpenVulkano::Scene } else if (error) { - throw std::runtime_error("Font file could not be opened or read or it's corrupted\n"); + throw std::runtime_error(fmt::format("Font file could not be opened or read or it's corrupted. {}", GetFreetypeErrorDescription(error))); } // some fancy font without unicode charmap @@ -48,9 +47,17 @@ namespace OpenVulkano::Scene { throw std::runtime_error("Selected font doesn't contain unicode charmap"); } - return std::make_pair(FT_LIB_REC(library), FT_FACE_REC(face)); + return { FtLibraryRecPtr(library), FtFaceRecPtr(face) }; } + std::string FontAtlasGeneratorBase::GetFreetypeErrorDescription(FT_Error error) + { + if (const char* s = FT_Error_String(error)) + { + return s; + } + return fmt::format("Error code is {}", error); + } void FontAtlasGeneratorBase::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const { @@ -81,82 +88,75 @@ namespace OpenVulkano::Scene fs.write(reinterpret_cast(&packedFlag), sizeof(uint32_t)); } - void FontAtlasGeneratorBase::SavePng(const std::string& output) const + void FontAtlasGeneratorBase::SavePng(std::string output) const { stbi_flip_vertically_on_write(1); - if (std::filesystem::path(output).extension() == ".png") + if (std::filesystem::path(output).extension() != ".png") { - stbi_write_png(output.c_str(), m_atlasData->img->resolution.x, m_atlasData->img->resolution.y, m_channelsCount, m_atlasData->img->data.Data(), - m_channelsCount * m_atlasData->img->resolution.x); - } - else - { - stbi_write_png((output + ".png").c_str(), m_atlasData->img->resolution.x, m_atlasData->img->resolution.y, - m_channelsCount, m_atlasData->img->data.Data(), m_channelsCount * m_atlasData->img->resolution.x); + output += ".png"; } + stbi_write_png(output.c_str(), m_atlasData->img->resolution.x, m_atlasData->img->resolution.y, m_channelsCount, + m_atlasData->img->data.Data(), m_channelsCount * m_atlasData->img->resolution.x); } void FontAtlasGeneratorBase::SetupAtlasData(Math::Vector2ui textureResolution, double lineHeight, FontAtlasType::Type atlasType) { // generate texture + m_atlasData->img = std::make_unique(); + m_atlasData->img->resolution = Math::Vector3ui(textureResolution, 1); if (m_channelsCount == 1) { - m_atlasData->img = std::make_unique(); m_atlasData->img->data = Array(textureResolution.x * textureResolution.y); - m_atlasData->img->resolution = Math::Vector3ui(textureResolution, 1); m_atlasData->img->dataFormat = OpenVulkano::DataFormat::R8_UNORM; } else { - m_atlasData->img = std::make_unique(); // RGBA m_atlasData->img->data = Array(textureResolution.x * textureResolution.y * 4); - m_atlasData->img->resolution = Math::Vector3ui(textureResolution, 1); m_atlasData->img->dataFormat = OpenVulkano::DataFormat::R8G8B8A8_UNORM; } m_atlasData->texture.resolution = m_atlasData->img->resolution; m_atlasData->texture.textureBuffer = m_atlasData->img->data.Data(); m_atlasData->texture.format = m_atlasData->img->dataFormat; m_atlasData->texture.size = m_atlasData->img->data.Size(); - m_atlasData->texture.m_samplerConfig = &SamplerConfig::NEAREST; m_atlasData->meta.atlasType = atlasType; m_atlasData->meta.lineHeight = lineHeight; + if (atlasType == FontAtlasType::BITMAP) + { + m_atlasData->texture.m_samplerConfig = &SamplerConfig::NEAREST; + } } void FontAtlasGeneratorBase::SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size, const Math::AABB& aabb, double advance) { - double bearingX = bearing.x; - double bearingY = bearing.y; - double w = size.x; - double h = size.y; - double l = aabb.min.x; - double r = aabb.max.x; - double t = aabb.max.y; - double b = aabb.min.y; + const double bearingX = bearing.x; + const double bearingY = bearing.y; + const double w = size.x; + const double h = size.y; + const double l = aabb.min.x; + const double r = aabb.max.x; + const double t = aabb.max.y; + const double b = aabb.min.y; info.xyz[0].x = bearingX; info.xyz[0].y = h - bearingY; - info.xyz[0].z = 0; info.uv[0].x = l / m_atlasData->texture.resolution.x; info.uv[0].y = b / m_atlasData->texture.resolution.y; info.xyz[1].x = bearingX + w; info.xyz[1].y = h - bearingY; - info.xyz[1].z = 0; info.uv[1].x = r / m_atlasData->texture.resolution.x; info.uv[1].y = b / m_atlasData->texture.resolution.y; info.xyz[2].x = bearingX + w; info.xyz[2].y = bearingY; //h - bearingY + h; - info.xyz[2].z = 0; info.uv[2].x = r / m_atlasData->texture.resolution.x; info.uv[2].y = t / m_atlasData->texture.resolution.y; info.xyz[3].x = bearingX; info.xyz[3].y = bearingY; - info.xyz[3].z = 0; info.uv[3].x = l / m_atlasData->texture.resolution.x; info.uv[3].y = t / m_atlasData->texture.resolution.y; diff --git a/openVulkanoCpp/Scene/FontAtlasGeneratorBase.hpp b/openVulkanoCpp/Scene/FontAtlasGeneratorBase.hpp index e1e1ea7..c4eae22 100644 --- a/openVulkanoCpp/Scene/FontAtlasGeneratorBase.hpp +++ b/openVulkanoCpp/Scene/FontAtlasGeneratorBase.hpp @@ -8,8 +8,7 @@ #include "IFontAtlasGenerator.hpp" #include "Math/AABB.hpp" -#include -#include FT_FREETYPE_H +#include "FreetypeHelper.hpp" #include #include @@ -17,38 +16,19 @@ namespace OpenVulkano::Scene { class FontAtlasGeneratorBase : public IFontAtlasGenerator { - struct LibDeleter - { - void operator()(FT_Library lib) - { - FT_Done_FreeType(lib); - } - }; - - struct FaceDeleter - { - void operator()(FT_Face face) - { - FT_Done_Face(face); - } - }; - public: - using FT_LIB_REC = std::unique_ptr; - using FT_FACE_REC = std::unique_ptr; - FontAtlasGeneratorBase(int channelsCount) : m_channelsCount(channelsCount) {} void SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile = true) const override; std::shared_ptr GetAtlasData() const { return m_atlasData; } int GetAtlasChannelsCount() const { return m_channelsCount; } - static std::set LoadAllGlyphs(const std::variant>& data); protected: - void SavePng(const std::string& output) const; + void SavePng(std::string output) const; void SetupAtlasData(Math::Vector2ui textureResolution, double lineHeight, FontAtlasType::Type atlasType); void SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size, const Math::AABB& aabb, double advance); - static std::pair InitFreetype(const std::variant>& source); + static std::string GetFreetypeErrorDescription(FT_Error error); + static std::pair InitFreetype(const std::variant>& source); protected: int m_channelsCount; std::shared_ptr m_atlasData; diff --git a/openVulkanoCpp/Scene/FreetypeHelper.hpp b/openVulkanoCpp/Scene/FreetypeHelper.hpp new file mode 100644 index 0000000..7860f50 --- /dev/null +++ b/openVulkanoCpp/Scene/FreetypeHelper.hpp @@ -0,0 +1,28 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include FT_FREETYPE_H +#include + +namespace OpenVulkano::Scene +{ + struct LibDeleter + { + void operator()(FT_Library lib) { FT_Done_FreeType(lib); } + }; + + struct FaceDeleter + { + void operator()(FT_Face face) { FT_Done_Face(face); } + }; + + using FtLibraryRecPtr = std::unique_ptr; + using FtFaceRecPtr = std::unique_ptr; + +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp b/openVulkanoCpp/Scene/SdfFontAtlasGenerator.cpp similarity index 82% rename from openVulkanoCpp/Scene/FontAtlasGenerator.cpp rename to openVulkanoCpp/Scene/SdfFontAtlasGenerator.cpp index 179bfcf..e806b76 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/SdfFontAtlasGenerator.cpp @@ -6,7 +6,7 @@ #if __has_include("msdfgen.h") -#include "FontAtlasGenerator.hpp" +#include "SdfFontAtlasGenerator.hpp" #include "Base/Logger.hpp" #include #include @@ -17,11 +17,11 @@ namespace OpenVulkano::Scene using namespace msdfgen; using namespace msdf_atlas; - FontAtlasGeneratorConfig FontAtlasGeneratorConfig::sdfDefaultConfig = { 42, 1.0, 5 }; - FontAtlasGeneratorConfig FontAtlasGeneratorConfig::msdfDefaultConfig = { 32, 1.0, 3 }; + SdfFontAtlasGeneratorConfig SdfFontAtlasGeneratorConfig::sdfDefaultConfig = { 42, 1.0, 5 }; + SdfFontAtlasGeneratorConfig SdfFontAtlasGeneratorConfig::msdfDefaultConfig = { 32, 1.0, 3 }; template - void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set& charset, + void SdfFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set& charset, const std::optional& pngOutput) { FreetypeHandle* ft; @@ -32,14 +32,14 @@ namespace OpenVulkano::Scene Generate(ft, font, s, pngOutput); } - template FontAtlasGenerator::FontAtlasGenerator() : FontAtlasGeneratorBase(Channels) + template SdfFontAtlasGenerator::SdfFontAtlasGenerator() : FontAtlasGeneratorBase(Channels) { - if constexpr (Channels == 1) m_config = FontAtlasGeneratorConfig::sdfDefaultConfig; - else m_config = FontAtlasGeneratorConfig::msdfDefaultConfig; + if constexpr (Channels == 1) m_config = SdfFontAtlasGeneratorConfig::sdfDefaultConfig; + else m_config = SdfFontAtlasGeneratorConfig::msdfDefaultConfig; } template - void FontAtlasGenerator::GenerateAtlas(const Array& fontData, const std::set& charset, + void SdfFontAtlasGenerator::GenerateAtlas(const Array& fontData, const std::set& charset, const std::optional& pngOutput) { FreetypeHandle* ft; @@ -51,7 +51,7 @@ namespace OpenVulkano::Scene } template - void FontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, + void SdfFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const Charset& charset, const std::optional& pngOutput) { // TODO: dynamic atlas and add only those symbols which are not present yet in current atlas @@ -62,7 +62,7 @@ namespace OpenVulkano::Scene } template - void FontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, + void SdfFontAtlasGenerator::GenerateAtlas(const msdfgen::byte* fontData, int length, const Charset& charset, const std::optional& pngOutput) { @@ -73,7 +73,7 @@ namespace OpenVulkano::Scene } template - void FontAtlasGenerator::InitFreetypeFromFile(FreetypeHandle*& ft, FontHandle*& font, + void SdfFontAtlasGenerator::InitFreetypeFromFile(FreetypeHandle*& ft, FontHandle*& font, const std::string& fontFile) { ft = initializeFreetype(); @@ -88,7 +88,7 @@ namespace OpenVulkano::Scene } template - void FontAtlasGenerator::InitFreetypeFromBuffer(FreetypeHandle*& ft, FontHandle*& font, + void SdfFontAtlasGenerator::InitFreetypeFromBuffer(FreetypeHandle*& ft, FontHandle*& font, const msdfgen::byte* fontData, int length) { ft = initializeFreetype(); @@ -103,7 +103,7 @@ namespace OpenVulkano::Scene } template - void FontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, + void SdfFontAtlasGenerator::Generate(FreetypeHandle* ft, FontHandle* font, const Charset& chset, const std::optional& pngOutput) { m_atlasData.reset(new AtlasData); @@ -200,7 +200,7 @@ namespace OpenVulkano::Scene deinitializeFreetype(ft); } - template class FontAtlasGenerator<1>; - template class FontAtlasGenerator<3>; + template class SdfFontAtlasGenerator<1>; + template class SdfFontAtlasGenerator<3>; } #endif \ No newline at end of file diff --git a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp b/openVulkanoCpp/Scene/SdfFontAtlasGenerator.hpp similarity index 86% rename from openVulkanoCpp/Scene/FontAtlasGenerator.hpp rename to openVulkanoCpp/Scene/SdfFontAtlasGenerator.hpp index 1ae7863..d368a48 100644 --- a/openVulkanoCpp/Scene/FontAtlasGenerator.hpp +++ b/openVulkanoCpp/Scene/SdfFontAtlasGenerator.hpp @@ -20,17 +20,17 @@ namespace OpenVulkano::Scene { - struct FontAtlasGeneratorConfig + struct SdfFontAtlasGeneratorConfig { - static FontAtlasGeneratorConfig sdfDefaultConfig; - static FontAtlasGeneratorConfig msdfDefaultConfig; + static SdfFontAtlasGeneratorConfig sdfDefaultConfig; + static SdfFontAtlasGeneratorConfig msdfDefaultConfig; int glyphSize; double miterLimit; msdfgen::Range pixelRange; }; template - class FontAtlasGenerator : public FontAtlasGeneratorBase + class SdfFontAtlasGenerator final : public FontAtlasGeneratorBase { private: using SdfGenerator = msdf_atlas::ImmediateAtlasGenerator>; public: using Generator = std::conditional::type; - using Config = FontAtlasGeneratorConfig; + using Config = SdfFontAtlasGeneratorConfig; static constexpr int channelsCount = (Channels == 1 ? 1 : 4); - FontAtlasGenerator(); + SdfFontAtlasGenerator(); void GenerateAtlas(const std::string& fontFile, const std::set& charset, const std::optional& pngOutput = std::nullopt) override; void GenerateAtlas(const Array& fontData, const std::set& charset, @@ -62,7 +62,7 @@ namespace OpenVulkano::Scene private: Config m_config; }; - using SdfFontAtlasGenerator = FontAtlasGenerator<1>; - using MsdfFontAtlasGenerator = FontAtlasGenerator<3>; + using SdfFontAtlasGeneratorT = SdfFontAtlasGenerator<1>; + using MsdfFontAtlasGenerator = SdfFontAtlasGenerator<3>; } #endif diff --git a/openVulkanoCpp/Scene/TextDrawable.cpp b/openVulkanoCpp/Scene/TextDrawable.cpp index 2f083aa..adffe2b 100644 --- a/openVulkanoCpp/Scene/TextDrawable.cpp +++ b/openVulkanoCpp/Scene/TextDrawable.cpp @@ -149,6 +149,10 @@ namespace OpenVulkano::Scene m_cfg = config; m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); m_uniBuffer.binding.stageFlags = ShaderProgramType::FRAGMENT; + if (m_atlasData->meta.atlasType == FontAtlasType::BITMAP) + { + m_material.texture->m_samplerConfig = &SamplerConfig::NEAREST; + } } TextDrawable::TextDrawable(const std::shared_ptr& atlasData, const TextConfig& config) @@ -268,10 +272,6 @@ namespace OpenVulkano::Scene ++i; } m_bbox.Init(bmin, bmax); - if (m_atlasData->meta.atlasType == FontAtlasType::BITMAP) - { - m_material.texture->m_samplerConfig = &SamplerConfig::NEAREST; - } SimpleDrawable::Init(m_shader, &m_geometry, &m_material, &m_uniBuffer); } diff --git a/openVulkanoCpp/Shader/text.frag b/openVulkanoCpp/Shader/text.frag index c960875..b906e40 100644 --- a/openVulkanoCpp/Shader/text.frag +++ b/openVulkanoCpp/Shader/text.frag @@ -20,12 +20,6 @@ layout(set = 3, binding = 0) uniform TextConfig void main() { - // interesting results - //float distance = texture(texSampler, texCoord).r; - //float alpha = smoothstep(textConfig.threshold - textConfig.smoothing, textConfig.threshold + textConfig.smoothing, distance); - //outColor = vec4(textConfig.textColor) * alpha; - - vec4 sampled = vec4(1.0, 1.0, 1.0, texture(texSampler, texCoord).r); outColor = vec4(textConfig.textColor) * sampled; }