Share my select spatial toon(outline) effect filter shader

hey,monkeys! I’m a beginer with shader.
The toon width is stable px:)
private void showOutlineEffect(Spatial model, int width, ColorRGBA color)
private void hideOutlineEffect(Spatial model)

Outline effect


Outline Pro effect (seem like Left 4 Dead 2)

here is the test code.
https://onedrive.live.com/redir?resid=9459CACD52147299!165&authkey=!ADJkm6qoKrWhq_w&ithint=file%2Crar

15 Likes

Cool, but why not github? Oh well did it myself :stuck_out_tongue:

5 Likes

Thanks for reply! My Internet speed at GitHub is not good…:cry:

@dingyf0523

Thank you very much, I tried it with my scene to highlight elements to pick

it works great

2 Likes

I fixed a bug. When viewport background is not black, outline is not visible.I will commit to github later.

3 Likes

Nice work:-)
Could you post the fix for the bug when viewport background color is not black?

Ok so my fix is to add:

outlineViewport.setClearFlags(true, false, false);
outlineViewport.setBackgroundColor(new ColorRGBA(0f, 0f, 0f, 0f));

You can use ColorRGBA.BlackNoAlpha.:grinning:

1 Like

This is nice.

I wonder, would there be a way to make it only draw an outline around the portion of the mesh that’s visible, that is, not covered by a different mesh in front of it?

This effect often works really well; however, most of the time when I use it in my projects, I get the dreaded “Scene graph is not properly updated for rendering” error. I can sometimes fix this by adding a few extra updateModelBound() and updateGeometricState() lines, but this is very hit-and-miss. Does anyone else have this problem or know of a fix? (BTW, I’m not using any kind of threads).

It’s impossible to say. The code you speak of is a post process filter. I can’t fathom a reason it would use threading either, but you should never call updateGeometricState manually. UpdateModelBound should bubble up and call it anyway, but at the appropriate time. I think it sets dirty flags or something? Can’t remember the last time I went through all of that area…

Just from this thread, it looks like it’s using its own viewport for some strange reason? If so then it sounds like maybe the viewport isn’t having its updateGeometricState() and updateLogicalState() done at the right times.

Thanks for the replies. From my limited knowledge, it looks like it’s using its own overlayed Viewport to draw the outline effect. Where/how should I call updateGeometricState() and updateLogicalState() on the new Viewport?

I don’t have time to look at the code so I don’t know if they are managing it with an app state or not… but generally, I guess do both updates in app state render. Essentially, the later the better as long as it comes before JME renders the scene. (AppState.render is called before JME renders the scene.)

Excuse Me, I have encountered a problem that when I setNumSamples for the FilterPostProcessor, the screen turned black. Could you help me?
Here is my code:

public class SelectObjectTest extends SimpleApplication {

  public static void main(String[] args) {
    SelectObjectTest app = new SelectObjectTest();
    app.setShowSettings(false);
    app.start();
  }
  private Node shootables;
  private Geometry mark;
  SelectObjectOutliner outliner;
  @Override
  public void simpleInitApp() {

    flyCam.setMoveSpeed(10);

    initCrossHairs(); // a "+" in the middle of the screen to help aiming
    initKeys();       // load custom key mappings
    initMark();       // a red sphere to mark the hit

    /** create four colored boxes and a floor to shoot at: */
    shootables = new Node("Shootables");
    rootNode.attachChild(shootables);
    shootables.attachChild(makeCube("a Dragon", -2f, 0f, 1f));
    shootables.attachChild(makeCube("a tin can", 1f, -2f, 0f));
    shootables.attachChild(makeCube("the Sheriff", 0f, 1f, -2f));
    shootables.attachChild(makeCube("the Deputy", 1f, 0f, -4f));
    shootables.attachChild(makeBall("Shandy", 3f, -1f, 2f));
    shootables.attachChild(makeFloor());
 
    
    // shader
    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
    viewPort.addProcessor(fpp);
    fpp.setNumSamples(16);
    //Declare
    outliner=new SelectObjectOutliner();
    //Init - using filter
    outliner.initOutliner(SelectObjectOutliner.OUTLINER_TYPE_FILTER, 2, ColorRGBA.Yellow,shootables,fpp, renderManager, assetManager, cam);
     //Init - using material
    //outliner.initOutliner(SelectObjectOutliner.OUTLINER_TYPE_MATERIAL, 2, ColorRGBA.Magenta,shootables,fpp, renderManager, assetManager, cam);
                 
             
  }

  /** Declaring the "Shoot" action and mapping to its triggers. */
  private void initKeys() {
    inputManager.addMapping("Shoot",
      new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar
      new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // trigger 2: left-button click
    inputManager.addListener(actionListener, "Shoot");
  }
  /** Defining the "Shoot" action: Determine what was hit and how to respond. */
  private ActionListener actionListener = new ActionListener() {

    public void onAction(String name, boolean keyPressed, float tpf) {
      if (name.equals("Shoot") && !keyPressed) {
        // 1. Reset results list.
        CollisionResults results = new CollisionResults();
        // 2. Aim the ray from cam loc to cam direction.
        Ray ray = new Ray(cam.getLocation(), cam.getDirection());
        // 3. Collect intersections between Ray and Shootables in results list.
        // DO NOT check collision with the root node, or else ALL collisions will hit the
        // skybox! Always make a separate node for objects you want to collide with.
        shootables.collideWith(ray, results);
        // 4. Print the results
        System.out.println("----- Collisions? " + results.size() + "-----");
        for (int i = 0; i < results.size(); i++) {
          // For each hit, we know distance, impact point, name of geometry.
          float dist = results.getCollision(i).getDistance();
          Vector3f pt = results.getCollision(i).getContactPoint();
          String hit = results.getCollision(i).getGeometry().getName();
          System.out.println("* Collision #" + i);
          System.out.println("  You shot " + hit + " at " + pt + ", " + dist + " wu away.");
        }
        // 5. Use the results (we mark the hit object)
        if (results.size() > 0) {
          // The closest collision point is what was truly hit:
          CollisionResult closest = results.getClosestCollision();
          // Let's interact - we mark the hit with a red dot.
          mark.setLocalTranslation(closest.getContactPoint());
          rootNode.attachChild(mark);
          //select 
          Geometry nextGeo=closest.getGeometry();
          if(selectedGeo==null || selectedGeo!=nextGeo)
            {
               if(selectedGeo!=null )
                  outliner.deselect(selectedGeo); 
               selectedGeo=nextGeo;
               outliner.select(selectedGeo);
            }
        } else {
          // No hits? Then remove the red mark.
          rootNode.detachChild(mark);
          //
          if(selectedGeo!=null)
            outliner.deselect(selectedGeo); 
          selectedGeo=null;
        }
      }
    }
  };

Gonna revive this topic anyway :smiley: I have following errors when trying the filter:

WARNING: Bad compile of:
1 #version 150 core
2 #define SRGB 1
3 #define FRAGMENT_SHADER 1
4 varying vec2 texCoord;
5
6 uniform sampler2D m_Texture;
7 uniform sampler2D m_NormalsTexture;
8 uniform sampler2D m_DepthTexture;
9
10 void main(){
11 vec4 color = texture2D(m_Texture, texCoord);
12 gl_FragColor=color;
13 }

Dec 16, 2023 8:34:24 PM com.jme3.app.LegacyApplication handleError
SEVERE: Uncaught exception thrown in Thread[#38,jME3 Main,5,main]
com.jme3.renderer.RendererException: compile error in: ShaderSource[name=Shaders/Outline/OutlinePre.frag, defines, type=Fragment, language=GLSL150]
0(4) : error C7555: ‘varying’ is deprecated, use ‘in/out’ instead
0(12) : error C7533: global variable gl_FragColor is deprecated after version 120

at com.jme3.renderer.opengl.GLRenderer.updateShaderSourceData(GLRenderer.java:1581)
at com.jme3.renderer.opengl.GLRenderer.updateShaderData(GLRenderer.java:1608)
at com.jme3.renderer.opengl.GLRenderer.setShader(GLRenderer.java:1673)
at com.jme3.material.logic.DefaultTechniqueDefLogic.render(DefaultTechniqueDefLogic.java:96)
at com.jme3.material.Technique.render(Technique.java:167)
at com.jme3.material.Material.render(Material.java:1052)
at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:682)
at com.jme3.post.FilterPostProcessor.renderProcessing(FilterPostProcessor.java:233)
at com.jme3.post.FilterPostProcessor.renderFilterChain(FilterPostProcessor.java:319)
at com.jme3.post.FilterPostProcessor.postFrame(FilterPostProcessor.java:340)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1233)
at com.jme3.renderer.RenderManager.render(RenderManager.java:1277)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:278)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:160)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:225)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:242)
at java.base/java.lang.Thread.run(Thread.java:1589)

try this one:

https://library.jmonkeyengine.org/#!entry=24022%2F5246c9ac-3f4c-4a5d-9fb0-470eb4026246

I’m actually viewing it. I haven’t tried but the source code looks identical…even some Chinese characters in the comment! :slight_smile: