even more raycasted lights tests

This commit is contained in:
DBotThePony 2022-09-16 19:54:41 +07:00
parent 5947252dc7
commit 08c2b5a685
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 211 additions and 63 deletions

View File

@ -1,8 +1,11 @@
package ru.dbotthepony.kstarbound.client package ru.dbotthepony.kstarbound.client
import org.lwjgl.opengl.GL46.* import org.lwjgl.opengl.GL46.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.gl.BlendFunc 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.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quad import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ
@ -18,6 +21,8 @@ import ru.dbotthepony.kvector.vector.Color
import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nfloat.Vector2f import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -192,17 +197,92 @@ class ClientWorld(
} }
*/ */
client.gl.quadWireframe { val result = rayLightCircleNaive(pos, 48.0, falloffByTravel = 1.0, falloffByTile = 3.0)
for ((intensity, tpos) in rayLightCircleNaive(pos, 24.0, falloffByTravel = 1.5, falloffByTile = 4.0)) { val result2 = rayLightCircleNaive(pos + Vector2d(-8.0), 24.0, falloffByTravel = 1.0, falloffByTile = 3.0)
it.quad( val frame = GLFrameBuffer(client.gl)
tpos.x.toFloat(), frame.attachTexture(client.viewportWidth, client.viewportHeight)
tpos.y.toFloat(),
tpos.x + 1f, frame.bind()
tpos.y + 1f
) client.gl.clearColor = Color.BLACK
glClear(GL_COLOR_BUFFER_BIT)
client.gl.blendFunc = BlendFunc.ADDITIVE
/*client.gl.quadColor {
for (row in 0 until result.rows) {
for (column in 0 until result.columns) {
if (result[column, row] > 0.05) {
val color = result[column, row].toFloat() * 1.5f
it.quad(
pos.x.roundToInt() - result.rows.toFloat() / 2f + row.toFloat(),
pos.y.roundToInt() - result.columns.toFloat() / 2f + column.toFloat(),
pos.x.roundToInt() - result.rows.toFloat() / 2f + row + 1f,
pos.y.roundToInt() - result.columns.toFloat() / 2f + column + 1f
) { a, b -> a.pushVec4f(color, color, color, 1f) }
}
}
} }
} }
client.gl.quadColor {
for (row in 0 until result2.rows) {
for (column in 0 until result2.columns) {
if (result2[column, row] > 0.05) {
val color = result2[column, row].toFloat() * 1.5f
it.quad(
pos.x.roundToInt() - 8f - result2.rows.toFloat() / 2f + row.toFloat(),
pos.y.roundToInt() - result2.columns.toFloat() / 2f + column.toFloat(),
pos.x.roundToInt() - 8f - result2.rows.toFloat() / 2f + row + 1f,
pos.y.roundToInt() - result2.columns.toFloat() / 2f + column + 1f
) { a, b -> a.pushVec4f(color, 0f, 0f, 1f) }
}
}
}
}*/
val lightTextureWidth = (client.viewportWidth / PIXELS_IN_STARBOUND_UNIT).roundToInt()
val lightTextureHeight = (client.viewportHeight / PIXELS_IN_STARBOUND_UNIT).roundToInt()
val textureBuffer = ByteBuffer.allocateDirect(lightTextureWidth * lightTextureHeight * 3)
textureBuffer.order(ByteOrder.LITTLE_ENDIAN)
for (x in 0 until result.columns.coerceAtMost(lightTextureWidth)) {
for (y in 0 until result.rows.coerceAtMost(lightTextureHeight)) {
textureBuffer.position(x * 3 + y * lightTextureWidth * 3)
if (result[x, y] > 0.05) {
val color = result[x, y].toFloat() * 1.5f
textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte())
textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte())
textureBuffer.put((color * 255).toInt().coerceAtMost(255).toByte())
}
}
}
frame.unbind()
val texture = GLTexture2D(client.gl)
textureBuffer.position(0)
texture.upload(GL_RGB, lightTextureWidth, lightTextureHeight, GL_RGB, GL_UNSIGNED_BYTE, textureBuffer)
texture.textureMinFilter = GL_LINEAR
texture.textureMagFilter = GL_LINEAR
client.gl.blendFunc = BlendFunc.MULTIPLY_BY_SRC
client.gl.activeTexture = 0
client.gl.texture2D = texture
client.gl.programs.viewTextureQuad.run()
client.gl.blendFunc = old
frame.close()
texture.close()
physics.debugDraw() physics.debugDraw()

View File

@ -362,6 +362,7 @@ class StarboundClient : AutoCloseable {
if (frameRenderTime != 0.0 && Starbound.initialized) if (frameRenderTime != 0.0 && Starbound.initialized)
world?.think(frameRenderTime) world?.think(frameRenderTime)
gl.clearColor = Color.SLATE_GREY
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
gl.matrixStack.clear(viewportMatrixWorld.toMutableMatrix4f()) gl.matrixStack.clear(viewportMatrixWorld.toMutableMatrix4f())

View File

@ -82,12 +82,11 @@ class GLFrameBuffer(val state: GLStateTracker) : AutoCloseable {
private set private set
override fun close() { override fun close() {
state.ensureSameThread()
if (!isValid) if (!isValid)
return return
cleanable.cleanManual() cleanable.cleanManual()
texture?.close()
isValid = false isValid = false
} }
} }

View File

@ -234,10 +234,15 @@ class GLStateTracker {
if (!cleanManual && LOGGER.isTraceEnabled) if (!cleanManual && LOGGER.isTraceEnabled)
LOGGER.trace("{} with ID {} was GC'd by JVM, without manually calling close()", name, nativeRef) LOGGER.trace("{} with ID {} was GC'd by JVM, without manually calling close()", name, nativeRef)
synchronized(cleanerHits) { if (isSameThread()) {
cleanerHits.add { fn(nativeRef)
fn(nativeRef) checkForGLError()
checkForGLError() } else {
synchronized(cleanerHits) {
cleanerHits.add {
fn(nativeRef)
checkForGLError()
}
} }
} }
} }
@ -570,30 +575,12 @@ class GLStateTracker {
} }
} }
val flat2DQuads = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS, 1024)
}
}
val flat2DTexturedQuads = object : GLStreamBuilderList { val flat2DTexturedQuads = object : GLStreamBuilderList {
override val small by lazy { override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS, 1024) return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VERTEX_TEXTURE, GeometryType.QUADS, 1024)
} }
} }
val flat2DQuadLines = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES, 1024)
}
}
val flat2DQuadWireframe = object : GLStreamBuilderList {
override val small by lazy {
return@lazy StreamVertexBuilder(this@GLStateTracker, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 1024)
}
}
val quadWireframe by lazy { val quadWireframe by lazy {
StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 16384) StreamVertexBuilder(this, GLAttributeList.VEC2F, GeometryType.QUADS_AS_LINES_WIREFRAME, 16384)
} }
@ -617,6 +604,19 @@ class GLStateTracker {
builder.draw(GL_LINES) builder.draw(GL_LINES)
} }
inline fun quadColor(lambda: (StreamVertexBuilder) -> Unit) {
val builder = programs.flatColor.builder
builder.begin()
lambda.invoke(builder)
builder.upload()
programs.flatColor.use()
programs.flatColor.transform.set(matrixStack.last)
builder.draw(GL_TRIANGLES)
}
inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (StreamVertexBuilder) -> Unit = {}) { inline fun quadWireframe(value: AABB, color: Color = Color.WHITE, chain: (StreamVertexBuilder) -> Unit = {}) {
quadWireframe(color) { quadWireframe(color) {
it.quad(value.mins.x.toFloat(), value.mins.y.toFloat(), value.maxs.x.toFloat(), value.maxs.y.toFloat()) it.quad(value.mins.x.toFloat(), value.mins.y.toFloat(), value.maxs.x.toFloat(), value.maxs.y.toFloat())

View File

@ -191,9 +191,37 @@ class GLTextureQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "sc
} }
} }
class GLTextureBlurredQuadProgram(state: GLStateTracker) : GLInternalProgram(state, "screen_quad_tex_blur") {
init {
link()
}
val texture = this["texture0"]!!
private val builder by lazy {
val builder = StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1)
builder.begin()
builder.quad(-1f, -1f, 1f, 1f, QuadTransformers.uv())
builder.upload()
builder
}
fun run(texture: Int = 0) {
use()
this.texture.set(texture)
builder.draw()
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC2F).build()
}
}
class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableColorableProgram(state, *shaders) { class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransformableColorableProgram(state, *shaders) {
val builder by lazy { val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 1024) StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 2048)
} }
companion object { companion object {
@ -201,15 +229,33 @@ class GLFlatProgram(state: GLStateTracker, vararg shaders: GLShader) : GLTransfo
} }
} }
class GLFlatColorProgram(state: GLStateTracker) : GLInternalProgram(state, "flat_color") {
init {
link()
}
val transform = this["transform"]!!
val builder by lazy {
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS, 16384)
}
companion object {
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).push(GLType.VEC4F).build()
}
}
class GLPrograms(val state: GLStateTracker) { class GLPrograms(val state: GLStateTracker) {
val tile by SimpleProgram("tile", ::GLTransformableColorableProgram) val tile by SimpleProgram("tile", ::GLTransformableColorableProgram)
val font by SimpleProgram("font", ::GLTransformableColorableProgram) val font by SimpleProgram("font", ::GLTransformableColorableProgram)
val flat by SimpleProgram("flat", ::GLFlatProgram) val flat by SimpleProgram("flat", ::GLFlatProgram)
val flatColor by lazy { GLFlatColorProgram(state) }
val liquid by lazy { GLLiquidProgram(state) } val liquid by lazy { GLLiquidProgram(state) }
val light by lazy { GLLightProgram(state) } val light by lazy { GLLightProgram(state) }
val hardLightGeometry by lazy { GLHardLightGeometryProgram(state) } val hardLightGeometry by lazy { GLHardLightGeometryProgram(state) }
val softLightGeometry by lazy { GLSoftLightGeometryProgram(state) } val softLightGeometry by lazy { GLSoftLightGeometryProgram(state) }
val colorQuad by lazy { GLColorQuadProgram(state) } val viewColorQuad by lazy { GLColorQuadProgram(state) }
val textureQuad by lazy { GLTextureQuadProgram(state) } val viewTextureQuad by lazy { GLTextureQuadProgram(state) }
val viewTextureBlurQuad by lazy { GLTextureBlurredQuadProgram(state) }
} }

View File

@ -98,7 +98,7 @@ class GPULightRenderer(val state: GLStateTracker) {
state.activeTexture = 0 state.activeTexture = 0
state.texture2D = outputTexture state.texture2D = outputTexture
state.blendFunc = BlendFunc.ADDITIVE state.blendFunc = BlendFunc.ADDITIVE
state.programs.textureQuad.run(0) state.programs.viewTextureQuad.run(0)
} finally { } finally {
state.blendFunc = oldFunc state.blendFunc = oldFunc
} }
@ -114,7 +114,7 @@ class GPULightRenderer(val state: GLStateTracker) {
state.activeTexture = 0 state.activeTexture = 0
state.texture2D = outputTexture state.texture2D = outputTexture
state.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA state.blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
state.programs.textureQuad.run(0) state.programs.viewTextureQuad.run(0)
} finally { } finally {
state.blendFunc = oldFunc state.blendFunc = oldFunc
} }
@ -129,7 +129,7 @@ class GPULightRenderer(val state: GLStateTracker) {
state.blendFunc = BlendFunc.ADDITIVE state.blendFunc = BlendFunc.ADDITIVE
state.activeTexture = 0 state.activeTexture = 0
state.texture2D = framebufferRender.texture state.texture2D = framebufferRender.texture
state.programs.textureQuad.run(0) state.programs.viewTextureQuad.run(0)
} finally { } finally {
state.blendFunc = oldFunc state.blendFunc = oldFunc
framebufferAccumulator.unbind() framebufferAccumulator.unbind()

View File

@ -17,11 +17,14 @@ import ru.dbotthepony.kstarbound.util.Timer
import ru.dbotthepony.kstarbound.world.entities.CollisionResolution import ru.dbotthepony.kstarbound.world.entities.CollisionResolution
import ru.dbotthepony.kstarbound.world.entities.Entity import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
import ru.dbotthepony.kvector.narray.Double2Dimensional
import ru.dbotthepony.kvector.narray.Int2Dimensional import ru.dbotthepony.kvector.narray.Int2Dimensional
import ru.dbotthepony.kvector.util2d.AABB import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.util2d.AABBi import ru.dbotthepony.kvector.util2d.AABBi
import ru.dbotthepony.kvector.vector.ndouble.Vector2d import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nint.Vector2i import ru.dbotthepony.kvector.vector.nint.Vector2i
import java.util.LinkedList
import java.util.function.DoubleBinaryOperator
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.cos import kotlin.math.cos
@ -67,10 +70,10 @@ private val veryPreciseFan by lazy { makeDirFan(0.25) }
private fun chooseLightRayFan(size: Double): List<Vector2d> { private fun chooseLightRayFan(size: Double): List<Vector2d> {
return when (size) { return when (size) {
in 0.0 .. 8.0 -> potatoDirFan in 0.0 .. 8.0 -> potatoDirFan
in 8.0 .. 12.0 -> veryRoughDirFan in 8.0 .. 16.0 -> veryRoughDirFan
in 12.0 .. 18.0 -> roughDirFan in 16.0 .. 24.0 -> roughDirFan
in 18.0 .. 24.0 -> dirFan in 24.0 .. 48.0 -> dirFan
in 24.0 .. 32.0 -> preciseFan in 48.0 .. 96.0 -> preciseFan
// in 32.0 .. 48.0 -> veryPreciseFan // in 32.0 .. 48.0 -> veryPreciseFan
else -> veryPreciseFan else -> veryPreciseFan
} }
@ -399,7 +402,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val dir = rayEnd - rayStart val dir = rayEnd - rayStart
val inc = 0.5 / dir.length val inc = 0.5 / dir.length
val tiles = ArrayList<Pair<Vector2i, ITileState>>() val tiles = LinkedList<Pair<Vector2i, ITileState>>()
var prev = Vector2i(Int.MIN_VALUE, Int.MAX_VALUE) var prev = Vector2i(Int.MIN_VALUE, Int.MAX_VALUE)
var hitTile: Pair<Vector2i, ITileState>? = null var hitTile: Pair<Vector2i, ITileState>? = null
@ -482,37 +485,33 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
return result return result
} }
/**
* Трассирует лучи света вокруг себя с заданной позицией, интенсивностью,
* падением интенсивности за проход сквозь тайл [falloffByTile] и
* падением интенсивности за проход по пустому месту [falloffByTravel].
*/
fun rayLightCircleNaive( fun rayLightCircleNaive(
position: Vector2d, position: Vector2d,
intensity: Double, intensity: Double,
falloffByTile: Double = 2.0, falloffByTile: Double = 2.0,
falloffByTravel: Double = 2.0, falloffByTravel: Double = 1.0,
): List<Pair<Double, Vector2i>> { ): Double2Dimensional {
val result = Object2DoubleAVLTreeMap<Vector2i> { a, b -> val combinedResult = Double2Dimensional(intensity.roundToInt() * 2, intensity.roundToInt() * 2)
val cmp = a.x.compareTo(b.x) val baselineX = position.x.roundToInt() - intensity.roundToInt()
val baselineY = position.y.roundToInt() - intensity.roundToInt()
if (cmp != 0) { val dirs = chooseLightRayFan(intensity)
return@Object2DoubleAVLTreeMap cmp val mul = 1.0 / dirs.size
}
return@Object2DoubleAVLTreeMap a.y.compareTo(b.y) for (dir in dirs) {
}
result.defaultReturnValue(-1.0)
for (dir in chooseLightRayFan(intensity)) {
val result2 = rayLightNaive(position, dir, intensity, falloffByTile, falloffByTravel) val result2 = rayLightNaive(position, dir, intensity, falloffByTile, falloffByTravel)
for (pair in result2) { for (pair in result2) {
val existing = result.getDouble(pair.second) combinedResult[pair.second.y - baselineY, pair.second.x - baselineX] += pair.first * mul
if (existing < pair.first) {
result.put(pair.second, pair.first)
}
} }
} }
return result.map { it.value to it.key } return combinedResult
} }
} }
@ -560,7 +559,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
intensity: Double, intensity: Double,
falloffByTile: Double = 2.0, falloffByTile: Double = 2.0,
falloffByTravel: Double = 2.0, falloffByTravel: Double = 2.0,
): List<Pair<Double, Vector2i>> { ): Double2Dimensional {
return CachedGetter().rayLightCircleNaive(position, intensity, falloffByTile, falloffByTravel) return CachedGetter().rayLightCircleNaive(position, intensity, falloffByTile, falloffByTravel)
} }

View File

@ -0,0 +1,10 @@
#version 460
in vec4 finalVertexColor;
out vec4 color;
void main() {
color = finalVertexColor;
}

View File

@ -0,0 +1,13 @@
#version 460
layout (location = 0) in vec2 vertexPos;
layout (location = 1) in vec4 vertexColor;
uniform mat4 transform;
out vec4 finalVertexColor;
void main() {
gl_Position = transform * vec4(vertexPos, 0.5, 1.0);
finalVertexColor = vertexColor;
}