Radial Gradient

Radial Gradient
0.0 0

#21

I can ask someone to take a look at this thread. GDC was a primary focus for us these last few weeks.

Also, remember that these forums are for everyone to participate. We don’t staff these forums as a dedicated support mechanism. We also participate as much as possible but each day varies with our other responsibilities. An official response to each thread would take full-time resources we don’t have,


#22

Thanks, looking forward for radial gradient layer… :slight_smile:


#23

@zhangxm looks like I’ve got some bug with RadialGradient.
Could you please take a look(last comment in this issue): https://github.com/cocos2d/cocos2d-x/issues/17480#issuecomment-306922153


#24

I think i fixed it, and i added comment in the issue.


#25

Hey @zhangxm. I have two questions about radial again :slight_smile:

  1. how I can use RenderTexture to get LayerRadialGradient just as sprite? Using just radial->visit(); gives me zero image…
  2. when I’m adding at same position on same node one radial gradient on top of another - I’ve got fps drop from 60fps to 30-45fps. And really lagging gameplay. What can it be? Any ideas? As soon as I remove one of any gradients, fps goes to solid 60. (for some reason I can’t re-create same situation in cpp-test, to show).

Actually if 1st will be solved… I probably just will render once my radial gradient and use it as static image, to improve performance.


#26

Hi @KAMIKAZE. About question 1), could you please show a the codes?
About question 2), radial gradient is cost because the fragment shader will loop each pixel, so you’d better don’s use it frequently.


#27

Sure, but it’s just standard.

Vec2 pos = posByScreen(0.5, 1.0);
    LayerRadialGradient *gradient = LayerRadialGradient::create(Color4B(145, 106, 209, 140), Color4B(0, 0, 0, 0), 600, pos, 0.0f);
    addChild(gradient);
    RenderTexture* render = RenderTexture::create((int)Director::getInstance()->getWinSize().width,
                                                  (int)Director::getInstance()->getWinSize().height, Texture2D::PixelFormat::RGBA8888);

    render->begin();
    gradient->visit();
    render->end();
    render->saveToFile("ololo.png",Image::Format::PNG);

I see, so it’s ideal for me to just create a static sprite from it.


#28

I can see the result, how did you check the result png?


#29

Ok how about a procedurally generated texture like this:

static float smoothstep(float edge0, float edge1, float x) {
	float t = clampf((x - edge0) / (edge1 - edge0), 0.0, 1.0);
	return t * t * (3.0 - 2.0 * t);
}

static Texture2D* generateRadialGradientTexture(int size) {

	auto colors = new (std::nothrow) GLubyte[size * size * 4];
	Color4B color(Color4B::WHITE);// change it to whatever you want

	for (int y = 0; y < size; y++)
	{
		for (int x = 0; x < size; x++)
		{
			auto uv = Vec2(x, y) / size;

			float distance = uv.distance(Vec2(0.5, 0.5));
			float alpha = smoothstep(0.5, 0.0, distance) * 0.3;

			int idx = (y * size + x) * 4;
			colors[idx + 0] = color.r;
			colors[idx + 1] = color.g;
			colors[idx + 2] = color.b;

			//alpha
			colors[idx + 3] = 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;
}

then simply this:

auto tex = generateRadialGradientTexture(256);
auto spr = Sprite::createWithTexture(tex);
spr->setPosition(winSize / 2);
this->addChild(spr);

#30

or what about this:

// with translucent edges
static Texture2D* generateRadialGradientTexture(int size) {
	auto radius = size / 2;
	auto colors = new (std::nothrow) GLubyte[size * size * 4];

	Vec3 col = Vec3(1, 1, 0);

	Vec2 center(radius, radius);
	for (int y = 0; y < size; y++)
	{
		for (int x = 0; x < size; x++)
		{
			// background
			Vec3 color = Vec3(1, 1, 1);

			float dist = (Vec2(x, y) - center).length();
			float f = dist / radius;

			f = sqrt(clampf(1.f - f*f, 0., 1.0));
			float step = smoothstep(radius, radius, dist);
			auto val = 1.f - step;

			color -= col * val * f;

			int idx = (y * size + x) * 4;
			float r = clampf(color.x * 256.f + 0.5f, 0.f, 255.f);
			float g = clampf(color.y * 256.f + 0.5f, 0.f, 255.f);
			float b = clampf(color.z * 256.f + 0.5f, 0.f, 255.f);
			float a = clampf(f * 256.f + 0.5f, 0.f, 255.f);

			///////////////////////////////
			colors[idx + 0] = std::abs(r);
			colors[idx + 1] = std::abs(g);
			colors[idx + 2] = std::abs(b);
			colors[idx + 3] = std::abs(a);
		}
	}

	auto txt = new Texture2D();
	txt->initWithData(colors, size * size * 4, Texture2D::PixelFormat::RGBA8888, size, size, Size(size, size));

	delete[] colors;
	return txt;
}

#31

or even this:

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 borderColor(Color4B::RED);
	Color4B centerColor(Color4B::YELLOW);

	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;
}
        // color presets
    	Color4B borderColor1(Color4B::RED);
    	Color4B centerColor1(Color4B::YELLOW);

    	Color4B borderColor2(Color4B::BLUE);
    	Color4B centerColor2(Color4B(0, 255, 255, 255));

    	Color4B borderColor3(Color4B::GREEN);
    	Color4B centerColor3(Color4B::YELLOW);

    	Color4B borderColor4(Color4B::MAGENTA);
    	Color4B centerColor4(Color4B::WHITE);

    	Color4B borderColor5(Color4B::RED);
    	Color4B centerColor5(Color4B::RED);

#32

I just opened it and it was just blank.


#33

That’s interesting, but can I achieve same results like with currently implementated gradient?


#34

Thanks, works perfectly :slight_smile:


#35

But i can see the png with correct result. I tested on iOS simulator.


#36

@Joseph39 good job, it is smart to do it like this.


#37

Tested on simulator too. Thats strange, but when using another color:
LayerRadialGradient::create(Color4B(179, 232, 184, 89), Color4B(0, 90, 128, 0), 600,center, 0.0f);

Image will be blank, but on simulator it’s ok.


#38

Did you mean it can not work on device? What device did you test on?


#39

No, I’m testing on simulator only. Just try that another color. Also, disable code for other gradients on the scene…


#40

Ok, i will test i tomorrow.