Soft light test
This commit is contained in:
parent
7eeb5f8a12
commit
1254fb276c
@ -4,21 +4,17 @@ import org.apache.logging.log4j.LogManager
|
|||||||
import org.lwjgl.Version
|
import org.lwjgl.Version
|
||||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||||
import org.lwjgl.opengl.GL11.GL_LINES
|
import org.lwjgl.opengl.GL11.GL_LINES
|
||||||
import org.lwjgl.opengl.GL11.GL_RGBA
|
import org.lwjgl.opengl.GL11.GL_TRIANGLES
|
||||||
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.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.BlendFunc
|
import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
|
import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowQuad
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
|
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
|
||||||
import ru.dbotthepony.kstarbound.client.render.LightRenderer
|
import ru.dbotthepony.kstarbound.client.render.LightRenderer
|
||||||
import ru.dbotthepony.kstarbound.io.*
|
import ru.dbotthepony.kstarbound.io.*
|
||||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||||
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
||||||
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
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
|
||||||
@ -195,25 +191,47 @@ fun main() {
|
|||||||
lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight)
|
lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight)
|
||||||
client.onViewportChanged(lightRenderer::resizeFramebuffer)
|
client.onViewportChanged(lightRenderer::resizeFramebuffer)
|
||||||
|
|
||||||
lightRenderer.addShadowGeometry { _, _ ->
|
lightRenderer.addShadowGeometry(object : LightRenderer.ShadowGeometryRenderer {
|
||||||
val builder = client.gl.programs.lightOccluder.builder
|
override fun renderHardGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram) {
|
||||||
|
val builder = lightRenderer.builder
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
builder.quad(0f, 0f, 2f, 2f)
|
builder.quad(-6f, 0f, -2f, 2f)
|
||||||
builder.quad(-6f, 0f, -2f, 2f)
|
builder.quad(0f, 0f, 2f, 2f)
|
||||||
builder.upload()
|
|
||||||
builder.draw(GL_LINES)
|
builder.upload()
|
||||||
}
|
builder.draw(GL_LINES)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun renderSoftGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram) {
|
||||||
|
val builder = lightRenderer.builderSoft
|
||||||
|
|
||||||
|
builder.begin()
|
||||||
|
builder.shadowQuad(-6f, 0f, -2f, 2f)
|
||||||
|
builder.shadowQuad(0f, 0f, 2f, 2f)
|
||||||
|
|
||||||
|
builder.upload()
|
||||||
|
builder.draw(GL_TRIANGLES)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
client.onPostDrawWorld {
|
client.onPostDrawWorld {
|
||||||
lightRenderer.begin()
|
lightRenderer.begin()
|
||||||
|
|
||||||
for ((lightPosition, color) in listOf(
|
for ((lightPosition, color) in listOf(
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-2f)) to Color.RED,
|
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(2f)) to Color.GREEN,
|
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(y = -2f)) to Color.BLUE,
|
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE,
|
||||||
)) {
|
)) {
|
||||||
lightRenderer.renderLight(lightPosition, color)
|
lightRenderer.renderHardLight(lightPosition, color, radius = 10f)
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((lightPosition, color) in listOf(
|
||||||
|
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(10.1f)) to Color.RED,
|
||||||
|
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(10.1f)) to Color.GREEN,
|
||||||
|
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(9.9f)) to Color.BLUE,
|
||||||
|
)) {
|
||||||
|
lightRenderer.renderSoftLight(lightPosition, color, radius = 10f)
|
||||||
}
|
}
|
||||||
|
|
||||||
lightRenderer.renderOutputAdditive()
|
lightRenderer.renderOutputAdditive()
|
||||||
|
@ -117,6 +117,13 @@ data class BlendFunc(
|
|||||||
Func.ZERO
|
Func.ZERO
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val ONLY_BLEND_ALPHA = BlendFunc(
|
||||||
|
Func.ZERO,
|
||||||
|
Func.ONE,
|
||||||
|
Func.ONE,
|
||||||
|
Func.ONE
|
||||||
|
)
|
||||||
|
|
||||||
val ONLY_COLOR = BlendFunc(
|
val ONLY_COLOR = BlendFunc(
|
||||||
Func.ONE,
|
Func.ONE,
|
||||||
Func.ZERO,
|
Func.ZERO,
|
||||||
@ -202,6 +209,7 @@ class GLStateTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var blend by GLStateSwitchTracker(GL_BLEND)
|
var blend by GLStateSwitchTracker(GL_BLEND)
|
||||||
|
var scissor by GLStateSwitchTracker(GL_SCISSOR_TEST)
|
||||||
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
||||||
|
|
||||||
var VBO: VertexBufferObject? = null
|
var VBO: VertexBufferObject? = null
|
||||||
|
@ -10,6 +10,7 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
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.render.LightRenderer
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
import ru.dbotthepony.kvector.vector.Color
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
@ -78,21 +79,34 @@ class GLLightProgram(state: GLStateTracker) : GLInternalProgram(state, "light")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GLLightOccluderProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("light_occluder"), listOf("light_occluder"), listOf("light_occluder")) {
|
class GLHardLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("hard_light_geometry"), listOf("hard_light_geometry"), listOf("hard_light_geometry")) {
|
||||||
init {
|
init {
|
||||||
link()
|
link()
|
||||||
}
|
}
|
||||||
|
|
||||||
// val baselineColor = this["baselineColor"]!!
|
|
||||||
val transform = this["transform"]!!
|
val transform = this["transform"]!!
|
||||||
val lightPosition = this["lightPosition"]!!
|
val lightPosition = this["lightPosition"]!!
|
||||||
|
|
||||||
val builder by lazy {
|
val builder by lazy {
|
||||||
StreamVertexBuilder(state, FORMAT, GeometryType.QUADS_AS_LINES, 32)
|
StreamVertexBuilder(state, LightRenderer.SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GLSoftLightGeometryProgram(state: GLStateTracker) : GLInternalProgram(state, listOf("soft_light_geometry2"), listOf("soft_light_geometry2")) {
|
||||||
|
init {
|
||||||
|
link()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
val transform = this["transform"]!!
|
||||||
val FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
|
val lightPenetration = this["lightPenetration"]!!
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vector3f(x, y, size)
|
||||||
|
*/
|
||||||
|
val lightPositionAndSize = this["lightPositionAndSize"]!!
|
||||||
|
|
||||||
|
val builder by lazy {
|
||||||
|
StreamVertexBuilder(state, LightRenderer.SHADOW_FORMAT_SOFT, GeometryType.QUADS_AS_LINES, 32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +204,8 @@ class GLPrograms(val state: GLStateTracker) {
|
|||||||
val flat by SimpleProgram("flat", ::GLFlatProgram)
|
val flat by SimpleProgram("flat", ::GLFlatProgram)
|
||||||
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 lightOccluder by lazy { GLLightOccluderProgram(state) }
|
val hardLightGeometry by lazy { GLHardLightGeometryProgram(state) }
|
||||||
|
val softLightGeometry by lazy { GLSoftLightGeometryProgram(state) }
|
||||||
|
|
||||||
val colorQuad by lazy { GLColorQuadProgram(state) }
|
val colorQuad by lazy { GLColorQuadProgram(state) }
|
||||||
val textureQuad by lazy { GLTextureQuadProgram(state) }
|
val textureQuad by lazy { GLTextureQuadProgram(state) }
|
||||||
|
@ -6,6 +6,7 @@ import org.lwjgl.opengl.GL46.GL_UNSIGNED_SHORT
|
|||||||
import org.lwjgl.opengl.GL46.GL_UNSIGNED_BYTE
|
import org.lwjgl.opengl.GL46.GL_UNSIGNED_BYTE
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLType
|
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject
|
import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
import ru.dbotthepony.kstarbound.util.writeLEFloat
|
import ru.dbotthepony.kstarbound.util.writeLEFloat
|
||||||
import ru.dbotthepony.kstarbound.util.writeLEInt
|
import ru.dbotthepony.kstarbound.util.writeLEInt
|
||||||
import ru.dbotthepony.kstarbound.util.writeLEShort
|
import ru.dbotthepony.kstarbound.util.writeLEShort
|
||||||
@ -101,6 +102,7 @@ abstract class AbstractVertexBuilder<out T : AbstractVertexBuilder<T>>(
|
|||||||
check(elementVertices == 0) { "Not fully built vertex element ($type requires ${type.elements} vertex points to be present, yet last strip has only $elementVertices elements)" }
|
check(elementVertices == 0) { "Not fully built vertex element ($type requires ${type.elements} vertex points to be present, yet last strip has only $elementVertices elements)" }
|
||||||
|
|
||||||
doUpload(vbo, ebo, drawType)
|
doUpload(vbo, ebo, drawType)
|
||||||
|
checkForGLError()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var elementVertices = 0
|
private var elementVertices = 0
|
||||||
@ -123,12 +125,12 @@ abstract class AbstractVertexBuilder<out T : AbstractVertexBuilder<T>>(
|
|||||||
val elementMemory = elementMemory
|
val elementMemory = elementMemory
|
||||||
val elementIndexType = elementIndexType
|
val elementIndexType = elementIndexType
|
||||||
|
|
||||||
for (index in type.indicies) {
|
for (index in type.indices) {
|
||||||
put(elementIndexType, elementMemory, index + elementIndexOffset)
|
put(elementIndexType, elementMemory, index + elementIndexOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
elementIndexOffset += type.elements
|
elementIndexOffset += type.elements
|
||||||
indexCount += type.indicies.size
|
indexCount += type.indices.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,6 +142,17 @@ abstract class AbstractVertexBuilder<out T : AbstractVertexBuilder<T>>(
|
|||||||
return this as T
|
return this as T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun pushVec4f(x: Float, y: Float, z: Float, w: Float): T {
|
||||||
|
expect(GLType.VEC4F)
|
||||||
|
val memory = vertexMemory
|
||||||
|
memory.writeLEFloat(x)
|
||||||
|
memory.writeLEFloat(y)
|
||||||
|
memory.writeLEFloat(z)
|
||||||
|
memory.writeLEFloat(w)
|
||||||
|
attributeIndex++
|
||||||
|
return this as T
|
||||||
|
}
|
||||||
|
|
||||||
fun pushVec3f(x: Float, y: Float, z: Float): T {
|
fun pushVec3f(x: Float, y: Float, z: Float): T {
|
||||||
expect(GLType.VEC3F)
|
expect(GLType.VEC3F)
|
||||||
val memory = vertexMemory
|
val memory = vertexMemory
|
||||||
@ -167,6 +180,39 @@ abstract class AbstractVertexBuilder<out T : AbstractVertexBuilder<T>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : AbstractVertexBuilder<T>> T.shadowLine(x0: Float, y0: Float, x1: Float, y1: Float): T {
|
||||||
|
vertex()
|
||||||
|
pushVec4f(x0, y0, x1, y1)
|
||||||
|
pushVec2f(0f, 0f)
|
||||||
|
|
||||||
|
vertex()
|
||||||
|
pushVec4f(x0, y0, x1, y1)
|
||||||
|
pushVec2f(0f, 1f)
|
||||||
|
|
||||||
|
vertex()
|
||||||
|
pushVec4f(x0, y0, x1, y1)
|
||||||
|
pushVec2f(1f, 1f)
|
||||||
|
|
||||||
|
vertex()
|
||||||
|
pushVec4f(x0, y0, x1, y1)
|
||||||
|
pushVec2f(1f, 0f)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : AbstractVertexBuilder<T>> T.dfShadowLine(x0: Float, y0: Float, x1: Float, y1: Float): T {
|
||||||
|
shadowLine(x0, y0, x1, y1)
|
||||||
|
shadowLine(x1, y1, x0, y0)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : AbstractVertexBuilder<T>> T.shadowQuad(x0: Float, y0: Float, x1: Float, y1: Float): T {
|
||||||
|
shadowLine(x0, y0, x1, y0)
|
||||||
|
shadowLine(x1, y0, x1, y1)
|
||||||
|
shadowLine(x1, y1, x0, y1)
|
||||||
|
shadowLine(x0, y1, x0, y0)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
// Помощники
|
// Помощники
|
||||||
fun <T : AbstractVertexBuilder<T>> T.quad(
|
fun <T : AbstractVertexBuilder<T>> T.quad(
|
||||||
|
@ -9,7 +9,7 @@ open class DirectVertexBuilder<T : DirectVertexBuilder<T>>(
|
|||||||
type: GeometryType,
|
type: GeometryType,
|
||||||
val maxElements: Int,
|
val maxElements: Int,
|
||||||
) : AbstractVertexBuilder<DirectVertexBuilder<T>>(attributes, type) {
|
) : AbstractVertexBuilder<DirectVertexBuilder<T>>(attributes, type) {
|
||||||
val maxIndexCount = maxElements * type.indicies.size
|
val maxIndexCount = maxElements * type.indices.size
|
||||||
val maxVertexCount = maxElements * type.elements
|
val maxVertexCount = maxElements * type.elements
|
||||||
|
|
||||||
final override val elementIndexType: Int = when (maxIndexCount) {
|
final override val elementIndexType: Int = when (maxIndexCount) {
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.vertex
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
enum class GeometryType(val elements: Int, val indicies: IntArray) {
|
enum class GeometryType(val elements: Int, val indices: IntArray) {
|
||||||
|
AS_IS(1, intArrayOf(0)),
|
||||||
LINES(2, intArrayOf(0, 1)),
|
LINES(2, intArrayOf(0, 1)),
|
||||||
TRIANGLES(3, intArrayOf(0, 1, 2)),
|
TRIANGLES(3, intArrayOf(0, 1, 2)),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A B C B C D
|
||||||
|
*/
|
||||||
QUADS(4, intArrayOf(0, 1, 2, 1, 2, 3)),
|
QUADS(4, intArrayOf(0, 1, 2, 1, 2, 3)),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A B C C D A
|
||||||
|
*/
|
||||||
|
QUADS_ALTERNATIVE(4, intArrayOf(0, 1, 2, 2, 3, 0)),
|
||||||
|
|
||||||
QUADS_AS_LINES(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3)),
|
QUADS_AS_LINES(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3)),
|
||||||
QUADS_AS_LINES_WIREFRAME(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
QUADS_AS_LINES_WIREFRAME(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class HeapVertexBuilder(
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
override fun ensureIndexCapacity() {
|
override fun ensureIndexCapacity() {
|
||||||
if (elementIndexType == GL_UNSIGNED_SHORT && elementMemory.length / 2 + type.indicies.size >= 30000) {
|
if (elementIndexType == GL_UNSIGNED_SHORT && elementMemory.length / 2 + type.indices.size >= 30000) {
|
||||||
val backing = elementMemory.array
|
val backing = elementMemory.array
|
||||||
val copy = FastByteArrayInputStream(ByteArray(elementMemory.length) { backing[it] })
|
val copy = FastByteArrayInputStream(ByteArray(elementMemory.length) { backing[it] })
|
||||||
|
|
||||||
|
@ -7,17 +7,27 @@ import ru.dbotthepony.kstarbound.client.gl.BlendFunc
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
|
import ru.dbotthepony.kstarbound.client.gl.GLFrameBuffer
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GLAttributeList
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
|
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
import ru.dbotthepony.kvector.vector.Color
|
||||||
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
||||||
|
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
|
||||||
|
|
||||||
|
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
||||||
// https://slembcke.github.io/SuperFastHardShadows
|
// https://slembcke.github.io/SuperFastHardShadows
|
||||||
|
// https://slembcke.github.io/SuperFastSoftShadows
|
||||||
class LightRenderer(val state: GLStateTracker) {
|
class LightRenderer(val state: GLStateTracker) {
|
||||||
fun interface ShadowGeometryRenderer {
|
interface ShadowGeometryRenderer {
|
||||||
fun render(lightPosition: Vector2f, stack: Matrix4fStack)
|
fun renderHardGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram)
|
||||||
|
fun renderSoftGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val geometry = ArrayList<ShadowGeometryRenderer>()
|
private val geometry = ArrayList<ShadowGeometryRenderer>()
|
||||||
@ -101,7 +111,23 @@ class LightRenderer(val state: GLStateTracker) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renderLight(
|
private fun blendResult() {
|
||||||
|
val oldFunc = state.blendFunc
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun renderHardLight(
|
||||||
position: Vector2f,
|
position: Vector2f,
|
||||||
color: Color = Color.WHITE,
|
color: Color = Color.WHITE,
|
||||||
radius: Float = 10f,
|
radius: Float = 10f,
|
||||||
@ -128,14 +154,14 @@ class LightRenderer(val state: GLStateTracker) {
|
|||||||
checkForGLError()
|
checkForGLError()
|
||||||
state.clearColor = old
|
state.clearColor = old
|
||||||
|
|
||||||
state.programs.lightOccluder.use()
|
state.programs.hardLightGeometry.use()
|
||||||
state.programs.lightOccluder.transform.set(stack.last)
|
state.programs.hardLightGeometry.transform.set(stack.last)
|
||||||
state.programs.lightOccluder.lightPosition.set(position)
|
state.programs.hardLightGeometry.lightPosition.set(position)
|
||||||
|
|
||||||
state.blendFunc = BlendFunc.ONLY_ALPHA
|
state.blendFunc = BlendFunc.ONLY_ALPHA
|
||||||
|
|
||||||
for (renderer in geometry) {
|
for (renderer in geometry) {
|
||||||
renderer.render(position, stack)
|
renderer.renderHardGeometry(position, stack, state.programs.hardLightGeometry)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.programs.light.use()
|
state.programs.light.use()
|
||||||
@ -156,26 +182,102 @@ class LightRenderer(val state: GLStateTracker) {
|
|||||||
framebufferRender.unbind()
|
framebufferRender.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
// накопление
|
blendResult()
|
||||||
try {
|
}
|
||||||
framebufferAccumulator.bind()
|
|
||||||
|
|
||||||
state.blendFunc = BlendFunc.ADDITIVE
|
fun renderSoftLight(
|
||||||
state.activeTexture = 0
|
position: Vector2f,
|
||||||
state.texture2D = framebufferRender.texture
|
color: Color = Color.WHITE,
|
||||||
state.programs.textureQuad.run(0)
|
radius: Float = 10f,
|
||||||
|
innerRadius: Float = 1f,
|
||||||
|
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 = CLEAR_COLOR_SOFT
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT)
|
||||||
|
checkForGLError()
|
||||||
|
state.clearColor = old
|
||||||
|
|
||||||
|
state.programs.softLightGeometry.use()
|
||||||
|
state.programs.softLightGeometry.transform.set(stack.last)
|
||||||
|
state.programs.softLightGeometry.lightPositionAndSize.set(Vector3f(position, innerRadius))
|
||||||
|
|
||||||
|
state.blendFunc = BlendFunc.ONLY_BLEND_ALPHA
|
||||||
|
|
||||||
|
for (renderer in geometry) {
|
||||||
|
renderer.renderSoftGeometry(position, stack, state.programs.softLightGeometry)
|
||||||
|
}
|
||||||
|
|
||||||
|
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_SOFT
|
||||||
|
builder.draw()
|
||||||
} finally {
|
} finally {
|
||||||
state.blendFunc = oldFunc
|
state.blendFunc = oldFunc
|
||||||
framebufferAccumulator.unbind()
|
framebufferRender.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blendResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder by lazy {
|
||||||
|
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.QUADS_AS_LINES, 256)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lineBuilder by lazy {
|
||||||
|
StreamVertexBuilder(state, SHADOW_FORMAT, GeometryType.LINES, 256)
|
||||||
|
}
|
||||||
|
|
||||||
|
val builderSoft by lazy {
|
||||||
|
StreamVertexBuilder(state, SHADOW_FORMAT_SOFT, GeometryType.QUADS_ALTERNATIVE, 256)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lineBuilderSoft by lazy {
|
||||||
|
StreamVertexBuilder(state, SHADOW_FORMAT_SOFT, GeometryType.LINES, 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
val CLEAR_COLOR_SOFT = Color(0f, 0f, 0f, 0f)
|
||||||
|
|
||||||
val BLEND_MODE = BlendFunc(
|
val BLEND_MODE = BlendFunc(
|
||||||
BlendFunc.Func.DST_ALPHA,
|
BlendFunc.Func.DST_ALPHA,
|
||||||
BlendFunc.Func.ZERO,
|
BlendFunc.Func.ZERO,
|
||||||
BlendFunc.Func.ZERO,
|
BlendFunc.Func.ZERO,
|
||||||
BlendFunc.Func.ONE,
|
BlendFunc.Func.ONE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val BLEND_MODE_SOFT = BlendFunc(
|
||||||
|
BlendFunc.Func.ONE_MINUS_DST_ALPHA,
|
||||||
|
BlendFunc.Func.ZERO,
|
||||||
|
BlendFunc.Func.ZERO,
|
||||||
|
BlendFunc.Func.ONE,
|
||||||
|
)
|
||||||
|
|
||||||
|
val SHADOW_FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
|
||||||
|
val SHADOW_FORMAT_SOFT = GLAttributeList.Builder().push(GLType.VEC4F).push(GLType.VEC2F).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/main/resources/shaders/soft_light_geometry.fsh
Normal file
10
src/main/resources/shaders/soft_light_geometry.fsh
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
out vec4 resultColor;
|
||||||
|
|
||||||
|
in vec4 penumbras;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
resultColor = vec4(1.0, 1.0, 1.0, 0.0);
|
||||||
|
}
|
150
src/main/resources/shaders/soft_light_geometry.gsh
Normal file
150
src/main/resources/shaders/soft_light_geometry.gsh
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (lines) in;
|
||||||
|
|
||||||
|
layout (triangle_strip, max_vertices = 5) out;
|
||||||
|
|
||||||
|
uniform mat4 transform;
|
||||||
|
uniform vec3 lightPositionAndSize;
|
||||||
|
|
||||||
|
in vec2 originalPos[];
|
||||||
|
|
||||||
|
out vec4 penumbras;
|
||||||
|
|
||||||
|
mat2 adjugate(mat2 m) {
|
||||||
|
return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workWithSegmentForward(float x) {
|
||||||
|
vec2 endpoint_a = originalPos[0];
|
||||||
|
vec2 endpoint_b = originalPos[1];
|
||||||
|
|
||||||
|
vec2 endpoint = mix(endpoint_a, endpoint_b, x);
|
||||||
|
float light_radius = lightPositionAndSize.z;
|
||||||
|
|
||||||
|
// Deltas from the segment to the light center.
|
||||||
|
vec2 delta_a = endpoint_a - lightPositionAndSize.xy;
|
||||||
|
vec2 delta_b = endpoint_b - lightPositionAndSize.xy;
|
||||||
|
vec2 delta = endpoint - lightPositionAndSize.xy;
|
||||||
|
|
||||||
|
// Offsets from the light center to the edge of the light volume.
|
||||||
|
vec2 offset_a = vec2(-light_radius, light_radius) * normalize(delta_a).xy;
|
||||||
|
vec2 offset_b = vec2( light_radius, -light_radius) * normalize(delta_b).xy;
|
||||||
|
vec2 offset = mix(offset_a, offset_b, x);
|
||||||
|
|
||||||
|
vec2 penumbra_a = adjugate(mat2( offset_a, -delta_a))*(delta - mix(offset, delta_a, 0));
|
||||||
|
vec2 penumbra_b = adjugate(mat2(-offset_b, delta_b))*(delta - mix(offset, delta_b, 0));
|
||||||
|
|
||||||
|
// Vertex projection.
|
||||||
|
gl_Position = transform * vec4(delta - offset, 0.0, 1.0);
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 a = gl_in[0].gl_Position;
|
||||||
|
vec4 b = gl_in[1].gl_Position;
|
||||||
|
|
||||||
|
vec2 aInv = originalPos[0];
|
||||||
|
vec2 bInv = originalPos[1];
|
||||||
|
|
||||||
|
vec2 lightPosition = lightPositionAndSize.xy;
|
||||||
|
float lightSize = lightPositionAndSize.z;
|
||||||
|
|
||||||
|
gl_Position = b;
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = a;
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
workWithSegmentForward(0);
|
||||||
|
workWithSegmentForward(1);
|
||||||
|
|
||||||
|
EndPrimitive();
|
||||||
|
|
||||||
|
// hard shadow geometry (umbra)
|
||||||
|
//gl_Position = transform * vec4(aInv + (aInv - lightPositionAndSize.xy) * 10000, 0, 1);
|
||||||
|
//EmitVertex();
|
||||||
|
|
||||||
|
//gl_Position = transform * vec4(bInv + (bInv - lightPositionAndSize.xy) * 10000, 0, 1);
|
||||||
|
//EmitVertex();
|
||||||
|
|
||||||
|
// находим направления к вершинам линии
|
||||||
|
/*vec2 deltaA = normalize(aInv - lightPosition);
|
||||||
|
vec2 deltaB = normalize(bInv - lightPosition);
|
||||||
|
|
||||||
|
// находим наклон прямых из центра
|
||||||
|
float degA = acos(deltaA.x);
|
||||||
|
float degB = acos(deltaB.x);
|
||||||
|
|
||||||
|
// корректируем угол в зависимости, находимся ли мы в отрицательном Y
|
||||||
|
if (deltaA.y < 0) {
|
||||||
|
degA = -degA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaB.y < 0) {
|
||||||
|
degB = -degB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (degA < 0) {
|
||||||
|
degA = 360 + degA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (degB < 0) {
|
||||||
|
degB = 360 + degB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// определяем, находимся ли мы "сзади"
|
||||||
|
if (degA < degB) {
|
||||||
|
vec2 temp = aInv;
|
||||||
|
aInv = bInv;
|
||||||
|
bInv = temp;
|
||||||
|
|
||||||
|
// находим направления к вершинам линии
|
||||||
|
deltaA = normalize(aInv - lightPosition);
|
||||||
|
deltaB = normalize(bInv - lightPosition);
|
||||||
|
|
||||||
|
// находим наклон прямых из центра
|
||||||
|
degA = acos(deltaA.x);
|
||||||
|
degB = acos(deltaB.x);
|
||||||
|
|
||||||
|
// корректируем угол в зависимости, находимся ли мы в отрицательном Y
|
||||||
|
if (deltaA.y < 0) {
|
||||||
|
degA = -degA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaB.y < 0) {
|
||||||
|
degB = -degB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (degA < 0) {
|
||||||
|
degA = 360 + degA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (degB < 0) {
|
||||||
|
degB = 360 + degB;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// поворачиваем наши углы на 90 градусов
|
||||||
|
/*degA -= 1.57079632;
|
||||||
|
degB += 1.57079632;
|
||||||
|
|
||||||
|
// Теперь для каждой вершины вычисляем касательную точку на окружности
|
||||||
|
vec2 pointA = vec2(cos(degA), sin(degA)) * lightSize + lightPosition;
|
||||||
|
vec2 pointB = vec2(cos(degB), sin(degB)) * lightSize + lightPosition;
|
||||||
|
|
||||||
|
// мы нашли касательные точки, теперь просто надо провести прямые начиная от вершин
|
||||||
|
|
||||||
|
// это у нас "hard shadows"
|
||||||
|
gl_Position = transform * vec4(aInv + (aInv - pointA) * 10000, 0, 1);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = transform * vec4(bInv + (bInv - pointB) * 10000, 0, 1);
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
gl_Position = b;
|
||||||
|
EmitVertex();
|
||||||
|
|
||||||
|
EndPrimitive();*/
|
||||||
|
}
|
13
src/main/resources/shaders/soft_light_geometry.vsh
Normal file
13
src/main/resources/shaders/soft_light_geometry.vsh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 vertexPos;
|
||||||
|
|
||||||
|
uniform mat4 transform;
|
||||||
|
|
||||||
|
out vec2 originalPos;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = transform * vec4(vertexPos, 0.0, 1.0);
|
||||||
|
originalPos = vertexPos;
|
||||||
|
}
|
30
src/main/resources/shaders/soft_light_geometry2.fsh
Normal file
30
src/main/resources/shaders/soft_light_geometry2.fsh
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
||||||
|
// https://slembcke.github.io/SuperFastHardShadows
|
||||||
|
// https://slembcke.github.io/SuperFastSoftShadows
|
||||||
|
|
||||||
|
out vec4 resultColor;
|
||||||
|
|
||||||
|
in vec4 v_penumbras;
|
||||||
|
in vec3 v_edges;
|
||||||
|
in vec3 v_proj_pos;
|
||||||
|
in vec4 v_endpoints;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Calculate the light intersection point, but clamp to endpoints to avoid artifacts.
|
||||||
|
float intersection_t = clamp(v_edges.x/abs(v_edges.y), -0.5, 0.5);
|
||||||
|
vec2 intersection_point = (0.5 - intersection_t)*v_endpoints.xy + (0.5 + intersection_t)*v_endpoints.zw;
|
||||||
|
// The delta from the intersection to the pixel.
|
||||||
|
vec2 penetration_delta = intersection_point - v_proj_pos.xy/v_proj_pos.z;
|
||||||
|
// Apply a simple falloff function.
|
||||||
|
float bleed = min(dot(penetration_delta, penetration_delta), 1.0);
|
||||||
|
|
||||||
|
// Penumbra mixing.
|
||||||
|
vec2 penumbras = smoothstep(-1.0, 1.0, v_penumbras.xz/v_penumbras.yw);
|
||||||
|
float penumbra = dot(penumbras, step(v_penumbras.yw, vec2(0.0)));
|
||||||
|
penumbra -= 1.0/64.0; // Numerical precision fudge factor.
|
||||||
|
|
||||||
|
resultColor = vec4(0.0, 0.0, 0.0, bleed * (1.0 - penumbra) * step(v_edges.z, 0.0));
|
||||||
|
}
|
69
src/main/resources/shaders/soft_light_geometry2.vsh
Normal file
69
src/main/resources/shaders/soft_light_geometry2.vsh
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
||||||
|
// https://slembcke.github.io/SuperFastHardShadows
|
||||||
|
// https://slembcke.github.io/SuperFastSoftShadows
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 packedVertexPos;
|
||||||
|
layout (location = 1) in vec2 edgeType;
|
||||||
|
|
||||||
|
uniform mat4 transform;
|
||||||
|
|
||||||
|
uniform vec3 lightPositionAndSize;
|
||||||
|
uniform float lightPenetration;
|
||||||
|
|
||||||
|
varying vec4 v_penumbras;
|
||||||
|
varying vec3 v_edges;
|
||||||
|
varying vec3 v_proj_pos;
|
||||||
|
varying vec4 v_endpoints;
|
||||||
|
|
||||||
|
// Keep in mind GLSL is column major. You'll need to swap row/column for HLSL.
|
||||||
|
mat2 adjugate(mat2 m) {
|
||||||
|
return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 lightPosition = lightPositionAndSize.xy;
|
||||||
|
float lightSize = lightPositionAndSize.z;
|
||||||
|
|
||||||
|
// Unpack the vertex shader input.
|
||||||
|
vec2 endpoint_a = packedVertexPos.zw;
|
||||||
|
vec2 endpoint_b = packedVertexPos.xy;
|
||||||
|
vec2 endpoint = mix(endpoint_a, endpoint_b, edgeType.x);
|
||||||
|
|
||||||
|
// Deltas from the segment to the light center.
|
||||||
|
vec2 delta_a = endpoint_a - lightPosition;
|
||||||
|
vec2 delta_b = endpoint_b - lightPosition;
|
||||||
|
vec2 delta = endpoint - lightPosition;
|
||||||
|
|
||||||
|
// Offsets from the light center to the edge of the light volume.
|
||||||
|
vec2 offset_a = vec2(-lightSize, lightSize) * normalize(delta_a).yx;
|
||||||
|
vec2 offset_b = vec2( lightSize, -lightSize) * normalize(delta_b).yx;
|
||||||
|
vec2 offset = mix(offset_a, offset_b, edgeType.x);
|
||||||
|
|
||||||
|
// Vertex projection.
|
||||||
|
float w = edgeType.y;
|
||||||
|
vec4 proj_pos = vec4(mix(delta - offset, endpoint, w), 0.0, w);
|
||||||
|
|
||||||
|
gl_Position = transform * proj_pos;
|
||||||
|
|
||||||
|
vec2 penumbra_a = adjugate(mat2( offset_a, -delta_a))*(delta - mix(offset, delta_a, w));
|
||||||
|
vec2 penumbra_b = adjugate(mat2(-offset_b, delta_b))*(delta - mix(offset, delta_b, w));
|
||||||
|
|
||||||
|
v_penumbras = (lightSize > 0.0 ? vec4(penumbra_a, penumbra_b) : vec4(0, 1, 0, 1));
|
||||||
|
|
||||||
|
// Edge values for light penetration and clipping.
|
||||||
|
vec2 seg_delta = endpoint_b - endpoint_a;
|
||||||
|
vec2 seg_normal = seg_delta.yx*vec2(-1.0, 1.0);
|
||||||
|
// Calculate where the light -> pixel ray will intersect with the segment.
|
||||||
|
v_edges.xy = -adjugate(mat2(seg_delta, delta_a + delta_b))*(delta - offset*(1.0 - w));
|
||||||
|
v_edges.y *= 2.0; // Skip a multiply in the fragment shader.
|
||||||
|
// Calculate a clipping coordinate that is 0 at the near edge (when w = 1)...
|
||||||
|
// otherwise calculate the dot product with the projected coordinate.
|
||||||
|
v_edges.z = dot(seg_normal, delta - offset)*(1.0 - w);
|
||||||
|
|
||||||
|
// Light penetration values.
|
||||||
|
v_proj_pos = vec3(proj_pos.xy, w * lightPenetration);
|
||||||
|
v_endpoints = vec4(endpoint_a, endpoint_b) / lightPenetration;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user