[orx-dnk3] Add segmentContourRenderer
This commit is contained in:
@@ -14,7 +14,7 @@ buildscript {
|
||||
apply plugin: 'org.jetbrains.dokka'
|
||||
|
||||
project.ext {
|
||||
openrndrVersion = "0.3.43-rc.11"
|
||||
openrndrVersion = "0.3.43-rc.12"
|
||||
kotlinVersion = "1.3.72"
|
||||
spekVersion = "2.0.10"
|
||||
libfreenectVersion = "0.5.7-1.5.3"
|
||||
|
||||
49
orx-dnk3/src/demo/kotlin/DemoSegmentContours01.kt
Normal file
49
orx-dnk3/src/demo/kotlin/DemoSegmentContours01.kt
Normal file
@@ -0,0 +1,49 @@
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.BufferMultisample
|
||||
import org.openrndr.extensions.SingleScreenshot
|
||||
import org.openrndr.extra.dnk3.*
|
||||
import org.openrndr.extra.dnk3.gltf.buildSceneNodes
|
||||
import org.openrndr.extra.dnk3.gltf.loadGltfFromFile
|
||||
import org.openrndr.extra.dnk3.renderers.segmentContourRenderer
|
||||
import org.openrndr.extras.camera.Orbital
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.mod_
|
||||
import java.io.File
|
||||
|
||||
fun main() = application {
|
||||
configure {
|
||||
width = 1280
|
||||
height = 720
|
||||
//multisample = WindowMultisample.SampleCount(8)
|
||||
}
|
||||
|
||||
program {
|
||||
if (System.getProperty("takeScreenshot") == "true") {
|
||||
extend(SingleScreenshot()) {
|
||||
this.outputFile = System.getProperty("screenshotPath")
|
||||
}
|
||||
}
|
||||
|
||||
val gltf = loadGltfFromFile(File("demo-data/gltf-models/fox/Fox.glb"))
|
||||
val scene = Scene(SceneNode())
|
||||
|
||||
val sceneData = gltf.buildSceneNodes()
|
||||
scene.root.children.addAll(sceneData.scenes.first())
|
||||
|
||||
// -- create a renderer, try it with BufferMultisample.SampleCount(8) for better results
|
||||
val renderer = segmentContourRenderer(BufferMultisample.Disabled)
|
||||
extend(Orbital()) {
|
||||
far = 500.0
|
||||
lookAt = Vector3(0.0, 40.0, 0.0)
|
||||
eye = Vector3(150.0, 40.0, 200.0)
|
||||
fov = 40.0
|
||||
}
|
||||
|
||||
extend {
|
||||
sceneData.animations[2].applyToTargets(seconds.mod_(sceneData.animations[2].duration))
|
||||
drawer.clear(ColorRGBa.PINK)
|
||||
renderer.draw(drawer, scene)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,18 +15,26 @@ enum class FacetType(val shaderFacet: String) {
|
||||
EMISSIVE("f_emission"),
|
||||
AMBIENT("f_ambient"),
|
||||
OCCLUSION("f_occlusion"),
|
||||
FRAGMENT_ID("f_fragmentID"),
|
||||
COLOR("m_color"),
|
||||
}
|
||||
|
||||
abstract class FacetCombiner(val facets: Set<FacetType>, val targetOutput: String) {
|
||||
abstract fun generateShader(): String
|
||||
override fun toString(): String {
|
||||
return "FacetCombiner(facets=$facets, targetOutput='$targetOutput')"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
abstract class ColorBufferFacetCombiner(facets: Set<FacetType>,
|
||||
targetOutput: String,
|
||||
val format: ColorFormat,
|
||||
val type: ColorType,
|
||||
val blendMode: BlendMode = BlendMode.BLEND) : FacetCombiner(facets, targetOutput)
|
||||
val blendMode: BlendMode = BlendMode.BLEND) : FacetCombiner(facets, targetOutput) {
|
||||
|
||||
}
|
||||
|
||||
class MomentsFacet : ColorBufferFacetCombiner(setOf(FacetType.WORLD_POSITION), "moments", ColorFormat.RG, ColorType.FLOAT16) {
|
||||
override fun generateShader(): String {
|
||||
@@ -111,6 +119,12 @@ class ClipPositionFacet : ColorBufferFacetCombiner(setOf(FacetType.CLIP_POSITION
|
||||
override fun generateShader() = "o_$targetOutput.rgb = gl_FragCoord.xyz;"
|
||||
}
|
||||
|
||||
class FragmentIDFacet: ColorBufferFacetCombiner(setOf(FacetType.FRAGMENT_ID), "fragmentID", ColorFormat.R, ColorType.UINT16_INT) {
|
||||
override fun generateShader(): String {
|
||||
return "o_$targetOutput = f_fragmentID;"
|
||||
}
|
||||
}
|
||||
|
||||
class LDRColorFacet : ColorBufferFacetCombiner(setOf(FacetType.DIFFUSE, FacetType.SPECULAR), "color", ColorFormat.RGBa, ColorType.UINT8) {
|
||||
override fun generateShader() = """
|
||||
vec3 finalColor = (max(vec3(0.0), f_diffuse.rgb) + max(vec3(0.0),f_specular.rgb) + max(vec3(0.0), f_emission.rgb)) * (1.0 - f_fog.a) + f_fog.rgb * f_fog.a;
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.openrndr.draw.shadeStyle
|
||||
interface Material {
|
||||
var doubleSided: Boolean
|
||||
var transparent: Boolean
|
||||
val fragmentID: Int
|
||||
fun generateShadeStyle(context: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle
|
||||
fun applyToShadeStyle(context: MaterialContext, shadeStyle: ShadeStyle)
|
||||
}
|
||||
@@ -15,13 +16,19 @@ interface Material {
|
||||
class DummyMaterial : Material {
|
||||
override var doubleSided: Boolean = true
|
||||
override var transparent: Boolean = false
|
||||
|
||||
override var fragmentID = 0
|
||||
|
||||
override fun generateShadeStyle(context: MaterialContext, primitiveContext: PrimitiveContext): ShadeStyle {
|
||||
return shadeStyle {
|
||||
fragmentPreamble = """
|
||||
int f_fragmentID = p_fragmentID;
|
||||
""".trimIndent()
|
||||
|
||||
fragmentTransform = """
|
||||
x_fill.rgb = vec3(normalize(v_viewNormal).z);
|
||||
""".trimIndent()
|
||||
|
||||
parameter("fragmentID", fragmentID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,11 +264,18 @@ class Texture(var source: TextureSource,
|
||||
}
|
||||
}
|
||||
|
||||
private var fragmentIDCounter = 1
|
||||
|
||||
class PBRMaterial : Material {
|
||||
override fun toString(): String {
|
||||
return "PBRMaterial(textures: $textures, color: $color, metalness: $metalness, roughness: $roughness, emissive: $emission))"
|
||||
}
|
||||
|
||||
override var fragmentID = fragmentIDCounter.apply {
|
||||
fragmentIDCounter++
|
||||
|
||||
}
|
||||
|
||||
override var doubleSided: Boolean = false
|
||||
override var transparent: Boolean = false
|
||||
var environmentMap = false
|
||||
@@ -277,6 +284,7 @@ class PBRMaterial : Material {
|
||||
var roughness = 1.0
|
||||
var emission = ColorRGBa.BLACK
|
||||
|
||||
var fragmentPreamble: String? = null
|
||||
var vertexPreamble: String? = null
|
||||
var vertexTransform: String? = null
|
||||
var parameters = mutableMapOf<String, Any>()
|
||||
@@ -304,6 +312,7 @@ class PBRMaterial : Material {
|
||||
val needLight = needLight(materialContext)
|
||||
val preambleFS = """
|
||||
vec4 m_color = p_color;
|
||||
uint f_fragmentID = uint(p_fragmentID);
|
||||
float m_f0 = 0.5;
|
||||
float m_roughness = p_roughness;
|
||||
float m_metalness = p_metalness;
|
||||
@@ -415,11 +424,12 @@ class PBRMaterial : Material {
|
||||
val vs = (this@PBRMaterial.vertexTransform ?: "") + textureVS + skinVS
|
||||
|
||||
shadeStyle {
|
||||
fragmentPreamble = this@PBRMaterial.fragmentPreamble ?: ""
|
||||
vertexPreamble = """
|
||||
$shaderNoRepetitionVert
|
||||
${(this@PBRMaterial.vertexPreamble) ?: ""}
|
||||
""".trimIndent()
|
||||
fragmentPreamble = """
|
||||
fragmentPreamble += """
|
||||
|$shaderLinePlaneIntersect
|
||||
|$shaderProjectOnPlane
|
||||
|$shaderSideOfPlane
|
||||
@@ -431,10 +441,14 @@ class PBRMaterial : Material {
|
||||
this.vertexTransform = vs
|
||||
fragmentTransform = fs
|
||||
materialContext.pass.combiners.map {
|
||||
if (rt.colorBuffers.size <= 1) {
|
||||
this.output(it.targetOutput, 0)
|
||||
} else
|
||||
this.output(it.targetOutput, rt.colorBufferIndex(it.targetOutput))
|
||||
if (rt is ProgramRenderTarget) {
|
||||
this.output(it.targetOutput, ShadeStyleOutput(0))
|
||||
} else {
|
||||
val index = rt.colorBufferIndex(it.targetOutput)
|
||||
val type = rt.colorBuffer(index).type
|
||||
val format = rt.colorBuffer(0).format
|
||||
this.output(it.targetOutput, ShadeStyleOutput(index, format, type))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,6 +467,7 @@ class PBRMaterial : Material {
|
||||
shadeStyle.parameter("color", color)
|
||||
shadeStyle.parameter("metalness", metalness)
|
||||
shadeStyle.parameter("roughness", roughness)
|
||||
shadeStyle.parameter("fragmentID", fragmentID)
|
||||
|
||||
parameters.forEach { (k, v) ->
|
||||
when (v) {
|
||||
|
||||
@@ -5,12 +5,12 @@ import org.openrndr.draw.DepthFormat
|
||||
import org.openrndr.draw.RenderTarget
|
||||
import org.openrndr.draw.renderTarget
|
||||
|
||||
class RenderPass(val combiners: List<FacetCombiner>,
|
||||
data class RenderPass(val combiners: List<FacetCombiner>,
|
||||
val renderOpaque: Boolean = true,
|
||||
val renderTransparent: Boolean = false,
|
||||
val depthWrite: Boolean = true
|
||||
val depthWrite: Boolean = true,
|
||||
val multisample: BufferMultisample = BufferMultisample.Disabled)
|
||||
|
||||
)
|
||||
|
||||
val DefaultPass = RenderPass(listOf(LDRColorFacet()))
|
||||
val DefaultOpaquePass = RenderPass(listOf(LDRColorFacet()), renderOpaque = true, renderTransparent = false)
|
||||
@@ -18,7 +18,7 @@ val DefaultTransparentPass = RenderPass(listOf(LDRColorFacet()), renderOpaque =
|
||||
val LightPass = RenderPass(emptyList())
|
||||
val VSMLightPass = RenderPass(listOf(MomentsFacet()))
|
||||
|
||||
fun RenderPass.createPassTarget(width: Int, height: Int, depthFormat: DepthFormat = DepthFormat.DEPTH24, multisample: BufferMultisample = BufferMultisample.Disabled): RenderTarget {
|
||||
fun RenderPass.createPassTarget(width: Int, height: Int, depthFormat: DepthFormat = DepthFormat.DEPTH24, multisample: BufferMultisample = this.multisample): RenderTarget {
|
||||
return renderTarget(width, height, multisample = multisample) {
|
||||
for (combiner in combiners) {
|
||||
when (combiner) {
|
||||
|
||||
@@ -110,7 +110,7 @@ class SceneRenderer {
|
||||
if (pass == outputPasses[0]) {
|
||||
outputPassTarget?.let {
|
||||
drawer.withTarget(it) {
|
||||
clear(ColorRGBa.PINK)
|
||||
clear(ColorRGBa.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,18 +136,8 @@ class SceneRenderer {
|
||||
val postContext = PostContext(lightContext, drawer.view.inversed)
|
||||
|
||||
for (postStep in postSteps) {
|
||||
// if (postStep is FilterPostStep) {
|
||||
// if (postStep.filter is Ssao) {
|
||||
// postStep.filter.projection = drawer.projection
|
||||
// }
|
||||
// if (postStep.filter is Sslr) {
|
||||
// val p = Matrix44.scale(drawer.width / 2.0, drawer.height / 2.0, 1.0) * Matrix44.translate(Vector3(1.0, 1.0, 0.0)) * drawer.projection
|
||||
// postStep.filter.projection = p
|
||||
// }
|
||||
// }
|
||||
postStep.apply(buffers, postContext)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
drawer.popStyle()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.openrndr.extra.dnk3
|
||||
|
||||
val shaderNoRepetition = """
|
||||
// -- shaderNoRepetition
|
||||
float sum( vec3 v ) { return v.x+v.y+v.z; }
|
||||
|
||||
// based on https://www.shadertoy.com/view/Xtl3zf
|
||||
@@ -35,7 +36,7 @@ vec4 textureNoTile(in sampler2D noiseTex, in sampler2D tex, in vec2 noiseOffset,
|
||||
"""
|
||||
|
||||
val shaderNoRepetitionVert = """
|
||||
// shaderNoRepetitionVert
|
||||
// -- shaderNoRepetitionVert
|
||||
float sum( vec3 v ) { return v.x+v.y+v.z; }
|
||||
|
||||
// based on https://www.shadertoy.com/view/Xtl3zf
|
||||
@@ -67,7 +68,7 @@ vec4 textureNoTile(in sampler2D tex, in vec2 noiseOffset, in vec2 x)
|
||||
"""
|
||||
|
||||
val shaderProjectOnPlane = """
|
||||
// shaderProjectOnPlane
|
||||
// -- shaderProjectOnPlane
|
||||
vec3 projectOnPlane(vec3 p, vec3 pc, vec3 pn) {
|
||||
float distance = dot(pn, p-pc);
|
||||
return p - distance * pn;
|
||||
@@ -75,18 +76,21 @@ vec3 projectOnPlane(vec3 p, vec3 pc, vec3 pn) {
|
||||
""".trimIndent()
|
||||
|
||||
val shaderSideOfPlane = """
|
||||
// -- shaderSideOfPlane
|
||||
int sideOfPlane(in vec3 p, in vec3 pc, in vec3 pn){
|
||||
if (dot(p-pc,pn) >= 0.0) return 1; else return 0;
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val shaderLinePlaneIntersect = """
|
||||
// -- shaderLinePlaneIntersect
|
||||
vec3 linePlaneIntersect(in vec3 lp, in vec3 lv, in vec3 pc, in vec3 pn){
|
||||
return lp+lv*(dot(pn,pc-lp)/dot(pn,lv));
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val shaderVSM = """
|
||||
|// -- shaderVSM
|
||||
|float linstep(float min, float max, float v)
|
||||
|{
|
||||
| return clamp((v - min) / (max - min), 0, 1);
|
||||
@@ -112,6 +116,7 @@ V - eye - world vertex position
|
||||
L - world light pos - world vertex position
|
||||
*/
|
||||
val shaderGGX = """
|
||||
// -- shaderGGX
|
||||
#define bias 0.125
|
||||
#define HASHSCALE 443.8975
|
||||
vec2 hash22(vec2 p) {
|
||||
|
||||
8
orx-dnk3/src/main/kotlin/post/SegmentContours.kt
Normal file
8
orx-dnk3/src/main/kotlin/post/SegmentContours.kt
Normal file
@@ -0,0 +1,8 @@
|
||||
package org.openrndr.extra.dnk3.post
|
||||
|
||||
import org.openrndr.draw.Filter
|
||||
import org.openrndr.draw.filterShaderFromUrl
|
||||
import org.openrndr.resourceUrl
|
||||
|
||||
class SegmentContoursMSAA8 : Filter(filterShaderFromUrl(resourceUrl("/shaders/segment-contours-msaa-8.frag")))
|
||||
class SegmentContours : Filter(filterShaderFromUrl(resourceUrl("/shaders/segment-contours.frag")))
|
||||
34
orx-dnk3/src/main/kotlin/renderers/SegmentContourRenderer.kt
Normal file
34
orx-dnk3/src/main/kotlin/renderers/SegmentContourRenderer.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package org.openrndr.extra.dnk3.renderers
|
||||
|
||||
import org.openrndr.draw.BufferMultisample
|
||||
import org.openrndr.draw.ColorFormat
|
||||
import org.openrndr.draw.ColorType
|
||||
import org.openrndr.extra.dnk3.*
|
||||
import org.openrndr.extra.dnk3.post.SegmentContours
|
||||
import org.openrndr.extra.dnk3.post.SegmentContoursMSAA8
|
||||
|
||||
fun segmentContourRenderer(multisample: BufferMultisample = BufferMultisample.Disabled): SceneRenderer {
|
||||
val sr = SceneRenderer()
|
||||
sr.outputPasses.clear()
|
||||
sr.outputPasses.add(
|
||||
RenderPass(
|
||||
listOf(FragmentIDFacet()),
|
||||
multisample = multisample
|
||||
)
|
||||
)
|
||||
sr.postSteps.add(
|
||||
FilterPostStep(1.0,
|
||||
when (multisample) {
|
||||
BufferMultisample.Disabled -> SegmentContours()
|
||||
BufferMultisample.SampleCount(8) -> SegmentContoursMSAA8()
|
||||
else -> error("unsupported multisampling mode $multisample")
|
||||
},
|
||||
listOf("fragmentID"),
|
||||
"segments",
|
||||
ColorFormat.RGB,
|
||||
ColorType.UINT8
|
||||
)
|
||||
)
|
||||
sr.drawFinalBuffer = true
|
||||
return sr
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#version 330
|
||||
|
||||
uniform usampler2DMS tex0;
|
||||
in vec2 v_texCoord0;
|
||||
out vec4 o_output;
|
||||
|
||||
void main() {
|
||||
ivec2 ts = textureSize(tex0);
|
||||
ivec2 pixel = ivec2(v_texCoord0 * ts);
|
||||
|
||||
ivec2 c = pixel;
|
||||
ivec2 n = c + ivec2(0, -1);
|
||||
ivec2 s = c + ivec2(0, 1);
|
||||
ivec2 w = c + ivec2(-1, 0);
|
||||
ivec2 e = c + ivec2(1, 0);
|
||||
|
||||
|
||||
float sf = 0.0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
float f = 1.0;
|
||||
uint sc = texelFetch(tex0, c, i).r;
|
||||
uint sn = texelFetch(tex0, n, i).r;
|
||||
uint ss = texelFetch(tex0, s, i).r;
|
||||
uint se = texelFetch(tex0, e, i).r;
|
||||
uint sw = texelFetch(tex0, w, i).r;
|
||||
|
||||
if (sc == se) f -= 0.25;
|
||||
if (sc == sw) f -= 0.25;
|
||||
if (sc == sn) f -= 0.25;
|
||||
if (sc == ss) f -= 0.25;
|
||||
sf+= f;
|
||||
}
|
||||
o_output = vec4(vec3(sf/4.0), 1.0);
|
||||
}
|
||||
33
orx-dnk3/src/main/resources/shaders/segment-contours.frag
Normal file
33
orx-dnk3/src/main/resources/shaders/segment-contours.frag
Normal file
@@ -0,0 +1,33 @@
|
||||
#version 330
|
||||
|
||||
uniform usampler2D tex0;
|
||||
in vec2 v_texCoord0;
|
||||
out vec4 o_output;
|
||||
|
||||
void main() {
|
||||
ivec2 ts = textureSize(tex0, 0);
|
||||
ivec2 pixel = ivec2(v_texCoord0 * ts);
|
||||
|
||||
ivec2 c = pixel;
|
||||
ivec2 n = c + ivec2(0, -1);
|
||||
ivec2 s = c + ivec2(0, 1);
|
||||
ivec2 w = c + ivec2(-1, 0);
|
||||
ivec2 e = c + ivec2(1, 0);
|
||||
|
||||
float sf = 0.0;
|
||||
for (int i = 0; i < 1; ++i) {
|
||||
float f = 1.0;
|
||||
uint sc = texelFetch(tex0, c, i).r;
|
||||
uint sn = texelFetch(tex0, n, i).r;
|
||||
uint ss = texelFetch(tex0, s, i).r;
|
||||
uint se = texelFetch(tex0, e, i).r;
|
||||
uint sw = texelFetch(tex0, w, i).r;
|
||||
|
||||
if (sc == se) f -= 0.25;
|
||||
if (sc == sw) f -= 0.25;
|
||||
if (sc == sn) f -= 0.25;
|
||||
if (sc == ss) f -= 0.25;
|
||||
sf+= f;
|
||||
}
|
||||
o_output = vec4(vec3(sf/0.5), 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user