skybox: two different setup for AM/PN

This commit is contained in:
minjaesong
2023-08-06 15:50:04 +09:00
parent 52ad8f0c46
commit 30fb57eca3
7 changed files with 115 additions and 64 deletions

Binary file not shown.

View File

@@ -0,0 +1,31 @@
{
"GAME_ITEM_CALENDAR": "Calendar",
"MENU_CALENDAR_CALENDAR": "Calendar",
"MENU_CALENDAR_EVENTS": "Events",
"MENU_CALENDAR_ADD_NEW_EVENT": "Add New Event…",
"CONTEXT_CALENDAR_SEASON_SPRING": "Spring",
"CONTEXT_CALENDAR_SEASON_SUMMER": "Summer",
"CONTEXT_CALENDAR_SEASON_AUTUMN": "Autumn",
"CONTEXT_CALENDAR_SEASON_WINTER": "Winter",
"CONTEXT_CALENDAR_SEASON_SPRI": "Spri",
"CONTEXT_CALENDAR_SEASON_SUMM": "Summ",
"CONTEXT_CALENDAR_SEASON_AUTM": "Autm",
"CONTEXT_CALENDAR_SEASON_WINT": "Wint",
"CONTEXT_CALENDAR_DAY_MONDAG_DNT": "Mondag",
"CONTEXT_CALENDAR_DAY_TYSDAG_DNT": "Tysdag",
"CONTEXT_CALENDAR_DAY_MIDTVEKE_DNT": "Midtveke",
"CONTEXT_CALENDAR_DAY_TORSDAG_DNT": "Torsdag",
"CONTEXT_CALENDAR_DAY_FREDAG_DNT": "Fredag",
"CONTEXT_CALENDAR_DAY_LAURDAG_DNT": "Laurdag",
"CONTEXT_CALENDAR_DAY_SUNDAG_DNT": "Sundag",
"CONTEXT_CALENDAR_DAY_VERDDAG_DNT": "Verddag",
"CONTEXT_CALENDAR_DAY_MON_DNT": "Mon",
"CONTEXT_CALENDAR_DAY_TYS_DNT": "Tys",
"CONTEXT_CALENDAR_DAY_MID_DNT": "Mid",
"CONTEXT_CALENDAR_DAY_TOR_DNT": "Tor",
"CONTEXT_CALENDAR_DAY_FRE_DNT": "Fre",
"CONTEXT_CALENDAR_DAY_LAU_DNT": "Lau",
"CONTEXT_CALENDAR_DAY_SUN_DNT": "Sun",
"CONTEXT_CALENDAR_DAY_VER_DNT": "Ver"
}

View File

@@ -0,0 +1,14 @@
{
"GAME_ITEM_CALENDAR": "달력",
"MENU_CALENDAR_CALENDAR": "달력",
"MENU_CALENDAR_EVENTS": "일정",
"MENU_CALENDAR_ADD_NEW_EVENT": "새 일정 추가…",
"CONTEXT_CALENDAR_SEASON_SPRING": "봄",
"CONTEXT_CALENDAR_SEASON_SUMMER": "여름",
"CONTEXT_CALENDAR_SEASON_AUTUMN": "가을",
"CONTEXT_CALENDAR_SEASON_WINTER": "겨울",
"CONTEXT_CALENDAR_SEASON_SPRI": "봄",
"CONTEXT_CALENDAR_SEASON_SUMM": "여름",
"CONTEXT_CALENDAR_SEASON_AUTM": "가을",
"CONTEXT_CALENDAR_SEASON_WINT": "겨울"
}

View File

@@ -19,7 +19,7 @@ fun main() {
// y: increasing turbidity (1.0 .. 10.0, in steps of 0.333) // 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]) // 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 texh = Skybox.gradSize * Skybox.turbCnt
val texw = Skybox.elevCnt * Skybox.albedoCnt val texw = Skybox.elevCnt * Skybox.albedoCnt * 2
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)
@@ -44,47 +44,50 @@ fun main() {
println("Generating texture atlas ($texw x $texh)...") println("Generating texture atlas ($texw x $texh)...")
// write pixels // write pixels
for (albedo0 in 0 until Skybox.albedoCnt) { for (gammaPair in 0..1) {
val albedo = Skybox.albedos[albedo0] for (albedo0 in 0 until Skybox.albedoCnt) {
println("Albedo=$albedo") val albedo = Skybox.albedos[albedo0]
for (turb0 in 0 until Skybox.turbCnt) { println("Albedo=$albedo")
val turbidity = Skybox.turbiditiesD[turb0] for (turb0 in 0 until Skybox.turbCnt) {
println("....... Turbidity=$turbidity") val turbidity = Skybox.turbiditiesD[turb0]
for (elev0 in 0 until Skybox.elevCnt) { println("....... Turbidity=$turbidity")
val elevationDeg = Skybox.elevationsD[elev0] for (elev0 in 0 until Skybox.elevCnt) {
val elevationRad = Math.toRadians(elevationDeg) val elevationDeg = Skybox.elevationsD[elev0]
val elevationRad = Math.toRadians(elevationDeg)
// println("... Elevation: $elevationDeg") // println("... Elevation: $elevationDeg")
val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs()) val state =
ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs())
for (yp in 0 until Skybox.gradSize) { for (yp in 0 until Skybox.gradSize) {
val yi = yp - 3 val yi = yp - 3
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
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)
// experiments visualisation: https://www.desmos.com/calculator/5crifaekwa // experiments visualisation: https://www.desmos.com/calculator/5crifaekwa
// if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333) // 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 *= -2.0 * asin(xf - 1.0) / PI
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
// vertical angle, where 0 is zenith, ±90 is ground (which is odd) // vertical angle, where 0 is zenith, ±90 is ground (which is odd)
// println("$yp\t$theta") // println("$yp\t$theta")
val xyz = CIEXYZ( val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 1).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 2).toFloat() ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 2).toFloat()
) )
val xyz2 = xyz.scaleToFit(elevationDeg) val xyz2 = xyz.scaleToFit(elevationDeg)
val rgb = xyz2.toRGB().toColor() val rgb = xyz2.toRGB().toColor()
val colour = rgb.toIntBits().toLittle() val colour = rgb.toIntBits().toLittle()
val imgOffX = (albedo0 * Skybox.elevCnt + elev0) 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) { for (i in 0..3) {
bytes[fileOffset + i] = colour[bytesLut[i]] bytes[fileOffset + i] = colour[bytesLut[i]]
}
} }
} }
} }

View File

@@ -13,6 +13,7 @@ import net.torvald.parametricsky.ArHosekSkyModel
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.toInt
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.* import kotlin.math.*
@@ -50,28 +51,24 @@ object Skybox : Disposable {
}*/ }*/
// use external LUT // use external LUT
operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion { operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double, isAfternoon: Boolean): TextureRegion {
val elev = elevationDeg.coerceIn(-elevMax, elevMax).roundToInt().plus(elevMax).roundToInt() TODO()
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt()
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(turbDivisor).roundToInt()
//printdbg(this, "elev $elevationDeg->$elev; turb $turbidity->$turb; alb $albedo->$alb")
return texRegions.get(alb * elevCnt + elev, turb)
} }
fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): Pair<Texture, FloatArray> { fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): Pair<Texture, FloatArray> {
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt() val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt()
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(turbDivisor).roundToInt() val alb = albedo.coerceIn(0.0, 1.0).times(turbDivisor).roundToInt()
val region = texStripRegions.get(alb, turb) val region1 = texStripRegions.get(alb + albedoCnt * 0, turb) // left half of the sheet
val region2 = texStripRegions.get(alb + albedoCnt * 1, turb) // right half of the sheet
val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt).times((elevCnt - 1.0) / elevCnt) val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt * 2).times((elevCnt - 1.0) / elevCnt)
val u = region.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded val uA = region1.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded
val uB = region2.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded
return tex to floatArrayOf( return tex to floatArrayOf(
u, uA, region1.v, uA, region1.v2,
region.v, uB, region2.v, uB, region2.v2,
u,
region.v2
) )
} }
@@ -113,7 +110,7 @@ object Skybox : Disposable {
val turbidities = (0..45) // 1, 1.2, 1.4, 1.6, ..., 10.0 val turbidities = (0..45) // 1, 1.2, 1.4, 1.6, ..., 10.0
val turbDivisor = 5.0 val turbDivisor = 5.0
val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor } val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor }
val albedos = arrayOf(0.1, 0.3, 0.5, 0.7, 0.9) val albedos = arrayOf(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0)
val elevCnt = elevations.count() val elevCnt = elevations.count()
val turbCnt = turbidities.count() val turbCnt = turbidities.count()
val albedoCnt = albedos.size val albedoCnt = albedos.size

View File

@@ -208,8 +208,10 @@ internal object WeatherMixer : RNGConsumer {
// TODO trilinear with (deg, turb, alb) // TODO trilinear with (deg, turb, alb)
val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f) val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f)
val (tex, uvs) = Skybox.getUV(solarElev, thisTurbidity, 0.3) val (tex, uvs) = Skybox.getUV(solarElev, thisTurbidity, 0.3)
val texBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200;
starmapTex.texture.bind(1) starmapTex.texture.bind(1)
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
@@ -220,20 +222,20 @@ internal object WeatherMixer : RNGConsumer {
batch.inUse { batch.inUse {
batch.shader = shaderBlendMax batch.shader = shaderBlendMax
shaderBlendMax.setUniformi("tex1", 1) shaderBlendMax.setUniformi("tex1", 1)
shaderBlendMax.setUniformf("drawOffset", 0f, gradY) shaderBlendMax.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderBlendMax.setUniformf("drawOffsetSize", App.scr.wf, gH) shaderBlendMax.setUniform4fv("skyboxUV1", uvs, 0, 4)
shaderBlendMax.setUniform2fv("skyboxUV1", uvs, 0, 2) shaderBlendMax.setUniform4fv("skyboxUV2", uvs, 4, 4)
shaderBlendMax.setUniform2fv("skyboxUV2", uvs, 2, 2) shaderBlendMax.setUniformf("texBlend", texBlend)
shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY) shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderBlendMax.setUniformf("randomNumber", shaderBlendMax.setUniformf("randomNumber",
// (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(37L) xor 862L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(37L) xor 862L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(29L) xor 2971L + 41L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(29L) xor 2971L + 41L).and(1023).toFloat(),
world.worldTime.TIME_T.div(+12.1f).plus(31L), world.worldTime.TIME_T.div(+14.1f).plus(31L),
world.worldTime.TIME_T.div(-11.8f).plus(37L), world.worldTime.TIME_T.div(-13.8f).plus(37L),
world.worldTime.TIME_T.div(+11.9f).plus(23L), world.worldTime.TIME_T.div(+13.9f).plus(23L),
world.worldTime.TIME_T.div(-12.3f).plus(29L), world.worldTime.TIME_T.div(-14.3f).plus(29L),
) )
batch.color = Color.WHITE batch.color = Color.WHITE

View File

@@ -13,10 +13,10 @@ out vec4 fragColor;
const vec2 boolean = vec2(0.0, 1.0); const vec2 boolean = vec2(0.0, 1.0);
uniform vec2 drawOffset; // value of the 'gradY' uniform vec4 drawOffsetSize; // (gradX, gradY, gradW, gradH)
uniform vec2 drawOffsetSize; // value of the 'gradH' uniform vec4 skyboxUV1; // (u, v, u2, v2) for the skybox drawing (morning)
uniform vec2 skyboxUV1; // (u, v) for the skybox drawing uniform vec4 skyboxUV2; // (u, v, u2, v2) for the skybox drawing (afternoon)
uniform vec2 skyboxUV2; // (u2, v2) for the skybox drawing uniform float texBlend;
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);
@@ -112,13 +112,17 @@ vec4 random(vec2 p) {
// draw call to this function must use UV coord of (0,0,1,1)! // draw call to this function must use UV coord of (0,0,1,1)!
void main(void) { void main(void) {
vec2 skyboxTexCoord = mix(skyboxUV1, skyboxUV2, v_texCoords); vec2 skyboxTexCoordMorning = mix(skyboxUV1.xy, skyboxUV1.zw, v_texCoords);
vec2 astrumTexCoord = (v_texCoords * drawOffsetSize + drawOffset + astrumScroll) / tex1Size; vec2 skyboxTexCoordAfternoon = mix(skyboxUV2.xy, skyboxUV2.zw, v_texCoords);
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
vec4 colorTex0 = texture(u_texture, skyboxTexCoord); vec4 colorTex0Morining = texture(u_texture, skyboxTexCoordMorning);
vec4 colorTex0Afternoon = texture(u_texture, skyboxTexCoordAfternoon);
vec4 colorTex0 = mix(colorTex0Morining, colorTex0Afternoon, texBlend);
vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness; vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness;