I am trying to get an interpolation of one color to another shade of the same color. (for eg: sky blue to dark blue and then back).
I stumbled upon some code that could be used if the range was from 0-255 or 0-1. However, in my case, I have the RGB codes for Color1 and Color2, and want the rotation to occur.
Color 1: 151,206,255
Color 2: 114,127,157
Any ideas how to go about this?
I suggest you convert RGB to HSV, then adjust its components, then convert back to RGB.
Wikipedia has an article about it, and it's been discussed here before:
Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
Also many frameworks have conversion functions, for example Qt has QColor class.
But the question was about the actual interpolation... here's a trivial interpolation function:
// 0 <= stepNumber <= lastStepNumber
int interpolate(int startValue, int endValue, int stepNumber, int lastStepNumber)
{
return (endValue - startValue) * stepNumber / lastStepNumber + startValue;
}
So call that for all color components you want to interpolate, in a loop. With RBG interpolation, you need to interpolate every component, in some other color space you may need to interpolate just one.
I know this is little bit old, but is worthy if someone is searching for it.
First of all, you can do interpolation in any color space, including RGB, which, in my opinion, is one of the easiest.
Let's assume the variation will be controlled by a fraction value between 0 and 1 (e.g. 0.3), where 0 means full color1 and 1 means full color2.
The theory:
Result = (color2 - color1) * fraction + color1
Applying:
As the RGB has 3 channels (red, green and blue) we have to perform this math for each one of the channels.
Using your example colors:
color1: 151,206,255
color2: 114,127,157
R = (114-151) * fraction + 151
G = (127-206) * fraction + 206
B = (157-255) * fraction + 255
Simple like that!
Convert your RGB colors to HSV then interpolate each component (not only the color, see end of answer), afterwards you can convert back to RGB.
You can do RGB interpolation, but the results are better with HSV, because in this space color is separated from luminance and saturation (Wikipedia article on HSV). HSV interpolation is more "logical" than the RGB one, because with the latter you can get extra colors while interpolating.
Some code for interpolation:
template<typename F>
ColorRGB interpolate(ColorRGB a, ColorRGB b, float t, F interpolator)
{
// 0.0 <= t <= 1.0
ColorHSV ca = convertRGB2HSV(a);
ColorHSV cb = convertRGB2HSV(b);
ColorHSV final;
final.h = interpolator(ca.h, cb.h, t);
final.s = interpolator(ca.s, cb.s, t);
final.v = interpolator(ca.v, cb.v, t);
return convertHSV2RGB(final);
}
int linear(int a, int b, float t)
{
return a * (1 - t) + b * t;
}
// use: result = interpolate(color1,color2,ratio,&linear);
The best color space to use for visual effects is HCL. This is a space created specifically to look good while traversing its dimension, where "look good" does not relate to any physical properties of light or ink, like RGB and CMYK respectively.
Using HCL is expensive so the best thing to do is to create a number of intermediary values in this space and then interpolate in RGB which is native. This is what I have done in my animation engine.
Here is a snippet from it, in Swift 4.0
extension UIColor {
typealias Components = (CGFloat, CGFloat, CGFloat, CGFloat)
enum Space: String {
case RGB = "RGB"
case HSB = "HSB"
case HCL = "HCL"
}
func components(in space: UIColor.Space) -> Components {
switch space {
case .RGB: return self.rgba // var defined in HandyUIKit's extension
case .HSB: return self.hsba // var defined in HandyUIKit's extension
case .HCL: return self.hlca // var defined in HandyUIKit's extension
}
}
func spectrum(to tcol: UIColor, for space: UIColor.Space) -> [UIColor] {
var spectrum = [UIColor]()
spectrum.append(self)
let fcomps = self.components(in: space)
let tcomps = tcol.components(in: space)
for i in 0 ... 5 {
let factor = CGFloat(i) / 5.0
let comps = (1.0 - factor) * fcomps + factor * tcomps
let color = UIColor(with: comps, in: space)
spectrum.append(color)
}
spectrum.append(tcol)
return spectrum
}
convenience init(with components: Components, in space: Space) {
switch space {
case .RGB: self.init(red: components.0, green: components.1, blue: components.2, alpha: components.3)
case .HSB: self.init(hue: components.0, saturation: components.1, brightness: components.2, alpha: components.3)
case .HCL: self.init(hue: components.0, luminance: components.1, chroma: components.2, alpha: components.3)
}
}
}
func *(lhs:CGFloat, rhs:UIColor.Components) -> UIColor.Components {
return (lhs * rhs.0, lhs * rhs.1, lhs * rhs.2, lhs * rhs.3)
}
func +(lhs:UIColor.Components, rhs:UIColor.Components) -> UIColor.Components {
return (lhs.0 + rhs.0, lhs.1 + rhs.1, lhs.2 + rhs.2, lhs.3 + rhs.3)
}
Both the engine and the example above are using HandyUIKit for the conversions between spaces so please add this project to whatever you are building for the code above to work.
I have written an article about it.
I see that you have tagged this question under "openframeworks" tag.
so all you need to do is use the method ofColor::getLerped
or ofColor::lerp
getLerped
returns new value, while lerp
modifies the color.
for example:
ofColor c1(151,206,255);
ofColor c2(114,127,157);
float p = 0.2f;
ofColor c3 = c1.getLerped(c2, p);
or
c1.lerp(c2, 0.3f);
I have adapted Synxis's C example (above) into an executable JavaScript program.
The program interpolates the color yellow, from red and green. The input and output is in RGB-space, but the interpolation is handled in the HSV-space. I also added an RGB interpolation example. As you can see below, a dark-yellow is produced if you interpolate red and green in RGB-space.
/** Main */
var red = { r : 255, g : 0, b : 0 };
var green = { r : 0, g : 255, b : 0 };
var yellow = interpolateHsv(red, green, 0.5, linear);
var darkYellow = interpolateRgb(red, green, 0.5, linear);
document.body.innerHTML =
'Yellow: ' + JSON.stringify(yellow, null, ' ') + '<br />' +
'Dark Yellow: ' + JSON.stringify(darkYellow, null, ' ');
/**
* Returns an HSV interpolated value between two rgb values.
*
* @param {Object} rgbA - rgb() tuple
* @param {Object} rgbB - rgb() tuple
* @param {Number} threshold - float between [0.0, 1.0]
* @param {function} interpolatorFn - interpolator function
* @return {Object} rbg
*/
function interpolateHsv(rgbA, rgbB, threshold, interpolatorFn) {
var hsvA = rgbToHsv(rgbA);
var hsvB = rgbToHsv(rgbB);
threshold = toArray(threshold, 3);
return hsvToRgb({
h : interpolatorFn(hsvA.h, hsvB.h, threshold[0]),
s : interpolatorFn(hsvA.s, hsvB.s, threshold[1]),
v : interpolatorFn(hsvA.v, hsvB.v, threshold[2])
});
}
/**
* Returns an RGB interpolated value between two rgb values.
*
* @param {Object} rgbA - rgb() tuple
* @param {Object} rgbB - rgb() tuple
* @param {Number} threshold - float between [0.0, 1.0]
* @param {function} interpolatorFn - interpolator function
* @return {Object} rbg
*/
function interpolateRgb(rgbA, rgbB, threshold, interpolatorFn) {
threshold = toArray(threshold, 3);
return {
r : ~~interpolatorFn(rgbA.r, rgbB.r, threshold[0]),
g : ~~interpolatorFn(rgbA.g, rgbB.g, threshold[1]),
b : ~~interpolatorFn(rgbA.b, rgbB.b, threshold[2])
};
}
/**
* Returns an interpolated value between two values.
*
* @param {Number} valueA - color channel int value
* @param {Number} valueB - color channel int value
* @param {Number} threshold - float between [0.0, 1.0]
* @param {function} interpolatorFn - interpolator function
* @return {int}
*/
function linear(valueA, valueB, threshold) {
return valueA * (1 - threshold) + valueB * threshold;
}
/**
* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v in the set [0, 1].
*
* @param {Object} rgb - Color in rgb mode
* @return {Object} - Color in hsv mode
*/
function rgbToHsv(rgb) {
var r = rgb.r / 255,
g = rgb.g / 255,
b = rgb.b / 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, v = max;
var d = max - min;
s = max === 0 ? 0 : d / max;
if (max == min) {
h = 0; // achromatic
} else {
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return {
h : h,
s : s,
v : v
};
}
/**
* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param {Object} hsv - Color in hsv mode
* @return {Object} - Color in rgb mode
*/
function hsvToRgb(hsv){
var r, g, b, i, f, p, q, t,
h = hsv.h,
s = hsv.s,
v = hsv.v;
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch(i % 6){
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return {
r : r * 255,
g : g * 255,
b : b * 255
};
}
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function toArray(arr, size) {
var isNum = isNumeric(arr);
arr = !Array.isArray(arr) ? [arr] : arr;
for (var i = 1; i < size; i++) {
if (arr.length < size) {
arr.push(isNum ? arr[0] : 0);
}
}
return arr;
}
Here's a Swift 2 version based on @hyde's answer:
import UIKit
func interpolate(start start: CGFloat, end: CGFloat, progress: CGFloat) -> CGFloat {
return (end - start) * progress + start
}
extension UIColor {
func interpolateTo(color end: UIColor, progress: CGFloat) -> UIColor {
var r1: CGFloat = 0
var g1: CGFloat = 0
var b1: CGFloat = 0
var a1: CGFloat = 0
getRed(&r1, green: &g1, blue: &b1, alpha: &a1)
var r2: CGFloat = 0
var g2: CGFloat = 0
var b2: CGFloat = 0
var a2: CGFloat = 0
end.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
return UIColor(
red: interpolate(start: r1, end: r2, progress: progress),
green: interpolate(start: g1, end: g2, progress: progress),
blue: interpolate(start: b1, end: b2, progress: progress),
alpha: interpolate(start: a1, end: a2, progress: progress)
)
}
}
You can use it like this:
color1.interpolateTo(color: color2, progress: t)
Where t
is the percentage (0-1) of your interpolation.
来源:https://stackoverflow.com/questions/13488957/interpolate-from-one-color-to-another