each cloud now carry its own gamma value

This commit is contained in:
minjaesong
2023-09-02 02:40:27 +09:00
parent 721a24d9dc
commit 78075d779b
9 changed files with 157 additions and 35 deletions

View File

@@ -4,8 +4,8 @@
"daylightClut": "clut_daylight.tga", "daylightClut": "clut_daylight.tga",
"classification": "generic", "classification": "generic",
"cloudChance": 125, "cloudChance": 125,
"cloudGamma": [0.48, 2.4], "cloudGamma": [0.45, 2.4],
"cloudGammaVariance": [0.1, 0.1], "cloudGammaVariance": [0.1, 0.0],
"windSpeed": 0.25, "windSpeed": 0.25,
"windSpeedVariance": 1.0, "windSpeedVariance": 1.0,
"clouds": { "clouds": {

View File

@@ -4,8 +4,8 @@
"daylightClut": "clut_daylight.tga", "daylightClut": "clut_daylight.tga",
"classification": "generic2", "classification": "generic2",
"cloudChance": 800, "cloudChance": 800,
"cloudGamma": [0.48, 2.4], "cloudGamma": [0.6, 2.4],
"cloudGammaVariance": [0.1, 0.1], "cloudGammaVariance": [0.1, 0.0],
"windSpeed": 0.25, "windSpeed": 0.25,
"windSpeedVariance": 1.0, "windSpeedVariance": 1.0,
"clouds": { "clouds": {

View File

@@ -4,8 +4,8 @@
"daylightClut": "clut_daylight.tga", "daylightClut": "clut_daylight.tga",
"classification": "overcast", "classification": "overcast",
"cloudChance": 300, "cloudChance": 300,
"cloudGamma": [2.2, 2.0], "cloudGamma": [3.7, 1.6],
"cloudGammaVariance": [0.1, 0.1], "cloudGammaVariance": [0.0, 0.0],
"windSpeed": 10.45, "windSpeed": 10.45,
"windSpeedVariance": 0.5, "windSpeedVariance": 0.5,
"clouds": { "clouds": {

View File

@@ -40,6 +40,16 @@ data class BaseModularWeather(
val v = 1f + rnd.absoluteValue * windSpeedVariance val v = 1f + rnd.absoluteValue * windSpeedVariance
return if (rnd < 0) windSpeed / v else windSpeed * v return if (rnd < 0) windSpeed / v else windSpeed * v
} }
fun getRandomCloudGamma(rnd1: Float, rnd2: Float): Vector2 {
val v = 1f + rnd1.absoluteValue * cloudGammaVariance.x
val gx = if (rnd1 < 0) cloudGamma.x / v else cloudGamma.x * v
val u = 1f + rnd2.absoluteValue * cloudGammaVariance.y
val gy = if (rnd2 < 0) cloudGamma.y / u else cloudGamma.y * u
return Vector2(gx, gy)
}
} }
data class CloudProps( data class CloudProps(

View File

@@ -146,24 +146,22 @@ internal object WeatherMixer : RNGConsumer {
oldCamPos.set(WorldCamera.camVector) oldCamPos.set(WorldCamera.camVector)
weatherbox = Weatherbox() weatherbox = Weatherbox()
weatherbox.initWith(weatherDict["generic01"]!!, 3600L) weatherbox.initWith(weatherDict["generic01"]!!, 7200L)
// TEST FILL WITH RANDOM VALUES // TEST FILL WITH RANDOM VALUES
(0..5).map { takeUniformRand(0f..1f) }.let { (0..4).map { takeUniformRand(0f..1f) }.let {
weatherbox.windDir.pM1 = it[0] weatherbox.windDir.pM1 = it[0]
weatherbox.windDir.p0 = it[1] weatherbox.windDir.p0 = it[1]
weatherbox.windDir.p1 = it[2] weatherbox.windDir.p1 = it[2]
weatherbox.windDir.p2 = it[3] weatherbox.windDir.p2 = it[3]
weatherbox.windDir.p3 = it[4] weatherbox.windDir.p3 = it[4]
// weatherbox.windDir.p4 = it[5]
} }
(0..5).map { takeUniformRand(-1f..1f) }.let { (0..4).map { takeUniformRand(-1f..1f) }.let {
weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[0]) weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[0])
weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[1]) weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[1])
weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[2]) weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[2])
weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(it[3]) weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(it[3])
weatherbox.windSpeed.p3 = currentWeather.getRandomWindSpeed(it[4]) weatherbox.windSpeed.p3 = currentWeather.getRandomWindSpeed(it[4])
// weatherbox.windSpeed.p4 = currentWeather.getRandomWindSpeed(it[5])
} }
} }
@@ -301,7 +299,7 @@ internal object WeatherMixer : RNGConsumer {
it.posX += camDelta.x * cloudParallaxMultX it.posX += camDelta.x * cloudParallaxMultX
it.posY += camDelta.y * cloudParallaxMultY it.posY += camDelta.y * cloudParallaxMultY
it.update(windVector) it.update(world, windVector)
if (DEBUG_CAUSE_OF_DESPAWN && it.life == 0) { if (DEBUG_CAUSE_OF_DESPAWN && it.life == 0) {
immDespawnCount += 1 immDespawnCount += 1
@@ -450,7 +448,15 @@ internal object WeatherMixer : RNGConsumer {
val sheetX = rA % cloud.spriteSheet.horizontalCount val sheetX = rA % cloud.spriteSheet.horizontalCount
val sheetY = rB % cloud.spriteSheet.verticalCount val sheetY = rB % cloud.spriteSheet.verticalCount
WeatherObjectCloud(cloud.spriteSheet.get(sheetX, sheetY), flip).also {
val cloudGamma = currentWeather.getRandomCloudGamma(takeUniformRand(-1f..1f), takeUniformRand(-1f..1f))
WeatherObjectCloud(
cloud.spriteSheet.get(sheetX, sheetY),
flip,
cloudGamma.x,
cloudGamma.y
).also {
it.scale = cloudScale * cloudSizeMult it.scale = cloudScale * cloudSizeMult
it.pos.set(precalculatedPos ?: getCloudSpawningPosition(cloud, hCloudSize, windVector)) it.pos.set(precalculatedPos ?: getCloudSpawningPosition(cloud, hCloudSize, windVector))
@@ -492,7 +498,7 @@ internal object WeatherMixer : RNGConsumer {
internal fun titleScreenInitWeather() { internal fun titleScreenInitWeather() {
weatherbox.initWith(weatherDict["titlescreen"]!!, Long.MAX_VALUE) weatherbox.initWith(weatherDict["titlescreen"]!!, Long.MAX_VALUE)
forceWindVec = Vector3(-0.98f, 0f, -0.21f) forceWindVec = Vector3(-0.98f, 0f, -0.21f).scl(1f/30f) // value taken from TitleScreen.kt; search for 'demoWorld.worldTime.timeDelta = '
initClouds() initClouds()
} }
@@ -525,12 +531,10 @@ internal object WeatherMixer : RNGConsumer {
private fun drawClouds(batch: SpriteBatch) { private fun drawClouds(batch: SpriteBatch) {
batch.inUse { _ -> batch.inUse { _ ->
batch.shader = shaderClouds batch.shader = shaderClouds
batch.shader.setUniformf("gamma", currentWeather.cloudGamma)
batch.shader.setUniformf("shadeCol", 0.06f, 0.07f, 0.08f, 1f) // TODO temporary value batch.shader.setUniformf("shadeCol", 0.06f, 0.07f, 0.08f, 1f) // TODO temporary value
clouds.forEach { clouds.forEach {
batch.color = Color(cloudDrawColour.r, cloudDrawColour.g, cloudDrawColour.b, it.alpha) it.render(batch, cloudDrawColour)
it.render(batch, 0f, 0f)
} }
} }
} }

View File

@@ -1,12 +1,14 @@
package net.torvald.terrarum.weather package net.torvald.terrarum.weather
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.math.Vector3 import com.badlogic.gdx.math.Vector3
import com.jme3.math.FastMath import com.jme3.math.FastMath
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.gameworld.GameWorld
import kotlin.math.absoluteValue
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.sign import kotlin.math.sign
@@ -14,7 +16,12 @@ import kotlin.math.sign
/** /**
* Created by minjaesong on 2023-08-21. * Created by minjaesong on 2023-08-21.
*/ */
class WeatherObjectCloud(private val texture: TextureRegion, private val flipW: Boolean) : WeatherObject(), Comparable<WeatherObjectCloud> { class WeatherObjectCloud(
private val texture: TextureRegion,
private val flipW: Boolean,
private val rgbGamma: Float,
private val aGamma: Float
) : WeatherObject(), Comparable<WeatherObjectCloud> {
override fun update() { override fun update() {
throw UnsupportedOperationException() throw UnsupportedOperationException()
@@ -33,11 +40,12 @@ class WeatherObjectCloud(private val texture: TextureRegion, private val flipW:
* FlowVector: In which direction the cloud flows. Vec3(dX, dY, dScale) * FlowVector: In which direction the cloud flows. Vec3(dX, dY, dScale)
* Resulting vector: (x + dX, y + dY, scale * dScale) * Resulting vector: (x + dX, y + dY, scale * dScale)
*/ */
fun update(flowVector: Vector3) { fun update(world: GameWorld, flowVector: Vector3) {
pos.add( pos.add(
flowVector.cpy(). flowVector.cpy().
scl(1f, 1f, getZflowMult(posZ)). // this will break the perspective if flowVector.z.abs() is close to 1, but it has to be here to "keep the distance" scl(1f, 1f, getZflowMult(posZ)). // this will break the perspective if flowVector.z.abs() is close to 1, but it has to be here to "keep the distance"
scl(vecMult) scl(vecMult).
scl(world.worldTime.timeDelta.toFloat())
) )
eigenAlpha = if (posZ < 1f) posZ.pow(0.5f) else -((posZ - 1f) / ALPHA_ROLLOFF_Z) + 1f eigenAlpha = if (posZ < 1f) posZ.pow(0.5f) else -((posZ - 1f) / ALPHA_ROLLOFF_Z) + 1f
@@ -66,19 +74,43 @@ class WeatherObjectCloud(private val texture: TextureRegion, private val flipW:
private val h = App.scr.hf * 0.5f private val h = App.scr.hf * 0.5f
private val vecMult = Vector3(1f, 1f, 1f / (4f * h)) private val vecMult = Vector3(1f, 1f, 1f / (4f * h))
/** private fun roundRgbGamma(x: Float): Int {
* X/Y position is a bottom-centre point of the image return RGB_GAMMA_TABLE.mapIndexed { i, f -> (f - x).absoluteValue to i }.minBy { it.first }.second
* Shader must be prepared prior to the render() call }
*/ private fun roundAgamma(x: Float): Int {
override fun render(batch: SpriteBatch, offsetX: Float, offsetY: Float) { return A_GAMMA_TABLE.mapIndexed { i, f -> (f - x).absoluteValue to i }.minBy { it.first }.second
}
fun render(batch: SpriteBatch, cloudDrawColour: Color) {
val sc = screenCoord val sc = screenCoord
val rgbGammaIndex = roundRgbGamma(rgbGamma)
val aGammaIndex = roundAgamma(aGamma)
// printdbg(this, "gamma: (${rgbGamma}, ${aGamma}) index: ($rgbGammaIndex, $aGammaIndex)")
val lightBits = cloudDrawColour.toIntBits()
val rbits = lightBits.ushr( 0).and(252) or rgbGammaIndex.ushr(2).and(3)
val gbits = lightBits.ushr( 8).and(252) or rgbGammaIndex.ushr(0).and(3)
val bbits = lightBits.ushr(16).and(252) or aGammaIndex
val abits = (alpha * 255).toInt()
batch.color = Color(rbits.shl(24) or gbits.shl(16) or bbits.shl(8) or abits)
if (flipW) if (flipW)
batch.draw(texture, sc.x + texture.regionWidth / posZ, sc.y, -texture.regionWidth * sc.z, texture.regionHeight * sc.z) batch.draw(texture, sc.x + texture.regionWidth / posZ, sc.y, -texture.regionWidth * sc.z, texture.regionHeight * sc.z)
else else
batch.draw(texture, sc.x, sc.y, texture.regionWidth * sc.z, texture.regionHeight * sc.z) batch.draw(texture, sc.x, sc.y, texture.regionWidth * sc.z, texture.regionHeight * sc.z)
} }
/**
* X/Y position is a bottom-centre point of the image
* Shader must be prepared prior to the render() call
*/
override fun render(batch: SpriteBatch, offsetX: Float, offsetY: Float) {
throw UnsupportedOperationException()
}
/** /**
* vec3(screen X, screenY, draw scale) * vec3(screen X, screenY, draw scale)
*/ */
@@ -133,5 +165,34 @@ class WeatherObjectCloud(private val texture: TextureRegion, private val flipW:
fun screenXtoWorldX(screenX: Float, z: Float) = screenX * z - App.scr.halfwf * (z - 1f) fun screenXtoWorldX(screenX: Float, z: Float) = screenX * z - App.scr.halfwf * (z - 1f)
const val ALPHA_ROLLOFF_Z = 64f const val ALPHA_ROLLOFF_Z = 64f
const val OLD_AGE_DECAY = 4000f const val OLD_AGE_DECAY = 4000f
val RGB_GAMMA_TABLE = floatArrayOf(
0.2f,
0.3f,
0.4f,
0.5f,
0.7f,
0.9f,
1.1f,
1.3f,
1.7f,
2.1f,
2.5f,
2.9f,
3.7f,
4.5f,
5.3f,
6.1f
)
val A_GAMMA_TABLE = floatArrayOf(
1.6f,
2.0f,
2.4f,
2.8f
)
} }
} }

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.weather package net.torvald.terrarum.weather
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.floorToInt import net.torvald.terrarum.floorToInt
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.gameworld.fmod
@@ -11,7 +12,7 @@ data class WeatherSchedule(val weather: BaseModularWeather = WeatherMixer.DEFAUL
class Weatherbox { class Weatherbox {
companion object { companion object {
private val WIND_DIR_TIME_UNIT = 3600f * 5 // every 5hr private val WIND_DIR_TIME_UNIT = 3600f * 6 // every 6hr
private val WIND_SPEED_TIME_UNIT = 3600f * 2 // every 2hr private val WIND_SPEED_TIME_UNIT = 3600f * 2 // every 2hr
private val HALF_PIF = 1.5707964f private val HALF_PIF = 1.5707964f
@@ -53,10 +54,10 @@ class Weatherbox {
// TODO add more random weathers // TODO add more random weathers
if (weatherSchedule.size == 1) { if (weatherSchedule.size == 1) {
val newName = if (currentWeather.identifier == "generic01") "overcast01" else "generic01" val newName = if (currentWeather.identifier == "generic01") "overcast01" else "generic01"
val newDuration = 3600L val newDuration = 7200L
weatherSchedule.add(WeatherSchedule(WeatherMixer.weatherDict[newName]!!, newDuration)) weatherSchedule.add(WeatherSchedule(WeatherMixer.weatherDict[newName]!!, newDuration))
println("Queueing next weather '$newName' that will last $newDuration seconds") // printdbg(this, "Queueing next weather '$newName' that will last $newDuration seconds")
} }
// subtract akku by old currentWeatherDuration // subtract akku by old currentWeatherDuration

View File

@@ -14,17 +14,63 @@ out vec4 fragColor;
const vec2 boolean = vec2(0.0, 1.0); const vec2 boolean = vec2(0.0, 1.0);
uniform vec2 gamma = vec2(10, 2.0); // vec2(gamma for RGB, gamma for A)
uniform LOWP vec4 shadeCol; uniform LOWP vec4 shadeCol;
const float rgbGammas[16] = float[](
0.2,
0.3,
0.4,
0.5,
0.7,
0.9,
1.1,
1.3,
1.7,
2.1,
2.5,
2.9,
3.7,
4.5,
5.3,
6.1
);
const float aGammas[4] = float[](
1.6,
2.0,
2.4,
2.8
);
void main() { void main() {
// vertex colour format:
// rrrrrrMM ggggggLL bbbbbbAA aaaaaaaa
// where:
// rrrrrr: 6-bit red component
// gggggg: 6-bit green component
// bbbbbb: 6-bit blue component
// MMLL: index to the rgbGammas
// AA: index to the aGammas
vec4 cloudCol = vec4(
(int(v_color.r * 255) >> 2) * 4.0 / 255.0,
(int(v_color.g * 255) >> 2) * 4.0 / 255.0,
(int(v_color.b * 255) >> 2) * 4.0 / 255.0,
v_color.a
);
float rgbGamma = rgbGammas[((int(v_color.r * 255) & 3) << 2) | (int(v_color.g * 255) & 3)];
float aGamma = aGammas[int(v_color.b * 255) & 3];
vec4 gamma = vec4(rgbGamma, rgbGamma, rgbGamma, aGamma);
// cloud colour format:
// r: bw diffuse map, g: normal, b: normal, a: bw diffuse alpha // r: bw diffuse map, g: normal, b: normal, a: bw diffuse alpha
vec4 inCol = texture(u_texture, v_texCoords); vec4 inCol = texture(u_texture, v_texCoords);
vec4 rawCol = pow(inCol, gamma.xxxy); vec4 rawCol = pow(inCol, gamma);
// do gradient mapping here // do gradient mapping here
vec4 outCol = fma(mix(shadeCol, v_color, rawCol.r), boolean.yyyx, rawCol * boolean.xxxy); vec4 outCol = fma(mix(shadeCol, cloudCol, rawCol.r), boolean.yyyx, rawCol * boolean.xxxy);
fragColor = outCol * fma(v_color, boolean.xxxy, boolean.yyyx); fragColor = outCol * fma(cloudCol, boolean.xxxy, boolean.yyyx);
} }

View File

@@ -11,7 +11,7 @@ out vec2 v_texCoords;
void main() { void main() {
v_color = a_color; v_color = a_color;
v_color.a = v_color.a * (255.0/254.0); v_color.a = v_color.a * (255.0/254.0); // GDX will crush the alpha value to 0,2,4,6,8,10,...,254; see com.badlogic.gdx.utils.NumberUtils.intToFloatColor
v_texCoords = a_texCoord0; v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position; gl_Position = u_projTrans * a_position;
} }