More shadows tests

This commit is contained in:
DBotThePony 2022-09-16 11:55:23 +07:00
parent 4198483974
commit 5c697c129e
Signed by: DBot
GPG Key ID: DCC23B5715498507
6 changed files with 183 additions and 63 deletions

View File

@ -52,8 +52,8 @@ fun main() {
var set = 0L
var parse = 0L
for (chunkX in 0 .. 94) {
for (chunkY in 0 .. 61) {
for (chunkX in 17 .. 18) {
for (chunkY in 21 .. 21) {
var t = System.currentTimeMillis()
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
find += System.currentTimeMillis() - t
@ -179,6 +179,7 @@ fun main() {
client.gl.font.render("${ent.position}", y = 100f, scale = 0.25f)
client.gl.font.render("${ent.movement.velocity}", y = 120f, scale = 0.25f)
client.gl.font.render("${client.camera.pos} ${client.settings.zoom}", y = 140f, scale = 0.25f)
client.gl.font.render("${ChunkPos.fromTilePosition(client.camera.pos.toDoubleVector())}", y = 160f, scale = 0.25f)
}
client.onPreDrawWorld {

View File

@ -9,6 +9,7 @@ import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.StatefulVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowLine
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowQuad
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
import ru.dbotthepony.kstarbound.client.render.EntityRenderer
@ -20,6 +21,7 @@ import ru.dbotthepony.kstarbound.world.*
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kvector.matrix.Matrix4fStack
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
import ru.dbotthepony.kvector.util2d.intersectCircleRectangle
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
@ -207,75 +209,89 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
foregroundRenderer.autoUpload()
}
private val hardShadowGeometry = StatefulVertexBuilder(state, LightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES)
private var hardShadowGeometryRev = -1
private val softShadowGeometry = StatefulVertexBuilder(state, LightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_ALTERNATIVE)
private var softShadowGeometryRev = -1
private inner class ShadowGeometryTracker(val x: Int, val y: Int) : LightRenderer.ShadowGeometryRenderer {
private val hardShadowGeometry = StatefulVertexBuilder(state, LightRenderer.SHADOW_FORMAT, GeometryType.LINES)
private var hardShadowGeometryRev = -1
private val softShadowGeometry = StatefulVertexBuilder(state, LightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_ALTERNATIVE)
private var softShadowGeometryRev = -1
val shadowGeometry: LightRenderer.ShadowGeometryRenderer get() {
return object : LightRenderer.ShadowGeometryRenderer {
override fun renderHardGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
stack: Matrix4fStack,
program: GLHardLightGeometryProgram
) {
if (hardShadowGeometryRev == tileChangeset) {
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
hardShadowGeometry.draw(GL_LINES)
return
}
private fun buildGeometry(builder: StatefulVertexBuilder, line: (StatefulVertexBuilder, Float, Float, Float, Float) -> Unit) {
builder.begin()
hardShadowGeometryRev = tileChangeset
for (x in this.x * SHADOW_GEOMETRY_SQUARE_SIZE until (this.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) {
for (y in this.y * SHADOW_GEOMETRY_SQUARE_SIZE until (this.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE) {
if (foreground[x, y].material?.renderParameters?.lightTransparent == false) {
if (x == 0 || foreground[x - 1, y].material?.renderParameters?.lightTransparent != true) {
line(builder, x.toFloat(), y.toFloat(), x.toFloat(), y + 1f)
}
val builder = hardShadowGeometry
if (x == CHUNK_SIZE - 1 || foreground[x + 1, y].material?.renderParameters?.lightTransparent != true) {
line(builder, x + 1f, y.toFloat(), x + 1f, y + 1f)
}
builder.begin()
if (y == 0 || foreground[x, y - 1].material?.renderParameters?.lightTransparent != true) {
line(builder, x.toFloat(), y.toFloat(), x + 1f, y.toFloat())
}
for (x in 0 until CHUNK_SIZE) {
for (y in 0 until CHUNK_SIZE) {
if (foreground[x, y].material != null) {
builder.quad(x.toFloat(), y.toFloat(), x + 1f, y + 1f)
if (y == CHUNK_SIZE - 1 || foreground[x, y + 1].material?.renderParameters?.lightTransparent != true) {
line(builder, x.toFloat(), y + 1f, x + 1f, y + 1f)
}
}
}
builder.upload()
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
builder.draw(GL_LINES)
}
override fun renderSoftGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
stack: Matrix4fStack,
program: GLSoftLightGeometryProgram
) {
if (softShadowGeometryRev == tileChangeset) {
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
softShadowGeometry.draw(GL_TRIANGLES)
return
}
builder.upload()
}
softShadowGeometryRev = tileChangeset
fun buildHardGeometry() {
hardShadowGeometryRev = tileChangeset
val builder = softShadowGeometry
buildGeometry(hardShadowGeometry) { it, x0, y0, x1, y1 ->
it.vertex().pushVec2f(x0, y0)
it.vertex().pushVec2f(x1, y1)
}
}
builder.begin()
fun buildSoftGeometry() {
softShadowGeometryRev = tileChangeset
buildGeometry(softShadowGeometry, StatefulVertexBuilder::shadowLine)
}
for (x in 0 until CHUNK_SIZE) {
for (y in 0 until CHUNK_SIZE) {
if (foreground[x, y].material != null) {
builder.shadowQuad(x.toFloat(), y.toFloat(), x + 1f, y + 1f)
}
}
}
override fun renderHardGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
lightRadius: Float,
stack: Matrix4fStack,
program: GLHardLightGeometryProgram
) {
if (hardShadowGeometryRev != tileChangeset) {
buildHardGeometry()
}
builder.upload()
hardShadowGeometry.draw(GL_LINES)
}
program.localToWorldTransform.set(Matrix4f.IDENTITY.translateWithMultiplication(Vector3f(x = pos.x * CHUNK_SIZEf, y = pos.y * CHUNK_SIZEf)))
builder.draw(GL_LINES)
override fun renderSoftGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
lightRadius: Float,
stack: Matrix4fStack,
program: GLSoftLightGeometryProgram
) {
if (softShadowGeometryRev != tileChangeset) {
buildSoftGeometry()
}
softShadowGeometry.draw(GL_TRIANGLES)
}
}
private val shadowGeometry = ArrayList<ShadowGeometryTracker>()
init {
for (x in 0 until SHADOW_GEOMETRY_SUBDIVISION) {
for (y in 0 until SHADOW_GEOMETRY_SUBDIVISION) {
shadowGeometry.add(ShadowGeometryTracker(x, y))
}
}
}
@ -286,9 +302,81 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
* Должен быть использован только один раз, после выкинут, иначе поведение
* кода невозможно будет предсказать
*/
inner class OneShotRenderer constructor(val origin: ChunkPos = pos) : ILayeredRenderer {
inner class OneShotRenderer constructor(val origin: ChunkPos = pos) : ILayeredRenderer, LightRenderer.ShadowGeometryRenderer {
private val layerQueue = ArrayDeque<Pair<(Matrix4fStack) -> Unit, Int>>()
override fun renderHardGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
lightRadius: Float,
stack: Matrix4fStack,
program: GLHardLightGeometryProgram
) {
if (!intersectCircleRectangle(lightPosition, lightRadius, origin.x * CHUNK_SIZEf, origin.y * CHUNK_SIZEf, (origin.x + 1) * CHUNK_SIZEf, (origin.y + 1) * CHUNK_SIZEf)) {
return
}
var setOnce = false
for (geometry in shadowGeometry) {
if (intersectCircleRectangle(
lightPosition,
lightRadius,
origin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
origin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
origin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
origin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
) {
if (!setOnce) {
program.localToWorldTransform.set(
Matrix4f.IDENTITY.translateWithMultiplication(
Vector3f(x = origin.x * CHUNK_SIZEf,
y = origin.y * CHUNK_SIZEf)))
setOnce = true
}
geometry.renderHardGeometry(renderer, lightPosition, lightRadius, stack, program)
}
}
}
override fun renderSoftGeometry(
renderer: LightRenderer,
lightPosition: Vector2f,
lightRadius: Float,
stack: Matrix4fStack,
program: GLSoftLightGeometryProgram
) {
if (!intersectCircleRectangle(lightPosition, lightRadius, origin.x * CHUNK_SIZEf, origin.y * CHUNK_SIZEf, (origin.x + 1) * CHUNK_SIZEf, (origin.y + 1) * CHUNK_SIZEf)) {
return
}
var setOnce = false
for (geometry in shadowGeometry) {
if (intersectCircleRectangle(
lightPosition,
lightRadius,
origin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
origin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
origin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
origin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
) {
if (!setOnce) {
program.localToWorldTransform.set(
Matrix4f.IDENTITY.translateWithMultiplication(
Vector3f(x = origin.x * CHUNK_SIZEf,
y = origin.y * CHUNK_SIZEf)))
setOnce = true
}
geometry.renderSoftGeometry(renderer, lightPosition, lightRadius, stack, program)
}
}
}
init {
for ((baked, zLevel) in backgroundRenderer.bakedMeshes) {
layerQueue.add(baked::renderStacked to (zLevel + Z_LEVEL_BACKGROUND))
@ -414,4 +502,9 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
renderer.close()
}
}
companion object {
const val SHADOW_GEOMETRY_SUBDIVISION = 4
const val SHADOW_GEOMETRY_SQUARE_SIZE = CHUNK_SIZE / SHADOW_GEOMETRY_SUBDIVISION
}
}

View File

@ -107,9 +107,10 @@ class ClientWorld(
client.lightRenderer.begin()
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
determineRenderers.add(chunk.second.OneShotRenderer(chunk.first))
val renderer = chunk.second.OneShotRenderer(chunk.first)
determineRenderers.add(renderer)
chunk.second.bake()
client.lightRenderer.addShadowGeometry(chunk.second.shadowGeometry)
client.lightRenderer.addShadowGeometry(renderer)
}
renderLayeredList(client.gl.matrixStack, determineRenderers)
@ -119,7 +120,7 @@ class ClientWorld(
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE,
)) {
client.lightRenderer.renderSoftLight(lightPosition, color, radius = 100f)
client.lightRenderer.renderSoftLight(lightPosition, color, radius = 20f, innerRadius = 1f)
}
val old = client.gl.blendFunc

View File

@ -25,6 +25,7 @@ import ru.dbotthepony.kvector.vector.nfloat.Vector2f
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.concurrent.locks.LockSupport
class StarboundClient : AutoCloseable {
val window: Long
@ -144,6 +145,9 @@ class StarboundClient : AutoCloseable {
return Vector2f(relativeX, relativeY)
}
var isRenderingGame = true
private set
init {
GLFWErrorCallback.create { error, description ->
LOGGER.error("LWJGL error {}: {}", error, description)
@ -166,6 +170,12 @@ class StarboundClient : AutoCloseable {
input.installCallback(window)
GLFW.glfwSetFramebufferSizeCallback(window) { _, w, h ->
if (w == 0 || h == 0) {
isRenderingGame = false
return@glfwSetFramebufferSizeCallback
}
isRenderingGame = true
viewportWidth = w
viewportHeight = h
viewportMatrixScreen = updateViewportMatrixScreen()
@ -287,6 +297,13 @@ class StarboundClient : AutoCloseable {
return false
}
if (!isRenderingGame) {
gl.cleanup()
GLFW.glfwPollEvents()
LockSupport.parkNanos(1_000_000L)
return true
}
val measure = GLFW.glfwGetTime()
if (frameRenderTime != 0.0 && Starbound.initialized)

View File

@ -43,6 +43,13 @@ class GLUniformLocation(val program: GLShaderProgram, val name: String, val poin
return this
}
fun set(value: Float): GLUniformLocation {
program.state.ensureSameThread()
glProgramUniform1f(program.pointer, pointer, value)
checkForGLError()
return this
}
private val buff3x3: FloatBuffer by lazy { ByteBuffer.allocateDirect(4 * 3 * 3).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer() }
private val buff4x4: FloatBuffer by lazy { ByteBuffer.allocateDirect(4 * 4 * 4).also { it.order(ByteOrder.nativeOrder()) }.asFloatBuffer() }

View File

@ -26,8 +26,8 @@ import ru.dbotthepony.kvector.vector.nfloat.Vector3f
// https://slembcke.github.io/SuperFastSoftShadows
class LightRenderer(val state: GLStateTracker) {
interface ShadowGeometryRenderer {
fun renderHardGeometry(renderer: LightRenderer, lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram)
fun renderSoftGeometry(renderer: LightRenderer, lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram)
fun renderHardGeometry(renderer: LightRenderer, lightPosition: Vector2f, lightRadius: Float, stack: Matrix4fStack, program: GLHardLightGeometryProgram)
fun renderSoftGeometry(renderer: LightRenderer, lightPosition: Vector2f, lightRadius: Float, stack: Matrix4fStack, program: GLSoftLightGeometryProgram)
}
private val geometry = ArrayList<ShadowGeometryRenderer>()
@ -169,7 +169,7 @@ class LightRenderer(val state: GLStateTracker) {
state.blendFunc = BlendFunc.ONLY_ALPHA
for (renderer in geometry) {
renderer.renderHardGeometry(this, position, stack, state.programs.hardLightGeometry)
renderer.renderHardGeometry(this, position, radius, stack, state.programs.hardLightGeometry)
}
state.programs.light.use()
@ -224,11 +224,12 @@ class LightRenderer(val state: GLStateTracker) {
state.programs.softLightGeometry.use()
state.programs.softLightGeometry.transform.set(stack.last)
state.programs.softLightGeometry.lightPositionAndSize.set(Vector3f(position, innerRadius))
state.programs.softLightGeometry.lightPenetration.set(1f)
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
for (renderer in geometry) {
renderer.renderSoftGeometry(this, position, stack, state.programs.softLightGeometry)
renderer.renderSoftGeometry(this, position, radius, stack, state.programs.softLightGeometry)
}
state.programs.light.use()