more testing on skydome / font change

This commit is contained in:
minjaesong
2020-04-29 08:48:06 +09:00
parent 826a13ff57
commit 226c8342cb
19 changed files with 468 additions and 424 deletions

View File

@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.3.10'
ext.kotlin_version = '1.3.72'
repositories {
mavenCentral()
@@ -27,6 +27,7 @@ repositories {
}
dependencies {
compile "org.jetbrains.kotlin:kotlinx-coroutines-core"
compile "org.jetbrains.kotlin:kotlin-stdlib"
compile fileTree(dir: 'lib', include: ['*.jar'])
implementation 'org.junit:junit-bom:5.2.0'

Binary file not shown.

View File

@@ -2,12 +2,6 @@ package net.torvald.colourutil
import com.badlogic.gdx.graphics.Color
import com.jme3.math.FastMath
import net.torvald.colourutil.CIELChabUtil.toLCh
import net.torvald.colourutil.CIELChabUtil.toLab
import net.torvald.colourutil.CIELabUtil.toLab
import net.torvald.colourutil.CIEXYZUtil.toXYZ
import net.torvald.colourutil.CIEXYZUtil.toColor
import net.torvald.colourutil.CIELabUtil.toXYZ
/**
* Cylindrical modification of CIELab colour space
*
@@ -16,47 +10,46 @@ import net.torvald.colourutil.CIELabUtil.toXYZ
* Created by minjaesong on 2016-09-01.
*/
object CIELChabUtil {
/** Sweet LCh_ab linear gradient */
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLCh()
val to = toCol.toLCh()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newC = FastMath.interpolateLinear(scale, from.C, to.C)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
val newH: Float
/** Sweet LCh_ab linear gradient */
fun cielch_getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLCh()
val to = toCol.toLCh()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newC = FastMath.interpolateLinear(scale, from.C, to.C)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
val newH: Float
if ((from.h - to.h).abs() == FastMath.PI) // exact opposite colour
return CIELabUtil.getGradient(scale, fromCol, toCol)
else if ((from.h - to.h).abs() > FastMath.PI) // reflex angle
newH = FastMath.interpolateLinear(scale, from.h, to.h + FastMath.TWO_PI)
else
newH = FastMath.interpolateLinear(scale, from.h, to.h)
if ((from.h - to.h).abs() == FastMath.PI) // exact opposite colour
return cielab_getGradient(scale, fromCol, toCol)
else if ((from.h - to.h).abs() > FastMath.PI) // reflex angle
newH = FastMath.interpolateLinear(scale, from.h, to.h + FastMath.TWO_PI)
else
newH = FastMath.interpolateLinear(scale, from.h, to.h)
return CIELCh(newL, newC, newH, newAlpha).toColor()
}
fun CIELab.toLCh(): CIELCh {
val c = (a.sqr() + b.sqr()).sqrt()
val h = FastMath.atan2(b, a)
return CIELCh(L, c, h, alpha)
}
fun CIELCh.toLab(): CIELab {
val a = C * FastMath.cos(h)
val b = C * FastMath.sin(h)
return CIELab(L, a, b, alpha)
}
private fun Float.sqr() = this * this
private fun Float.sqrt() = Math.sqrt(this.toDouble()).toFloat()
private fun Float.abs() = FastMath.abs(this)
return CIELCh(newL, newC, newH, newAlpha).toColor()
}
fun CIELab.toLCh(): CIELCh {
val c = (a.sqr() + b.sqr()).sqrt()
val h = FastMath.atan2(b, a)
return CIELCh(L, c, h, alpha)
}
fun CIELCh.toLab(): CIELab {
val a = C * FastMath.cos(h)
val b = C * FastMath.sin(h)
return CIELab(L, a, b, alpha)
}
private fun Float.sqr() = this * this
private fun Float.sqrt() = Math.sqrt(this.toDouble()).toFloat()
private fun Float.abs() = FastMath.abs(this)
fun Color.toLCh() = this.toXYZ().toLab().toLCh()
fun CIELCh.toColor() = this.toLab().toXYZ().toColor()

View File

@@ -2,11 +2,6 @@ package net.torvald.colourutil
import com.badlogic.gdx.graphics.Color
import com.jme3.math.FastMath
import net.torvald.colourutil.CIELabUtil.toLab
import net.torvald.colourutil.CIEXYZUtil.toColor
import net.torvald.colourutil.CIEXYZUtil.toRGB
import net.torvald.colourutil.CIEXYZUtil.toXYZ
import net.torvald.colourutil.CIELabUtil.toXYZ
/**
* A modification of CIEXYZ that is useful for surface colours
@@ -23,70 +18,69 @@ import net.torvald.colourutil.CIELabUtil.toXYZ
*
* Created by minjaesong on 2016-09-01.
*/
object CIELabUtil {
fun Color.brighterLab(scale: Float): Color {
val brighten = scale + 1f
fun Color.brighterLab(scale: Float): Color {
val brighten = scale + 1f
val lab = this.toLab()
lab.L *= brighten
return lab.toColor()
}
fun Color.darkerLab(scale: Float): Color {
val darken = 1f - scale
val lab = this.toLab()
lab.L *= darken
return lab.toColor()
}
/** Tend to have more natural (or less saturated) colour */
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLab()
val to = toCol.toLab()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newA = FastMath.interpolateLinear(scale, from.a, to.a)
val newB = FastMath.interpolateLinear(scale, from.b, to.b)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
return CIELab(newL, newA, newB, newAlpha).toColor()
}
fun CIEXYZ.toLab(): CIELab {
val x = pivotXYZ(X / D65.X)
val y = pivotXYZ(Y / D65.Y)
val z = pivotXYZ(Z / D65.Z)
val L = Math.max(0f, 116 * y - 16)
val a = 500 * (x - y)
val b = 200 * (y - z)
return CIELab(L, a, b, alpha)
}
fun CIELab.toXYZ(): CIEXYZ {
val y = L.plus(16).div(116f)
val x = a / 500f + y
val z = y - b / 200f
val x3 = x.cube()
val z3 = z.cube()
return CIEXYZ(
D65.X * if (x3 > epsilon) x3 else (x - 16f / 116f) / 7.787f,
D65.Y * if (L > kappa * epsilon) (L.plus(16f) / 116f).cube() else L / kappa,
D65.Z * if (z3 > epsilon) z3 else (z - 16f / 116f) / 7.787f,
alpha
)
}
private fun pivotXYZ(n: Float) = if (n > epsilon) n.cbrt() else (kappa * n + 16f) / 116f
private fun Float.cbrt() = FastMath.pow(this, 1f / 3f)
private fun Float.cube() = this * this * this
private fun Float.powerOf(exp: Float) = FastMath.pow(this, exp)
val lab = this.toLab()
lab.L *= brighten
return lab.toColor()
}
fun Color.darkerLab(scale: Float): Color {
val darken = 1f - scale
val lab = this.toLab()
lab.L *= darken
return lab.toColor()
}
/** Tend to have more natural (or less saturated) colour */
fun cielab_getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLab()
val to = toCol.toLab()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newA = FastMath.interpolateLinear(scale, from.a, to.a)
val newB = FastMath.interpolateLinear(scale, from.b, to.b)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
return CIELab(newL, newA, newB, newAlpha).toColor()
}
fun CIEXYZ.toLab(): CIELab {
val x = pivotXYZ(X / D65.X)
val y = pivotXYZ(Y / D65.Y)
val z = pivotXYZ(Z / D65.Z)
val L = Math.max(0f, 116 * y - 16)
val a = 500 * (x - y)
val b = 200 * (y - z)
return CIELab(L, a, b, alpha)
}
fun CIELab.toXYZ(): CIEXYZ {
val y = L.plus(16).div(116f)
val x = a / 500f + y
val z = y - b / 200f
val x3 = x.cube()
val z3 = z.cube()
return CIEXYZ(
D65.X * if (x3 > epsilon) x3 else (x - 16f / 116f) / 7.787f,
D65.Y * if (L > kappa * epsilon) (L.plus(16f) / 116f).cube() else L / kappa,
D65.Z * if (z3 > epsilon) z3 else (z - 16f / 116f) / 7.787f,
alpha
)
}
private fun pivotXYZ(n: Float) = if (n > epsilon) n.cbrt() else (kappa * n + 16f) / 116f
private fun Float.cbrt() = FastMath.pow(this, 1f / 3f)
private fun Float.cube() = this * this * this
private fun Float.powerOf(exp: Float) = FastMath.pow(this, exp)
fun Color.toLab() = this.toXYZ().toLab()
fun RGB.toLab() = this.toXYZ().toLab()
fun CIELab.toColor() = this.toXYZ().toColor()

View File

@@ -2,11 +2,6 @@ package net.torvald.colourutil
import com.jme3.math.FastMath
import com.badlogic.gdx.graphics.Color
import net.torvald.colourutil.CIEXYZUtil.toColor
import net.torvald.colourutil.CIEXYZUtil.toRGB
import net.torvald.colourutil.CIEXYZUtil.toXYZ
import net.torvald.colourutil.CIELuvUtil.toLuv
import net.torvald.colourutil.CIELuvUtil.toXYZ
/**
* A modification of CIEXYZ that is useful for additive mixtures of lights.
@@ -18,97 +13,96 @@ import net.torvald.colourutil.CIELuvUtil.toXYZ
*
* Created by minjaesong on 2016-09-06.
*/
object CIELuvUtil {
fun Color.brighterLuv(scale: Float): Color {
val brighten = scale + 1f
fun Color.brighterLuv(scale: Float): Color {
val brighten = scale + 1f
val luv = this.toLuv()
luv.L *= brighten
return luv.toColor()
}
fun Color.darkerLuv(scale: Float): Color {
val darken = 1f - scale
val luv = this.toLuv()
luv.L *= darken
return luv.toColor()
}
/** Tend to have more vivid (or saturated) colour */
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLuv()
val to = toCol.toLuv()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newU = FastMath.interpolateLinear(scale, from.u, to.u)
val newV = FastMath.interpolateLinear(scale, from.v, to.v)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
return CIELab(newL, newU, newV, newAlpha).toColor()
}
/**
* Alpha value will be overwritten to 1.0
*/
infix fun Color.additiveLuv(other: Color): Color {
val rgb = RGB(r, g, b) additiveLuv RGB(other.r, other.g, other.b)
return Color(rgb.r, rgb.g, rgb.b, a)
}
/**
* Alpha value will be overwritten to 1.0
*/
infix fun RGB.additiveLuv(other: RGB): RGB {
val thisLuv = this.toXYZ().toLuv()
val otherLuv = other.toXYZ().toLuv()
val newL = 1f - (1f - thisLuv.L) * (1 - otherLuv.L)
val newU = thisLuv.u.times(otherLuv.L / newL) + otherLuv.u.times(otherLuv.L).times(1f - thisLuv.L).div(newL)
val newV = thisLuv.v.times(otherLuv.L / newL) + otherLuv.v.times(otherLuv.L).times(1f - thisLuv.L).div(newL)
return CIELuv(newL, newU, newV).toRGB()
}
fun CIEXYZ.toLuv(): CIELuv {
val yRef = Y / D65.Y
val uPrime = 4f * X / (X + 15f * Y + 3f * Z)
val vPrime = 9f * Y / (X + 15f * Y + 3f * Z)
val uRefPrime = 4f * D65.X / (D65.X + 15f * D65.Y + 3f * D65.Z)
val vRefPrime = 9f * D65.Y / (D65.X + 15f * D65.Y + 3f * D65.Z)
val L = if (yRef > epsilon)
116f * yRef.cbrt() - 16f
else
kappa * yRef
val u = 13f * L * (uPrime - uRefPrime)
val v = 13f * L * (vPrime - vRefPrime)
return CIELuv(L, u, v, alpha)
}
fun CIELuv.toXYZ(): CIEXYZ {
val Y = if (L > kappa * epsilon)
L.plus(16f).div(116f).cube()
else
L.div(kappa)
val uRef = 4f * D65.X / (D65.X + 15f * D65.Y + 3f * D65.Z)
val vRef = 9f * D65.Y / (D65.X + 15f * D65.Y + 3f * D65.Z)
val a = (1f / 3f) * (52.times(L) / u.plus(13 * L * uRef)).minus(1f)
val b = -5f * Y
val c = -1f / 3f
val d = Y * (39f.times(L) / v.plus(13f * L * vRef)).minus(5f)
val X = (d - b) / (a - c)
val Z = X * a + b
return CIEXYZ(X, Y, Z, alpha)
}
private fun Float.cbrt() = FastMath.pow(this, 1f / 3f)
private fun Float.cube() = this * this * this
val luv = this.toLuv()
luv.L *= brighten
return luv.toColor()
}
fun Color.darkerLuv(scale: Float): Color {
val darken = 1f - scale
val luv = this.toLuv()
luv.L *= darken
return luv.toColor()
}
/** Tend to have more vivid (or saturated) colour */
fun cieluv_getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toLuv()
val to = toCol.toLuv()
val newL = FastMath.interpolateLinear(scale, from.L, to.L)
val newU = FastMath.interpolateLinear(scale, from.u, to.u)
val newV = FastMath.interpolateLinear(scale, from.v, to.v)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
return CIELab(newL, newU, newV, newAlpha).toColor()
}
/**
* Alpha value will be overwritten to 1.0
*/
infix fun Color.additiveLuv(other: Color): Color {
val rgb = RGB(r, g, b) additiveLuv RGB(other.r, other.g, other.b)
return Color(rgb.r, rgb.g, rgb.b, a)
}
/**
* Alpha value will be overwritten to 1.0
*/
infix fun RGB.additiveLuv(other: RGB): RGB {
val thisLuv = this.toXYZ().toLuv()
val otherLuv = other.toXYZ().toLuv()
val newL = 1f - (1f - thisLuv.L) * (1 - otherLuv.L)
val newU = thisLuv.u.times(otherLuv.L / newL) + otherLuv.u.times(otherLuv.L).times(1f - thisLuv.L).div(newL)
val newV = thisLuv.v.times(otherLuv.L / newL) + otherLuv.v.times(otherLuv.L).times(1f - thisLuv.L).div(newL)
return CIELuv(newL, newU, newV).toRGB()
}
fun CIEXYZ.toLuv(): CIELuv {
val yRef = Y / D65.Y
val uPrime = 4f * X / (X + 15f * Y + 3f * Z)
val vPrime = 9f * Y / (X + 15f * Y + 3f * Z)
val uRefPrime = 4f * D65.X / (D65.X + 15f * D65.Y + 3f * D65.Z)
val vRefPrime = 9f * D65.Y / (D65.X + 15f * D65.Y + 3f * D65.Z)
val L = if (yRef > epsilon)
116f * yRef.cbrt() - 16f
else
kappa * yRef
val u = 13f * L * (uPrime - uRefPrime)
val v = 13f * L * (vPrime - vRefPrime)
return CIELuv(L, u, v, alpha)
}
fun CIELuv.toXYZ(): CIEXYZ {
val Y = if (L > kappa * epsilon)
L.plus(16f).div(116f).cube()
else
L.div(kappa)
val uRef = 4f * D65.X / (D65.X + 15f * D65.Y + 3f * D65.Z)
val vRef = 9f * D65.Y / (D65.X + 15f * D65.Y + 3f * D65.Z)
val a = (1f / 3f) * (52.times(L) / u.plus(13 * L * uRef)).minus(1f)
val b = -5f * Y
val c = -1f / 3f
val d = Y * (39f.times(L) / v.plus(13f * L * vRef)).minus(5f)
val X = (d - b) / (a - c)
val Z = X * a + b
return CIEXYZ(X, Y, Z, alpha)
}
private fun Float.cbrt() = FastMath.pow(this, 1f / 3f)
private fun Float.cube() = this * this * this
fun Color.toLuv() = this.toXYZ().toLuv()
fun CIELuv.toRGB() = this.toXYZ().toRGB()
fun CIELuv.toColor() = this.toXYZ().toColor()

View File

@@ -2,224 +2,225 @@ package net.torvald.colourutil
import com.badlogic.gdx.graphics.Color
import com.jme3.math.FastMath
import net.torvald.gdx.graphics.Cvec
/**
* Created by minjaesong on 2017-01-12.
*/
object CIEXYZUtil {
/**
* 0..255 -> 0.0..1.0
*/
private val rgbLinLUT = FloatArray(256) {
val step = it / 255f
/**
* 0..255 -> 0.0..1.0
*/
private val rgbLinLUT = FloatArray(256) {
val step = it / 255f
if (step > 0.04045f)
((step + 0.055f) / 1.055f).powerOf(2.4f)
else step / 12.92f
}
if (step > 0.04045f)
((step + 0.055f) / 1.055f).powerOf(2.4f)
else step / 12.92f
}
/**
* 0..255 -> 0.0..1.0
*/
private val rgbUnLinLUT = FloatArray(256) {
val step = it / 255f
/**
* 0..255 -> 0.0..1.0
*/
private val rgbUnLinLUT = FloatArray(256) {
val step = it / 255f
if (step > 0.0031308f)
1.055f * step.powerOf(1f / 2.4f) - 0.055f
else
step * 12.92f
}
if (step > 0.0031308f)
1.055f * step.powerOf(1f / 2.4f) - 0.055f
else
step * 12.92f
}
private val rgbToXyzLut_XR = FloatArray(256) { 0.4124564f * (it / 255f) }
private val rgbToXyzLut_XR = FloatArray(256) { 0.4124564f * (it / 255f) }
fun Color.brighterXYZ(scale: Float): Color {
val xyz = this.toXYZ()
xyz.X = xyz.X.times(1f + scale).clampOne()
xyz.Y = xyz.Y.times(1f + scale).clampOne()
xyz.Z = xyz.Z.times(1f + scale).clampOne()
return xyz.toColor()
}
fun Color.darkerXYZ(scale: Float): Color {
val xyz = this.toXYZ()
xyz.X = xyz.X.times(1f - scale).clampOne()
xyz.Y = xyz.Y.times(1f - scale).clampOne()
xyz.Z = xyz.Z.times(1f - scale).clampOne()
return xyz.toColor()
}
/*@Deprecated("Use one in CIELAB or CIELUV; CIEXYZ is not perceptually uniform")
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toXYZ()
val to = toCol.toXYZ()
val newX = FastMath.interpolateLinear(scale, from.X, to.X)
val newY = FastMath.interpolateLinear(scale, from.Y, to.Y)
val newZ = FastMath.interpolateLinear(scale, from.Z, to.Z)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
fun Color.brighterXYZ(scale: Float): Color {
val xyz = this.toXYZ()
xyz.X = xyz.X.times(1f + scale).clampOne()
xyz.Y = xyz.Y.times(1f + scale).clampOne()
xyz.Z = xyz.Z.times(1f + scale).clampOne()
return xyz.toColor()
}
return CIEXYZ(newX, newY, newZ, newAlpha).toColor()
}*/
fun Color.darkerXYZ(scale: Float): Color {
val xyz = this.toXYZ()
xyz.X = xyz.X.times(1f - scale).clampOne()
xyz.Y = xyz.Y.times(1f - scale).clampOne()
xyz.Z = xyz.Z.times(1f - scale).clampOne()
return xyz.toColor()
}
fun Color.toXYZ(): CIEXYZ = RGB(this).toXYZ()
/*@Deprecated("Use one in CIELAB or CIELUV; CIEXYZ is not perceptually uniform")
fun getGradient(scale: Float, fromCol: Color, toCol: Color): Color {
val from = fromCol.toXYZ()
val to = toCol.toXYZ()
val newX = FastMath.interpolateLinear(scale, from.X, to.X)
val newY = FastMath.interpolateLinear(scale, from.Y, to.Y)
val newZ = FastMath.interpolateLinear(scale, from.Z, to.Z)
val newAlpha = FastMath.interpolateLinear(scale, from.alpha, to.alpha)
return CIEXYZ(newX, newY, newZ, newAlpha).toColor()
}*/
fun Color.toXYZ(): CIEXYZ = RGB(this).toXYZ()
/**
* "Linearise" the sRGB triads. This use lookup table to speed up calculation.
* Integer values (1/255, 2/255, .. , 254/255, 255/255) are accurate but any values in between are
* linearly interpolated and thus slightly less accurate. Visually there's little-to-no difference,
* but may not optimal for rigorous maths.
*/
fun RGB.linearise(): RGB {
val out = floatArrayOf(0f, 0f, 0f)
for (i in 0..2) {
val value = when (i) {
0 -> this.r
1 -> this.g
2 -> this.b
else -> throw InternalError("Fuck you")
}
val step = value.clampOne() * 255f // 0.0 .. 255.0
val intStep = step.toInt() // 0 .. 255
val NeXTSTEP = minOf(intStep + 1, 255) // 1 .. 255
out[i] = interpolateLinear(step - intStep, rgbLinLUT[intStep], rgbLinLUT[NeXTSTEP])
/**
* "Linearise" the sRGB triads. This use lookup table to speed up calculation.
* Integer values (1/255, 2/255, .. , 254/255, 255/255) are accurate but any values in between are
* linearly interpolated and thus slightly less accurate. Visually there's little-to-no difference,
* but may not optimal for rigorous maths.
*/
/*fun RGB.linearise(): RGB {
val out = floatArrayOf(0f, 0f, 0f)
for (i in 0..2) {
val value = when (i) {
0 -> this.r
1 -> this.g
2 -> this.b
else -> throw InternalError("Fuck you")
}
val step = value.clampOne() * 255f // 0.0 .. 255.0
val intStep = step.toInt() // 0 .. 255
val NeXTSTEP = minOf(intStep + 1, 255) // 1 .. 255
return RGB(out[0], out[1], out[2], alpha)
out[i] = interpolateLinear(step - intStep, rgbLinLUT[intStep], rgbLinLUT[NeXTSTEP])
}
/** Suitable for rigorous maths but slower */
fun RGB.lineariseSuper(): RGB {
val newR = if (r > 0.04045f)
((r + 0.055f) / 1.055f).powerOf(2.4f)
else r / 12.92f
val newG = if (g > 0.04045f)
((g + 0.055f) / 1.055f).powerOf(2.4f)
else g / 12.92f
val newB = if (b > 0.04045f)
((b + 0.055f) / 1.055f).powerOf(2.4f)
else b / 12.92f
return RGB(out[0], out[1], out[2], alpha)
}*/
/** Suitable for rigorous maths but slower */
fun RGB.linearise(): RGB {
val newR = if (r > 0.04045f)
((r + 0.055f) / 1.055f).powerOf(2.4f)
else r / 12.92f
val newG = if (g > 0.04045f)
((g + 0.055f) / 1.055f).powerOf(2.4f)
else g / 12.92f
val newB = if (b > 0.04045f)
((b + 0.055f) / 1.055f).powerOf(2.4f)
else b / 12.92f
return RGB(newR, newG, newB, alpha)
}
return RGB(newR, newG, newB, alpha)
}
/**
* "Un-linearise" the RGB triads. That is, codes the linear RGB into sRGB. This use lookup table to speed up calculation.
* Integer values (1/255, 2/255, .. , 254/255, 255/255) are accurate but any values in between are
* linearly interpolated and thus slightly less accurate. Visually there's little-to-no difference,
* but may not optimal for rigorous maths.
*/
fun RGB.unLinearise(): RGB {
val out = floatArrayOf(0f, 0f, 0f)
for (i in 0..2) {
val value = when (i) {
0 -> this.r
1 -> this.g
2 -> this.b
else -> throw InternalError("Fuck you")
}
val step = value.clampOne() * 255f // 0.0 .. 255.0
val intStep = step.toInt() // 0 .. 255
val NeXTSTEP = minOf(intStep + 1, 255) // 1 .. 255
out[i] = interpolateLinear(step - intStep, rgbUnLinLUT[intStep], rgbUnLinLUT[NeXTSTEP])
/**
* "Un-linearise" the RGB triads. That is, codes the linear RGB into sRGB. This use lookup table to speed up calculation.
* Integer values (1/255, 2/255, .. , 254/255, 255/255) are accurate but any values in between are
* linearly interpolated and thus slightly less accurate. Visually there's little-to-no difference,
* but may not optimal for rigorous maths.
*/
/*fun RGB.unLinearise(): RGB {
val out = floatArrayOf(0f, 0f, 0f)
for (i in 0..2) {
val value = when (i) {
0 -> this.r
1 -> this.g
2 -> this.b
else -> throw InternalError("Fuck you")
}
val step = value.clampOne() * 255f // 0.0 .. 255.0
val intStep = step.toInt() // 0 .. 255
val NeXTSTEP = minOf(intStep + 1, 255) // 1 .. 255
return RGB(out[0], out[1], out[2], alpha)
out[i] = interpolateLinear(step - intStep, rgbUnLinLUT[intStep], rgbUnLinLUT[NeXTSTEP])
}
/** Suitable for rigorous maths but slower */
fun RGB.unLineariseSuper(): RGB {
val newR = if (r > 0.0031308f)
1.055f * r.powerOf(1f / 2.4f) - 0.055f
else
r * 12.92f
val newG = if (g > 0.0031308f)
1.055f * g.powerOf(1f / 2.4f) - 0.055f
else
g * 12.92f
val newB = if (b > 0.0031308f)
1.055f * b.powerOf(1f / 2.4f) - 0.055f
else
b * 12.92f
return RGB(out[0], out[1], out[2], alpha)
}*/
/** Suitable for rigorous maths but slower */
fun RGB.unLinearise(): RGB {
val newR = if (r > 0.0031308f)
1.055f * r.powerOf(1f / 2.4f) - 0.055f
else
r * 12.92f
val newG = if (g > 0.0031308f)
1.055f * g.powerOf(1f / 2.4f) - 0.055f
else
g * 12.92f
val newB = if (b > 0.0031308f)
1.055f * b.powerOf(1f / 2.4f) - 0.055f
else
b * 12.92f
return RGB(newR, newG, newB, alpha)
return RGB(newR, newG, newB, alpha)
}
fun RGB.toXYZ(): CIEXYZ {
val new = this.linearise()
val x = 0.4124564f * new.r + 0.3575761f * new.g + 0.1804375f * new.b
val y = 0.2126729f * new.r + 0.7151522f * new.g + 0.0721750f * new.b
val z = 0.0193339f * new.r + 0.1191920f * new.g + 0.9503041f * new.b
return CIEXYZ(x, y, z, alpha)
}
fun RGB.toColor() = Color(r, g, b, alpha)
fun RGB.toCvec() = Cvec(r, g, b, alpha)
fun CIEXYZ.toRGB(): RGB {
val r = 3.2404542f * X - 1.5371385f * Y - 0.4985314f * Z
val g = -0.9692660f * X + 1.8760108f * Y + 0.0415560f * Z
val b = 0.0556434f * X - 0.2040259f * Y + 1.0572252f * Z
return RGB(r, g, b, alpha).unLinearise()
}
fun CIEXYZ.toRGBRaw(): RGB {
val r = 3.2404542f * X - 1.5371385f * Y - 0.4985314f * Z
val g = -0.9692660f * X + 1.8760108f * Y + 0.0415560f * Z
val b = 0.0556434f * X - 0.2040259f * Y + 1.0572252f * Z
return RGB(r, g, b, alpha)
}
fun CIEXYZ.toColor(): Color {
val rgb = this.toRGB()
return Color(rgb.r, rgb.g, rgb.b, rgb.alpha)
}
fun CIEXYZ.mul(scalar: Float) = CIEXYZ(this.X * scalar, this.Y * scalar, this.Z * scalar, this.alpha)
fun CIEXYZ.toColorRaw(): Color {
val rgb = this.toRGBRaw()
return Color(rgb.r, rgb.g, rgb.b, rgb.alpha)
}
fun CIEYXY.toXYZ(): CIEXYZ {
return CIEXYZ(x * yy / y, yy, (1f - x - y) * yy / y)
}
fun colourTempToXYZ(temp: Float, Y: Float): CIEXYZ {
val x = if (temp < 7000)
-4607000000f / FastMath.pow(temp, 3f) + 2967800f / FastMath.pow(temp, 2f) + 99.11f / temp + 0.244063f
else
-2006400000f / FastMath.pow(temp, 3f) + 1901800f / FastMath.pow(temp, 2f) + 247.48f / temp + 0.237040f
val y = -3f * FastMath.pow(x, 2f) + 2.870f * x - 0.275f
return CIEXYZ(x * Y / y, Y, (1 - x - y) * Y / y)
}
private fun Float.powerOf(exp: Float) = FastMath.pow(this, exp)
private fun Float.clampOne() = if (this > 1f) 1f else if (this < 0f) 0f else this
private fun interpolateLinear(scale: Float, startValue: Float, endValue: Float): Float {
if (startValue == endValue) {
return startValue
}
fun RGB.toXYZ(): CIEXYZ {
val new = this.linearise()
val x = 0.4124564f * new.r + 0.3575761f * new.g + 0.1804375f * new.b
val y = 0.2126729f * new.r + 0.7151522f * new.g + 0.0721750f * new.b
val z = 0.0193339f * new.r + 0.1191920f * new.g + 0.9503041f * new.b
return CIEXYZ(x, y, z, alpha)
if (scale <= 0f) {
return startValue
}
fun CIEXYZ.toRGB(): RGB {
val r = 3.2404542f * X - 1.5371385f * Y - 0.4985314f * Z
val g = -0.9692660f * X + 1.8760108f * Y + 0.0415560f * Z
val b = 0.0556434f * X - 0.2040259f * Y + 1.0572252f * Z
return RGB(r, g, b, alpha).unLinearise()
}
fun CIEXYZ.toRGBRaw(): RGB {
val r = 3.2404542f * X - 1.5371385f * Y - 0.4985314f * Z
val g = -0.9692660f * X + 1.8760108f * Y + 0.0415560f * Z
val b = 0.0556434f * X - 0.2040259f * Y + 1.0572252f * Z
return RGB(r, g, b, alpha)
}
fun CIEXYZ.toColor(): Color {
val rgb = this.toRGB()
return Color(rgb.r, rgb.g, rgb.b, rgb.alpha)
}
fun CIEXYZ.toColorRaw(): Color {
val rgb = this.toRGBRaw()
return Color(rgb.r, rgb.g, rgb.b, rgb.alpha)
}
fun CIEYXY.toXYZ(): CIEXYZ {
return CIEXYZ(x * yy / y, yy, (1f - x - y) * yy / y)
}
fun colourTempToXYZ(temp: Float, Y: Float): CIEXYZ {
val x = if (temp < 7000)
-4607000000f / FastMath.pow(temp, 3f) + 2967800f / FastMath.pow(temp, 2f) + 99.11f / temp + 0.244063f
else
-2006400000f / FastMath.pow(temp, 3f) + 1901800f / FastMath.pow(temp, 2f) + 247.48f / temp + 0.237040f
val y = -3f * FastMath.pow(x, 2f) + 2.870f * x - 0.275f
return CIEXYZ(x * Y / y, Y, (1 - x - y) * Y / y)
}
private fun Float.powerOf(exp: Float) = FastMath.pow(this, exp)
private fun Float.clampOne() = if (this > 1f) 1f else if (this < 0f) 0f else this
private fun interpolateLinear(scale: Float, startValue: Float, endValue: Float): Float {
if (startValue == endValue) {
return startValue
}
if (scale <= 0f) {
return startValue
}
return if (scale >= 1f) {
endValue
}
else (1f - scale) * startValue + scale * endValue
return if (scale >= 1f) {
endValue
}
else (1f - scale) * startValue + scale * endValue
}

View File

@@ -1,7 +1,6 @@
package net.torvald.colourutil
import com.badlogic.gdx.graphics.Color
import net.torvald.colourutil.CIEXYZUtil.toColor
import net.torvald.terrarum.GdxColorMap
import net.torvald.terrarum.ModMgr
@@ -24,5 +23,5 @@ object ColourTemp {
/** returns CIExyY-based colour converted to slick.color
* @param CIE_Y 0.0 - 1.0+ */
operator fun invoke(temp: Float, CIE_Y: Float): Color =
CIEXYZUtil.colourTempToXYZ(temp, CIE_Y).toColor()
colourTempToXYZ(temp, CIE_Y).toColor()
}

View File

@@ -2,7 +2,6 @@ package net.torvald.colourutil
import com.badlogic.gdx.graphics.Color
import com.jme3.math.FastMath
import net.torvald.colourutil.CIEXYZUtil.linearise
/**
* Created by minjaesong on 2016-07-26.

View File

@@ -5,16 +5,22 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Screen
import com.badlogic.gdx.backends.lwjgl.LwjglApplication
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.EMDASH
import net.torvald.colourutil.*
import net.torvald.parametricsky.datasets.DatasetCIEXYZ
import net.torvald.parametricsky.datasets.DatasetRGB
import net.torvald.parametricsky.datasets.DatasetSpectral
import net.torvald.terrarum.inUse
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
import net.torvald.terrarum.modulebasegame.worldgenerator.TWO_PI
import java.awt.Dimension
import javax.swing.*
import kotlin.math.PI
import kotlin.math.pow
const val WIDTH = 1200
@@ -50,8 +56,9 @@ class Application : Game() {
private lateinit var testTex: Texture
var turbidity = 5.0
var albedo = 0.0
var albedo = 0.1
var elevation = 0.0
var scalefactor = 1f
override fun getScreen(): Screen {
return super.getScreen()
@@ -64,12 +71,14 @@ class Application : Game() {
override fun render() {
Gdx.graphics.setTitle("Daylight Model $EMDASH F: ${Gdx.graphics.framesPerSecond}")
if (turbidity <= 0) throw IllegalStateException()
// we need to use different modelstate to accomodate different albedo for each spectral band but oh well...
genTexLoop(ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevation))
val tex = Texture(oneScreen)
tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
tex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
batch.inUse {
batch.draw(tex, 0f, 0f, WIDTH.toFloat(), HEIGHT.toFloat())
@@ -94,8 +103,8 @@ class Application : Game() {
oneScreen.dispose()
}
val outTexWidth = 32
val outTexHeight = 16
val outTexWidth = 256
val outTexHeight = 256
/**
* Generated texture is as if you took the panorama picture of sky: up 70deg to horizon, east-south-west;
@@ -112,6 +121,28 @@ class Application : Game() {
}
for (y in 0 until oneScreen.height) {
for (x in 0 until oneScreen.width) {
val gamma = (x / oneScreen.width.toDouble()) * TWO_PI // 0deg..360deg
val theta = (1.0 - (y / oneScreen.height.toDouble())) * HALF_PI // 90deg..0deg
val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat().times(scalefactor / 10f),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat().times(scalefactor / 10f),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat().times(scalefactor / 10f)
)
val rgb = xyz.toRGB().toColor()
rgb.a = 1f
oneScreen.setColor(rgb)
oneScreen.drawPixel(x, y)
//println("x: ${xyz.X}, y: ${xyz.Y}, z: ${xyz.Z}")
}
}
//System.exit(0)
}
/**
@@ -190,14 +221,21 @@ class Application : Game() {
val mainPanel = JPanel()
val turbidityControl = JSpinner(SpinnerNumberModel(5, 1, 10, 1))
val albedoControl = JSpinner(SpinnerNumberModel(0.3, 0.0, 1.0, 0.05))
val elevationControl = JSpinner(SpinnerNumberModel(45, 0, 90, 5))
val turbidityControl = JSpinner(SpinnerNumberModel(5.0, 1.0, 10.0, 0.1))
val albedoControl = JSpinner(SpinnerNumberModel(0.1, 0.0, 1.0, 0.05))
val elevationControl = JSpinner(SpinnerNumberModel(0.0, 0.0, 90.0, 0.5))
val scalefactorControl = JSpinner(SpinnerNumberModel(1.0, 0.0, 2.0, 0.01))
init {
val turbidityPanel = JPanel()
val albedoPanel = JPanel()
val elevationPanel = JPanel()
val scalefactorPanel = JPanel()
turbidityControl.preferredSize = Dimension(45, 18)
albedoControl.preferredSize = Dimension(45, 18)
elevationControl.preferredSize = Dimension(45, 18)
scalefactorControl.preferredSize = Dimension(45, 18)
turbidityPanel.add(JLabel("Turbidity"))
turbidityPanel.add(turbidityControl)
@@ -208,9 +246,13 @@ class Application : Game() {
elevationPanel.add(JLabel("Elevation"))
elevationPanel.add(elevationControl)
scalefactorPanel.add(JLabel("Scaling Factor"))
scalefactorPanel.add(scalefactorControl)
mainPanel.add(turbidityPanel)
mainPanel.add(albedoPanel)
mainPanel.add(elevationPanel)
mainPanel.add(scalefactorPanel)
this.isVisible = true
this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
@@ -228,7 +270,11 @@ class Application : Game() {
}
elevationControl.addChangeListener {
app.elevation = Math.toRadians((elevationControl.value as Int).toDouble())
app.elevation = Math.toRadians(elevationControl.value as Double)
}
scalefactorControl.addChangeListener {
app.scalefactor = (scalefactorControl.value as Double).toFloat()
}
}

View File

@@ -160,8 +160,10 @@ object ArHosekSkyModel {
// internal definitions
private class DoubleArrayPtr(val arr: DoubleArray, var ptrOffset: Int) {
init {
if (ptrOffset < 0) throw IllegalArgumentException("Negative ptrOffset: $ptrOffset")
}
operator fun get(i: Int) = arr[ptrOffset + i]
}
private class DoubleArrayArrayPtr(var arr: Array<DoubleArray>, var ptrOffset: Int) {
@@ -185,6 +187,8 @@ object ArHosekSkyModel {
albedo: Double,
solar_elevation: Double
) {
if (turbidity < 1) throw IllegalArgumentException("Turbidity must be equal to or greater than 1 (got $turbidity)")
var elev_matrix: DoubleArrayPtr
val int_turbidity = turbidity.toInt()
@@ -193,7 +197,6 @@ object ArHosekSkyModel {
val solar_elevation = pow(solar_elevation / (MATH_PI / 2.0), (1.0 / 3.0))
// alb 0 low turb
elev_matrix = DoubleArrayPtr(dataset, 9 * 6 * (int_turbidity - 1))
@@ -672,13 +675,13 @@ object ArHosekSkyModel {
state.configs[channel],
theta,
gamma
) * state.radiances[channel];
) * state.radiances[channel]
}
private const val pieces = 45
private const val order = 4
fun arhosekskymodel_sr_internal(
private fun arhosekskymodel_sr_internal(
state: ArHosekSkyModelState,
turbidity: Int,
wl: Int,
@@ -686,7 +689,7 @@ object ArHosekSkyModel {
): Double {
var pos = (pow(2.0 * elevation / MATH_PI, 1.0 / 3.0) * pieces).toInt() // floor
if (pos > 44) pos = 44;
if (pos > 44) pos = 44
val break_x = pow((pos.toDouble() / pieces.toDouble()), 3.0) * (MATH_PI * 0.5)
@@ -773,7 +776,7 @@ object ArHosekSkyModel {
val singamma = sin(gamma)
var sc2 = 1.0 - ar2 * singamma * singamma
if (sc2 < 0.0) sc2 = 0.0
var sampleCosine = sqrt (sc2);
var sampleCosine = sqrt(sc2)
// The following will be improved in future versions of the model:
// here, we directly use fitted 5th order polynomials provided by the
@@ -791,9 +794,9 @@ object ArHosekSkyModel {
ldCoefficient[4] * pow(sampleCosine, 4.0) +
ldCoefficient[5] * pow(sampleCosine, 5.0)
direct_radiance *= darkeningFactor;
direct_radiance *= darkeningFactor
return direct_radiance;
return direct_radiance
}
fun arhosekskymodel_solar_radiance(
@@ -816,7 +819,7 @@ object ArHosekSkyModel {
wavelength
)
return direct_radiance + inscattered_radiance;
return direct_radiance + inscattered_radiance
}
}

View File

@@ -194,6 +194,8 @@ object DatasetSpectral {
solarDataset720
)
val waves = floatArrayOf(320f, 360f, 400f, 440f, 480f, 520f, 560f, 600f, 640f, 680f, 720f)
val limbDarkeningDataset320 = doubleArrayOf(0.087657, 0.767174, 0.658123, -1.02953, 0.703297, -0.186735)
val limbDarkeningDataset360 = doubleArrayOf(0.122953, 1.01278, 0.238687, -1.12208, 1.17087, -0.424947)

View File

@@ -4,7 +4,7 @@ import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.jme3.math.FastMath
import net.torvald.colourutil.CIELabUtil.darkerLab
import net.torvald.colourutil.darkerLab
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.Second
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid

View File

@@ -86,4 +86,5 @@ infix fun Long.shake(other: Long): Long {
return s0 + s1
}
val TWO_PI = Math.PI * 2.0
val TWO_PI = Math.PI * 2.0
val HALF_PI = Math.PI / 2.0

View File

@@ -1,2 +1,20 @@
package net.torvald.terrarum.tests
import net.torvald.colourutil.CIEXYZ
import net.torvald.colourutil.getXYZUsingIntegral
import net.torvald.colourutil.toRGB
import net.torvald.colourutil.toRGBRaw
fun main() {
val waves = floatArrayOf(485f,495f,505f,515f,525f,535f,545f,555f,565f)
val samples = floatArrayOf( 0f,0.2f,0.5f,1f, 1f, 1f,0.5f,0.2f,0f)
val xyz = getXYZUsingIntegral(waves, samples)
val srgb = xyz.toRGB()
println(xyz)
println(srgb)
println(CIEXYZ(100f/3f, 100f/3f, 100f/3f).toRGB())
}

View File

@@ -1,10 +1,10 @@
package net.torvald.terrarum.tests
import net.torvald.colourutil.CIEXYZUtil.linearise
import net.torvald.colourutil.CIEXYZUtil.unLinearise
import net.torvald.colourutil.ColourUtil.getLuminosity
import net.torvald.colourutil.ColourUtil.getLuminosityQuick
import net.torvald.colourutil.RGB
import net.torvald.colourutil.linearise
import net.torvald.colourutil.unLinearise
import net.torvald.random.HQRNG
import kotlin.system.measureNanoTime

View File

@@ -1,15 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JVM 12" allPlatforms="JVM [12]">
<compilerSettings />
<compilerArguments>
<option name="jvmTarget" value="12" />
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">

Binary file not shown.

BIN
work_files/skylight/XYZJCGT.pdf LFS Normal file

Binary file not shown.