More on cartoon shading...

Hi,

I’m sorry for making so many questions lately, but now to different questions have arisen:

a) Is there any way to get “multi-pass” ramp-lighting working, to get different tonalities of shadows and not just dark and light?

b) Is there any way to get the ink shader to take distance into account, and therefore shade with stronger lines nearer geometry and thiner ones the far ones?

May I need to use a different shader implementation appart from the ones in the Common Filters? Can you suggest a free one?

Thanks a lot :slight_smile:

Sounds like you need to either commission a shader from someone or teach yourself the theory and make one on your own. The effects you describe are definitely doable in shaders, but even just mix-n-matching “off-the-shelf” code chunks probably won’t give quite the effect you want unless you know what the shader code is actually doing.

I was wondering wether the shaders distributed with panda could actually do that, but I see that the answer is probably not :stuck_out_tongue: Then I will stick to the current behaviour of panda’s cartoon shading and maybe later go into writing my own shaders.

Thanks!

Actually, I have seen here: panda3d.org/apiref.php?page= … eThreshold
that my question (a) can actually be solved inside Panda3d. However, I have tried almost every possible combination of values to makeDoubleThreshold without success, it always has the same effect as a makeSingleThreshold() --it just uses t0, l0 and discards t1, l1 :frowning:

Hm, make sure you have called setShaderAuto() on the model.

I’m currently trying the same thing and don’t see any effect as well

import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.filter.CommonFilters import CommonFilters

base.cam.setY(-25)
base.cam.setZ(5)

filters = CommonFilters(base.win, base.cam)
filters.setCartoonInk(separation=1)

character = loader.loadModel('bunny')
character.reparentTo(render)
character.setAttrib(LightRampAttrib.makeDoubleThreshold(0.4, 0.4, 0.7, 0.4))
character.setShaderAuto()

dlightnode = DirectionalLight('dlight')
dlight = render.attachNewNode(dlightnode)
dlight.setPos(6,-15,10)
dlight.lookAt(character)
render.setLight(dlight)

alightnode = AmbientLight("ambient light")
alightnode.setColor(Vec4(0.8,0.8,0.8,1))
alight = render.attachNewNode(alightnode)
render.setLight(alight)

run()

If i understand, I solved a similar problem in 1.6.2 with writing custom shaders from panda samples.

with outlines

maybe it will be useful, although the code is very dirty

Well thanks. The only problem I see with using custom shaders is it rewrites anything applied with the shader generator, but thats with all shaders, nothing to do with you.
Other than that I see these:


Notice that the model is still kinda phong shaded.

As for the actual question: is this a bug then? I will like to know whether or not to report this as a bug.

I’ve read this thread a few times but I fail to see the actual question or bug. Could you maybe explain it again?

This is what (at least me and ninth) mean:

discourse.panda3d.org/viewtopic … 5789#55789

Yes, I did not notice it. Replace the “l_smooth” with the “0.6” value in the last lines of the “shadowmap.sha”
Should get
o_color = baseColor*(falloff * shade * 0.6 + k_ambient.x)l_brite;
instead
o_color = baseColor
(falloff * shade * l_smooth + k_ambient.x)*l_brite;

I know, I replied to that topic telling how to dump it.
But I’m afraid just gluing the shaders together isn’t going to solve every problem

Will try

Yes, I needed to change something
And a postprocess filters generally imposed separately on the 2D plane, containing the final image.
Also, my version of the implementation of all this is fairly ugly )

For someone who is interested, turns out you CAN have 2 shades, one from specularity. Simply have specularity and change the Hardness of the material to 1. I think this should be mentioned in the manual.

So… does anyone know what doubleThreshold does then?

First, sorry for resurrecting an old thread, but I think I might have figured out what is going on.

I was trying to use light ramping with two thresholds, but it wouldn’t work. After going through the examples (especially Tut-Cartoon-Basic.py), trying makeDoubleThreshold() and some Googling, I ended up here. Using Panda3D 1.8.1, I get the exact same behaviour as reported by the original poster: the double threshold mode works just like the single threshold mode (the parameters t1 and l1 are ignored).

Looking at the source code, I think this is actually a bug in panda/src/pgraph/lightRampAttrib.cxx and panda/src/pgraphnodes/shaderGenerator.cxx.

In lightRampAttrib.cxx of Panda3D 1.8.1, the function LightRampAttrib::make_double_threshold() says

CPT(RenderAttrib) LightRampAttrib::
make_double_threshold(PN_stdfloat thresh0, PN_stdfloat val0, PN_stdfloat thresh1, PN_stdfloat val1) {
  LightRampAttrib *attrib = new LightRampAttrib();
  attrib->_mode = LRT_single_threshold;
  attrib->_threshold[0] = thresh0;
  attrib->_level[0] = val0;
  attrib->_threshold[1] = thresh1;
  attrib->_level[1] = val1;
  return return_new(attrib);
}

Note that the attrib mode is set as LRT_singlethreshold. However, for double threshold mode, shaderGenerator.cxx expects LRTdouble_threshold. This is why the parameters for the second threshold are getting ignored.

Additionally, reading through ShaderGenerator::synthesize_shader(), there is a related problem. The shader generator actually expects three levels when an LRT_double_threshold light ramp is active (quoting from shaderGenerator.cxx):

      case LightRampAttrib::LRT_double_threshold:
        {
          PN_stdfloat t0 = light_ramp->get_threshold(0);
          PN_stdfloat t1 = light_ramp->get_threshold(1);
          PN_stdfloat l0 = light_ramp->get_level(0);
          PN_stdfloat l1 = light_ramp->get_level(1);
          PN_stdfloat l2 = light_ramp->get_level(2);
          text << "\t // Double-threshold light ramp\n";
          text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
          text << "\t float lr_out = " << l0 << "\n";
          text << "\t if (lr_in > " << t0 << ") lr_out=" << l1 << ";\n";
          text << "\t if (lr_in > " << t1 << ") lr_out=" << l2 << ";\n";
          text << "\t tot_diffuse = tot_diffuse * (lr_out / lr_in);\n";
          break;
        }

However, LightRampAttrib only has two levels available (according to lightRampAttrib.h and lightRampAttrib.I - see the array allocations and the functions LightRampAttrib::get_level(), LightRampAttrib::get_threshold()). Retrieving the level for an out-of-range index returns 0.0 (see LightRampAttrib::get_level() in lightRampAttrib.I). Hence, with the code quoted here, the light level will drop to black when the input is above t1.

To fix this, I propose the following, adapting the approach of the single threshold mode (where the “low” value is always 0.0):

      case LightRampAttrib::LRT_double_threshold:
        {
          PN_stdfloat t0 = light_ramp->get_threshold(0);
          PN_stdfloat t1 = light_ramp->get_threshold(1);
          PN_stdfloat l0 = light_ramp->get_level(0);
          PN_stdfloat l1 = light_ramp->get_level(1);
          text << "\t // Double-threshold light ramp\n";
          text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
          text << "\t float lr_out = 0.0\n";
          text << "\t if (lr_in > " << t0 << ") lr_out=" << l0 << ";\n";
          text << "\t if (lr_in > " << t1 << ") lr_out=" << l1 << ";\n";
          text << "\t tot_diffuse = tot_diffuse * (lr_out / lr_in);\n";
          break;
        }

The only changes are in the levels: 0.0 for less than t0, l0 for between t0 and t1, and l1 for greater than t1. This version matches the Python API documentation for makeDoubleThreshold() ( http://www.panda3d.org/reference/1.8.1/python/classpanda3d.core.LightRampAttrib.php ), and is consistent with the behaviour of makeSingleThreshold().

Finally, a practical question: should I file a bug about this somewhere?

Please do file this as a bug, thanks for looking into this!

Done.

https://bugs.launchpad.net/panda3d/+bug/1214782