better skybox model for better sunset

This commit is contained in:
minjaesong
2023-09-21 19:45:22 +09:00
parent 3f8b1d4f49
commit e83b2d1908
5 changed files with 83 additions and 41 deletions

View File

@@ -206,7 +206,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
val theta = yf * HALF_PI
val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI
val gamma = if (y < halfHeight) Math.toRadians(cameraHeading) else Math.toRadians(cameraHeading + 180)
val xyz = CIEXYZ(
@@ -291,7 +291,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
app.solarBearing = (it.value as Double)
}
}
val cameraHeading = JSpinner(SpinnerNumberModel(90.0, 0.0, 180.0, 1.0)).also {
val cameraHeading = JSpinner(SpinnerNumberModel(90.0, -360.0, 360.0, 1.0)).also {
it.preferredSize = dialSize
it.addChangeListener { _ ->
app.cameraHeading = (it.value as Double)

View File

@@ -11,6 +11,8 @@ import net.torvald.terrarum.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.cos
/**
* Created by minjaesong on 2023-08-01.
@@ -23,6 +25,47 @@ fun main() {
val TGA_HEADER_SIZE = 18
val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26)
fun generateStrip(gammaPair: Int, albedo: Double, turbidity: Double, elevationDeg: Double, writefun: (Int, Int, Byte) -> Unit) {
val elevationRad = Math.toRadians(elevationDeg)
/*val gamma = if (gammaPair == 0) HALF_PI else {
Math.toRadians(180 + 114 + 24 * cos(PI * elevationDeg / 40))
}*/
val gamma = Math.toRadians(115 + 25 * cos(PI * elevationDeg / 40)) + (gammaPair * PI)
// 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 - 10
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, gamma, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat()
)
val xyz2 = xyz.scaleToFit(elevationDeg)
val rgb = xyz2.toRGB().toColor()
val colour = rgb.toIntBits().toLittle()
for (i in 0..3) {
writefun(yp, i, colour[bytesLut[i]])
}
}
}
// write header
byteArrayOf(
0, // ID field
@@ -45,7 +88,6 @@ fun main() {
// write pixels
for (gammaPair in 0..1) {
val gamma = if (gammaPair == 0) HALF_PI else 3* HALF_PI
for (albedo0 in 0 until Skybox.albedoCnt) {
val albedo = Skybox.albedos[albedo0]
@@ -54,42 +96,13 @@ fun main() {
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 - 10
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, gamma, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat()
)
val xyz2 = xyz.scaleToFit(elevationDeg)
val rgb = xyz2.toRGB().toColor()
val colour = rgb.toIntBits().toLittle()
var elevationDeg = Skybox.elevationsD[elev0]
if (elevationDeg == 0.0) elevationDeg = 0.5 // dealing with the edge case
generateStrip(gammaPair, albedo, turbidity, elevationDeg) { yp, i, colour ->
val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair
val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp)
val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX)
for (i in 0..3) {
bytes[fileOffset + i] = colour[bytesLut[i]]
}
bytes[fileOffset + i] = colour
}
}
}
@@ -101,4 +114,5 @@ fun main() {
File("./assets/clut/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

@@ -571,6 +571,7 @@ internal object WeatherMixer : RNGConsumer {
gdxBlendNormalStraightAlpha()
val oldNewBlend = weatherbox.weatherBlend.times(2f).coerceAtMost(1f)
val mornNoonBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200;
turbidity0 = (world.weatherbox.oldWeather.json.getDouble("atmoTurbidity") + turbidityCoeff * 2.5).coerceIn(1.0, 10.0)
turbidity1 = (currentWeather.json.getDouble("atmoTurbidity") + turbidityCoeff * 2.5).coerceIn(1.0, 10.0)
@@ -603,7 +604,7 @@ internal object WeatherMixer : RNGConsumer {
shaderAstrum.setUniform4fv("uvG", uvs, 24, 4)
shaderAstrum.setUniform4fv("uvH", uvs, 28, 4)
shaderAstrum.setUniformf("texBlend1", turbTihsBlend, albThisBlend, turbOldBlend, albOldBlend)
shaderAstrum.setUniformf("texBlend2", oldNewBlend, 0f, 0f, 0f)
shaderAstrum.setUniformf("texBlend2", oldNewBlend, mornNoonBlend, 0f, 0f)
shaderAstrum.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderAstrum.setUniformf("randomNumber",
world.worldTime.TIME_T.div(+14.1f).plus(31L),