In cocos2d-x there is already a LayerRadialGradient
.
Works well, but I've other shaders running on the my game scene, so performance in a problem now. It's using a shader.
I want to replace it with just a Sprite with generated radial gradient texture.
Here is example of LayerRadialGradient
drawing result:
LayerRadialGradient::create(
Color4B(179, 232, 184, 89),
Color4B(0, 90, 128, 0),
620,
center,
0.0f);
I've some code suggestions like:
static Color4B Lerp(const Color4B& value1, const Color4B& value2, float amount)
{
amount = clampf(amount, 0.0f, 1.0f);
return Color4B(
(int)MathUtil::lerp(value1.r, value2.r, amount),
(int)MathUtil::lerp(value1.g, value2.g, amount),
(int)MathUtil::lerp(value1.b, value2.b, amount),
(int)MathUtil::lerp(value1.a, value2.a, amount)
);
}
static float Falloff(float distance, float maxDistance, float scalingFactor)
{
if (distance <= maxDistance / 3)
{
return scalingFactor * (1 - 3 * distance * distance / (maxDistance * maxDistance));
}
else if (distance <= maxDistance)
{
float x = 1 - distance / maxDistance;
return (3.f / 2.f) * scalingFactor * x * x;
}
else
return 0;
}
static float Falloff(float distance, float maxDistance)
{
return Falloff(distance, maxDistance, 1.f);
}
static Texture2D* generateRadialGradientTexture(int size) {
auto radius = size / 2;
auto colors = new (std::nothrow) GLubyte[size * size * 4];
Color4B centerColor(Color4B(179, 232, 184, 89));
Color4B borderColor(Color4B(0, 90, 128, 0));
for (int y = 0; y < size; y++)
{
for (int x = 0; x < size; x++)
{
float distance = Vec2::ONE.distance(Vec2(x, y) / radius);
float alpha = Falloff(distance, 1, 1);
int idx = (y * size + x) * 4;
float innerGradient = Falloff(distance, 0.6f, 0.8f);
auto color = Lerp(borderColor, centerColor, innerGradient);
colors[idx + 0] = color.r;
colors[idx + 1] = color.g;
colors[idx + 2] = color.b;
//alpha
colors[idx + 3] = (GLbyte)clampf(alpha * 256.f + 0.5f, 0.f, 255.f);
}
}
auto txt = new Texture2D();
txt->initWithData(colors, size * size * 4, Texture2D::PixelFormat::RGBA8888, size, size, Size(size, size));
delete[] colors;
return txt;
}
And use just:
Sprite::createWithTexture(generateRadialGradientTexture(radius));
But result is really different:
I investigated the shader code of the questions and developed a similar algorithm in C++. The algorithm linearly interpolates between two colors, starting at a center point, with a inner color, radially outwards to a outer color.
The C++ algorithm generates a texture with a size, specified by input parameters:
e.g. a quadratic texture wiht a size of 600*600 and a gradient highlight in the center of the texture:
Extension to the answer
The conditional branches can be avoided, by using the GLSL
clamp
function, by limiting the interpolation parameter of themix
function to the range (0, 1).I suggest to optimize the fragment shader like this: