mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-14 15:46:06 +09:00
better skybox model for better sunset
This commit is contained in:
BIN
assets/clut/skybox.png
LFS
BIN
assets/clut/skybox.png
LFS
Binary file not shown.
@@ -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)
|
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)
|
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
|
||||||
val theta = yf * HALF_PI
|
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(
|
val xyz = CIEXYZ(
|
||||||
@@ -291,7 +291,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
|
|||||||
app.solarBearing = (it.value as Double)
|
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.preferredSize = dialSize
|
||||||
it.addChangeListener { _ ->
|
it.addChangeListener { _ ->
|
||||||
app.cameraHeading = (it.value as Double)
|
app.cameraHeading = (it.value as Double)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import net.torvald.terrarum.clut.Skybox.scaleToFit
|
|||||||
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
|
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
|
||||||
import net.torvald.terrarum.serialise.toLittle
|
import net.torvald.terrarum.serialise.toLittle
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.math.PI
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by minjaesong on 2023-08-01.
|
* Created by minjaesong on 2023-08-01.
|
||||||
@@ -23,6 +25,47 @@ fun main() {
|
|||||||
val TGA_HEADER_SIZE = 18
|
val TGA_HEADER_SIZE = 18
|
||||||
|
|
||||||
val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26)
|
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
|
// write header
|
||||||
byteArrayOf(
|
byteArrayOf(
|
||||||
0, // ID field
|
0, // ID field
|
||||||
@@ -45,7 +88,6 @@ fun main() {
|
|||||||
|
|
||||||
// write pixels
|
// write pixels
|
||||||
for (gammaPair in 0..1) {
|
for (gammaPair in 0..1) {
|
||||||
val gamma = if (gammaPair == 0) HALF_PI else 3* HALF_PI
|
|
||||||
|
|
||||||
for (albedo0 in 0 until Skybox.albedoCnt) {
|
for (albedo0 in 0 until Skybox.albedoCnt) {
|
||||||
val albedo = Skybox.albedos[albedo0]
|
val albedo = Skybox.albedos[albedo0]
|
||||||
@@ -54,42 +96,13 @@ fun main() {
|
|||||||
val turbidity = Skybox.turbiditiesD[turb0]
|
val turbidity = Skybox.turbiditiesD[turb0]
|
||||||
println("....... Turbidity=$turbidity")
|
println("....... Turbidity=$turbidity")
|
||||||
for (elev0 in 0 until Skybox.elevCnt) {
|
for (elev0 in 0 until Skybox.elevCnt) {
|
||||||
val elevationDeg = Skybox.elevationsD[elev0]
|
var elevationDeg = Skybox.elevationsD[elev0]
|
||||||
val elevationRad = Math.toRadians(elevationDeg)
|
if (elevationDeg == 0.0) elevationDeg = 0.5 // dealing with the edge case
|
||||||
// println("... Elevation: $elevationDeg")
|
generateStrip(gammaPair, albedo, turbidity, elevationDeg) { yp, i, colour ->
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair
|
val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair
|
||||||
val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp)
|
val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp)
|
||||||
val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX)
|
val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX)
|
||||||
for (i in 0..3) {
|
bytes[fileOffset + i] = colour
|
||||||
bytes[fileOffset + i] = colour[bytesLut[i]]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,4 +114,5 @@ fun main() {
|
|||||||
File("./assets/clut/skybox.tga").writeBytes(bytes)
|
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
|
private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work
|
||||||
@@ -571,6 +571,7 @@ internal object WeatherMixer : RNGConsumer {
|
|||||||
gdxBlendNormalStraightAlpha()
|
gdxBlendNormalStraightAlpha()
|
||||||
|
|
||||||
val oldNewBlend = weatherbox.weatherBlend.times(2f).coerceAtMost(1f)
|
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)
|
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)
|
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("uvG", uvs, 24, 4)
|
||||||
shaderAstrum.setUniform4fv("uvH", uvs, 28, 4)
|
shaderAstrum.setUniform4fv("uvH", uvs, 28, 4)
|
||||||
shaderAstrum.setUniformf("texBlend1", turbTihsBlend, albThisBlend, turbOldBlend, albOldBlend)
|
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("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
|
||||||
shaderAstrum.setUniformf("randomNumber",
|
shaderAstrum.setUniformf("randomNumber",
|
||||||
world.worldTime.TIME_T.div(+14.1f).plus(31L),
|
world.worldTime.TIME_T.div(+14.1f).plus(31L),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ uniform sampler2D tex1; // glow texture, SHOULD contain alpha of all 1.0
|
|||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
|
|
||||||
const vec2 boolean = vec2(0.0, 1.0);
|
const vec2 boolean = vec2(0.0, 1.0);
|
||||||
|
const vec2 halfx = vec2(0.5, 0.0);
|
||||||
|
|
||||||
uniform vec4 drawOffsetSize; // (gradX, gradY, gradW, gradH)
|
uniform vec4 drawOffsetSize; // (gradX, gradY, gradW, gradH)
|
||||||
uniform vec4 uvA; // (u, v, u2, v2) for now, turbLow, albLow
|
uniform vec4 uvA; // (u, v, u2, v2) for now, turbLow, albLow
|
||||||
@@ -23,11 +24,12 @@ uniform vec4 uvF; // (u, v, u2, v2) for old, turbLow, albHigh
|
|||||||
uniform vec4 uvG; // (u, v, u2, v2) for now, turbHigh, albHigh
|
uniform vec4 uvG; // (u, v, u2, v2) for now, turbHigh, albHigh
|
||||||
uniform vec4 uvH; // (u, v, u2, v2) for old, turbHigh, albHigh
|
uniform vec4 uvH; // (u, v, u2, v2) for old, turbHigh, albHigh
|
||||||
uniform vec4 texBlend1; // (turbidity now, albedo now, turbidity old, albedo old)
|
uniform vec4 texBlend1; // (turbidity now, albedo now, turbidity old, albedo old)
|
||||||
uniform vec4 texBlend2; // (old-now blend, unused, unused, unused)
|
uniform vec4 texBlend2; // (old-now blend, morn-noon blend, unused, unused)
|
||||||
uniform vec2 tex1Size = vec2(4096.0);
|
uniform vec2 tex1Size = vec2(4096.0);
|
||||||
uniform vec2 astrumScroll = vec2(0.0);
|
uniform vec2 astrumScroll = vec2(0.0);
|
||||||
uniform vec4 randomNumber = vec4(1.0, -2.0, 3.0, -4.0);
|
uniform vec4 randomNumber = vec4(1.0, -2.0, 3.0, -4.0);
|
||||||
|
|
||||||
|
|
||||||
vec3 mod289(vec3 x) {
|
vec3 mod289(vec3 x) {
|
||||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||||
}
|
}
|
||||||
@@ -128,6 +130,15 @@ void main(void) {
|
|||||||
vec4 colorTexG = texture(u_texture, mix(uvG.xy, uvG.zw, v_texCoords));
|
vec4 colorTexG = texture(u_texture, mix(uvG.xy, uvG.zw, v_texCoords));
|
||||||
vec4 colorTexH = texture(u_texture, mix(uvH.xy, uvH.zw, v_texCoords));
|
vec4 colorTexH = texture(u_texture, mix(uvH.xy, uvH.zw, v_texCoords));
|
||||||
|
|
||||||
|
vec4 colorTexA2 = texture(u_texture, mix(uvA.xy, uvA.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexB2 = texture(u_texture, mix(uvB.xy, uvB.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexC2 = texture(u_texture, mix(uvC.xy, uvC.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexD2 = texture(u_texture, mix(uvD.xy, uvD.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexE2 = texture(u_texture, mix(uvE.xy, uvE.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexF2 = texture(u_texture, mix(uvF.xy, uvF.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexG2 = texture(u_texture, mix(uvG.xy, uvG.zw, v_texCoords) + halfx);
|
||||||
|
vec4 colorTexH2 = texture(u_texture, mix(uvH.xy, uvH.zw, v_texCoords) + halfx);
|
||||||
|
|
||||||
|
|
||||||
vec2 astrumTexCoord = (v_texCoords * drawOffsetSize.zw + drawOffsetSize.xy + astrumScroll) / tex1Size;
|
vec2 astrumTexCoord = (v_texCoords * drawOffsetSize.zw + drawOffsetSize.xy + astrumScroll) / tex1Size;
|
||||||
vec4 randomness = snoise4((gl_FragCoord.xy - astrumScroll) * 0.16) * 2.0; // multiply by 2 so that the "density" of the stars would be same as the non-random version
|
vec4 randomness = snoise4((gl_FragCoord.xy - astrumScroll) * 0.16) * 2.0; // multiply by 2 so that the "density" of the stars would be same as the non-random version
|
||||||
@@ -136,7 +147,7 @@ void main(void) {
|
|||||||
vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness;
|
vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness;
|
||||||
|
|
||||||
// notations used: https://en.wikipedia.org/wiki/File:Enclosing_points.svg and https://en.wikipedia.org/wiki/File:3D_interpolation2.svg
|
// notations used: https://en.wikipedia.org/wiki/File:Enclosing_points.svg and https://en.wikipedia.org/wiki/File:3D_interpolation2.svg
|
||||||
vec4 colorTex0 = mix(
|
vec4 colorTex0Morn = mix(
|
||||||
mix( // now-values
|
mix( // now-values
|
||||||
mix(colorTexA, colorTexE, texBlend1.y), // c00 = c000..c100
|
mix(colorTexA, colorTexE, texBlend1.y), // c00 = c000..c100
|
||||||
mix(colorTexC, colorTexG, texBlend1.y), // c10 = c010..c110
|
mix(colorTexC, colorTexG, texBlend1.y), // c10 = c010..c110
|
||||||
@@ -150,6 +161,22 @@ void main(void) {
|
|||||||
texBlend2.x
|
texBlend2.x
|
||||||
); // c = c0..c1
|
); // c = c0..c1
|
||||||
|
|
||||||
|
vec4 colorTex0Noon = mix(
|
||||||
|
mix( // now-values
|
||||||
|
mix(colorTexA2, colorTexE2, texBlend1.y), // c00 = c000..c100
|
||||||
|
mix(colorTexC2, colorTexG2, texBlend1.y), // c10 = c010..c110
|
||||||
|
texBlend1.x
|
||||||
|
), // c0 = c00..c10
|
||||||
|
mix( // old-values
|
||||||
|
mix(colorTexB2, colorTexF2, texBlend1.w), // c01 = c001..c101
|
||||||
|
mix(colorTexD2, colorTexH2, texBlend1.w), // c11 = c011..c111
|
||||||
|
texBlend1.z
|
||||||
|
), // c1 = c01..c11
|
||||||
|
texBlend2.x
|
||||||
|
); // c = c0..c1
|
||||||
|
|
||||||
|
vec4 colorTex0 = mix(colorTex0Morn, colorTex0Noon, texBlend2.y);
|
||||||
|
|
||||||
|
|
||||||
// fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy;
|
// fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy;
|
||||||
fragColor = fma(max(colorTex0, colorTex1), boolean.yyyx, boolean.xxxy);
|
fragColor = fma(max(colorTex0, colorTex1), boolean.yyyx, boolean.xxxy);
|
||||||
|
|||||||
Reference in New Issue
Block a user