[orx-shade-styles] Add OKLab color space support to gradients

This commit is contained in:
Edwin Jakobs
2021-03-22 10:52:01 +01:00
parent bb470fc706
commit 226a36d440
9 changed files with 189 additions and 60 deletions

View File

@@ -9,7 +9,8 @@ sourceSets {
}
dependencies {
api project(":orx-parameters")
implementation project(":orx-color")
implementation project(":orx-shader-phrases")
demoImplementation("org.openrndr:openrndr-core:$openrndrVersion")
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")

View File

@@ -0,0 +1,34 @@
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extensions.SingleScreenshot
import org.openrndr.extra.shadestyles.linearGradient
import org.openrndr.extra.shadestyles.radialGradient
import org.openrndr.extras.color.spaces.toOKLABa
import kotlin.math.cos
fun main() {
application {
program {
if (System.getProperty("takeScreenshot") == "true") {
extend(SingleScreenshot()) {
this.outputFile = System.getProperty("screenshotPath")
}
}
extend {
drawer.shadeStyle = linearGradient(
ColorRGBa.RED.toOKLABa(),
ColorRGBa.BLUE.toOKLABa(),
)
drawer.rectangle(120.0, 40.0, 200.0, 400.0)
drawer.shadeStyle = linearGradient(
ColorRGBa.RED,
ColorRGBa.BLUE
)
drawer.rectangle(120.0+200.0, 40.0, 200.0, 400.0)
}
}
}
}

View File

@@ -0,0 +1,13 @@
package org.openrndr.extra.shadestyles
import org.openrndr.color.ColorRGBa
import org.openrndr.extras.color.spaces.ColorOKLABa
import kotlin.reflect.KClass
internal fun generateColorTransform(kClass: KClass<*>): String {
return when (kClass) {
ColorRGBa::class -> """"""
ColorOKLABa::class -> """gradient = linear_rgb_to_srgb(oklab_to_linear_rgb(gradient));"""
else -> error("color space not supported $kClass")
}
}

View File

@@ -1,25 +1,32 @@
package org.openrndr.extra.shadestyles
import org.openrndr.color.ColorRGBa
import org.openrndr.color.*
import org.openrndr.draw.ShadeStyle
import org.openrndr.extra.parameters.ColorParameter
import org.openrndr.extra.parameters.Description
import org.openrndr.extra.parameters.DoubleParameter
import org.openrndr.extra.shaderphrases.preprocess
import org.openrndr.extras.color.phrases.ColorPhraseBook
import org.openrndr.extras.color.spaces.ColorOKLABa
import org.openrndr.math.CastableToVector4
import org.openrndr.math.Vector2
import kotlin.reflect.KClass
@Description("Linear gradient")
class LinearGradient(
color0: ColorRGBa,
color1: ColorRGBa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0) : ShadeStyle() {
open class LinearGradientBase<C>(
color0: C,
color1: C,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0
) : ShadeStyle()
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C: CastableToVector4 {
@ColorParameter("start color", order = 0)
var color0: ColorRGBa by Parameter()
var color0: C by Parameter()
@ColorParameter("end color", order = 1)
var color1: ColorRGBa by Parameter()
var color1: C by Parameter()
var offset: Vector2 by Parameter()
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
@@ -29,12 +36,16 @@ class LinearGradient(
var exponent: Double by Parameter()
init {
ColorPhraseBook.register()
this.color0 = color0
this.color1 = color1
this.offset = offset
this.rotation = rotation
this.exponent = exponent
fragmentPreamble = """
|#pragma import color.oklab_to_linear_rgb
|#pragma import color.linear_rgb_to_srgb""".trimMargin().preprocess()
fragmentTransform = """
vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset);
@@ -45,29 +56,48 @@ class LinearGradient(
float f = clamp(rc.y + 0.5, 0.0, 1.0);
vec4 color0 = p_color0;
color0.rgb *= color0.a;
vec4 color1 = p_color1;
color1.rgb *= color1.a;
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
x_fill = fn * gradient;
if (x_fill.a != 0) {
x_fill.rgb /= x_fill.a;
}
${generateColorTransform(color0::class)}
x_fill *= gradient;
"""
}
}
class LinearGradient(
color0: ColorRGBa,
color1: ColorRGBa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0
): LinearGradientBase<ColorRGBa>(color0, color1, offset, rotation, exponent)
class LinearGradientOKLab(
color0: ColorOKLABa,
color1: ColorOKLABa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0
): LinearGradientBase<ColorOKLABa>(color0, color1, offset, rotation, exponent)
fun linearGradient(
color0: ColorRGBa,
color1: ColorRGBa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0
): ShadeStyle {
color0: ColorRGBa,
color1: ColorRGBa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0
): LinearGradient {
return LinearGradient(color0, color1, offset, rotation, exponent)
}
fun linearGradient(
color0: ColorOKLABa,
color1: ColorOKLABa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
exponent: Double = 1.0
): LinearGradientOKLab {
return LinearGradientOKLab(color0, color1, offset, rotation, exponent)
}

View File

@@ -4,7 +4,6 @@ import org.openrndr.color.ColorRGBa
import org.openrndr.draw.ShadeStyle
import org.openrndr.extra.parameters.Description
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
@Description("N-Point gradient")
class NPointGradient(

View File

@@ -1,18 +1,25 @@
package org.openrndr.extra.shadestyles
import org.openrndr.color.AlgebraicColor
import org.openrndr.color.ColorRGBa
import org.openrndr.color.ConvertibleToColorRGBa
import org.openrndr.draw.ShadeStyle
import org.openrndr.extra.parameters.Description
import org.openrndr.extras.color.spaces.ColorOKLABa
import org.openrndr.math.CastableToVector4
import org.openrndr.math.Vector2
@Description("Multicolor linear gradient")
class NPointLinearGradient(
colors: Array<ColorRGBa>,
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0) : ShadeStyle() {
open class NPointLinearGradientBase<C>(
colors: Array<C>,
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0
) : ShadeStyle()
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C : CastableToVector4 {
var colors: Array<C> by Parameter()
var colors: Array<ColorRGBa> by Parameter()
// Sorted normalized values defining relative positions of colors
var points: Array<Double> by Parameter()
var offset: Vector2 by Parameter()
@@ -37,17 +44,14 @@ class NPointLinearGradient(
while(i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; }
vec4 color0 = p_colors[i];
color0.rgb *= color0.a;
vec4 color1 = p_colors[i+1];
color1.rgb *= color1.a;
float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]);
vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0));
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
${generateColorTransform(colors[0]::class)}
x_fill = fn * gradient;
x_fill *= gradient;
if (x_fill.a != 0) {
x_fill.rgb /= x_fill.a;
}
@@ -55,3 +59,19 @@ class NPointLinearGradient(
"""
}
}
class NPointLinearGradient(
colors: Array<ColorRGBa>,
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0
) : NPointLinearGradientBase<ColorRGBa>(colors, points, offset, rotation)
class NPointLinearGradientOKLab(
colors: Array<ColorOKLABa>,
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0
) : NPointLinearGradientBase<ColorOKLABa>(colors, points, offset, rotation)

View File

@@ -11,7 +11,10 @@ class NPointRadialGradient(
points: Array<Double> = Array(colors.size) { it / (colors.size - 1.0) },
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
length: Double = 1.0) : ShadeStyle() {
length: Double = 1.0) : ShadeStyle()
{
var colors: Array<ColorRGBa> by Parameter()

View File

@@ -1,26 +1,29 @@
package org.openrndr.extra.shadestyles
import org.openrndr.color.ColorRGBa
import org.openrndr.color.*
import org.openrndr.draw.ShadeStyle
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.parameters.ColorParameter
import org.openrndr.extra.parameters.Description
import org.openrndr.extra.parameters.DoubleParameter
import org.openrndr.extras.color.spaces.ColorOKLABa
import org.openrndr.math.CastableToVector4
import org.openrndr.math.Vector2
@Description("Radial gradient")
class RadialGradient(
color0: ColorRGBa,
color1: ColorRGBa,
open class RadialGradientBase<C>(
color0: C,
color1: C,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
length: Double = 1.0,
exponent: Double = 1.0) : ShadeStyle() {
exponent: Double = 1.0)
: ShadeStyle()
where C : ConvertibleToColorRGBa, C : AlgebraicColor<C>, C: CastableToVector4 {
@ColorParameter("start color", order = 0)
var color0 : ColorRGBa by Parameter()
var color0 : C by Parameter()
@ColorParameter("end color", order = 1)
var color1 : ColorRGBa by Parameter()
var color1 : C by Parameter()
var offset : Vector2 by Parameter()
@DoubleParameter("rotation", -180.0, 180.0, order = 2)
var rotation : Double by Parameter()
@@ -47,23 +50,34 @@ class RadialGradient(
float f = clamp(p_length * length(rc), 0.0, 1.0);
vec4 color0 = p_color0;
color0.rgb *= color0.a;
vec4 color1 = p_color1;
color1.rgb *= color1.a;
vec4 gradient = mix(color0, color1, pow(f, p_exponent));
${generateColorTransform(color0::class)}
vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a;
x_fill = fn * gradient;
if (x_fill.a != 0) {
x_fill.rgb /= x_fill.a;
}
x_fill *= gradient;
"""
}
}
class RadialGradient(
color0: ColorRGBa,
color1: ColorRGBa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
length: Double = 1.0,
exponent: Double = 1.0
): RadialGradientBase<ColorRGBa>(color0, color1, offset, rotation, length, exponent)
class RadialGradientOKLab(
color0: ColorOKLABa,
color1: ColorOKLABa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
length: Double = 1.0,
exponent: Double = 1.0
): RadialGradientBase<ColorOKLABa>(color0, color1, offset, rotation, length, exponent)
fun radialGradient(
color0: ColorRGBa,
color1: ColorRGBa,
@@ -71,6 +85,19 @@ fun radialGradient(
rotation: Double = 0.0,
length: Double = 1.0,
exponent: Double = 1.0
): ShadeStyle {
): RadialGradient {
return RadialGradient(color0, color1, offset, rotation, length, exponent)
}
fun radialGradient(
color0: ColorOKLABa,
color1: ColorOKLABa,
offset: Vector2 = Vector2.ZERO,
rotation: Double = 0.0,
length: Double = 1.0,
exponent: Double = 1.0
): RadialGradientOKLab {
return RadialGradientOKLab(color0, color1, offset, rotation, length, exponent)
}

View File

@@ -128,6 +128,7 @@ fun preprocessShader(source: String, symbols: Set<String> = emptySet()): String
newSymbols
)
} catch (e: NoSuchFieldException) {
println(source)
error("field \"$fieldName\" not found in \"#pragma import $packageClass.$fieldName\" on line ${index + 1}")
}
}
@@ -137,6 +138,7 @@ fun preprocessShader(source: String, symbols: Set<String> = emptySet()): String
throw IllegalArgumentException("class $packageClass has no ShaderPhrases annotation")
}
} catch (e: ClassNotFoundException) {
println(source)
error("class \"$packageClass\" not found in \"#pragma import $packageClass\" on line ${index + 1}")
}
} else {