From 4a0c161038cfb6924dc5cf75e24fcea1106fceaa Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 13 Aug 2024 16:23:53 +0900 Subject: [PATCH] crt-like shader --- .../torvald/tsvm/peripheral/CLCDDisplay.kt | 4 +- .../tsvm/peripheral/CharacterLCDdisplay.kt | 6 +- .../tsvm/peripheral/GraphicsAdapter.kt | 5 +- .../torvald/tsvm/peripheral/TexticsAdapter.kt | 4 +- tsvm_executable/src/net/torvald/tsvm/VMGUI.kt | 135 ++++++++++++++++-- 5 files changed, 137 insertions(+), 17 deletions(-) diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/CLCDDisplay.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/CLCDDisplay.kt index b3c9a06..e1c6cea 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/CLCDDisplay.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/CLCDDisplay.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer +import com.badlogic.gdx.graphics.glutils.ShaderProgram import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUlong import net.torvald.tsvm.TsvmTextureRegionPack import net.torvald.tsvm.VM @@ -41,6 +42,7 @@ class CLCDDisplay(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, xoff: Float, yoff: Float, flipY: Boolean, + shader: ShaderProgram?, uiFBO: FrameBuffer? ) { batch.shader = null @@ -48,7 +50,7 @@ class CLCDDisplay(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm, batch.color = Color.WHITE batch.draw(machine, xoff, yoff) } - super.render(delta, batch, xoff+60, yoff+90, flipY, uiFBO) + super.render(delta, batch, xoff+60, yoff+90, flipY, shader, uiFBO) } diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/CharacterLCDdisplay.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/CharacterLCDdisplay.kt index 82a2052..7d6801b 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/CharacterLCDdisplay.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/CharacterLCDdisplay.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer +import com.badlogic.gdx.graphics.glutils.ShaderProgram import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUlong import net.torvald.tsvm.TsvmTextureRegionPack import net.torvald.tsvm.VM @@ -36,6 +37,7 @@ class CharacterLCDdisplay(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRo xoff: Float, yoff: Float, flipY: Boolean, + shader: ShaderProgram?, uiFBO: FrameBuffer? ) { batch.shader = null @@ -44,9 +46,9 @@ class CharacterLCDdisplay(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRo batch.draw(machine, xoff, yoff) } if (!flipY) - super.render(delta, batch, xoff+74, yoff+102, flipY, uiFBO) + super.render(delta, batch, xoff+74, yoff+102, flipY, shader, uiFBO) else - super.render(delta, batch, xoff+74, yoff+72, flipY, uiFBO) + super.render(delta, batch, xoff+74, yoff+72, flipY, shader, uiFBO) // draw BMS and RTC val batPerc = "89" diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt index 78acb52..7523e4d 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/GraphicsAdapter.kt @@ -10,6 +10,7 @@ import com.badlogic.gdx.graphics.g2d.Gdx2DPixmap import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.glutils.FrameBuffer +import com.badlogic.gdx.graphics.glutils.ShaderProgram import com.badlogic.gdx.math.Matrix4 import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.GdxRuntimeException @@ -1039,7 +1040,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi private val isRefSize = (WIDTH == 560 && HEIGHT == 448) - open fun render(delta: Float, uiBatch: SpriteBatch, xoff: Float, yoff: Float, flipY: Boolean = false, uiFBO: FrameBuffer? = null) { + open fun render(delta: Float, uiBatch: SpriteBatch, xoff: Float, yoff: Float, flipY: Boolean = false, shader: ShaderProgram? = null, uiFBO: FrameBuffer? = null) { uiFBO?.end() @@ -1346,7 +1347,7 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi uiFBO?.begin() uiBatch.inUse { - uiBatch.shader = null + uiBatch.shader = shader //Gdx.gl.glClearColor(0f, 0f, 0f, 0f) //Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) blendNormal(uiBatch) diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/TexticsAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/TexticsAdapter.kt index 8c8f881..0748044 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/TexticsAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/TexticsAdapter.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.GL20 import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer +import com.badlogic.gdx.graphics.glutils.ShaderProgram import net.torvald.tsvm.VM import net.torvald.tsvm.kB import kotlin.math.absoluteValue @@ -60,10 +61,11 @@ open class TexticsAdapterBase(assetsRoot: String, vm: VM, config: AdapterConfig) xoff: Float, yoff: Float, flipY: Boolean, + shader: ShaderProgram?, uiFBO: FrameBuffer? ) { - super.render(delta, batch, xoff, yoff, flipY, uiFBO) + super.render(delta, batch, xoff, yoff, flipY, shader, uiFBO) batch.inUse { batch.enableBlending() diff --git a/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt b/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt index d7d7682..2f22360 100644 --- a/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt +++ b/tsvm_executable/src/net/torvald/tsvm/VMGUI.kt @@ -5,12 +5,14 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.TextureRegion -import kotlin.coroutines.* +import com.badlogic.gdx.graphics.glutils.FrameBuffer +import com.badlogic.gdx.graphics.glutils.ShaderProgram import net.torvald.terrarum.DefaultGL32Shaders -import net.torvald.terrarum.modulecomputers.tsvmperipheral.WorldRadar import net.torvald.tsvm.peripheral.* -import java.io.File +import net.torvald.tsvm.peripheral.GraphicsAdapter.Companion.DRAW_SHADER_VERT +import java.util.* import java.util.concurrent.atomic.AtomicBoolean +import kotlin.coroutines.* class EmulInstance( @@ -48,10 +50,31 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe lateinit var coroutineJob: Thread lateinit var memvwr: Memvwr lateinit var fullscreenQuad: Mesh + lateinit var gpuFBO: FrameBuffer val usememvwr = false private lateinit var crtGradTex: TextureRegion + private lateinit var crtShader: ShaderProgram + + fun loadShaderInline(frag0: String): ShaderProgram { + // insert version code + val frag: String + if (Gdx.graphics.glVersion.majorVersion >= 4) { + frag = "#version 400\n$frag0" + } + else { + frag = "#version 330\n#define fma(a,b,c) (((a)*(b))+(c))\n$frag0" + } + + val s = ShaderProgram(DRAW_SHADER_VERT, frag) + + if (s.log.lowercase(Locale.getDefault()).contains("error")) { + throw java.lang.Error(String.format("Shader program loaded with %s failed:\n%s", frag, s.log)) + } + + return s + } override fun create() { super.create() @@ -74,6 +97,10 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe it.flip(false, true) } + crtShader = loadShaderInline(CRT_POST_SHADER) + + gpuFBO = FrameBuffer(Pixmap.Format.RGBA8888, viewportWidth, viewportHeight, false) + init() } @@ -83,7 +110,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe if (loaderInfo.display != null) { val loadedClass = Class.forName(loaderInfo.display) val loadedClassConstructor = loadedClass.getConstructor(String::class.java, vm::class.java) - val loadedClassInstance = loadedClassConstructor.newInstance("./assets", vm, ) + val loadedClassInstance = loadedClassConstructor.newInstance("./assets", vm) gpu = (loadedClassInstance as GraphicsAdapter) vm.peripheralTable[1] = PeripheralEntry( @@ -218,15 +245,35 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe private val defaultGuiBackgroundColour = Color(0x444444ff) private fun renderGame(delta: Float) { - val clearCol = gpu?.getBackgroundColour() ?: defaultGuiBackgroundColour - Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a) - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) - gpu?.render(delta, batch, (viewportWidth - loaderInfo.drawWidth).div(2).toFloat(), (viewportHeight - loaderInfo.drawHeight).div(2).toFloat()) + gpuFBO.begin() + val clearCol = gpu?.getBackgroundColour() ?: defaultGuiBackgroundColour + Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + + gpu?.render( + delta, batch, + (viewportWidth - loaderInfo.drawWidth).div(2).toFloat(), + (viewportHeight - loaderInfo.drawHeight).div(2).toFloat(), + flipY = true, + uiFBO = gpuFBO + ) + gpuFBO.end() + + + Gdx.gl.glClearColor(0f, 0f, 0f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) - // draw CRT glass overlay batch.inUse { - batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_COLOR) batch.color = Color.WHITE + + // draw GPU and border + batch.shader = crtShader + batch.setBlendFunctionSeparate(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA, GL20.GL_SRC_ALPHA, GL20.GL_ONE) + batch.draw(gpuFBO.colorBufferTexture, 0f, 0f) + + // draw CRT glass overlay + batch.shader = null + batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_COLOR) batch.draw(crtGradTex, 0f, 0f, viewportWidth.toFloat(), viewportHeight.toFloat()) } @@ -269,6 +316,8 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe fullscreenQuad.dispose() coroutineJob.interrupt() crtGradTex.texture.dispose() + crtShader.dispose() + gpuFBO.dispose() vm.dispose() } @@ -322,4 +371,68 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe } } -const val EMDASH = 0x2014.toChar() \ No newline at end of file +const val EMDASH = 0x2014.toChar() + +const val CRT_POST_SHADER = """ +#ifdef GL_ES + precision mediump float; +#endif + +in vec4 v_color; +in vec4 v_generic; +in vec2 v_texCoords; +uniform sampler2D u_texture; +uniform vec2 resolution = vec2(560.0, 448.0); +out vec4 fragColor; + +const vec4 scanline = vec4(0.9, 0.9, 0.9, 1.0); +const vec4 one = vec4(1.0); + +const mat4 rgb_to_yuv = mat4( + 0.2126, -0.09991, 0.615, 0.0, + 0.7152, -0.33609, -0.55861, 0.0, + 0.0722, 0.436, -0.05639, 0.0, + 0.0, 0.0, 0.0, 1.0 +); + +const mat4 yuv_to_rgb = mat4( + 1.0, 1.0, 1.0, 0.0, + 0.0, -0.21482, 2.12798, 0.0, + 1.28033,-0.38059, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +); + +const float gamma = 2.4; +const float blur = 0.8; + +vec4 toYUV(vec4 rgb) { return rgb_to_yuv * rgb; } +vec4 toRGB(vec4 ycc) { return yuv_to_rgb * ycc; } + +vec4 avr(vec4 a, vec4 b, float gam) { + return vec4( + pow((pow(a.x, 1.0 / gam) + pow(b.x, 1.0 / gam)) / 2.0, gam), + (a.y + b.y) / 2.0, + (a.z + b.z) / 2.0, + (a.w + b.w) / 2.0 + ); +} + +void main() { + vec4 rgbColourIn = v_color * texture(u_texture, v_texCoords); + vec4 rgbColourL = v_color * texture(u_texture, v_texCoords + (vec2(-blur, 0.0) / resolution)); + vec4 rgbColourR = v_color * texture(u_texture, v_texCoords + (vec2(+blur, 0.0) / resolution)); + + vec4 colourIn = toYUV(rgbColourIn); + vec4 colourL = toYUV(rgbColourL); + vec4 colourR = toYUV(rgbColourR); + + vec4 LRavr = avr(colourL, colourR, gamma); + vec4 wgtavr = avr(LRavr, colourIn, gamma); + + vec4 outCol = wgtavr * ((mod(gl_FragCoord.y, 2.0) >= 1.0) ? scanline : one); + + fragColor = toRGB(outCol); + +} + +""" \ No newline at end of file