Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 66 additions & 40 deletions core/2d/Label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,15 @@ Label::BatchCommand::BatchCommand()
textCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
shadowCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
shadowCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
outLineCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
outLineCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
effectCommand.setDrawType(CustomCommand::DrawType::ELEMENT);
effectCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE);
}

Label::BatchCommand::~BatchCommand()
{
AX_SAFE_RELEASE(textCommand.getPipelineDescriptor().programState);
AX_SAFE_RELEASE(shadowCommand.getPipelineDescriptor().programState);
AX_SAFE_RELEASE(outLineCommand.getPipelineDescriptor().programState);
AX_SAFE_RELEASE(effectCommand.getPipelineDescriptor().programState);
}

void Label::BatchCommand::setProgramState(backend::ProgramState* programState)
Expand All @@ -225,14 +225,14 @@ void Label::BatchCommand::setProgramState(backend::ProgramState* programState)
AX_SAFE_RELEASE(programStateShadow);
programStateShadow = programState->clone();

auto& programStateOutline = outLineCommand.getPipelineDescriptor().programState;
auto& programStateOutline = effectCommand.getPipelineDescriptor().programState;
AX_SAFE_RELEASE(programStateOutline);
programStateOutline = programState->clone();
}

std::array<CustomCommand*, 3> Label::BatchCommand::getCommandArray()
{
return std::array<CustomCommand*, 3>{&textCommand, &shadowCommand, &outLineCommand};
return std::array<CustomCommand*, 3>{&textCommand, &shadowCommand, &effectCommand};
}

Label* Label::create()
Expand Down Expand Up @@ -604,6 +604,7 @@ void Label::reset()
TTFConfig temp;
_fontConfig = temp;
_outlineSize = 0.f;
_glowRadius = 0.f;

_bmFontPath = "";
_bmSubTextureKey = "";
Expand Down Expand Up @@ -775,6 +776,7 @@ void Label::updateUniformLocations()
_textureLocation = _programState->getUniformLocation(backend::Uniform::TEXTURE);
_textColorLocation = _programState->getUniformLocation(backend::Uniform::TEXT_COLOR);
_effectColorLocation = _programState->getUniformLocation(backend::Uniform::EFFECT_COLOR);
_effectWidthLocation = _programState->getUniformLocation(backend::Uniform::EFFECT_WIDTH);
_passLocation = _programState->getUniformLocation(backend::Uniform::LABEL_PASS);
_distanceSpreadLocation = _programState->getUniformLocation(backend::Uniform::DISTANCE_SPREAD);
}
Expand Down Expand Up @@ -1286,6 +1288,7 @@ bool Label::setTTFConfigInternal(const TTFConfig& ttfConfig)
unsigned int mods = 0;
mods |= (ttfConfig.fontSize != _fontConfig.fontSize);
mods |= (ttfConfig.outlineSize != _fontConfig.outlineSize);
mods |= (ttfConfig.distanceFieldEnabled != _fontConfig.distanceFieldEnabled);
_fontConfig = ttfConfig;
return updateTTFConfigInternal(mods);
}
Expand Down Expand Up @@ -1383,17 +1386,17 @@ void Label::scaleFontSize(float fontSize)
}
}

void Label::enableGlow(const Color4B& glowColor)
void Label::enableGlow(const Color4B& glowColor, float glowRadius)
{
if (_currentLabelType == LabelType::TTF)
if (glowRadius <= 0)
glowRadius = FontFreeType::DistanceMapSpread / 2.0f;

if (_currentLabelType == LabelType::TTF && (glowRadius > 0 || _currLabelEffect == LabelEffect::GLOW))
{
_glowRadius = glowRadius;

auto config = _fontConfig;
int mods = 0;
if (config.outlineSize > 0)
{
config.outlineSize = 0;
++mods;
}
// Note: axmol only support Glow effect in SDF rendering mode
if (!_fontConfig.distanceFieldEnabled)
{
Expand Down Expand Up @@ -1958,9 +1961,9 @@ void Label::updateEffectUniforms(BatchCommand& batch,
{
pass = 2;
Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
auto* programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState;
programStateShadow->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4));
programStateShadow->setUniform(_passLocation, &pass, sizeof(pass));
auto shadowPS = batch.shadowCommand.getPipelineDescriptor().programState;
shadowPS->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4));
shadowPS->setUniform(_passLocation, &pass, sizeof(pass));
batch.shadowCommand.init(_globalZOrder);
renderer->addCommand(&batch.shadowCommand);
}
Expand All @@ -1970,22 +1973,23 @@ void Label::updateEffectUniforms(BatchCommand& batch,
// outline pass
{
pass = 1;
effectColor.w = (_outlineSize > 0 ? _outlineSize : _fontConfig.outlineSize) *
_director->getContentScaleFactor();
auto& outlinePS = batch.outLineCommand.getPipelineDescriptor().programState;
updateBuffer(textureAtlas, batch.outLineCommand);
outlinePS->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
outlinePS->setUniform(_passLocation, &pass, sizeof(pass));
float outlineWidth = (_outlineSize > 0 ? _outlineSize : _fontConfig.outlineSize) *
_director->getContentScaleFactor();
float distanceFieldSpread = FontFreeType::DistanceMapSpread * _director->getContentScaleFactor();
outlinePS->setUniform(_distanceSpreadLocation, &distanceFieldSpread, sizeof(distanceFieldSpread));
batch.outLineCommand.init(_globalZOrder);
renderer->addCommand(&batch.outLineCommand);
auto effectPS = batch.effectCommand.getPipelineDescriptor().programState;
updateBuffer(textureAtlas, batch.effectCommand);
effectPS->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
effectPS->setUniform(_effectWidthLocation, &outlineWidth, sizeof(float));
effectPS->setUniform(_distanceSpreadLocation, &distanceFieldSpread, sizeof(distanceFieldSpread));
effectPS->setUniform(_passLocation, &pass, sizeof(pass));
batch.effectCommand.init(_globalZOrder);
renderer->addCommand(&batch.effectCommand);
}

// text pass
{
pass = 0;
auto* textPS = batch.textCommand.getPipelineDescriptor().programState;
auto textPS = batch.textCommand.getPipelineDescriptor().programState;

textPS->setUniform(_effectColorLocation, &effectColor, sizeof(effectColor));
textPS->setUniform(_passLocation, &pass, sizeof(pass));
Expand All @@ -1996,12 +2000,12 @@ void Label::updateEffectUniforms(BatchCommand& batch,
// outline pass
{
pass = 1;
updateBuffer(textureAtlas, batch.outLineCommand);
auto* outlinePS = batch.outLineCommand.getPipelineDescriptor().programState;
outlinePS->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
outlinePS->setUniform(_passLocation, &pass, sizeof(pass));
batch.outLineCommand.init(_globalZOrder);
renderer->addCommand(&batch.outLineCommand);
updateBuffer(textureAtlas, batch.effectCommand);
auto effectPS = batch.effectCommand.getPipelineDescriptor().programState;
effectPS->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
effectPS->setUniform(_passLocation, &pass, sizeof(pass));
batch.effectCommand.init(_globalZOrder);
renderer->addCommand(&batch.effectCommand);
}

// text pass
Expand All @@ -2020,29 +2024,51 @@ void Label::updateEffectUniforms(BatchCommand& batch,
if (_shadowEnabled)
{
Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
auto* programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState;
programStateShadow->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4));
auto shadowPS = batch.shadowCommand.getPipelineDescriptor().programState;
shadowPS->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4));
batch.shadowCommand.init(_globalZOrder);
renderer->addCommand(&batch.shadowCommand);
}
}
break;
case LabelEffect::GLOW:
{
// draw shadow
int pass = 0;
// shadow pass
if (_shadowEnabled)
{
pass = 2;
Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
auto* programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState;
programStateShadow->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4));
programStateShadow->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4));
auto shadowPS = batch.shadowCommand.getPipelineDescriptor().programState;
shadowPS->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4));
shadowPS->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4));
shadowPS->setUniform(_passLocation, &pass, sizeof(pass));
batch.shadowCommand.init(_globalZOrder);
renderer->addCommand(&batch.shadowCommand);
}

// glow pass
{
pass = 1;
const float glowRadius = _glowRadius * _director->getContentScaleFactor();
const float distanceFieldSpread = FontFreeType::DistanceMapSpread * _director->getContentScaleFactor();
Vec4 effectColor(_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
updateBuffer(textureAtlas, batch.effectCommand);
auto effectPS = batch.effectCommand.getPipelineDescriptor().programState;
effectPS->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
effectPS->setUniform(_effectWidthLocation, &glowRadius, sizeof(float));
effectPS->setUniform(_distanceSpreadLocation, &distanceFieldSpread, sizeof(distanceFieldSpread));
effectPS->setUniform(_passLocation, &pass, sizeof(pass));
batch.effectCommand.init(_globalZOrder);
renderer->addCommand(&batch.effectCommand);
}

// text pass
pass = 0;
Vec4 effectColor(_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
batch.textCommand.getPipelineDescriptor().programState->setUniform(_effectColorLocation, &effectColor,
sizeof(Vec4));
auto textPS = batch.textCommand.getPipelineDescriptor().programState;
textPS->setUniform(_effectColorLocation, &effectColor, sizeof(Vec4));
textPS->setUniform(_passLocation, &pass, sizeof(pass));
}
break;
default:
Expand Down Expand Up @@ -2151,7 +2177,7 @@ void Label::draw(Renderer* renderer, const Mat4& transform, uint32_t flags)
}
batch.textCommand.getPipelineDescriptor().programState->setUniform(_mvpMatrixLocation, matrixMVP.m,
sizeof(matrixMVP.m));
batch.outLineCommand.getPipelineDescriptor().programState->setUniform(_mvpMatrixLocation, matrixMVP.m,
batch.effectCommand.getPipelineDescriptor().programState->setUniform(_mvpMatrixLocation, matrixMVP.m,
sizeof(matrixMVP.m));
updateEffectUniforms(batch, textureAtlas, renderer, transform);
}
Expand Down
10 changes: 7 additions & 3 deletions core/2d/Label.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ typedef struct _ttfConfig
GlyphCollection glyphs;
float fontSize; // The desired render font size
int faceSize; // The original face size of font, used when distanceFieldEnabled == true
int outlineSize;
int outlineSize; // The Outline width used in non‑SDF rendering; ignored when distance field is enabled

bool distanceFieldEnabled;
bool italics;
Expand Down Expand Up @@ -442,7 +442,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol
* Enable glow effect to Label.
* @warning Limiting use to only when the Label created with true type font.
*/
virtual void enableGlow(const Color4B& glowColor);
virtual void enableGlow(const Color4B& glowColor, float glowRadius = -1);

/**
* Enable italics rendering
Expand Down Expand Up @@ -503,6 +503,8 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol
*/
float getOutlineSize() const { return _outlineSize; }

float getGlowRadius() const { return _glowRadius; }

/**
* Return current effect type.
*/
Expand Down Expand Up @@ -759,7 +761,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol
std::array<CustomCommand*, 3> getCommandArray();

CustomCommand textCommand;
CustomCommand outLineCommand;
CustomCommand effectCommand; // effect: outline or glow
CustomCommand shadowCommand;
};

Expand Down Expand Up @@ -857,6 +859,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol
LabelType _currentLabelType;
int _numberOfLines;
float _outlineSize;
float _glowRadius;
float _systemFontSize;

int _lengthOfString;
Expand Down Expand Up @@ -943,6 +946,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol
backend::UniformLocation _textureLocation;
backend::UniformLocation _textColorLocation;
backend::UniformLocation _effectColorLocation;
backend::UniformLocation _effectWidthLocation;
backend::UniformLocation _passLocation;
backend::UniformLocation _distanceSpreadLocation;

Expand Down
1 change: 1 addition & 0 deletions core/renderer/backend/ShaderModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum Uniform : uint32_t
TEXTURE3,
TEXT_COLOR,
EFFECT_COLOR,
EFFECT_WIDTH,
LABEL_PASS,
DISTANCE_SPREAD,
UNIFORM_MAX // Maximum uniforms
Expand Down
1 change: 1 addition & 0 deletions core/renderer/backend/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ static constexpr auto UNIFORM_NAME_TEXTURE2 = "u_tex2"sv;
static constexpr auto UNIFORM_NAME_TEXTURE3 = "u_tex3"sv;
static constexpr auto UNIFORM_NAME_TEXT_COLOR = "u_textColor"sv;
static constexpr auto UNIFORM_NAME_EFFECT_COLOR = "u_effectColor"sv;
static constexpr auto UNIFORM_NAME_EFFECT_WIDTH = "u_effectWidth"sv;
static constexpr auto UNIFORM_NAME_LABEL_PASS = "u_labelPass"sv;
static constexpr auto UNIFORM_NAME_DISTANCE_SPREAD = "u_distanceSpread"sv;

Expand Down
3 changes: 3 additions & 0 deletions core/renderer/backend/metal/ShaderModuleMTL.mm
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ of this software and associated documentation files (the "Software"), to deal
/// u_effectColor
_builtinUniforms[Uniform::EFFECT_COLOR] = getUniformInfo(UNIFORM_NAME_EFFECT_COLOR);

/// u_effectWidth
_builtinUniforms[Uniform::EFFECT_WIDTH] = getUniformInfo(UNIFORM_NAME_EFFECT_WIDTH);

/// u_textPass
_builtinUniforms[Uniform::LABEL_PASS] = getUniformInfo(UNIFORM_NAME_LABEL_PASS);

Expand Down
3 changes: 3 additions & 0 deletions core/renderer/backend/opengl/ProgramGL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ void ProgramGL::setBuiltinLocations()
/// u_effectColor
_builtinUniformLocation[Uniform::EFFECT_COLOR] = getUniformLocation(UNIFORM_NAME_EFFECT_COLOR);

/// u_effectWidth
_builtinUniformLocation[Uniform::EFFECT_WIDTH] = getUniformLocation(UNIFORM_NAME_EFFECT_WIDTH);

/// u_textPass
_builtinUniformLocation[Uniform::LABEL_PASS] = getUniformLocation(UNIFORM_NAME_LABEL_PASS);

Expand Down
35 changes: 27 additions & 8 deletions core/renderer/shaders/label_distanceGlow.frag
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#version 310 es
precision highp float;

#include "base.glsl"

layout(location = COLOR0) in vec4 v_color;
Expand All @@ -9,19 +8,39 @@ layout(location = TEXCOORD0) in vec2 v_texCoord;
layout(binding = 0) uniform sampler2D u_tex0;

layout(std140) uniform fs_ub {
vec4 u_textColor;
vec4 u_effectColor;
vec4 u_textColor; // text color
vec4 u_effectColor; // effect color (rgb = color, a = intensity)
float u_effectWidth; // glow width in pixels
float u_distanceSpread;// default: 6.0
int u_labelPass; // 0: text, 1: glow, 2: shadow
};

layout(location = SV_Target0) out vec4 FragColor;

void main()
{
float dist = texture(u_tex0, v_texCoord).x;
float smoothing = FWIDTH(dist);
float smoothing = fwidth(dist);

float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
float mu = smoothstep(0.5, 1.0, sqrt(dist));
vec4 color = u_effectColor*(1.0-alpha) + u_textColor*alpha;
FragColor = v_color * vec4(color.rgb, max(alpha,mu)*color.a);
if (u_labelPass == 0) {
// Text pass: solid core
float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
FragColor = v_color * vec4(u_textColor.rgb, u_textColor.a * alpha);
}
else if (u_labelPass == 1) {
// Glow pass: soft halo around text
// Use distance field falloff to create smooth glow
// Map u_effectWidth (in "visual units") into SDF domain
float glowWidth = clamp(u_effectWidth / u_distanceSpread, 0.0, 1.0);
float pivot = 0.5 + (1.0 - glowWidth);
float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
float mu = smoothstep(0.5, pivot, sqrt(dist));
vec4 color = u_effectColor * (1.0 - alpha) + u_textColor * alpha;
FragColor = v_color * vec4(color.rgb, max(alpha, mu) * color.a);
}
else {
// Shadow pass: simple fill
float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
FragColor = v_color * vec4(u_effectColor.rgb, u_effectColor.a * alpha);
}
}
9 changes: 5 additions & 4 deletions core/renderer/shaders/label_distanceOutline.frag
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ layout(binding = 0) uniform sampler2D u_tex0;
layout(std140) uniform fs_ub {
vec4 u_textColor;
vec4 u_effectColor;
int u_labelPass; // 0: text, 1: outline, 2: shadow
float u_effectWidth; // outline thickness in pixels
float u_distanceSpread; // default: 6.0
int u_labelPass; // 0: text, 1: outline, 2: shadow
};

layout(location = SV_Target0) out vec4 FragColor;
Expand All @@ -33,9 +34,9 @@ void main()
}
else if (u_labelPass == 1) {
// Outline pass: only draw outer ring, exclude text core
float outlineSize = clamp(u_effectColor.w * outlineScale, 0.0, u_distanceSpread * 0.5);
float thickness = outlineSize / (2.0 * u_distanceSpread);
float pivot = 0.5 - thickness;
// clamp(u_effectWidth * outlineScale, 0.0, u_distanceSpread * 0.5);
float thickness = u_effectWidth * outlineScale;
float pivot = 0.5 - (thickness / (2.0 * u_distanceSpread));

float textAlpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, dist);
float outlineAlpha = smoothstep(pivot - smoothing, pivot + smoothing, dist);
Expand Down
Loading
Loading