[orx-dnk3] Add segmentContourRenderer

This commit is contained in:
Edwin Jakobs
2020-06-14 20:53:42 +02:00
parent 057f15dbff
commit 2f26049367
12 changed files with 214 additions and 25 deletions

View 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)
}
}
}

View File

@@ -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;

View File

@@ -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)
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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) {

View 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")))

View 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
}

View File

@@ -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);
}

View 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);
}