The past few days I’ve been working on the design of the classes required to make the user defined shaders feature possible. Today, I managed to write the c3dl.Effect and c3d.InstanceEffect classes. I have not posted up running demo, but here is a screenshot of the results so far:
Not too exciting since the shaders are fairly simple, but it demonstrates what will be possible with this feature. In this example, I created three teapots and assigned two of them instance effects. Since the middle teapot wasn’t assigned an effect, it is rendered using the standard shaders, or ones which closely match the fixed function pipeline.
In the next few days, I’ll post a much more exciting and interactive example.
Last week it was decided that even though allowing users to create their own shaders is nice, users should not be required to write some of the more ‘common’ shaders. Some shaders will therefore be provided by the library as ‘templates’. I listed here a few which might be useful, and therefore which the library will provide:
- Standard shader (currently implemented)
- Fragment-based lighting shader
- Cel shader
- Sepia shader
- Black and white shader
- Gooch shader
- Scanline Shader
- Heat signature shader
- Fur shader
- Hatch shader
- Brick shader
// The library would be responsible for creating the effect, // the user only needs to create an instance of the effect. var goochShader = new c3dl.InstanceEffect(c3dl.GoochEffect); goochShader.setParam("coolColor", [0,0,1]); goochShader.setParam("warmColor", [0.5, 0.5, 0]); var teapot = new c3dl.Collada(); teapot.init("teapot.dae"); teapot.setEffect(goochShader);The user here wants to use blue for the cool (far) color and yellow for warm (close). They do not need to actually specify the parameters since default ones can be used.
The problem is how does the user know what the parameters are for a Gooch shader? For the user to properly make use of the effect, they would need to know:
- Name of the effect
- What it does / what instance is it useful
- Parameter names, types, default values and constraints
Here is another issue I found while working on the user defined shader feature.
Let’s say a user wants two teapot objects rendered in one scene. The user wants them both rendered using a cartoon shader, but wants each to use a different quantization map. Given the current design, the user would have to create two instances of an effect and assign each one a callback function which differs only by one line. In one callback, a hard coded “blueQMap.jpg” is sent to the shader and the other one would send “redQMap.jpg”. The number of lines required to set all the uniform and attribute variables tends to be a lot, especially if the user wants the teapots rendered twice, once for the silhouette edge and another for the actual object. It would be unfair to ask the user to duplicate their callback function just to change one line of code, therefore another approach must be taken.
A new requirement for creating effects includes allowing the user to ‘parameterize’ their effects.
Since it’s likely this scenario would repeat for other shaders, it is important enough to implement. The user must somehow be able to tell the library to send their callback one set of arguments for one situation and a different set of arguments for other situations. When those arguments are passed into their callback, they can assign them to the shader uniform variables. This would alleviate the need for two near-duplicate callback functions.
var cartoonEffect = new c3dl.Effect(); cartoonEffect.setShaders(cel_vs, cel_fs); cartoonEffect.setRenderingCallback(cel_rc); // The first parameter of addParam is the name of the parameter // The second is the default param to be used. cartoonEffect.addParam("shades", "shades.jpg"); cartoonEffect.addParam ("isOutlined", true); // serves to prevent the object from being modified once it is created. cartoonEffect.init(); // Now the user can make as many types of cartoon effects they want. var blueCartoonEffect = new c3dl.InstanceEffect(); blueCartoonEffect.init(cartoonEffect); blueCartoonEffect.setParam("shades", "blueShades.jpg"); // did not set "isOutline" param, so default value is used. var c = new c3dl.Collada(); c.init("plane.dae"); c.setEffect(blueCartoonEffect);The parameters will exist within the instance effect as key/value pairs and will be sent to the user’s callback on render. The user can then extract the values and assign them to whatever uniforms need to be set. The next problem I will address is how to allow users to assign pre-defined shaders to objects they create. Implementation of a Gooch shader:
I wrote a Gooch shader to see if any problems would arise if a user decided to write one and use it in the new feature. I didn’t run into any major problems, but I decided to post up the demo anyway. Actually, I did notice the outlining may be a concern, but I’ll save that for another blog.