skybox atlas texture generation

This commit is contained in:
minjaesong
2023-08-01 16:50:37 +09:00
parent 0c00b3b7cc
commit 451808cd1c
13 changed files with 218 additions and 93 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -107,13 +107,13 @@ import kotlin.test.assertEquals
object DatasetCIEXYZ {
val datasetXYZ1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZ1.bin")
val datasetXYZ2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZ2.bin")
val datasetXYZ3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZ3.bin")
val datasetXYZ1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZ1.bin")
val datasetXYZ2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZ2.bin")
val datasetXYZ3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZ3.bin")
val datasetXYZRad1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZRad1.bin")
val datasetXYZRad2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZRad2.bin")
val datasetXYZRad3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZRad3.bin")
val datasetXYZRad1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZRad1.bin")
val datasetXYZRad2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZRad2.bin")
val datasetXYZRad3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZRad3.bin")
init {
assertEquals(1080, datasetXYZ2.size, "Dataset size mismatch: expected 1080, got ${datasetXYZ2.size}")

View File

@@ -107,13 +107,13 @@ import kotlin.test.assertEquals
object DatasetRGB {
val datasetRGB1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGB1.bin")
val datasetRGB2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGB2.bin")
val datasetRGB3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGB3.bin")
val datasetRGB1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGB1.bin")
val datasetRGB2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGB2.bin")
val datasetRGB3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGB3.bin")
val datasetRGBRad1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGBRad1.bin")
val datasetRGBRad2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGBRad2.bin")
val datasetRGBRad3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGBRad3.bin")
val datasetRGBRad1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGBRad1.bin")
val datasetRGBRad2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGBRad2.bin")
val datasetRGBRad3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGBRad3.bin")
init {
assertEquals(1080, datasetRGB2.size, "Dataset size mismatch: expected 1080, got ${datasetRGB2.size}")

View File

@@ -108,41 +108,41 @@ import kotlin.test.assertEquals
object DatasetSpectral {
val dataset320 = readDatasetFromFile("./assets/clut/hosek/dataset320.bin")
val dataset360 = readDatasetFromFile("./assets/clut/hosek/dataset360.bin")
val dataset400 = readDatasetFromFile("./assets/clut/hosek/dataset400.bin")
val dataset440 = readDatasetFromFile("./assets/clut/hosek/dataset440.bin")
val dataset480 = readDatasetFromFile("./assets/clut/hosek/dataset480.bin")
val dataset520 = readDatasetFromFile("./assets/clut/hosek/dataset520.bin")
val dataset560 = readDatasetFromFile("./assets/clut/hosek/dataset560.bin")
val dataset600 = readDatasetFromFile("./assets/clut/hosek/dataset600.bin")
val dataset640 = readDatasetFromFile("./assets/clut/hosek/dataset640.bin")
val dataset680 = readDatasetFromFile("./assets/clut/hosek/dataset680.bin")
val dataset720 = readDatasetFromFile("./assets/clut/hosek/dataset720.bin")
val dataset320 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset320.bin")
val dataset360 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset360.bin")
val dataset400 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset400.bin")
val dataset440 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset440.bin")
val dataset480 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset480.bin")
val dataset520 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset520.bin")
val dataset560 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset560.bin")
val dataset600 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset600.bin")
val dataset640 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset640.bin")
val dataset680 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset680.bin")
val dataset720 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset720.bin")
val datasetRad320 = readDatasetFromFile("./assets/clut/hosek/datasetRad320.bin")
val datasetRad360 = readDatasetFromFile("./assets/clut/hosek/datasetRad360.bin")
val datasetRad400 = readDatasetFromFile("./assets/clut/hosek/datasetRad400.bin")
val datasetRad440 = readDatasetFromFile("./assets/clut/hosek/datasetRad440.bin")
val datasetRad480 = readDatasetFromFile("./assets/clut/hosek/datasetRad480.bin")
val datasetRad520 = readDatasetFromFile("./assets/clut/hosek/datasetRad520.bin")
val datasetRad560 = readDatasetFromFile("./assets/clut/hosek/datasetRad560.bin")
val datasetRad600 = readDatasetFromFile("./assets/clut/hosek/datasetRad600.bin")
val datasetRad640 = readDatasetFromFile("./assets/clut/hosek/datasetRad640.bin")
val datasetRad680 = readDatasetFromFile("./assets/clut/hosek/datasetRad680.bin")
val datasetRad720 = readDatasetFromFile("./assets/clut/hosek/datasetRad720.bin")
val datasetRad320 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad320.bin")
val datasetRad360 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad360.bin")
val datasetRad400 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad400.bin")
val datasetRad440 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad440.bin")
val datasetRad480 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad480.bin")
val datasetRad520 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad520.bin")
val datasetRad560 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad560.bin")
val datasetRad600 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad600.bin")
val datasetRad640 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad640.bin")
val datasetRad680 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad680.bin")
val datasetRad720 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad720.bin")
val solarDataset320 = readDatasetFromFile("./assets/clut/hosek/solarDataset320.bin")
val solarDataset360 = readDatasetFromFile("./assets/clut/hosek/solarDataset360.bin")
val solarDataset400 = readDatasetFromFile("./assets/clut/hosek/solarDataset400.bin")
val solarDataset440 = readDatasetFromFile("./assets/clut/hosek/solarDataset440.bin")
val solarDataset480 = readDatasetFromFile("./assets/clut/hosek/solarDataset480.bin")
val solarDataset520 = readDatasetFromFile("./assets/clut/hosek/solarDataset520.bin")
val solarDataset560 = readDatasetFromFile("./assets/clut/hosek/solarDataset560.bin")
val solarDataset600 = readDatasetFromFile("./assets/clut/hosek/solarDataset600.bin")
val solarDataset640 = readDatasetFromFile("./assets/clut/hosek/solarDataset640.bin")
val solarDataset680 = readDatasetFromFile("./assets/clut/hosek/solarDataset680.bin")
val solarDataset720 = readDatasetFromFile("./assets/clut/hosek/solarDataset720.bin")
val solarDataset320 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset320.bin")
val solarDataset360 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset360.bin")
val solarDataset400 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset400.bin")
val solarDataset440 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset440.bin")
val solarDataset480 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset480.bin")
val solarDataset520 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset520.bin")
val solarDataset560 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset560.bin")
val solarDataset600 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset600.bin")
val solarDataset640 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset640.bin")
val solarDataset680 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset680.bin")
val solarDataset720 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset720.bin")
init {
assertEquals(1080, dataset600.size, "Dataset size mismatch - expected 1080, got ${dataset600.size}")

View File

@@ -241,7 +241,8 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
uiContainer.add(uiRemoCon)
CommandDict // invoke
Skybox // invoke
Skybox.loadlut() // invoke
// Skybox.initiate() // invoke the lengthy calculation
// TODO add console here

View File

@@ -0,0 +1,102 @@
package net.torvald.terrarum.modulebasegame.clut
import net.torvald.colourutil.CIEXYZ
import net.torvald.colourutil.toColor
import net.torvald.colourutil.toRGB
import net.torvald.parametricsky.ArHosekSkyModel
import net.torvald.terrarum.abs
import net.torvald.terrarum.modulebasegame.clut.Skybox.coerceInSmoothly
import net.torvald.terrarum.modulebasegame.clut.Skybox.mapCircle
import net.torvald.terrarum.modulebasegame.clut.Skybox.scaleToFit
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
import net.torvald.terrarum.serialise.toLittle
import java.io.File
import kotlin.math.PI
import kotlin.math.pow
import kotlin.math.roundToInt
/**
* Created by minjaesong on 2023-08-01.
*/
fun main() {
// y: increasing turbidity (1.0 .. 10.0, in steps of 0.333)
// x: elevations (-75 .. 75 in steps of 1, then albedo of [0.1, 0.3, 0.5, 0.7, 0.9])
val texh = Skybox.gradSize * Skybox.turbCnt
val texw = 2 * Skybox.elevCnt * 5
val TGA_HEADER_SIZE = 18
val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26)
// write header
byteArrayOf(
0, // ID field
0, // colour map (none)
2, // colour type (unmapped RGB)
0,0,0,0,0, // colour map spec (empty)
0,0, // x origin (0)
0,0, // y origin (0)
(texw and 255).toByte(),(texw.ushr(8) and 255).toByte(), // width
(texh and 255).toByte(),(texh.ushr(8) and 255).toByte(), // height
32, // bits-per-pixel (8bpp RGBA)
8 // image descriptor
).forEachIndexed { i,b -> bytes[i] = b }
// write footer
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000TRUEVISION-XFILE\u002E\u0000".forEachIndexed { i, c -> bytes[18 + texw * texh * 4 + i] =
c.code.toByte()
}
println("Generating texture atlas ($texw x $texh)...")
// write pixels
for (albedo0 in 0 until Skybox.albedoCnt) {
val albedo = Skybox.albedos[albedo0]
println("Albedo=$albedo")
for (turb0 in 0 until Skybox.turbCnt) {
val turbidity = Skybox.turbiditiesD[turb0]
println("....... Turbidity=$turbidity")
for (elev0 in 0 until Skybox.elevCnt) {
val elevationDeg = Skybox.elevationsD[elev0]
val elevationRad = Math.toRadians(elevationDeg)
// println("... Elevation: $elevationDeg")
val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs())
for (yp in 0 until Skybox.gradSize) {
val yi = yp - 3
val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
// experiments visualisation: https://www.desmos.com/calculator/5crifaekwa
// if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333)
// if (elevationDeg < 0) yf *= -2.0 * asin(xf - 1.0) / PI
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
val theta = yf * HALF_PI
// vertical angle, where 0 is zenith, ±90 is ground (which is odd)
// println("$yp\t$theta")
val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 2).toFloat()
)
val xyz2 = xyz.scaleToFit(elevationDeg)
val rgb = xyz2.toRGB().toColor()
val colour = rgb.toIntBits().toLittle()
val imgOffX = 2 * (albedo0 * Skybox.elevCnt + elev0)
val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp)
val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX)
for (i in 0..7) {
bytes[fileOffset + i] = colour[bytesLut[i]]
}
}
}
}
}
println("Atlas generation done!")
File("./assets/mods/basegame/weathers/main_skybox.tga").writeBytes(bytes)
}
private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.modulebasegame.clut
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath
import net.torvald.colourutil.CIEXYZ
@@ -10,8 +11,10 @@ import net.torvald.colourutil.toRGB
import net.torvald.parametricsky.ArHosekSkyModel
import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.abs
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.*
/**
@@ -21,29 +24,39 @@ object Skybox : Disposable {
const val gradSize = 64
private val gradTexBinLowAlbedo: Array<Texture>
private val gradTexBinHighAlbedo: Array<Texture>
private lateinit var gradTexBinLowAlbedo: Array<TextureRegion>
private lateinit var gradTexBinHighAlbedo: Array<TextureRegion>
operator fun get(elevationDeg: Double, turbidity: Double, highAlbedo: Boolean = false): Texture {
// if (elevationDeg !in elevationsD) {
// throw IllegalArgumentException("Elevation not in ±75° (got $elevationDeg)")
// }
// if (turbidity !in turbiditiesD) {
// throw IllegalArgumentException("Turbidity not in 1..10 (got $turbidity)")
// }
private lateinit var tex: Texture
private lateinit var texRegions: TextureRegionPack
val elev = elevationDeg.coerceIn(elevationsD).toInt() - elevations.first
val turb = ((turbidity.coerceIn(turbiditiesD) - turbiditiesD.start) / (turbidities.step / 10.0)).toInt()
fun loadlut() {
tex = Texture(ModMgr.getGdxFile("basegame", "weathers/main_skybox.png"))
tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1)
}
// printdbg(this, "$elevationDeg $turbidity ; $elev $turb")
// use internal LUT
/*operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion {
val elev = elevationDeg.coerceIn(-75.0, 75.0).times(2.0).roundToInt().plus(150)
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(3.0).roundToInt()
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(5.0).roundToInt()
return gradTexBinLowAlbedo[elev * turbCnt + turb]
}*/
return (if (highAlbedo) gradTexBinHighAlbedo else gradTexBinLowAlbedo)[elev * turbCnt + turb]
// use external LUT
operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion {
val elev = elevationDeg.coerceIn(-75.0, 75.0).roundToInt().plus(75)
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(3.0).roundToInt()
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(5.0).roundToInt()
//printdbg(this, "elev $elevationDeg->$elev; turb $turbidity->$turb; alb $albedo->$alb")
return texRegions.get(alb * elevCnt + elev, turb)
}
private fun Float.scaleFun() =
(1f - 1f / 2f.pow(this/6f)) * 0.97f
private fun CIEXYZ.scaleToFit(elevationDeg: Double): CIEXYZ {
internal fun CIEXYZ.scaleToFit(elevationDeg: Double): CIEXYZ {
return if (elevationDeg >= 0) {
CIEXYZ(
this.X.scaleFun(),
@@ -67,20 +80,21 @@ object Skybox : Disposable {
}
}
private val elevations = (-75..75) //zw 151
private val elevationsD = (elevations.first.toDouble() .. elevations.last.toDouble())
private val turbidityStep = 5
private val turbidities = (1_0..10_0 step turbidityStep) // (100 / turbidityStep) - 1
private val turbiditiesD = (turbidities.first / 10.0..turbidities.last / 10.0)
private val elevCnt = elevations.count()
private val turbCnt = turbidities.count()
private val albedoLow = 0.1
private val albedoHight = 0.8 // for theoretical "winter wonderland"?
private val gamma = HALF_PI
val elevations = (0..150) //
val elevationsD = elevations.map { -75.0 + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast)
val turbidities = (0..27) // 1, 1.333, 1.666, 2, 2,333, ... , 10.0
val turbiditiesD = turbidities.map { 1.0 + it / 3.0 }
val albedos = arrayOf(0.1, 0.3, 0.5, 0.7, 0.9)
val elevCnt = elevations.count()
val turbCnt = turbidities.count()
val albedoCnt = albedos.size
val albedoLow = 0.1
val albedoHight = 0.8 // for theoretical "winter wonderland"?
val gamma = HALF_PI
private fun Double.mapCircle() = sin(HALF_PI * this)
internal fun Double.mapCircle() = sin(HALF_PI * this)
init {
internal fun initiate() {
printdbg(this, "Initialising skybox model")
gradTexBinLowAlbedo = getTexturmaps(albedoLow)
@@ -97,7 +111,7 @@ object Skybox : Disposable {
* @param q polynomial degree. 2+. Larger value means sharper transition around the point p
* @param x the 'x' value of the function, as in `y=f(x)`. 0.0..1.0
*/
private fun polynomialDecay(p: Double, q: Int, x: Double): Double {
internal fun polynomialDecay(p: Double, q: Int, x: Double): Double {
val sign = if (q % 2 == 1) -1 else 1
val a1 = -1.0 / p
val a2 = 1.0 / (1.0 - p)
@@ -108,7 +122,7 @@ object Skybox : Disposable {
sign * a2.pow(q - 1.0) * (x - 1.0).pow(q)
}
private fun polynomialDecay2(p: Double, q: Int, x: Double): Double {
internal fun polynomialDecay2(p: Double, q: Int, x: Double): Double {
val sign = if (q % 2 == 1) 1 else -1
val a1 = -1.0 / p
val a2 = 1.0 / (1.0 - p)
@@ -119,11 +133,11 @@ object Skybox : Disposable {
sign * a2.pow(q - 1.0) * (x - 1.0).pow(q) + 1.0
}
private fun superellipsoidDecay(p: Double, x: Double): Double {
internal fun superellipsoidDecay(p: Double, x: Double): Double {
return 1.0 - (1.0 - (1.0 - x).pow(1.0 / p)).pow(p)
}
private fun Double.coerceInSmoothly(low: Double, high: Double): Double {
internal fun Double.coerceInSmoothly(low: Double, high: Double): Double {
val x = this.coerceIn(low, high)
val x2 = ((x - low) * (high - low).pow(-1.0))
// return FastMath.interpolateLinear(polynomialDecay2(0.5, 2, x2), low, high)
@@ -133,25 +147,26 @@ object Skybox : Disposable {
/**
* To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl
*/
private fun smoothLinear(p: Double, x0: Double): Double {
internal fun smoothLinear(p: Double, x0: Double): Double {
val x = x0 - 0.5
val t = 0.5 * sqrt(1.0 - 2.0 * p)
val p1 = sqrt(1.0 - 2.0 * p)
val t = 0.5 * p1
val y0 = if (x < -t)
(1.0 / p) * (x + 0.5).pow(2) - 0.5
else if (x > t)
-(1.0 / p) * (x - 0.5).pow(2) + 0.5
else
x * 2.0 / (1.0 + sqrt(1.0 - 2.0 * p))
x * 2.0 / (1.0 + p1)
return y0 + 0.5
}
private fun getTexturmaps(albedo: Double): Array<Texture> {
private fun getTexturmaps(albedo: Double): Array<TextureRegion> {
return Array(elevCnt * turbCnt) {
val elevationDeg = (it / turbCnt).plus(elevations.first).toDouble()
val elevationDeg = elevationsD[it / turbCnt]
val elevationRad = Math.toRadians(elevationDeg)
val turbidity = 1.0 + (it % turbCnt) / (10.0 / turbidityStep)
val turbidity = turbiditiesD[it % turbCnt]
val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs())
val pixmap = Pixmap(1, gradSize, Pixmap.Format.RGBA8888)
@@ -161,15 +176,17 @@ object Skybox : Disposable {
for (yp in 0 until gradSize) {
val yi = yp - 3
val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceInSmoothly(0.0, 0.95)
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
// experiments visualisation: https://www.desmos.com/calculator/5crifaekwa
// if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333)
// if (elevationDeg < 0) yf *= -2.0 * asin(xf - 1.0) / PI
if (elevationDeg < 0) yf *= superellipsoidDecay(1.0 / 3.0, xf)
val theta = (yf.mapCircle() * HALF_PI)
val theta = yf * HALF_PI
// vertical angle, where 0 is zenith, ±90 is ground (which is odd)
// println("$yp\t$theta")
val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(),
@@ -187,12 +204,13 @@ object Skybox : Disposable {
it.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
}
pixmap.dispose()
texture
TextureRegion(texture)
}
}
override fun dispose() {
gradTexBinLowAlbedo.forEach { it.dispose() }
gradTexBinHighAlbedo.forEach { it.dispose() }
if (::gradTexBinLowAlbedo.isInitialized) gradTexBinLowAlbedo.forEach { it.texture.dispose() }
if (::gradTexBinHighAlbedo.isInitialized) gradTexBinHighAlbedo.forEach { it.texture.dispose() }
if (::tex.isInitialized) tex.dispose()
}
}

View File

@@ -152,7 +152,7 @@ internal object WeatherMixer : RNGConsumer {
}
var turbidity = 4.0; private set
private var gH = (4f/3f) * App.scr.height
private var gH = 1.4f * App.scr.height
// private var gH = 0.8f * App.scr.height
internal var parallaxPos = 0f; private set
@@ -195,13 +195,17 @@ internal object WeatherMixer : RNGConsumer {
gdxBlendNormalStraightAlpha()
val degThis = if (timeNow < HALF_DAY) solarElev.floorToDouble() else solarElev.ceilToDouble()
val degThis = if (timeNow < HALF_DAY)
solarElev.floorToDouble()
else
solarElev.ceilToDouble()
val degNext = degThis + if (timeNow < HALF_DAY) 1 else -1 // Skybox.get has internal coerceIn
val thisTurbidity = forceTurbidity ?: turbidity
val texture1 = Skybox[degThis, thisTurbidity]
val texture2 = Skybox[degNext, thisTurbidity]
// TODO trilinear with (deg, turb, alb)
val texture1 = Skybox[degThis, thisTurbidity, 0.1]
val texture2 = Skybox[degNext, thisTurbidity, 0.1]
val lerpScale = (if (timeNow < HALF_DAY) solarElev - degThis else -(solarElev - degThis)).toFloat()
// println("degThis=$degThis, degNext=$degNext, lerp=$lerpScale")
@@ -210,10 +214,10 @@ internal object WeatherMixer : RNGConsumer {
batch.inUse {
batch.shader = null
batch.color = Color.WHITE
batch.draw(texture1, 0f, gradY, App.scr.wf, gH)
batch.draw(texture1, -App.scr.halfwf, gradY, 2f * App.scr.wf, gH)
batch.color = Color(1f, 1f, 1f, lerpScale)
batch.draw(texture2, 0f, gradY, App.scr.wf, gH)
batch.draw(texture2, -App.scr.halfwf, gradY, 2f * App.scr.wf, gH)
batch.color = Color.WHITE
}