Ignore technique with no shader

Hi,

I’m porting my deferred renderer to 3.1.0 (sRGB,…). To avoid render twice geometries, I define empty “default” technique for material that should be diplayed via deferred renderer.
In 3.0.10 I workaround the issue with a custom Material that include the following line :

    override void render(Geometry geom, RenderManager rm) {
        // super.autoSelectTechnique(rm);
        var technique = super.getActiveTechnique()
        if (technique == null) {
            selectTechnique("Default", rm)
            technique = super.getActiveTechnique()
        }
        if ("Default".equals(technique.getDef().getName())) {
            val techDef = technique.getDef()
            if (!techDef.isUsingShaders() || technique.getShader().getId() < 1) return;
        }
        super.render(geom, rm)
    }

With 3.1.0, it no longer works(1), from the stackstrace it seems that it’s a Material.render (and not MaterialCustom.render) that is called. So I’ve got exception (try to compile shader with 0 source file).

If I included the following line in Material.render. It works.

        Shader shader = technique.getShader();
        if (shader.getSources().size() == 0) {
            return;
        }

My questions :

  1. Will you accept the PR that ignore technique without shader (silently) ?
  2. Is it the right way to avoid some geometry to be ignored by default render pipeline ?

(1) I’ve got the issue on Linux and Windows, not on Mac (strange).

Thanks.

An empty default technique was used for fixed function version of the material in 3.0, and in 3.1 fixed function was removed so empty techniques are essentially not allowed.
I cannot understand exactly why you need to do this at all though, since if you’re rendering the geometries manually, can’t you simply clear the render queue after you’re done so there’s nothing to render for the regular rendering pass?

I didn’t clear render queue to allow some material to be renderer by the regular pipeline (or both deferred+regular). I only manage object in Bucket.Opaque.

iirc the parser doesn’t allow material without default technique, so I create empty.
An other workaround was to make a shader that discard, but it’s a waste of CPU/GPU resources.

If I call “RenderQueue.clear()” at end of SceneProcessor.postQueue(RenderQueue rq) then my “particles” light aren’t displayed.

            var Geometry geom=new Geometry("particle",new Quad(size,size)) 
            geom.setLocalTranslation(-0.5f * size, -0.5f * size, 0.0f)
            val lightMaterial= new Material(assetManager,"Common/MatDefs/Misc/UnshadedNodes.j3md") 
            lightMaterial.setColor("Color", color)
            lightMaterial.setTexture("LightMap", assetManager.loadTexture("Textures/particletexture.jpg"))
            lightMaterial.getAdditionalRenderState().setBlendMode(RenderState.BlendMode::Additive)
            lightMaterial.getAdditionalRenderState().setDepthWrite(false)
            geom.setMaterial(lightMaterial)
            geom.setQueueBucket(Bucket::Transparent)

And a SceneProcessor can’t modify RenderQueue (except clear).

IIRC the last time, you (core team) talk about custom Bucket :wink: as an other alternative.

And default technique is always required :

java.lang.IllegalArgumentException: No default techniques are available on material 'gbuffer'
    at com.jme3.material.Material.selectTechnique(Material.java:983)
    at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:553)

Why can’t you clear the opaque render queue only and leave the other ones untouched?

iirc, from a SceneProcessor (or the public api), you can’t clear only one of the queue.
Maybe we can from a RenderManager (I did not used custom RenderManager).

Exposing that functionality to SceneProcessors is probably a better approach, then. Otherwise it seems like putting a hack in the core so that a hack outside the core can work …

You means something like provide accessor to the mutable list of geometries for each bucket. And then allow SceneProcessor to read, modify them freely ?

IMHO, it’s better to ignore material if the requested technique (default or named) doesn’t exist or doesn’t include shader.

No, the capability to clear a render queue bucket. But the ability to access them could also be useful.

If the user selects a technique that doesn’t exist, then it should result in an exception. If it doesn’t include a shader, that could be interpreted different ways … Maybe there should be special directive in the technique that says “Don’t Render” to make it explicit.

I prefer if it silently ignore inexistant technique. But I’m fine with a n explicit directive/attribute in technique, better backward compatibility. I’ll try to implement it.

Any suggestion for the directive in j3md and the attribute name ?

Could be something like this …

MaterialDef Whatever {
     // ...
     Technique { 
         NoRender
     }
}

Honestly, I do to.

Are we talking about Material.selectTechnique(String) doing nothing if it doesn’t exist?

If so, how would it be if this:

SomeClass obj = null;
obj.doStuff();

Never threw an NullPointerException but did nothing instead? Its kind of the same thing

I was talking about Material.render() (see top message).
It’s same as NullObject (in design pattern) or Option/Maybe (in functionnal language) than null.

[+/- OT]

I guess “Default” and named material could be managed the same way:

  • allowing several Technique per name, choose the first one that match current Caps, so store material’s techniques into a Map<String, List> instead of Map<String, Technique>
  • no name => “Default” (already done), and remove Default special code elsewhere
  • same behavior if no matching (name+Caps) technique found (throw exception or do nothing could be a hardcoded or an AppSettings)