[orx-poisson-fill] move to orx-jvm
This commit is contained in:
10
orx-jvm/orx-poisson-fill/README.md
Normal file
10
orx-jvm/orx-poisson-fill/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# orx-poisson-fill
|
||||
|
||||
Post processing effect that fills transparent parts of the image interpolating the edge pixel colors. GPU-based.
|
||||
|
||||
<!-- __demos__ -->
|
||||
## Demos
|
||||
### DemoPoissonFill01
|
||||
[source code](src/demo/kotlin/DemoPoissonFill01.kt)
|
||||
|
||||

|
||||
19
orx-jvm/orx-poisson-fill/build.gradle
Normal file
19
orx-jvm/orx-poisson-fill/build.gradle
Normal file
@@ -0,0 +1,19 @@
|
||||
sourceSets {
|
||||
demo {
|
||||
java {
|
||||
srcDirs = ["src/demo/kotlin"]
|
||||
compileClasspath += main.getCompileClasspath()
|
||||
runtimeClasspath += main.getRuntimeClasspath()
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation project(":orx-fx")
|
||||
implementation project(":orx-noise")
|
||||
implementation("org.openrndr:openrndr-filter:$openrndrVersion")
|
||||
demoImplementation("org.openrndr:openrndr-application:$openrndrVersion")
|
||||
demoImplementation("org.openrndr:openrndr-extensions:$openrndrVersion")
|
||||
demoRuntimeOnly("org.openrndr:openrndr-gl3:$openrndrVersion")
|
||||
demoRuntimeOnly("org.openrndr:openrndr-gl3-natives-$openrndrOS:$openrndrVersion")
|
||||
demoImplementation(sourceSets.getByName("main").output)
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import org.openrndr.MouseButton
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorHSVa
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.color.rgb
|
||||
import org.openrndr.draw.ColorType
|
||||
import org.openrndr.draw.colorBuffer
|
||||
import org.openrndr.draw.isolatedWithTarget
|
||||
import org.openrndr.draw.renderTarget
|
||||
import org.openrndr.extensions.SingleScreenshot
|
||||
import org.openrndr.extra.noise.Random
|
||||
import org.openrndr.math.Polar
|
||||
import org.openrndr.math.clamp
|
||||
import org.openrndr.math.mix
|
||||
import org.openrndr.poissonfill.PoissonFill
|
||||
import org.openrndr.shape.Rectangle
|
||||
import kotlin.math.sin
|
||||
|
||||
data class Thing(val color: ColorRGBa, var pos: Polar, val speed: Polar)
|
||||
|
||||
suspend fun main() {
|
||||
application {
|
||||
program {
|
||||
|
||||
// -- this block is for automation purposes only
|
||||
if (System.getProperty("takeScreenshot") == "true") {
|
||||
extend(SingleScreenshot()) {
|
||||
this.outputFile = System.getProperty("screenshotPath")
|
||||
}
|
||||
}
|
||||
|
||||
val dry = renderTarget(width, height) {
|
||||
colorBuffer(type = ColorType.FLOAT32)
|
||||
}
|
||||
val wet = colorBuffer(width, height)
|
||||
|
||||
val fx = PoissonFill()
|
||||
|
||||
var borderOpacity = 0.0
|
||||
|
||||
// Create a list of things with
|
||||
// color, polar position and polar speed
|
||||
val things = List(10) {
|
||||
Thing(
|
||||
ColorHSVa(it * 182.0,
|
||||
Random.double(0.3, 0.6),
|
||||
Random.double(0.1, 0.9)).toRGBa(),
|
||||
Polar(Random.double0(360.0),
|
||||
100.0 + it * 10.0),
|
||||
Polar(Random.double(-1.0, 1.0), 0.0))
|
||||
}
|
||||
|
||||
extend {
|
||||
drawer.isolatedWithTarget(dry) {
|
||||
stroke = null
|
||||
clear(ColorRGBa.TRANSPARENT)
|
||||
|
||||
// draw color circles
|
||||
things.forEach { thing ->
|
||||
fill = thing.color.shade(0.9 +
|
||||
0.1 * sin(thing.pos.theta * 0.3))
|
||||
circle(thing.pos.cartesian + bounds.center, 5.0)
|
||||
// A. Use after fix in Polar.kt
|
||||
//thing.pos += thing.speed
|
||||
// B. temporary solution
|
||||
thing.pos = Polar(thing.pos.theta +
|
||||
thing.speed.theta, thing.pos.radius)
|
||||
}
|
||||
|
||||
// draw dark gray window border.
|
||||
// hold mouse button to fade in.
|
||||
borderOpacity += if (MouseButton.LEFT in mouse.pressedButtons) 0.01 else -0.01
|
||||
borderOpacity = borderOpacity.clamp(0.0, 1.0)
|
||||
stroke = rgb(0.2).opacify(borderOpacity)
|
||||
fill = null
|
||||
strokeWeight = 3.0
|
||||
rectangle(bounds)
|
||||
}
|
||||
|
||||
fx.apply(dry.colorBuffer(0), wet)
|
||||
drawer.image(wet)
|
||||
|
||||
// draw white rectangle
|
||||
drawer.stroke = ColorRGBa.WHITE.opacify(0.9)
|
||||
drawer.fill = null
|
||||
drawer.strokeWeight = 6.0
|
||||
drawer.rectangle(Rectangle.fromCenter(drawer.bounds.center,
|
||||
300.0, 300.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
139
orx-jvm/orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt
Normal file
139
orx-jvm/orx-poisson-fill/src/main/kotlin/ConvolutionPyramid.kt
Normal file
@@ -0,0 +1,139 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.extra.fx.blend.Passthrough
|
||||
import org.openrndr.math.IntVector2
|
||||
import org.openrndr.resourceUrl
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.min
|
||||
|
||||
internal class Downscale(filterUrl: String = "/shaders/gl3/poisson/downscale.frag")
|
||||
: Filter(filterShaderFromUrl(resourceUrl(filterUrl))) {
|
||||
var h1: FloatArray by parameters
|
||||
init {
|
||||
h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Upscale(filterUrl: String = "/shaders/gl3/poisson/upscale.frag")
|
||||
: Filter(filterShaderFromUrl(resourceUrl(filterUrl))) {
|
||||
var h1: FloatArray by parameters
|
||||
var h2: Float by parameters
|
||||
var g: FloatArray by parameters
|
||||
|
||||
init {
|
||||
h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
|
||||
h2 = 0.0f
|
||||
g = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Convolution(filterUrl: String = "/shaders/gl3/poisson/filter.frag")
|
||||
: Filter(filterShaderFromUrl(resourceUrl(filterUrl))) {
|
||||
var g: FloatArray by parameters
|
||||
|
||||
init {
|
||||
g = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
}
|
||||
}
|
||||
|
||||
private val passthrough by lazy { Passthrough() }
|
||||
internal class ConvolutionPyramid(width: Int, height: Int,
|
||||
private val padding: Int = 0, cutOff: Int = 10000,
|
||||
private val downscale: Downscale = Downscale(),
|
||||
private val upscale: Upscale = Upscale(),
|
||||
private val filter: Convolution = Convolution(),
|
||||
val type: ColorType = ColorType.FLOAT32) {
|
||||
var h1 = floatArrayOf(0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
|
||||
var h2 = 0.0f
|
||||
var g = floatArrayOf(0.0f, 0.0f, 0.0f)
|
||||
|
||||
private val size = 5
|
||||
private val resolution = IntVector2(width + 2 * padding, height + 2 * padding)
|
||||
private val minResolution = min(resolution.x, resolution.y)
|
||||
private val depth = min(cutOff, ceil(log2(minResolution.toDouble())).toInt())
|
||||
|
||||
private val levelsIn = mutableListOf<RenderTarget>()
|
||||
private val levelsOut = mutableListOf<RenderTarget>()
|
||||
|
||||
private val result = colorBuffer(width, height, type = type)
|
||||
|
||||
init {
|
||||
var levelWidth = resolution.x + 2 * size
|
||||
var levelHeight = resolution.y + 2 * size
|
||||
|
||||
for (i in 0 until depth) {
|
||||
levelsIn.add(renderTarget(levelWidth, levelHeight) {
|
||||
colorBuffer(type = type)
|
||||
})
|
||||
|
||||
levelsOut.add(renderTarget(levelWidth, levelHeight) {
|
||||
colorBuffer(type = type)
|
||||
})
|
||||
|
||||
levelWidth /= 2
|
||||
levelHeight /= 2
|
||||
levelWidth += 2 * size
|
||||
levelHeight += 2 * size
|
||||
}
|
||||
}
|
||||
|
||||
fun process(input: ColorBuffer): ColorBuffer {
|
||||
for (l in levelsIn) {
|
||||
l.clearColor(0, ColorRGBa.TRANSPARENT)
|
||||
}
|
||||
|
||||
for (l in levelsOut) {
|
||||
l.clearColor(0, ColorRGBa.TRANSPARENT)
|
||||
}
|
||||
|
||||
downscale.h1 = h1
|
||||
|
||||
upscale.g = g
|
||||
upscale.h1 = h1
|
||||
upscale.h2 = h2
|
||||
|
||||
filter.g = g
|
||||
|
||||
passthrough.padding = (levelsIn[0].width - input.width) / 2
|
||||
passthrough.apply(input, levelsIn[0].colorBuffer(0))
|
||||
passthrough.padding = 0
|
||||
|
||||
for (i in 1 until levelsIn.size) {
|
||||
downscale.padding = 0
|
||||
downscale.apply(levelsIn[i - 1].colorBuffer(0),
|
||||
levelsIn[i].colorBuffer(0)
|
||||
)
|
||||
}
|
||||
|
||||
filter.apply(levelsIn.last().colorBuffer(0), levelsOut.last().colorBuffer(0))
|
||||
|
||||
for (i in levelsOut.size - 2 downTo 0) {
|
||||
upscale.padding = 0
|
||||
upscale.apply(arrayOf(levelsIn[i].colorBuffer(0), levelsOut[i + 1].colorBuffer(0)), arrayOf(levelsOut[i].colorBuffer(0)))
|
||||
}
|
||||
|
||||
passthrough.padding = -size - padding
|
||||
passthrough.apply(levelsOut[0].colorBuffer(0), result)
|
||||
passthrough.padding = 0
|
||||
return result
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
result.destroy()
|
||||
(levelsIn+levelsOut).forEach {
|
||||
it.colorAttachments.forEach {
|
||||
when(it) {
|
||||
is ColorBufferAttachment -> it.colorBuffer.destroy()
|
||||
is CubemapAttachment -> it.cubemap.destroy()
|
||||
is ArrayTextureAttachment -> it.arrayTexture.destroy()
|
||||
is ArrayCubemapAttachment -> it.arrayCubemap.destroy()
|
||||
}
|
||||
}
|
||||
it.detachColorAttachments()
|
||||
it.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
internal class PassthroughNoAlpha : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/passthrough-noalpha.frag")))
|
||||
|
||||
/**
|
||||
* Laplacian filter
|
||||
*/
|
||||
class Laplacian : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/laplacian.frag")))
|
||||
|
||||
class LaplacianIntegrator(width: Int, height: Int, downscaling: Int = 1, type: ColorType = ColorType.FLOAT32) {
|
||||
private val pyramid = ConvolutionPyramid(2 + width / downscaling, 2 + height / downscaling, type = type)
|
||||
private val h1 = floatArrayOf(0.15f, 0.5f, 0.7f, 0.5f, 0.15f)
|
||||
private val h2 = 1.0f
|
||||
private val g = floatArrayOf(0.175f, 0.547f, 0.175f)
|
||||
private val preproc = colorBuffer(width + 2, height + 2, type = type)
|
||||
private val combined = colorBuffer(width, height)
|
||||
private val passthrough = PassthroughNoAlpha()
|
||||
|
||||
init {
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
}
|
||||
|
||||
fun process(input: ColorBuffer): ColorBuffer {
|
||||
preproc.fill(ColorRGBa.TRANSPARENT)
|
||||
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
|
||||
passthrough.padding = 1
|
||||
passthrough.apply(input, preproc)
|
||||
passthrough.padding = 0
|
||||
|
||||
val result = pyramid.process(preproc)
|
||||
passthrough.padding = -1
|
||||
passthrough.apply(result, combined)
|
||||
passthrough.padding = 0
|
||||
return combined
|
||||
}
|
||||
}
|
||||
117
orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonBlender.kt
Normal file
117
orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonBlender.kt
Normal file
@@ -0,0 +1,117 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.extra.fx.blend.Passthrough
|
||||
import org.openrndr.extra.fx.blend.Subtract
|
||||
import org.openrndr.filter.color.delinearize
|
||||
import org.openrndr.filter.color.linearize
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
internal class BlendBoundary : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/blend-boundary.frag")))
|
||||
class AlphaToBitmap : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/alpha-to-bitmap.frag")))
|
||||
|
||||
internal class BlendCombine : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/blend-combine.frag"))) {
|
||||
var softMaskGain: Double by parameters
|
||||
init {
|
||||
softMaskGain = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
internal class Clamp : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/clamp.frag"))) {
|
||||
var minValue: Double by parameters
|
||||
var maxValue: Double by parameters
|
||||
}
|
||||
|
||||
private val passthrough by lazy { Passthrough() }
|
||||
private val subtract by lazy { Subtract() }
|
||||
|
||||
class PoissonBlender(val width: Int, val height: Int, type: ColorType = ColorType.FLOAT32) {
|
||||
private val pyramid = ConvolutionPyramid(width, height, 0, type = type)
|
||||
private val preprocess = colorBuffer(width, height, type = type)
|
||||
private val combined = colorBuffer(width, height, type = type)
|
||||
|
||||
private val fillBoundary = BlendBoundary()
|
||||
private val fillCombine = BlendCombine()
|
||||
|
||||
private val difference = colorBuffer(width, height, type = type)
|
||||
|
||||
private val h1 = floatArrayOf(0.1507146f, 0.6835785f, 1.0334191f, 0.6836f, 0.1507f)
|
||||
private val h2 = 0.0269546f
|
||||
private val g = floatArrayOf(0.0311849f, 0.7752854f, 0.0311849f)
|
||||
|
||||
|
||||
init {
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
}
|
||||
|
||||
fun process(target: ColorBuffer, source: ColorBuffer, mask: ColorBuffer,
|
||||
softMask: ColorBuffer = mask, softMaskGain: Double = 1.0): ColorBuffer {
|
||||
subtract.apply(arrayOf(target, source), difference)
|
||||
fillBoundary.apply(arrayOf(difference, mask), preprocess)
|
||||
val result = pyramid.process(preprocess)
|
||||
fillCombine.softMaskGain = softMaskGain
|
||||
fillCombine.apply(arrayOf(result, target, source, mask, softMask), arrayOf(combined))
|
||||
return combined
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
pyramid.destroy()
|
||||
preprocess.destroy()
|
||||
combined.destroy()
|
||||
difference.destroy()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PoissonBlend: Filter() {
|
||||
private var blender: PoissonBlender? = null
|
||||
|
||||
val alphaToBitmap = AlphaToBitmap()
|
||||
var mask: ColorBuffer? = null
|
||||
|
||||
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
|
||||
if (target.isNotEmpty()) {
|
||||
|
||||
mask?.let {
|
||||
if (it.width != target[0].width || it.height != target[0].height) {
|
||||
it.destroy()
|
||||
mask = null
|
||||
}
|
||||
}
|
||||
|
||||
if (mask == null) {
|
||||
mask = colorBuffer(target[0].width, target[0].height)
|
||||
}
|
||||
|
||||
blender?.let {
|
||||
if (it.width != target[0].width || it.height != target[0].height) {
|
||||
it.destroy()
|
||||
blender = null
|
||||
}
|
||||
}
|
||||
|
||||
if (blender == null) {
|
||||
blender = PoissonBlender(target[0].width, target[0].height)
|
||||
}
|
||||
|
||||
mask?.let {
|
||||
alphaToBitmap.apply(source[1], it)
|
||||
}
|
||||
|
||||
|
||||
blender?.let {
|
||||
|
||||
linearize.apply(source[0], source[0])
|
||||
linearize.apply(source[1], source[1])
|
||||
|
||||
val result = it.process(source[0], source[1], mask ?: error("no mask"))
|
||||
result.copyTo(target[0])
|
||||
|
||||
delinearize.apply(target[0], target[0])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
65
orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonFiller.kt
Normal file
65
orx-jvm/orx-poisson-fill/src/main/kotlin/PoissonFiller.kt
Normal file
@@ -0,0 +1,65 @@
|
||||
package org.openrndr.poissonfill
|
||||
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
internal class FillBoundary : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/fill-boundary.frag")))
|
||||
internal class FillCombine : Filter(filterShaderFromUrl(resourceUrl("/shaders/gl3/poisson/fill-combine.frag")))
|
||||
|
||||
class PoissonFiller(val width: Int, val height: Int, type: ColorType = ColorType.FLOAT32) {
|
||||
private val pyramid = ConvolutionPyramid(width, height, 0, type = type)
|
||||
private val preproc = colorBuffer(width, height, type = type)
|
||||
private val combined = colorBuffer(width, height, type = type)
|
||||
private val fillBoundary = FillBoundary()
|
||||
private val fillCombine = FillCombine()
|
||||
|
||||
private val h1 = floatArrayOf(0.1507146f, 0.6835785f, 1.0334191f, 0.6836f, 0.1507f)
|
||||
private val h2 = 0.0269546f
|
||||
private val g = floatArrayOf(0.0311849f, 0.7752854f, 0.0311849f)
|
||||
|
||||
init {
|
||||
pyramid.h1 = h1
|
||||
pyramid.g = g
|
||||
pyramid.h2 = h2
|
||||
}
|
||||
|
||||
fun process(input: ColorBuffer): ColorBuffer {
|
||||
fillBoundary.apply(input, preproc)
|
||||
val result = pyramid.process(preproc)
|
||||
fillCombine.apply(arrayOf(result, input), arrayOf(combined))
|
||||
return combined
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
preproc.destroy()
|
||||
combined.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Poison filling as a filter
|
||||
*/
|
||||
class PoissonFill : Filter() {
|
||||
private var filler: PoissonFiller? = null
|
||||
override fun apply(source: Array<ColorBuffer>, target: Array<ColorBuffer>) {
|
||||
|
||||
if (target.isNotEmpty()) {
|
||||
|
||||
filler?.let {
|
||||
if (it.width != target[0].width || it.height != target[0].height) {
|
||||
it.destroy()
|
||||
filler = null
|
||||
}
|
||||
}
|
||||
|
||||
if (filler == null) {
|
||||
filler = PoissonFiller(target[0].width, target[0].height)
|
||||
}
|
||||
|
||||
filler?.let {
|
||||
val result = it.process(source[0])
|
||||
result.copyTo(target[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0;
|
||||
out vec4 o_output;
|
||||
|
||||
void main(){
|
||||
vec4 c = texture(tex0, v_texCoord0);
|
||||
o_output.rgb = vec3(step(0.01, c.a));
|
||||
o_output.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// adapted from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0; // input image
|
||||
uniform sampler2D tex1; // mask
|
||||
|
||||
out vec4 o_output;
|
||||
|
||||
/** Output color only on the edges of the black regions in the input image, along with a 1.0 alpha. */
|
||||
void main(){
|
||||
|
||||
o_output = vec4(0.0);
|
||||
|
||||
vec4 fullColor = textureLod(tex0, v_texCoord0, 0.0);
|
||||
float maskColor = textureLod(tex1, v_texCoord0, 0.0).r;
|
||||
|
||||
float isInMask = maskColor == 1.0 ? 1.0 : 0.0;
|
||||
|
||||
float maskLaplacian = -4.0 * isInMask;
|
||||
float mask110 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2( 1, 0)).r;
|
||||
float mask101 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2( 0, 1)).r;
|
||||
float mask010 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2(-1, 0)).r;
|
||||
float mask001 = textureLodOffset(tex1, v_texCoord0, 0.0, ivec2( 0,-1)).r;
|
||||
|
||||
maskLaplacian += mask110 == 1.0 ? 1.0 : 0.0;
|
||||
maskLaplacian += mask101 == 1.0 ? 1.0 : 0.0;
|
||||
maskLaplacian += mask010 == 1.0 ? 1.0 : 0.0;
|
||||
maskLaplacian += mask001 == 1.0 ? 1.0 : 0.0;
|
||||
|
||||
if(maskLaplacian > 0.0){
|
||||
o_output.rgb = fullColor.rgb;
|
||||
o_output.a = 1.0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
|
||||
uniform sampler2D tex0; // membrane
|
||||
uniform sampler2D tex1; // target image
|
||||
uniform sampler2D tex2; // source image
|
||||
uniform sampler2D tex3; // mask
|
||||
uniform sampler2D tex4; // soft mask
|
||||
|
||||
uniform float softMaskGain;
|
||||
out vec4 o_output;
|
||||
|
||||
void main(){
|
||||
|
||||
vec4 targetColor = textureLod(tex1, v_texCoord0, 0.0).rgba;
|
||||
float maskColor = textureLod(tex3, v_texCoord0, 0.0).r;
|
||||
float mask = maskColor == 1.0 ? 1.0 : 0.0;
|
||||
float softMask = textureLod(tex4, v_texCoord0, 0.0).r;
|
||||
|
||||
float maskLaplacian = -4.0 * mask;
|
||||
float mask110 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2( 1, 0)).r;
|
||||
float mask101 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2( 0, 1)).r;
|
||||
float mask010 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2(-1, 0)).r;
|
||||
float mask001 = textureLodOffset(tex3, v_texCoord0, 0.0, ivec2( 0,-1)).r;
|
||||
|
||||
maskLaplacian += mask110 == 1.0 ? 1.0 : 0.0;
|
||||
maskLaplacian += mask101 == 1.0 ? 1.0 : 0.0;
|
||||
maskLaplacian += mask010 == 1.0 ? 1.0 : 0.0;
|
||||
maskLaplacian += mask001 == 1.0 ? 1.0 : 0.0;
|
||||
|
||||
if (maskLaplacian > 0) {
|
||||
mask = 1;
|
||||
}
|
||||
{
|
||||
vec4 sourceColor = textureLod(tex2, v_texCoord0, 0.0);
|
||||
vec4 membraneColor = textureLod(tex0, v_texCoord0, 0.0);
|
||||
membraneColor.rgb /= membraneColor.a;
|
||||
|
||||
vec3 blend = membraneColor.rgb + sourceColor.rgb;
|
||||
|
||||
o_output.rgb = mix(targetColor.rgb, blend, mask*max(0.0,min(1.0, softMask * softMaskGain)));
|
||||
o_output.a = 1.0;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0;
|
||||
uniform int levels;
|
||||
out vec4 o_output;
|
||||
uniform float scale;
|
||||
uniform float phase;
|
||||
|
||||
uniform float minValue;
|
||||
uniform float maxValue;
|
||||
|
||||
void main(){
|
||||
|
||||
vec4 c = texture(tex0, v_texCoord0);
|
||||
|
||||
c.rgb = clamp(c.rgb, vec3(minValue), vec3(maxValue));
|
||||
|
||||
o_output = c;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/downscale.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
|
||||
uniform vec2 targetSize;
|
||||
uniform sampler2D tex0;
|
||||
out vec4 o_output;
|
||||
uniform vec2 padding;
|
||||
uniform float h1[5]; ///< h1 filter parameters.
|
||||
|
||||
/** Denotes if a pixel falls outside an image.
|
||||
\param pos the pixel position
|
||||
\param size the image size
|
||||
\return true if the pixel is outside of the image
|
||||
*/
|
||||
bool isOutside(ivec2 pos, ivec2 size){
|
||||
|
||||
return (pos.x < 0 || pos.y < 0 || pos.x > size.x || pos.y > size.y);
|
||||
}
|
||||
|
||||
/** Apply the h1 filter and downscale the input data by a factor of 2. */
|
||||
void main(){
|
||||
|
||||
vec4 accum = vec4(0.0);
|
||||
|
||||
ivec2 size = textureSize(tex0, 0).xy;
|
||||
|
||||
ivec2 ts = size;
|
||||
|
||||
//ivec2 ts = ivec2(targetSize - 2 * padding);
|
||||
|
||||
// Our current size is half this one, so we have to scale by 2.
|
||||
|
||||
ivec2 coords = ivec2(floor( targetSize * v_texCoord0)) * 2 - ivec2(10);
|
||||
|
||||
|
||||
for(int dy = -2; dy <=2; dy++){
|
||||
for(int dx = -2; dx <=2; dx++){
|
||||
ivec2 newPix = coords+ivec2(dx,dy);
|
||||
if(isOutside(newPix, size)){
|
||||
continue;
|
||||
//accum = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
accum += h1[dx+2] * h1[dy+2] * texelFetch(tex0, newPix,0);
|
||||
}
|
||||
}
|
||||
o_output = accum;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0;
|
||||
|
||||
//layout(binding = 0) uniform sampler2D screenTexture; ///< Image to process.
|
||||
|
||||
//layout(location = 0) out vec4 fragColor; ///< Color.
|
||||
|
||||
out vec4 o_output;
|
||||
|
||||
/** Denotes if a pixel falls outside an image.
|
||||
\param pos the pixel position
|
||||
\param size the image size
|
||||
\return true if the pixel is outside of the image
|
||||
*/
|
||||
|
||||
/** Output color only on the edges of the black regions in the input image, along with a 1.0 alpha. */
|
||||
void main(){
|
||||
|
||||
o_output = vec4(0.0);
|
||||
|
||||
vec4 fullColor = textureLod(tex0, v_texCoord0, 0.0);
|
||||
|
||||
float isInMask = fullColor.a == 1.0 ? 0.0 : 1.0; //float(all(equal(fullColor.rgb, vec3(0.0))));
|
||||
float maskLaplacian = -4.0*(1.0-fullColor.a);
|
||||
|
||||
// float maskLaplacian = -4.0 * isInMask;
|
||||
vec4 cola110 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2( 1, 0));
|
||||
vec4 cola101 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2( 0, 1));
|
||||
vec4 cola010 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2(-1, 0));
|
||||
vec4 cola001 = textureLodOffset(tex0, v_texCoord0, 0.0, ivec2( 0,-1));
|
||||
|
||||
vec3 col110 = cola110.rgb; // cola110.a;
|
||||
vec3 col101 = cola101.rgb; // cola101.a;
|
||||
vec3 col010 = cola010.rgb; // cola010.a;
|
||||
vec3 col001 = cola001.rgb; // cola001.a;
|
||||
// maskLaplacian += float(all(equal(col110, vec3(0.0))));
|
||||
// maskLaplacian += float(all(equal(col101, vec3(0.0))));
|
||||
// maskLaplacian += float(all(equal(col010, vec3(0.0))));
|
||||
// maskLaplacian += float(all(equal(col001, vec3(0.0))));
|
||||
|
||||
// maskLaplacian += cola110.a == 1.0 ? 0.0 : 1.0;
|
||||
// maskLaplacian += cola101.a == 1.0 ? 0.0 : 1.0;
|
||||
// maskLaplacian += cola010.a == 1.0 ? 0.0 : 1.0;
|
||||
// maskLaplacian += cola001.a == 1.0 ? 0.0 : 1.0;
|
||||
|
||||
maskLaplacian += (1.0-cola110.a);
|
||||
maskLaplacian += (1.0-cola101.a);
|
||||
maskLaplacian += (1.0-cola010.a);
|
||||
maskLaplacian += (1.0-cola001.a);
|
||||
|
||||
if(maskLaplacian > 0.0){
|
||||
o_output.rgb = fullColor.rgb;///fullColor.a;;
|
||||
o_output.a = fullColor.a;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-combine.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
|
||||
uniform sampler2D tex0; // result of pyramid convolution
|
||||
uniform sampler2D tex1; // input image
|
||||
|
||||
out vec4 o_output;
|
||||
|
||||
/** Composite the initial image and the filled image in the regions where the initial image is black. */
|
||||
void main(){
|
||||
|
||||
vec4 inputColor = textureLod(tex1, v_texCoord0, 0.0);
|
||||
float mask = 1.0 - inputColor.a;
|
||||
|
||||
vec4 fillColor = textureLod(tex0, v_texCoord0, 0.0);
|
||||
fillColor.rgb /= fillColor.a;
|
||||
|
||||
o_output.rgb = fillColor.rgb * (mask) + inputColor.rgb; //mix(inputColor.rgb, fillColor.rgb, mask);
|
||||
o_output.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/filter.frag
|
||||
|
||||
#version 330
|
||||
|
||||
//layout(binding = 0) uniform sampler2D screenTexture; ///< Level to filter.
|
||||
|
||||
uniform sampler2D tex0;
|
||||
in vec2 v_texCoord0;
|
||||
out vec4 o_output;
|
||||
|
||||
uniform float g[3]; ///< g filter parameters.
|
||||
|
||||
/** Denotes if a pixel falls outside an image.
|
||||
\param pos the pixel position
|
||||
\param size the image size
|
||||
\return true if the pixel is outside of the image
|
||||
*/
|
||||
bool isOutside(ivec2 pos, ivec2 size){
|
||||
return (pos.x < 0 || pos.y < 0 || pos.x >= size.x || pos.y >= size.y);
|
||||
}
|
||||
|
||||
/** Apply the g filter to the input data. */
|
||||
void main(){
|
||||
vec4 accum = vec4(0.0);
|
||||
ivec2 size = textureSize(tex0, 0).xy;
|
||||
|
||||
ivec2 coords = ivec2(v_texCoord0 * size);
|
||||
|
||||
for(int dy = -1; dy <=1; dy++){
|
||||
for(int dx = -1; dx <=1; dx++){
|
||||
|
||||
ivec2 newPix = coords + ivec2(dx,dy);
|
||||
|
||||
if(isOutside(newPix, size)){
|
||||
continue;
|
||||
}
|
||||
accum += g[dx+1] * g[dy+1] * texelFetch(tex0, newPix,0 );
|
||||
}
|
||||
}
|
||||
o_output = accum;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// adapted from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/laplacian.frag
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0;
|
||||
out vec4 o_output;
|
||||
|
||||
/** Denotes if UV coordinates falls outside an image.
|
||||
\param pos the UV coordinates
|
||||
\return true if the UV are outside of the image
|
||||
*/
|
||||
bool isOutside(vec2 pos){
|
||||
return (pos.x < 0.0 || pos.y < 0.0 || pos.x > 1.0 || pos.y > 1.0);
|
||||
}
|
||||
|
||||
/** Compute the Laplacian field of an input RGB image, adding a 1px black border around it before computing the gradients and divergence. */
|
||||
void main(){
|
||||
vec3 div = vec3(0.0);
|
||||
ivec2 size = textureSize(tex0, 0).xy;
|
||||
|
||||
vec3 pixelShift = vec3(0.0);
|
||||
pixelShift.xy = 1.0/vec2(size);
|
||||
|
||||
vec2 uvs = v_texCoord0;
|
||||
if(!isOutside(uvs)){
|
||||
vec3 col = textureLod(tex0, uvs, 0.0).rgb;
|
||||
div = 4.0 * col;
|
||||
}
|
||||
|
||||
vec2 uvs110 = uvs + pixelShift.xz;
|
||||
if(!isOutside(uvs110)){
|
||||
vec3 col110 = textureLod(tex0, uvs110, 0.0).rgb;
|
||||
div -= col110;
|
||||
}
|
||||
vec2 uvs101 = uvs + pixelShift.zy;
|
||||
if(!isOutside(uvs101)){
|
||||
vec3 col101 = textureLod(tex0, uvs101, 0.0).rgb;
|
||||
div -= col101;
|
||||
}
|
||||
vec2 uvs010 = uvs - pixelShift.xz;
|
||||
if(!isOutside(uvs010)){
|
||||
vec3 col010 = textureLod(tex0, uvs010, 0.0).rgb;
|
||||
div -= col010;
|
||||
}
|
||||
vec2 uvs001 = uvs - pixelShift.zy;
|
||||
if(!isOutside(uvs001)){
|
||||
vec3 col001 = textureLod(tex0, uvs001, 0.0).rgb;
|
||||
div -= col001;
|
||||
}
|
||||
o_output.rgb = div;
|
||||
o_output.a = 1.0f;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0;
|
||||
|
||||
out vec4 o_output;
|
||||
|
||||
/** Denotes if a pixel falls outside an image.
|
||||
\param pos the pixel position
|
||||
\param size the image size
|
||||
\return true if the pixel is outside of the image
|
||||
*/
|
||||
|
||||
/** Output an image translated by a fixed number of pixels on each axis. useful for padding when rendering in a larger framebuffer. */
|
||||
void main(){
|
||||
vec4 c = texture(tex0, v_texCoord0);
|
||||
|
||||
o_output.rgb = c.rgb;
|
||||
o_output.a = 1.0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/fill-boundary.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
uniform sampler2D tex0;
|
||||
|
||||
out vec4 o_output;
|
||||
|
||||
void main(){
|
||||
o_output = vec4(0.0);
|
||||
vec4 fullColor = textureLod(tex0, v_texCoord0, 0.0);
|
||||
|
||||
if (fullColor.a == 1.0) {
|
||||
o_output = fullColor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// from https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/convolution-pyramid/upscale.frag
|
||||
|
||||
#version 330
|
||||
|
||||
in vec2 v_texCoord0;
|
||||
|
||||
uniform sampler2D tex0; ///< Current h1 filtered level.
|
||||
uniform sampler2D tex1; ///< Previous h1+g filtered level.
|
||||
|
||||
out vec4 o_output; ///< Color.
|
||||
|
||||
uniform float h1[5]; ///< h1 filter parameters.
|
||||
uniform float h2; ///< h2 scaling parameter.
|
||||
uniform float g[3]; ///< g filter parameters.
|
||||
|
||||
/** Denotes if a pixel falls outside an image.
|
||||
\param pos the pixel position
|
||||
\param size the image size
|
||||
\return true if the pixel is outside of the image
|
||||
*/
|
||||
bool isOutside(ivec2 pos, ivec2 size){
|
||||
return (pos.x < 0 || pos.y < 0 || pos.x >= size.x || pos.y >= size.y);
|
||||
}
|
||||
|
||||
/** Combine previous level filtered with h2 (applying a 0-filled upscaling) and the current level filtered with g.
|
||||
*/
|
||||
void main(){
|
||||
vec4 accum = vec4(0.0);
|
||||
ivec2 size = textureSize(tex0, 0).xy;
|
||||
ivec2 coords = ivec2(v_texCoord0 * size);
|
||||
|
||||
for(int dy = -1; dy <=1; dy++){
|
||||
for(int dx = -1; dx <=1; dx++){
|
||||
ivec2 newPix = coords+ivec2(dx,dy);
|
||||
if(isOutside(newPix, size)){
|
||||
continue;
|
||||
}
|
||||
accum += g[dx+1] * g[dy+1] * texelFetch(tex0, newPix,0);
|
||||
}
|
||||
}
|
||||
|
||||
ivec2 sizeSmall = textureSize(tex1, 0).xy;
|
||||
|
||||
for(int dy = -2; dy <=2; dy++){
|
||||
for(int dx = -2; dx <=2; dx++){
|
||||
ivec2 newPix = coords+ivec2(dx,dy);
|
||||
// The filter is applied to a texture upscaled by inserting zeros.
|
||||
if(newPix.x%2 != 0 || newPix.y%2 != 0){
|
||||
continue;
|
||||
}
|
||||
newPix /= 2;
|
||||
newPix += 5;
|
||||
if(isOutside(newPix, sizeSmall)){
|
||||
accum = vec4(0.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
accum += h2 * h1[dx+2] * h1[dy+2] * texelFetch(tex1, newPix, 0);
|
||||
}
|
||||
}
|
||||
o_output = accum;
|
||||
}
|
||||
Reference in New Issue
Block a user