Light renderer dispatcher

This commit is contained in:
DBotThePony 2022-09-14 14:54:10 +07:00
parent 0614158a9c
commit 7eeb5f8a12
Signed by: DBot
GPG Key ID: DCC23B5715498507
5 changed files with 230 additions and 83 deletions

View File

@ -5,18 +5,23 @@ import org.lwjgl.Version
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
import org.lwjgl.opengl.GL11.GL_LINES
import org.lwjgl.opengl.GL11.GL_RGBA
import org.lwjgl.opengl.GL11.GL_SCISSOR_TEST
import org.lwjgl.opengl.GL11.glDisable
import org.lwjgl.opengl.GL11.glEnable
import org.lwjgl.opengl.GL11.glScissor
import org.lwjgl.opengl.GL46
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.render.LightRenderer
import ru.dbotthepony.kstarbound.io.*
import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.io.File
@ -180,88 +185,40 @@ fun main() {
client.gl.font.render("${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
}
val lightmap = GLFrameBuffer(client.gl)
lightmap.reattachTexture(client.viewportWidth, client.viewportHeight, GL_RGBA)
client.onViewportChanged { width, height -> lightmap.reattachTexture(width, height, GL_RGBA) }
client.onPostDrawWorld {
lightmap.bind()
GL46.glClear(GL46.GL_COLOR_BUFFER_BIT or GL46.GL_DEPTH_BUFFER_BIT)
//client.gl.programs.colorQuad.clearAlpha()
client.gl.programs.light.use()
client.gl.programs.light.transform.set(client.gl.matrixStack.last)
// client.gl.programs.light.transform.set(Matrix4f.IDENTITY)
client.gl.programs.light.baselineColor.set(Color.WHITE)
var builder = client.gl.programs.light.builder
val (rX, rY) = client.screenToWorld(client.mouseCoordinatesF)
builder.begin()
//val size = 20f / client.settings.scale
val size = 10f
builder.quad(rX - size, rY - size, rX + size, rY + size, QuadTransformers.uv())
builder.upload()
//client.gl.blendFunc = BlendFunc(
// BlendFunc.Func.DST_ALPHA,
// BlendFunc.Func.ONE_MINUS_DST_ALPHA
//)
builder.draw()
client.gl.blendFunc = BlendFunc.ALPHA_TEST
client.gl.programs.lightOccluder.use()
client.gl.programs.lightOccluder.transform.set(client.gl.matrixStack.last)
builder = client.gl.programs.lightOccluder.builder
builder.begin()
val lightPosition = client.screenToWorld(client.mouseCoordinatesF)
builder.quad(0f, 0f, 2f, 2f)
builder.quad(-6f, 0f, -2f, 2f)
client.gl.programs.lightOccluder.lightPosition.set(lightPosition)
builder.upload()
client.gl.blendFunc = BlendFunc.ONLY_ALPHA
builder.draw(GL_LINES)
client.gl.blendFunc = BlendFunc.ALPHA_TEST
lightmap.unbind()
client.gl.activeTexture = 0
client.gl.texture2D = lightmap.texture
client.gl.programs.textureQuad.run(0)
client.gl.programs.flat.use()
client.gl.programs.flat.color.set(Color.BLACK)
client.gl.programs.flat.transform.set(client.gl.matrixStack.last)
builder = client.gl.programs.flat.builder
builder.begin()
builder.quad(0f, 0f, 2f, 2f)
builder.quad(-6f, 0f, -2f, 2f)
builder.upload()
builder.draw()
}
client.onPreDrawWorld {
//client.camera.pos.x = ent.pos.x.toFloat()
//client.camera.pos.y = ent.pos.y.toFloat()
}
val lightRenderer = LightRenderer(client.gl)
lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight)
client.onViewportChanged(lightRenderer::resizeFramebuffer)
lightRenderer.addShadowGeometry { _, _ ->
val builder = client.gl.programs.lightOccluder.builder
builder.begin()
builder.quad(0f, 0f, 2f, 2f)
builder.quad(-6f, 0f, -2f, 2f)
builder.upload()
builder.draw(GL_LINES)
}
client.onPostDrawWorld {
lightRenderer.begin()
for ((lightPosition, color) in listOf(
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-2f)) to Color.RED,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(2f)) to Color.GREEN,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(y = -2f)) to Color.BLUE,
)) {
lightRenderer.renderLight(lightPosition, color)
}
lightRenderer.renderOutputAdditive()
}
client.gl.box2dRenderer.drawShapes = false
client.gl.box2dRenderer.drawPairs = false
client.gl.box2dRenderer.drawAABB = false

View File

@ -133,6 +133,16 @@ class StarboundClient : AutoCloseable {
return screenToWorld(value.x, value.y)
}
/**
* Преобразует мировые координаты в экранные
*/
fun worldToScreen(x: Float, y: Float): Vector2f {
val relativeX = (x - camera.pos.x) * PIXELS_IN_STARBOUND_UNITf * settings.zoom + viewportWidth / 2f
val relativeY = (camera.pos.y - y) * PIXELS_IN_STARBOUND_UNITf * settings.zoom + viewportHeight / 2f
return Vector2f(relativeX, relativeY)
}
init {
GLFWErrorCallback.create { error, description ->
LOGGER.error("LWJGL error {}: {}", error, description)
@ -205,7 +215,7 @@ class StarboundClient : AutoCloseable {
gl.clearColor = Color.SLATE_GREY
gl.blend = true
gl.blendFunc = BlendFunc.ALPHA_TEST
gl.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
}
var frameRenderTime = 0.0

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.kstarbound.client.gl
import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL
import org.lwjgl.opengl.GL14
import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.freetype.FreeType
@ -25,7 +24,6 @@ import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Color
import java.io.FileNotFoundException
import java.lang.ref.Cleaner
import java.util.*
import java.util.concurrent.ThreadFactory
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
@ -109,7 +107,8 @@ data class BlendFunc(
companion object {
val DEFAULT = BlendFunc()
val ALPHA_TEST = BlendFunc(Func.SRC_ALPHA, Func.ONE_MINUS_SRC_ALPHA)
val MULTIPLY_WITH_ALPHA = BlendFunc(Func.SRC_ALPHA, Func.ONE_MINUS_SRC_ALPHA)
val ADDITIVE = BlendFunc(Func.ONE, Func.ONE)
val ONLY_ALPHA = BlendFunc(
Func.ZERO,

View File

@ -0,0 +1,181 @@
package ru.dbotthepony.kstarbound.client.render
import org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT
import org.lwjgl.opengl.GL11.GL_RGBA
import org.lwjgl.opengl.GL11.glClear
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kvector.matrix.Matrix4fStack
import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
// https://slembcke.github.io/SuperFastHardShadows
class LightRenderer(val state: GLStateTracker) {
fun interface ShadowGeometryRenderer {
fun render(lightPosition: Vector2f, stack: Matrix4fStack)
}
private val geometry = ArrayList<ShadowGeometryRenderer>()
fun addShadowGeometry(geometry: ShadowGeometryRenderer): LightRenderer {
this.geometry.add(geometry)
return this
}
fun removeShadowGeometry(geometry: ShadowGeometryRenderer): Boolean {
return this.geometry.remove(geometry)
}
/**
* Сюда происходит рендер маски света и самого света
*/
private val framebufferRender = GLFrameBuffer(state)
/**
* Сюда накапливается отрисованный свет
*/
private val framebufferAccumulator = GLFrameBuffer(state)
val outputTexture: GLTexture2D? get() = framebufferAccumulator.texture
fun resizeFramebuffer(width: Int, height: Int) {
framebufferRender.reattachTexture(width, height, GL_RGBA)
framebufferAccumulator.reattachTexture(width, height, GL_RGBA)
}
fun begin() {
state.ensureSameThread()
if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) {
return
}
val old = state.clearColor
state.clearColor = Color.BLACK
framebufferRender.bind()
glClear(GL_COLOR_BUFFER_BIT)
checkForGLError()
framebufferAccumulator.bind()
glClear(GL_COLOR_BUFFER_BIT)
checkForGLError()
state.clearColor = old
framebufferAccumulator.unbind()
}
/**
* Отрисовывает результат на весь текущий framebuffer в режиме additive
*/
fun renderOutputAdditive() {
val oldFunc = state.blendFunc
try {
state.activeTexture = 0
state.texture2D = outputTexture
state.blendFunc = BlendFunc.ADDITIVE
state.programs.textureQuad.run(0)
} finally {
state.blendFunc = oldFunc
}
}
/**
* Отрисовывает результат на весь текущий framebuffer в режиме additive
*/
fun renderOutputMultiplicative() {
val oldFunc = state.blendFunc
try {
state.activeTexture = 0
state.texture2D = outputTexture
state.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
state.programs.textureQuad.run(0)
} finally {
state.blendFunc = oldFunc
}
}
fun renderLight(
position: Vector2f,
color: Color = Color.WHITE,
radius: Float = 10f,
stack: Matrix4fStack = state.matrixStack
) {
state.ensureSameThread()
if (!framebufferRender.isComplete || !framebufferAccumulator.isComplete) {
return
}
val oldFunc = state.blendFunc
// отрисовка
try {
framebufferRender.bind()
// state.programs.colorQuad.clearAlpha()
// Геометрия
val old = state.clearColor
state.clearColor = Color.BLACK
glClear(GL_COLOR_BUFFER_BIT)
checkForGLError()
state.clearColor = old
state.programs.lightOccluder.use()
state.programs.lightOccluder.transform.set(stack.last)
state.programs.lightOccluder.lightPosition.set(position)
state.blendFunc = BlendFunc.ONLY_ALPHA
for (renderer in geometry) {
renderer.render(position, stack)
}
state.programs.light.use()
state.programs.light.transform.set(stack.last)
state.programs.light.baselineColor.set(color)
// Свет
val builder = state.programs.light.builder
builder.begin()
builder.quad(position.x - radius, position.y - radius, position.x + radius, position.y + radius, QuadTransformers.uv())
builder.upload()
state.blendFunc = BLEND_MODE
builder.draw()
} finally {
state.blendFunc = oldFunc
framebufferRender.unbind()
}
// накопление
try {
framebufferAccumulator.bind()
state.blendFunc = BlendFunc.ADDITIVE
state.activeTexture = 0
state.texture2D = framebufferRender.texture
state.programs.textureQuad.run(0)
} finally {
state.blendFunc = oldFunc
framebufferAccumulator.unbind()
}
}
companion object {
val BLEND_MODE = BlendFunc(
BlendFunc.Func.DST_ALPHA,
BlendFunc.Func.ZERO,
BlendFunc.Func.ZERO,
BlendFunc.Func.ONE,
)
}
}

View File

@ -7,5 +7,5 @@ out vec4 resultColor;
uniform vec4 baselineColor;
void main() {
resultColor = vec4(baselineColor) * smoothstep(0.0, 1.0, 1.0 - sqrt(pow(oUVCoords.x - 0.5, 2.0) + pow(oUVCoords.y - 0.5, 2.0)) * 2.0);
resultColor = vec4(baselineColor.rgb * smoothstep(0.0, 1.0, 1.0 - sqrt(pow(oUVCoords.x - 0.5, 2.0) + pow(oUVCoords.y - 0.5, 2.0)) * 2.0), baselineColor.a);
}