Изменение цвета текстуры индикатора здоровья в GLSL ES

Я создаю 2D-игру, используя OpenGL ES 2 в Android, и в нее входит полоса здоровья. Полоса здоровья меняет 5 цветов по мере уменьшения здоровья (зеленый, желто-зеленый, желтый, оранжевый, красный). Текстура для шкалы здоровья берется из атласа текстур, и я бы предпочел не загромождать атлас текстур полосой для каждого цвета (в будущем также будет проще изменить эти цвета).

Это оригинальный зеленый цвет, который я хотел бы воспроизвести с помощью GLSL. Базовый цвет - # 77ee9a.

Я пробовал создать черно-белую версию текстуры. Базовый зеленый цвет здесь становится #aeaeae.

Затем я умножаю желаемый цвет на это черно-белое изображение. Я понял, что это не сработает точно, поэтому я взял желаемый зеленый цвет (# 77ee9a) и разделил его на его черно-белый эквивалент (#aeaeae) перед умножением. Это привело к:

Я понял, что деление на серый привело к тому, что зеленая часть значения rgb для моего исходного зеленого превысила 255, создавая артефакты вокруг ярких частей изображения.

Я не уверен, есть ли лучший способ добиться этого эффекта окраски, но я надеюсь, что есть решение, которое позволит мне это сделать.

Если этого не произойдет, я смогу создать две текстуры, одну чисто белую в форме полосы, а другую с эффектами прозрачности. Затем я мог бы наложить их, но для этого потребовались бы и другая текстура, и еще один спрайт в игре.

Спасибо!


person Sarathi    schedule 05.07.2015    source источник


Ответы (2)


Вы можете вычислить 2 отдельных градиента с помощью функции смешивания GLSL: один, который идет от черного до выбранного цвета, соответствующий значениям серого от 0 до 0,68 (#aeaeae), и другой, который идет от выбранного цвета до белого для серого. значения от 0,68 до 1,0.

Передайте оттенок (например, зеленый) как единообразный:

uniform vec4 uTintColor;

Считайте значение серого и возьмите любой компонент (так как они все одинаковые). Затем вычислите, где находится значение серого в каждом из градиентов, и примените их. Когда он находится в нижнем градиенте, upperGradientCol будет равен uTintColor, потому что upperGradientPos будет равен нулю, поэтому upperGradientCol можно использовать в качестве верхней границы нижнего градиента.

// read grey scale value
vec4 texel = texture2D(uTexture, vTexCoord);
float greyValue = texel.r;

// compute position in gradients:
float upperGradientPos = clamp((greyValue - 0.68) / 0.32, 0.0, 1.0);
float lowerGradientPos = clamp(greyValue / 0.68, 0.0, 1.0);

// apply upper gradient from uTintColor up to white:
vec4 upperGradientCol = mix(uTintColor, vec4(1.0, 1.0, 1.0, 1.0), upperGradientPos);
// apply lower gradient from black up to uTintColor:
vec4 lowerGradientCol = mix(vec4(0.0, 0.0, 0.0, 1.0), upperGradientCol, lowerGradientPos);
gl_FragColor = vec4(lowerGradientCol.rgb, texel.a);

Чтобы сделать это более универсальным, вы можете передать в качестве униформы разные цвета вместо черного и белого, и то же самое для значения серого, соответствующего цвету оттенка (в настоящее время жестко запрограммировано на 0,68).

person samgak    schedule 06.07.2015

Фактически вы можете написать шейдер, так что вам просто нужна 1 текстура, которая будет изменена следующим образом: чем больше теряется холода, тем больше серого оно становится. Чтобы получить значение цвета "graytexture", которое будет использоваться, это:

Значение серого = 0,299 × красный + 0,587 × зеленый + 0,114 × синий

Итак, формула в шейдере будет такой:

Healthpercantage * 0,711 + 0,299, Healthpercantage * 413 + 0,587, Healthpercantage * 0,886 + 0,114

person Christian Mosz    schedule 05.07.2015