mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +09:00
contextual darkening of ui
This commit is contained in:
@@ -8,8 +8,10 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion
|
|||||||
import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
|
import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
|
||||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
||||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
||||||
|
import com.badlogic.gdx.utils.BufferUtils
|
||||||
import com.badlogic.gdx.utils.Disposable
|
import com.badlogic.gdx.utils.Disposable
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import net.torvald.random.HQRNG
|
import net.torvald.random.HQRNG
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.App.*
|
import net.torvald.terrarum.App.*
|
||||||
@@ -87,12 +89,21 @@ object IngameRenderer : Disposable {
|
|||||||
|
|
||||||
private lateinit var fboRGBwall: Float16FrameBuffer // for masking away the shadows
|
private lateinit var fboRGBwall: Float16FrameBuffer // for masking away the shadows
|
||||||
|
|
||||||
|
private lateinit var fboUI: Float16FrameBuffer // UI composite target for scene-aware dimming
|
||||||
|
private lateinit var fboSceneAvg: Float16FrameBuffer // tiny target for brightness readback
|
||||||
|
|
||||||
private lateinit var rgbTex: TextureRegion
|
private lateinit var rgbTex: TextureRegion
|
||||||
private lateinit var aTex: TextureRegion
|
private lateinit var aTex: TextureRegion
|
||||||
private lateinit var mixedOutTex: TextureRegion
|
private lateinit var mixedOutTex: TextureRegion
|
||||||
|
private lateinit var uiTex: TextureRegion
|
||||||
private lateinit var lightTex: TextureRegion
|
private lateinit var lightTex: TextureRegion
|
||||||
private lateinit var blurTex: TextureRegion
|
private lateinit var blurTex: TextureRegion
|
||||||
|
|
||||||
|
// Contextual UI dimming: dim slightly when the world scene is dark.
|
||||||
|
private val sceneAvgBuf: ByteBuffer = BufferUtils.newByteBuffer(SCENE_AVG_SIZE * SCENE_AVG_SIZE * 4)
|
||||||
|
@Volatile private var smoothedSceneLuma = 1f
|
||||||
|
private var uiDimFactor = 1f
|
||||||
|
|
||||||
private lateinit var fboBlurHalf: Float16FrameBuffer
|
private lateinit var fboBlurHalf: Float16FrameBuffer
|
||||||
// private lateinit var fboBlurQuarter: Float16FrameBuffer
|
// private lateinit var fboBlurQuarter: Float16FrameBuffer
|
||||||
|
|
||||||
@@ -136,6 +147,14 @@ object IngameRenderer : Disposable {
|
|||||||
/** lower value = greater lozenge artefact from linear intp */
|
/** lower value = greater lozenge artefact from linear intp */
|
||||||
const val lightmapDownsample = 2f // still has choppy look when the camera moves but unnoticeable when blurred
|
const val lightmapDownsample = 2f // still has choppy look when the camera moves but unnoticeable when blurred
|
||||||
|
|
||||||
|
// Contextual UI dimming parameters.
|
||||||
|
private const val SCENE_AVG_SIZE = 16 // side of the small RGBA8 FBO used for brightness readback
|
||||||
|
private const val SCENE_AVG_INTERVAL = 6L // sample every N frames; smoothing fills the gaps
|
||||||
|
private const val UI_DIM_DARK_FACTOR = 0.8f // 80% brightness when the scene is dark
|
||||||
|
private const val UI_DIM_DARK_LUMA = 0.08f // luma at/below this -> fully dimmed
|
||||||
|
private const val UI_DIM_BRIGHT_LUMA = 0.22f // luma at/above this -> no dimming
|
||||||
|
private const val UI_DIM_SMOOTHING = 0.12f // per-sample EMA factor for luma
|
||||||
|
|
||||||
private var debugMode = 0
|
private var debugMode = 0
|
||||||
|
|
||||||
var renderingActorsCount = 0
|
var renderingActorsCount = 0
|
||||||
@@ -444,20 +463,38 @@ object IngameRenderer : Disposable {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// draw UI
|
// contextual UI dimming: keep uiDimFactor in sync with the rendered scene's brightness
|
||||||
setCameraPosition(0f, 0f)
|
sampleSceneBrightness()
|
||||||
|
|
||||||
batch.inUse {
|
// draw UI into its own FBO so individual UIs can keep using batch.color freely
|
||||||
batch.shader = null
|
fboUI.inAction(camera, batch) {
|
||||||
batch.color = Color.WHITE
|
gdxClearAndEnableBlend(0f, 0f, 0f, 0f)
|
||||||
|
blendNormalStraightAlpha(batch)
|
||||||
|
|
||||||
if (!KeyToggler.isOn(Input.Keys.F4)) {
|
batch.inUse {
|
||||||
uiContainer?.forEach {
|
batch.shader = null
|
||||||
it?.render(frameDelta, batch, camera)
|
batch.color = Color.WHITE
|
||||||
|
|
||||||
|
if (!KeyToggler.isOn(Input.Keys.F4)) {
|
||||||
|
uiContainer?.forEach {
|
||||||
|
it?.render(frameDelta, batch, camera)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// composite UI on top of the world with the contextual dim tint
|
||||||
|
setCameraPosition(0f, 0f)
|
||||||
|
blendNormalStraightAlpha(batch)
|
||||||
|
|
||||||
|
uiTex.texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
||||||
|
batch.inUse {
|
||||||
|
batch.shader = null
|
||||||
|
batch.color = Color(uiDimFactor, uiDimFactor, uiDimFactor, 1f)
|
||||||
|
batch.drawFlipped(uiTex, 0f, 0f, fboUI.width.toFloat(), fboUI.height.toFloat())
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
}
|
||||||
|
|
||||||
// works but some UI elements have wrong transparency -> should be fixed with Terrarum.gdxCleanAndSetBlend -- Torvald 2019-01-12
|
// works but some UI elements have wrong transparency -> should be fixed with Terrarum.gdxCleanAndSetBlend -- Torvald 2019-01-12
|
||||||
blendNormalStraightAlpha(batch)
|
blendNormalStraightAlpha(batch)
|
||||||
batch.color = Color.WHITE
|
batch.color = Color.WHITE
|
||||||
@@ -466,6 +503,48 @@ object IngameRenderer : Disposable {
|
|||||||
if (newWorldLoadedLatch) newWorldLoadedLatch = false
|
if (newWorldLoadedLatch) newWorldLoadedLatch = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downsamples [fboMixedOut] into [fboSceneAvg], reads it back, and updates [smoothedSceneLuma]
|
||||||
|
* and [uiDimFactor]. Sampling is throttled to every [SCENE_AVG_INTERVAL] frames; the EMA fills gaps.
|
||||||
|
*/
|
||||||
|
private fun sampleSceneBrightness() {
|
||||||
|
if (App.GLOBAL_RENDER_TIMER % SCENE_AVG_INTERVAL == 0L) {
|
||||||
|
mixedOutTex.texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
|
||||||
|
|
||||||
|
fboSceneAvg.inAction(camera, batch) {
|
||||||
|
gdxClearAndEnableBlend(0f, 0f, 0f, 1f)
|
||||||
|
batch.inUse {
|
||||||
|
batch.shader = null
|
||||||
|
batch.color = Color.WHITE
|
||||||
|
batch.draw(mixedOutTex, 0f, 0f, fboSceneAvg.width.toFloat(), fboSceneAvg.height.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
sceneAvgBuf.rewind()
|
||||||
|
Gdx.gl.glReadPixels(0, 0, fboSceneAvg.width, fboSceneAvg.height,
|
||||||
|
GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, sceneAvgBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
sceneAvgBuf.rewind()
|
||||||
|
var sumR = 0L
|
||||||
|
var sumG = 0L
|
||||||
|
var sumB = 0L
|
||||||
|
val count = fboSceneAvg.width * fboSceneAvg.height
|
||||||
|
for (i in 0 until count) {
|
||||||
|
sumR += (sceneAvgBuf.get().toInt() and 0xFF)
|
||||||
|
sumG += (sceneAvgBuf.get().toInt() and 0xFF)
|
||||||
|
sumB += (sceneAvgBuf.get().toInt() and 0xFF)
|
||||||
|
sceneAvgBuf.get() // alpha discarded
|
||||||
|
}
|
||||||
|
val inv = 1f / (count * 255f)
|
||||||
|
val luma = 0.2126f * (sumR * inv) + 0.7152f * (sumG * inv) + 0.0722f * (sumB * inv)
|
||||||
|
smoothedSceneLuma += (luma - smoothedSceneLuma) * UI_DIM_SMOOTHING
|
||||||
|
}
|
||||||
|
|
||||||
|
val t = ((smoothedSceneLuma - UI_DIM_DARK_LUMA) / (UI_DIM_BRIGHT_LUMA - UI_DIM_DARK_LUMA))
|
||||||
|
.coerceIn(0f, 1f)
|
||||||
|
uiDimFactor = UI_DIM_DARK_FACTOR + (1f - UI_DIM_DARK_FACTOR) * t
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prepLightmapRGBA() {
|
private fun prepLightmapRGBA() {
|
||||||
lightmapFbo.inAction(null, null) {
|
lightmapFbo.inAction(null, null) {
|
||||||
@@ -1305,6 +1384,9 @@ object IngameRenderer : Disposable {
|
|||||||
|
|
||||||
fboBlurHalf.dispose()
|
fboBlurHalf.dispose()
|
||||||
//fboBlurQuarter.dispose()
|
//fboBlurQuarter.dispose()
|
||||||
|
|
||||||
|
if (::fboUI.isInitialized) fboUI.dispose()
|
||||||
|
if (::fboSceneAvg.isInitialized) fboSceneAvg.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlocksDrawer and LightmapRenderer must be resized before lightmapFbo is created,
|
// BlocksDrawer and LightmapRenderer must be resized before lightmapFbo is created,
|
||||||
@@ -1332,11 +1414,15 @@ object IngameRenderer : Disposable {
|
|||||||
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(),
|
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
fboUI = Float16FrameBuffer(width, height, false)
|
||||||
|
fboSceneAvg = Float16FrameBuffer(SCENE_AVG_SIZE, SCENE_AVG_SIZE, false)
|
||||||
|
|
||||||
rgbTex = TextureRegion(fboRGB_lightMixed.colorBufferTexture)
|
rgbTex = TextureRegion(fboRGB_lightMixed.colorBufferTexture)
|
||||||
aTex = TextureRegion(fboA_lightMixed.colorBufferTexture)
|
aTex = TextureRegion(fboA_lightMixed.colorBufferTexture)
|
||||||
lightTex = TextureRegion(lightmapFbo.colorBufferTexture)
|
lightTex = TextureRegion(lightmapFbo.colorBufferTexture)
|
||||||
blurTex = TextureRegion()
|
blurTex = TextureRegion()
|
||||||
mixedOutTex = TextureRegion(fboMixedOut.colorBufferTexture)
|
mixedOutTex = TextureRegion(fboMixedOut.colorBufferTexture)
|
||||||
|
uiTex = TextureRegion(fboUI.colorBufferTexture)
|
||||||
|
|
||||||
fboBlurHalf = Float16FrameBuffer(
|
fboBlurHalf = Float16FrameBuffer(
|
||||||
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2,
|
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2,
|
||||||
@@ -1394,6 +1480,8 @@ object IngameRenderer : Disposable {
|
|||||||
if (::fboRGBactorsMiddleShadow.isInitialized) fboRGBactorsMiddleShadow.tryDispose()
|
if (::fboRGBactorsMiddleShadow.isInitialized) fboRGBactorsMiddleShadow.tryDispose()
|
||||||
if (::fboRGBterrainShadow.isInitialized) fboRGBterrainShadow.tryDispose()
|
if (::fboRGBterrainShadow.isInitialized) fboRGBterrainShadow.tryDispose()
|
||||||
if (::fboRGBwall.isInitialized) fboRGBwall.tryDispose()
|
if (::fboRGBwall.isInitialized) fboRGBwall.tryDispose()
|
||||||
|
if (::fboUI.isInitialized) fboUI.tryDispose()
|
||||||
|
if (::fboSceneAvg.isInitialized) fboSceneAvg.tryDispose()
|
||||||
|
|
||||||
blurtex0.tryDispose()
|
blurtex0.tryDispose()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user