From 1ef585e34e9c10461d04dfbfbe9d1b614d9c42c3 Mon Sep 17 00:00:00 2001 From: "remy.bouquet@gmail.com" Date: Wed, 16 Mar 2011 17:12:45 +0000 Subject: [PATCH] Added CrossHatchFilter contribution by Roy Straver a.k.a. Baal Garnaal git-svn-id: http://jmonkeyengine.googlecode.com/svn/trunk@7000 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../core-data/Common/MatDefs/Post/CrossHatch.frag | 51 ++++ .../core-data/Common/MatDefs/Post/CrossHatch.j3md | 41 +++ .../Common/MatDefs/Post/CrossHatch15.frag | 53 ++++ .../com/jme3/post/filters/CrossHatchFilter.java | 296 +++++++++++++++++++++ engine/src/test/jme3test/post/TestCrossHatch.java | 156 +++++++++++ 5 files changed, 597 insertions(+) create mode 100644 engine/src/core-data/Common/MatDefs/Post/CrossHatch.frag create mode 100644 engine/src/core-data/Common/MatDefs/Post/CrossHatch.j3md create mode 100644 engine/src/core-data/Common/MatDefs/Post/CrossHatch15.frag create mode 100644 engine/src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java create mode 100644 engine/src/test/jme3test/post/TestCrossHatch.java diff --git a/engine/src/core-data/Common/MatDefs/Post/CrossHatch.frag b/engine/src/core-data/Common/MatDefs/Post/CrossHatch.frag new file mode 100644 index 000000000..d76b4cc4b --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Post/CrossHatch.frag @@ -0,0 +1,51 @@ +uniform sampler2D m_Texture; +varying vec2 texCoord; + +uniform vec4 m_LineColor; +uniform vec4 m_PaperColor; +uniform float m_ColorInfluenceLine; +uniform float m_ColorInfluencePaper; + +uniform float m_FillValue; +uniform float m_Luminance1; +uniform float m_Luminance2; +uniform float m_Luminance3; +uniform float m_Luminance4; +uniform float m_Luminance5; + +uniform int m_LineDistance; +uniform int m_LineThickness; + +void main() { + vec4 texVal = texture2D(m_Texture, texCoord); + float linePixel = 0; + + float lum = texVal.r*0.2126 + texVal.g*0.7152 + texVal.b*0.0722; + + if (lum < m_Luminance1){ + if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance2){ + if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance3){ + if (mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance4){ + if (mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance5){ // No line, make a blob instead + linePixel = m_FillValue; + } + + // Mix line color with existing color information + vec4 lineColor = mix(m_LineColor, texVal, m_ColorInfluenceLine); + // Mix paper color with existing color information + vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper); + + gl_FragColor = mix(paperColor, lineColor, linePixel); +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Post/CrossHatch.j3md b/engine/src/core-data/Common/MatDefs/Post/CrossHatch.j3md new file mode 100644 index 000000000..e25782d9a --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Post/CrossHatch.j3md @@ -0,0 +1,41 @@ +MaterialDef CrossHatch { + + MaterialParameters { + Int NumSamples + Texture2D Texture; + Vector4 LineColor; + Vector4 PaperColor; + Float ColorInfluenceLine; + Float ColorInfluencePaper; + Float FillValue; + Float Luminance1; + Float Luminance2; + Float Luminance3; + Float Luminance4; + Float Luminance5; + Int LineThickness; + Int LineDistance; + } + + Technique { + VertexShader GLSL150: Common/MatDefs/Post/Post15.vert + FragmentShader GLSL150: Common/MatDefs/Post/CrossHatch15.frag + + WorldParameters { + WorldViewProjectionMatrix + } + } + + Technique { + VertexShader GLSL100: Common/MatDefs/Post/Post.vert + FragmentShader GLSL100: Common/MatDefs/Post/CrossHatch.frag + + WorldParameters { + WorldViewProjectionMatrix + } + } + + Technique FixedFunc { + } + +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Post/CrossHatch15.frag b/engine/src/core-data/Common/MatDefs/Post/CrossHatch15.frag new file mode 100644 index 000000000..10b24f049 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Post/CrossHatch15.frag @@ -0,0 +1,53 @@ +#import "Common/ShaderLib/MultiSample.glsllib" + +uniform COLORTEXTURE m_Texture; +in vec2 texCoord; + +uniform vec4 m_LineColor; +uniform vec4 m_PaperColor; +uniform float m_ColorInfluenceLine; +uniform float m_ColorInfluencePaper; + +uniform float m_FillValue; +uniform float m_Luminance1; +uniform float m_Luminance2; +uniform float m_Luminance3; +uniform float m_Luminance4; +uniform float m_Luminance5; + +uniform int m_LineDistance; +uniform int m_LineThickness; + +void main() { + vec4 texVal = getColor(m_Texture, texCoord); + float linePixel = 0; + + float lum = texVal.r*0.2126 + texVal.g*0.7152 + texVal.b*0.0722; + + if (lum < m_Luminance1){ + if (mod(gl_FragCoord.x + gl_FragCoord.y, m_LineDistance * 2) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance2){ + if (mod(gl_FragCoord.x - gl_FragCoord.y, m_LineDistance * 2) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance3){ + if (mod(gl_FragCoord.x + gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance4){ + if (mod(gl_FragCoord.x - gl_FragCoord.y - m_LineDistance, m_LineDistance) < m_LineThickness) + linePixel = 1; + } + if (lum < m_Luminance5){ // No line, make a blob instead + linePixel = m_FillValue; + } + + // Mix line color with existing color information + vec4 lineColor = mix(m_LineColor, texVal, m_ColorInfluenceLine); + // Mix paper color with existing color information + vec4 paperColor = mix(m_PaperColor, texVal, m_ColorInfluencePaper); + + gl_FragColor = mix(paperColor, lineColor, linePixel); +} \ No newline at end of file diff --git a/engine/src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java b/engine/src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java new file mode 100644 index 000000000..1ba5d541d --- /dev/null +++ b/engine/src/desktop-fx/com/jme3/post/filters/CrossHatchFilter.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.post.filters; + +import com.jme3.asset.AssetManager; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.post.Filter; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.ViewPort; + +/* + * A Post Processing filter that makes the screen look like it was drawn as + * diagonal lines with a pen. + * Try combining this with a cartoon edge filter to obtain manga style visuals. + * + * Based on an article from Geeks3D: + * http://www.geeks3d.com/20110219/shader-library-crosshatching-glsl-filter/ + * + * @author: Roy Straver a.k.a. Baal Garnaal + */ +public class CrossHatchFilter extends Filter { + + private ColorRGBA lineColor = ColorRGBA.Black.clone(); + private ColorRGBA paperColor = ColorRGBA.White.clone(); + private float colorInfluenceLine = 0.8f; + private float colorInfluencePaper = 0.1f; + private float fillValue = 0.9f; + private float luminance1 = 0.9f; + private float luminance2 = 0.7f; + private float luminance3 = 0.5f; + private float luminance4 = 0.3f; + private float luminance5 = 0.0f; + private int lineThickness = 1; + private int lineDistance = 4; + + public CrossHatchFilter() { + super("CrossHatchFilter"); + } + + public CrossHatchFilter(ColorRGBA lineColor, ColorRGBA paperColor) { + this(); + this.lineColor = lineColor; + this.paperColor = paperColor; + } + + @Override + public boolean isRequiresDepthTexture() { + return false; + } + + @Override + public void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { + material = new Material(manager, "Common/MatDefs/Post/CrossHatch.j3md"); + material.setColor("LineColor", lineColor); + material.setColor("PaperColor", paperColor); + + material.setFloat("ColorInfluenceLine", colorInfluenceLine); + material.setFloat("ColorInfluencePaper", colorInfluencePaper); + + material.setFloat("FillValue", fillValue); + + material.setFloat("Luminance1", luminance1); + material.setFloat("Luminance2", luminance2); + material.setFloat("Luminance3", luminance3); + material.setFloat("Luminance4", luminance4); + material.setFloat("Luminance5", luminance5); + + material.setInt("LineThickness", lineThickness); + material.setInt("LineDistance", lineDistance); + } + + @Override + public Material getMaterial() { + return material; + } + + @Override + public void preRender(RenderManager renderManager, ViewPort viewPort) { + } + + @Override + public void cleanUpFilter(Renderer r) { + } + + /* + * Sets color used to draw lines + */ + public void setLineColor(ColorRGBA lineColor) { + this.lineColor = lineColor; + if (material != null) { + material.setColor("LineColor", lineColor); + } + } + + /* + * Sets color used as background + */ + public void setPaperColor(ColorRGBA paperColor) { + this.paperColor = paperColor; + if (material != null) { + material.setColor("PaperColor", paperColor); + } + } + /* + * Sets color influence of original image on lines drawn + */ + + public void setColorInfluenceLine(float colorInfluenceLine) { + this.colorInfluenceLine = colorInfluenceLine; + if (material != null) { + material.setFloat("ColorInfluenceLine", colorInfluenceLine); + } + } + + /* + * Sets color influence of original image on non-line areas + */ + public void setColorInfluencePaper(float colorInfluencePaper) { + this.colorInfluencePaper = colorInfluencePaper; + if (material != null) { + material.setFloat("ColorInfluencePaper", colorInfluencePaper); + } + } + + /* + * Sets line/paper color ratio for areas with values < luminance5, + * really dark areas get no lines but a filled blob instead + */ + public void setFillValue(float fillValue) { + this.fillValue = fillValue; + if (material != null) { + material.setFloat("FillValue", fillValue); + } + } + + /* + * Sets minimum luminance levels for lines drawn + * Luminance1: Top-left to down right 1 + * Luminance2: Top-right to bottom left 1 + * Luminance3: Top-left to down right 2 + * Luminance4: Top-right to bottom left 2 + * Luminance5: Blobs + */ + public void setLuminanceLevels(float luminance1, float luminance2, float luminance3, float luminance4, float luminance5) { + this.luminance1 = luminance1; + this.luminance2 = luminance2; + this.luminance3 = luminance3; + this.luminance4 = luminance4; + this.luminance5 = luminance5; + + if (material != null) { + material.setFloat("Luminance1", luminance1); + material.setFloat("Luminance2", luminance2); + material.setFloat("Luminance3", luminance3); + material.setFloat("Luminance4", luminance4); + material.setFloat("Luminance5", luminance5); + } + } + + /* + * Sets the thickness of lines drawn + */ + public void setLineThickness(int lineThickness) { + this.lineThickness = lineThickness; + if (material != null) { + material.setInt("LineThickness", lineThickness); + } + } + + /* + * Sets minimum distance between lines drawn + * Primary lines are drawn at 2*lineDistance + * Secondary lines are drawn at lineDistance + */ + public void setLineDistance(int lineDistance) { + this.lineDistance = lineDistance; + if (material != null) { + material.setInt("LineDistance", lineDistance); + } + } + + /* + * Returns line color + */ + public ColorRGBA getLineColor() { + return lineColor; + } + + /* + * Returns paper background color + */ + public ColorRGBA getPaperColor() { + return paperColor; + } + + /* + * Returns current influence of image colors on lines + */ + public float getColorInfluenceLine() { + return colorInfluenceLine; + } + + /* + * Returns current influence of image colors on paper background + */ + public float getColorInfluencePaper() { + return colorInfluencePaper; + } + + /* + * Returns line/paper color ratio for blobs + */ + public float getFillValue() { + return fillValue; + } + + /* + * Returns the thickness of the lines drawn + */ + public int getLineThickness() { + return lineThickness; + } + + /* + * Returns minimum distance between lines + */ + public int getLineDistance() { + return lineDistance; + } + + /* + * Returns treshold for lines 1 + */ + public float getLuminance1() { + return luminance1; + } + + /* + * Returns treshold for lines 2 + */ + public float getLuminance2() { + return luminance2; + } + + /* + * Returns treshold for lines 3 + */ + public float getLuminance3() { + return luminance3; + } + + /* + * Returns treshold for lines 4 + */ + public float getLuminance4() { + return luminance4; + } + + /* + * Returns treshold for blobs + */ + public float getLuminance5() { + return luminance5; + } +} \ No newline at end of file diff --git a/engine/src/test/jme3test/post/TestCrossHatch.java b/engine/src/test/jme3test/post/TestCrossHatch.java new file mode 100644 index 000000000..cc405f95f --- /dev/null +++ b/engine/src/test/jme3test/post/TestCrossHatch.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package jme3test.post; + +import com.jme3.app.SimpleApplication; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.BloomFilter; +import com.jme3.post.filters.CrossHatchFilter; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Spatial; +import com.jme3.scene.debug.WireFrustum; +import com.jme3.scene.shape.Box; +import com.jme3.util.SkyFactory; + +public class TestCrossHatch extends SimpleApplication { + + float angle; + Spatial lightMdl; + Spatial teapot; + Geometry frustumMdl; + WireFrustum frustum; + boolean active=true; + FilterPostProcessor fpp; + + public static void main(String[] args){ + TestCrossHatch app = new TestCrossHatch(); + app.start(); + } + + @Override + public void simpleInitApp() { + // put the camera in a bad position + cam.setLocation(new Vector3f(-2.336393f, 11.91392f, -7.139601f)); + cam.setRotation(new Quaternion(0.23602544f, 0.11321983f, -0.027698677f, 0.96473104f)); + //cam.setFrustumFar(1000); + + + Material mat = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md"); + mat.setFloat("Shininess", 15f); + mat.setBoolean("UseMaterialColors", true); + mat.setColor("Ambient", ColorRGBA.Yellow.mult(0.2f)); + mat.setColor("Diffuse", ColorRGBA.Yellow.mult(0.2f)); + mat.setColor("Specular", ColorRGBA.Yellow.mult(0.8f)); + + + + + Material matSoil = new Material(assetManager,"Common/MatDefs/Light/Lighting.j3md"); + matSoil.setFloat("Shininess", 15f); + matSoil.setBoolean("UseMaterialColors", true); + matSoil.setColor("Ambient", ColorRGBA.Gray); + matSoil.setColor("Diffuse", ColorRGBA.Black); + matSoil.setColor("Specular", ColorRGBA.Gray); + + + + teapot = assetManager.loadModel("Models/Teapot/Teapot.obj"); + teapot.setLocalTranslation(0,0,10); + + teapot.setMaterial(mat); + teapot.setShadowMode(ShadowMode.CastAndReceive); + teapot.setLocalScale(10.0f); + rootNode.attachChild(teapot); + + + + Geometry soil=new Geometry("soil", new Box(new Vector3f(0, -13, 550), 800, 10, 700)); + soil.setMaterial(matSoil); + soil.setShadowMode(ShadowMode.CastAndReceive); + rootNode.attachChild(soil); + + DirectionalLight light=new DirectionalLight(); + light.setDirection(new Vector3f(-1, -1, -1).normalizeLocal()); + light.setColor(ColorRGBA.White.mult(1.5f)); + rootNode.addLight(light); + + // load sky + Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/FullskiesBlueClear03.dds", false); + sky.setCullHint(Spatial.CullHint.Never); + rootNode.attachChild(sky); + + fpp=new FilterPostProcessor(assetManager); + CrossHatchFilter chf=new CrossHatchFilter(); + + + + viewPort.addProcessor(fpp); + fpp.addFilter(chf); + initInputs(); + + } + + private void initInputs() { + inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); + + ActionListener acl = new ActionListener() { + + public void onAction(String name, boolean keyPressed, float tpf) { + if (name.equals("toggle") && keyPressed) { + if(active){ + active=false; + viewPort.removeProcessor(fpp); + }else{ + active=true; + viewPort.addProcessor(fpp); + } + } + } + }; + + inputManager.addListener(acl, "toggle"); + + } + + + +} -- 2.11.0