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.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 org.lwjgl.opengl.GL11.GL_TRIANGLES
|
||||
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.vertex.QuadTransformers
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLHardLightGeometryProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.program.GLSoftLightGeometryProgram
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowQuad
|
||||
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.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
||||
@ -195,25 +191,47 @@ fun main() {
|
||||
lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight)
|
||||
client.onViewportChanged(lightRenderer::resizeFramebuffer)
|
||||
|
||||
lightRenderer.addShadowGeometry { _, _ ->
|
||||
val builder = client.gl.programs.lightOccluder.builder
|
||||
lightRenderer.addShadowGeometry(object : LightRenderer.ShadowGeometryRenderer {
|
||||
override fun renderHardGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram) {
|
||||
val builder = lightRenderer.builder
|
||||
|
||||
builder.begin()
|
||||
builder.quad(0f, 0f, 2f, 2f)
|
||||
builder.quad(-6f, 0f, -2f, 2f)
|
||||
builder.upload()
|
||||
builder.draw(GL_LINES)
|
||||
}
|
||||
builder.begin()
|
||||
builder.quad(-6f, 0f, -2f, 2f)
|
||||
builder.quad(0f, 0f, 2f, 2f)
|
||||
|
||||
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 {
|
||||
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,
|
||||
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
|
||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
|
||||
(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()
|
||||
|
@ -117,6 +117,13 @@ data class BlendFunc(
|
||||
Func.ZERO
|
||||
)
|
||||
|
||||
val ONLY_BLEND_ALPHA = BlendFunc(
|
||||
Func.ZERO,
|
||||
Func.ONE,
|
||||
Func.ONE,
|
||||
Func.ONE
|
||||
)
|
||||
|
||||
val ONLY_COLOR = BlendFunc(
|
||||
Func.ONE,
|
||||
Func.ZERO,
|
||||
@ -202,6 +209,7 @@ class GLStateTracker {
|
||||
}
|
||||
|
||||
var blend by GLStateSwitchTracker(GL_BLEND)
|
||||
var scissor by GLStateSwitchTracker(GL_SCISSOR_TEST)
|
||||
var depthTest by GLStateSwitchTracker(GL_DEPTH_TEST)
|
||||
|
||||
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.QuadTransformers
|
||||
import ru.dbotthepony.kstarbound.client.gl.vertex.quad
|
||||
import ru.dbotthepony.kstarbound.client.render.LightRenderer
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
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 {
|
||||
link()
|
||||
}
|
||||
|
||||
// val baselineColor = this["baselineColor"]!!
|
||||
val transform = this["transform"]!!
|
||||
val lightPosition = this["lightPosition"]!!
|
||||
|
||||
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 FORMAT = GLAttributeList.Builder().push(GLType.VEC2F).build()
|
||||
val transform = this["transform"]!!
|
||||
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 liquid by lazy { GLLiquidProgram(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 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 ru.dbotthepony.kstarbound.client.gl.GLType
|
||||
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.writeLEInt
|
||||
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)" }
|
||||
|
||||
doUpload(vbo, ebo, drawType)
|
||||
checkForGLError()
|
||||
}
|
||||
|
||||
private var elementVertices = 0
|
||||
@ -123,12 +125,12 @@ abstract class AbstractVertexBuilder<out T : AbstractVertexBuilder<T>>(
|
||||
val elementMemory = elementMemory
|
||||
val elementIndexType = elementIndexType
|
||||
|
||||
for (index in type.indicies) {
|
||||
for (index in type.indices) {
|
||||
put(elementIndexType, elementMemory, index + elementIndexOffset)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
expect(GLType.VEC3F)
|
||||
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(
|
||||
|
@ -9,7 +9,7 @@ open class DirectVertexBuilder<T : DirectVertexBuilder<T>>(
|
||||
type: GeometryType,
|
||||
val maxElements: Int,
|
||||
) : AbstractVertexBuilder<DirectVertexBuilder<T>>(attributes, type) {
|
||||
val maxIndexCount = maxElements * type.indicies.size
|
||||
val maxIndexCount = maxElements * type.indices.size
|
||||
val maxVertexCount = maxElements * type.elements
|
||||
|
||||
final override val elementIndexType: Int = when (maxIndexCount) {
|
||||
|
@ -1,9 +1,20 @@
|
||||
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)),
|
||||
TRIANGLES(3, intArrayOf(0, 1, 2)),
|
||||
|
||||
/**
|
||||
* A B C B C D
|
||||
*/
|
||||
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_WIREFRAME(4, intArrayOf(0, 1, 0, 2, 1, 3, 2, 3, 0, 3, 1, 2)),
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class HeapVertexBuilder(
|
||||
private set
|
||||
|
||||
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 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.GLStateTracker
|
||||
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.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.StreamVertexBuilder
|
||||
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
|
||||
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
|
||||
|
||||
// Huge thanks to articles by Scott [slembcke] Lembcke!
|
||||
// https://slembcke.github.io/SuperFastHardShadows
|
||||
// https://slembcke.github.io/SuperFastSoftShadows
|
||||
class LightRenderer(val state: GLStateTracker) {
|
||||
fun interface ShadowGeometryRenderer {
|
||||
fun render(lightPosition: Vector2f, stack: Matrix4fStack)
|
||||
interface ShadowGeometryRenderer {
|
||||
fun renderHardGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram)
|
||||
fun renderSoftGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram)
|
||||
}
|
||||
|
||||
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,
|
||||
color: Color = Color.WHITE,
|
||||
radius: Float = 10f,
|
||||
@ -128,14 +154,14 @@ class LightRenderer(val state: GLStateTracker) {
|
||||
checkForGLError()
|
||||
state.clearColor = old
|
||||
|
||||
state.programs.lightOccluder.use()
|
||||
state.programs.lightOccluder.transform.set(stack.last)
|
||||
state.programs.lightOccluder.lightPosition.set(position)
|
||||
state.programs.hardLightGeometry.use()
|
||||
state.programs.hardLightGeometry.transform.set(stack.last)
|
||||
state.programs.hardLightGeometry.lightPosition.set(position)
|
||||
|
||||
state.blendFunc = BlendFunc.ONLY_ALPHA
|
||||
|
||||
for (renderer in geometry) {
|
||||
renderer.render(position, stack)
|
||||
renderer.renderHardGeometry(position, stack, state.programs.hardLightGeometry)
|
||||
}
|
||||
|
||||
state.programs.light.use()
|
||||
@ -156,26 +182,102 @@ class LightRenderer(val state: GLStateTracker) {
|
||||
framebufferRender.unbind()
|
||||
}
|
||||
|
||||
// накопление
|
||||
try {
|
||||
framebufferAccumulator.bind()
|
||||
blendResult()
|
||||
}
|
||||
|
||||
state.blendFunc = BlendFunc.ADDITIVE
|
||||
state.activeTexture = 0
|
||||
state.texture2D = framebufferRender.texture
|
||||
state.programs.textureQuad.run(0)
|
||||
fun renderSoftLight(
|
||||
position: Vector2f,
|
||||
color: Color = Color.WHITE,
|
||||
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 {
|
||||
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 {
|
||||
val CLEAR_COLOR_SOFT = Color(0f, 0f, 0f, 0f)
|
||||
|
||||
val BLEND_MODE = BlendFunc(
|
||||
BlendFunc.Func.DST_ALPHA,
|
||||
BlendFunc.Func.ZERO,
|
||||
BlendFunc.Func.ZERO,
|
||||
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