New Surfaces Materials?
-
On 17/04/2013 at 16:12, xxxxxxxx wrote:
You will need to implement InitRender, for (at least) two reasons. If you need to access any data from the shader interface, do it here and store the results - otherwise you could be accessing the parameters hundreds of times whenever a render occurs. Also, if you have any shader links in your shader you absolutely must call their InitRender functions, and you do that here.
To get started, here's the complete listing of a simple shader I wrote to mirror-image a bitmap. It's very simple and you won't need the resource to understand it. It just lets the user choose to mirror the bitmap horizontally and/or vertically, and the only other interface element is a link for the bitmap:
// BMFlip // bmflip.cpp // includes #include "c4d.h" #include "bmflip.h" #include "xbmflip.h" #include "c4d_symbols.h" SHADERINFO BitmapFlip::GetRenderInfo(BaseShader *sh) { return SHADERINFO_BUMP_SUPPORT; } Bool BitmapFlip::Init(GeListNode *node) { // these are class-level variables shader = NULL; flipX = FALSE; flipY = FALSE; return TRUE; } void BitmapFlip::FreeRender(BaseShader *chn) { if(shader) shader->FreeRender(); shader = NULL; } Bool BitmapFlip::Message(GeListNode *node, LONG type, void *msgdat) { BaseContainer *data; data = ((BaseShader* )node)->GetDataInstance(); HandleInitialChannel(node, BMFLIPSHADER_TEXTURE, type, msgdat); HandleShaderMessage(node, (BaseShader* )data->GetLink(BMFLIPSHADER_TEXTURE, node->GetDocument(), Xbase), type, msgdat); return TRUE; } INITRENDERRESULT BitmapFlip::InitRender(BaseShader *chn, const InitRenderStruct& irs) { BaseContainer *data; INITRENDERRESULT result; data = chn->GetDataInstance(); // get gadget values flipX = data->GetBool(BMFLIPSHADER_FLIPX); flipY = data->GetBool(BMFLIPSHADER_FLIPY); shader = (BaseShader* )data->GetLink(BMFLIPSHADER_TEXTURE, irs.doc, Xbase); if(shader) { result = shader->InitRender(irs); return result; } return INITRENDERRESULT_OK; } Vector BitmapFlip::Output(BaseShader *chn, ChannelData *cd) { Vector res, uv; if(!shader) return Vector(0.0, 0.0, 0.0); // return black if no bitmap present uv = cd->p; // get the original UV value so we can restore it later if(flipX) cd->p.x = 1.0 - cd->p.x; if(flipY) cd->p.y = 1.0 - cd->p.y; res = shader->Sample(cd); // get the bitmap value at this point cd->p = uv; // restore the UVs return res; // return the colour of the bitmap at the mirrored position } // register the plugin Bool RegisterBMFlip(void) { String name; name = "Bitmap Transform"; if(RegisterShaderPlugin(ID_BMFLIP, GeGetDefaultFilename(DEFAULTFILENAME_SHADER_EFFECTS) + String("Bitmap Transform"), 0, BitmapFlip::Alloc,"Xbmflip", 0)) { return TRUE; } else { return FALSE; } }
Hope that helps!
Steve
-
On 17/04/2013 at 16:22, xxxxxxxx wrote:
Originally posted by xxxxxxxx
How to create a colored checkerboard pattern using cd->p?
I haven't tried it but I imagine that for a four-square checkerboard Output() would look something like this:
Vector MyShader::Output(BaseShader *chn, ChannelData *cd) { Vector col; // cd.p.x and cd.p.y range from 0 to 1 if((cd.p.x < 0.5 && cd.p.y < 0.5) || (cd.p.x >= 0.5 && cd.p.y >= 0.5)) col = Vector(0.0); // return black for top-left and bottom-right squares else col = Vector(1.0); // return white for the other two squares return col; }
No idea if it works! But try it and see.
-
On 17/04/2013 at 16:47, xxxxxxxx wrote:
^Yes. It does work.
Thanks Steve.But I'm a bit puzzled why it works.
You aren't using a loop to get the positions of p.
I would have thought that I would have needed to use a loop in this case. To check the position (then set the color) of p.
That's how I normally get the colors of a bitmap for example. looping through each line of pixels.But what you did.
Setting certain sections of the shader to black or white without using a loop. Seems strange to me. And something I'll need to understand. Because I think I'll need to use a similar technique to create other things. Like circles and more complex shapes.-ScottA
-
On 18/04/2013 at 01:00, xxxxxxxx wrote:
Glad to know it works. You don't need a loop because Cinema calls Output() for each point to be rendered, setting cd->p accordingly. So you don't iterate through all the points in the UV data, you just calculate the colour to return at the point passed to Output().
-
On 18/04/2013 at 07:38, xxxxxxxx wrote:
OK. Thanks a lot for the help Steve.
I have enough information now to start looking at formulas and trying to putting something together.
But I'm a long way from controlling gaps and shapes.Once again. I must make this final plea the the Maxon team (or anyone willing).
Could you please provide us with one example of a procedural shader like the pavement shader?
It doesn't matter what the pattern is, or what the colors are.
Something that shows us how to change the gaps, shapes, and colors of the pattern so we can use it as a guide making our own shaders.If I end up figuring it out myself. I'll share what I come up with.
Thanks,
-ScottA -
On 18/04/2013 at 12:29, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Could you please provide us with one example of a procedural shader like the pavement shader?
// A simple turbulence shader Vector ExampleShader::Output(BaseShader *chn, ChannelData *cd) { Vector p; if (cd->vd) p = cd->vd->p; else p = cd->p; return Vector(Turbulence(p, 3.0, TRUE)); } // Using the turbulence value to output either red or green Vector ExampleShader::Output(BaseShader *chn, ChannelData *cd) { Vector p; if (cd->vd) p = cd->vd->p; else p = cd->p; Real threshold = 0.5; // Introduce a threshold to decide on the output color if (Turbulence(p, 3.0, TRUE) > threshold) return Vector(1.0, 0.0, 0.0); else return Vector(0.0, 1.0, 0.0); } // Or: Displacing the turbulence with a noise Vector ExampleShader::Output(BaseShader *chn, ChannelData *cd) { Vector p; if (cd->vd) p = cd->vd->p; else p = cd->p; Real displace_scale = 0.5; // Introduce a scale for the displacing noise Real displace_intensity = 0.75; // Introduce a value to control the intensity of the displacement effect return Vector(Turbulence(p + Noise(p * displace_scale) * displace_intensity, 3.0, TRUE)); } // Or: a simple 2D checkerboard (without MIP mapping, rather ugly) Vector ExampleShader::Output(BaseShader *chn, ChannelData *cd) { Vector p = cd->p; p *= 0.2; // Introduce a scale to control the size of the squares p.x -= (LONG)Floor(p.x); p.y -= (LONG)Floor(p.y); if ((p.x > 0.5) != (p.y > 0.5)) return Vector(1.0); else return Vector(0.0); }
Of course, all the control values I have introduced in the Output() function would normally be retrieved from the shader's BaseContainer in InitRender() and then stored in private class members.
You see, writing a shader is very easy. But it can also get very complex, depending on the pattern you want.
Algorithms for common patterns can be easily found on the web. For example, here are some old Renderman shaders from the 80s
You can open the .sl files in a text editor.http://www.cs.rit.edu/~mrp9521/cg/renderman.html
http://www.renderman.org/RMR/Shaders/BMRTShaders/Cheers,
Frank -
On 18/04/2013 at 12:51, xxxxxxxx wrote:
Thanks Frank,
I think the thing I'm going to have the toughest time figuring out is how to control gap size between shapes. That's probably what I'll really need to see an example of the most.
Also.
After setting up my plugin and getting the basic stuff in place. I came across another issue.
If I use a vector type as a color. I get lots really horrible noises all over the object when I render it.
But If I use an LVector with a value of 125 like the mandelbrot SDK example uses. It fixes that problem.
Example:#define CCOUNT 125 Vector *colors; colors = GeAllocType(Vector,CCOUNT);
I'm wondering whats exactly going on with that?
I'm guessing that it maybe has something to do with the amount of bits a standard vector holds?
I'm also wondering what the value 125 means. And what happens if I use a higher of lower number?-ScottA
-
On 18/04/2013 at 23:45, xxxxxxxx wrote:
I don't know what your shader code looks like, and why you would need a Vector array with 125 elements. The Mandelbrot shader needs an array because it calculates a fixed set of colors in InitRender() that it later chooses its result color from in Output(). But that's a special thing and you probably won't need that in your shader.
Vector is the correct type for a single color value, a Vector array would store lots of colors.
I can only imagine you're screwing your results up by doing some stuff in the Output() that might behave strange in a multi-threaded context. Remember, Output() is called for each (sub-)pixel that each thread renders.
And about the gaps, it really would just be a very simple addition to the checkerboard code I have posted. You already know where the seams between the squares are, so you could just introduce a value for gap width, add or subtract it from the position (depending on if you are closer to a lower or a higher border of a square) and fill it with another color.
It is all very basic stuff, you just have to read up on it
Did you have a look e.g. at the Renderman brick shader on the second page I linked to in my previous posting? It shows everything you're just asking for, and it's even commented (well, a bit).
-
On 19/04/2013 at 08:58, xxxxxxxx wrote:
I have only had a chance to briefly look at those Renderman shaders.
I had to get my basic shader plugin working first. But I'll take a close look at them today.I'm a complete newb at this shader stuff. So I copied a lot of the code in the Mandelbrot example to get me started.
I think I might have gotten that noisy result because I used my color vector as a pointer.
Today I removed that color pointer code from my code. And just created a standard color vector. And now it seems to work fine without using an LVector.Your 2D checkerboard example doesn't work for me for some reason. The shader is all black.
The other examples you posted work fine. But for some reason that one doesn't. And I'm trying to figure out why it isn't working correctly.-ScottA
*Edit- I found where the noise problem is coming from.
If I declare the vector (pointer or not) as a class member variable. Then use that vector in my Output() method. I get horrendous noise when rendering.
But if I declare the vector inside of the Output() method. It renders fine.
That seems pretty strange to me... -
On 20/04/2013 at 00:22, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Your 2D checkerboard example doesn't work for me for some reason. The shader is all black.The other examples you posted work fine. But for some reason that one doesn't. And I'm trying to figure out why it isn't working correctly.
I wrote that out of my memory and didn't test the code. There might be a bug in it.
Originally posted by xxxxxxxx
If I declare the vector (pointer or not) as a class member variable. Then use that vector in my Output() method. I get horrendous noise when rendering.But if I declare the vector inside of the Output() method. It renders fine.That seems pretty strange to me...
The difference is that the Vector pointer in the Mandelbrot example is an array of vectors (see definition of GeAllocType() in the SDK docs).
However, the reason for the noise is exactly the one I have already suspected and pointed out: Your are storing a color as a class member variable, and then all your render threads write to it and read from it simultaneuosly. That can't work, because the threads will overwrite each other's results.
If you really want to store your result color in the class you will have to store it separately for each thread. But there's no reason for it, really. The only reason the Mandelbrot shader does it, is because it's calculating the colors before rendering; and then simply chooses (reads) from the colors during rendering process.
-
On 20/04/2013 at 00:35, xxxxxxxx wrote:
By the way, the checkerboard code works fine, here's a test render.
I simply took the code and pasted it into the Output function of the mandelbrot shader.Of course, it's ugly because it does not yet take the MIP delta into account, but it's exactly what I have promised
_<_img src="http://www.c4d-jack.de/darkside/checkerboard_test.jpg" border="0" /_>_
The reason why the 2D shader preview in the Material Editor is black is: The shader preview is sampled from the UV space (0;0 to 1;1). Since we used a scale value of 0.2 and multiply p with it, the tiles ((p.x > 0.5) == (p.y > 0.5) creates black) are as big as 5 times the single UV space (0;0 to 5;5). Hence, the first black tile covers the entire preview area.
Use a value of e.g. 2.0 instead and you'll see.
-
On 20/04/2013 at 09:05, xxxxxxxx wrote:
Thanks Frank,
This is helping me a lot.-
Q1 : How would I go about adding MIP delta to these shaders to make them look better?
-
Q2 : This is an example I made using the VolumeData method to create a checkerboard shader.
But the problem is. It's in world coords. So the pattern doesn't stick to the object when it moves.
How do I write this kind of VolumeData shader so that sticks to the object?
Vector colors; if(cd->vd && enabled) //"enabled" and "offset" are options in my code to control the pattern with gizmos { Real r,s,t; Bool rs = cd->vd->GetRS(cd->vd->lhit,cd->vd->p,&r,&s); //Use volume data to do the rendering Vector pos = cd->vd->p; if(pos.x < offset && pos.y > offset || pos.x > offset && pos.y < offset) colors = Vector(0,0,0); else colors = Vector(1,1,1); } return colors;
-ScottA
-
-
On 22/04/2013 at 15:05, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Q1 : How would I go about adding MIP delta to these shaders to make them look better?
Check the cd->d values and use them to drive your level of detail. The smaller the values, the greater your detail should be. The exact value range depends on your shader and how you design it. Clamp the value into shape
Especially for the checkerboard, you should read something about MIP mapping. You have to come up with a clever idea to blur the checkerboard. The greater the d values, the stronger the blur.
With Noise-based shaders, I usually just reduce the noise octaves with increasing d values.Originally posted by xxxxxxxx
Q2 : This is an example I made using the VolumeData method to create a checkerboard shader. But the problem is. It's in world coords. So the pattern doesn't stick to the object when it moves. How do I write this kind of VolumeData shader so that sticks to the object?
You can use the inverse texture matrix cd->tex->im and multiply cd->vd->p with it. That should transform it into local space. Actually, the local texture space (controlled by the coordinates in the Texture Tag), which is what you want to use in Cinema.
-
On 23/04/2013 at 08:55, xxxxxxxx wrote:
I can't figure out how to use cd->d to do that.
I also can't find anything resembling cd->tex->im in the SDK.
I understand the theory about inverting a matrix to change global to local. I do that fairly often with mesh points.
But I don't see how to write that code for VolumeData textures in the SDK.-ScottA
-
On 23/04/2013 at 09:29, xxxxxxxx wrote:
Originally posted by xxxxxxxx
I can't figure out how to use cd->d to do that.
for the check board shader example you could blend the result color with the
mixed vector of colA and colB with an increasing mip radius. distant points
would become gray for a black / white check board which somewhat reflects
the natural human perception of high contrast patterns.i would like to bring up again Modelling and Texturing : A Procedural Approach.
a really good book, which is worth the money. all topics covered in this thread
are described in the book (procedural patterns, various noise types / fractal
noises, anti-aliasing, mip-mapping and much more). -
On 23/04/2013 at 10:35, xxxxxxxx wrote:
I appreciate the help.
But please don't point me towards anymore Renderman related resources. They aren't helping me.
I can't use these various Renderman resources until I know how to use the C4D SDK code first.
That book does discuss general theory. But it uses Renderman code to explain them. And the Renderman SDK code is very different from the C4D SDK.
Sure. I can convert some of the things like: Renderman: colors == C4D: Vector
But there's far, far too many Renderman specific things (like noises and proprietary methods) these books and websites use that I'm having trouble converting to the C4D SDK.
I do have some Renderman tutorials. Which show me how to do things like sticking a volumetric shader by parenting Maya shader nodes. But C4D doesn't work the same way.At this point in time. I need to see only C4D SDK code to understand how this stuff works.
I know the basic principles Between Renderman & C4D are the same. But the code is just too different for me to make the connection yet.So please guys. I really do thank you for the help.
But these kinds of things do not help me at all:
-Renderman references.
-Broad generalizations like: Mix this color with that color vector. And then invert it's matrix.If you can't provide C4D specific code. Please don't waste your time trying to explain it to me in a lecture form. Because I won't understand how to write it. And you'll be wasting your time on me.
I don't want you guys to waste your valuable time on me.I'm sure I will be able to use those resources later on to do more advanced things.
But first I need to see the C4D code on how to do these things. Not generalized theories.
Not only to understand the general theory. But to see how to write the actual C4D SDK code.
This is why I share so many plugins with the source code. So people like me who can't understand the SDK can see a working example of how to write the code.
It really does make all the difference in the world to see the actual code used in examples.Looking at the large view count for this thread. I'm guessing that this is something that many people are also interested in. And have lots of questions about how to write the C4D code like me.
-ScottA
-
On 23/04/2013 at 11:40, xxxxxxxx wrote:
Originally posted by xxxxxxxx
I also can't find anything resembling cd->tex->im in the SDK.
Oh, sorry. It's cd->vd->tex->im.
Originally posted by xxxxxxxx
I understand the theory about inverting a matrix to change global to local. I do that fairly often with mesh points.But I don't see how to write that code for VolumeData textures in the SDK.
Just do exactly as I said: Multiply your cd->vd->p with the inverted matrix. It doesn't matter if its the position of a mesh vertex or of a ray hit, the math is the same.
-
On 23/04/2013 at 15:39, xxxxxxxx wrote:
Thanks Frank.
That's got it sticking to the object now.Here's the code I have for people lurking:
Vector MyShader::Output(BaseShader *chn, ChannelData *cd) { Vector colors; if(cd->vd) { Real r,s,t; Bool rs = cd->vd->GetRS(cd->vd->lhit,cd->vd->p,&r,&s); //Use volume data to do the rendering Vector pos = cd->vd->p; //Get the the texture positions Vector localPos = pos * cd->vd->tex->im; //Invert the texture matrix so the texture sticks to the object when it's moved localPos.x *= 2; //The number of times to tile the shader localPos.y *= 2; localPos.x -= (LONG)Floor(localPos.x); localPos.y -= (LONG)Floor(localPos.y); if(localPos.x < offset && localPos.y > offset || localPos.x > offset && localPos.y < offset) colors = Vector(0,0,0); else colors = Vector(1,1,1); } return colors; }
-ScottA
-
On 24/04/2013 at 01:07, xxxxxxxx wrote:
Good work! You see, it's not that difficult, once you actually dive into it.
Sorry about all those Renderman examples, but those are really the most suitable examples. It's like learning the Blues scale before becoming a metal guitarist. It makes you understand the basics
Your code can still be optimized. This is how I would do it:
Vector MyShader::Output(BaseShader *chn, ChannelData *cd) { // hard-coded parameters (ideally, you get these from the BaseContainer in InitRender() and store them in private class members) const Real tileX = 2.0; const Real tileY = 2.0; const Real offset = 0.4; const Vector col1 = Vector(0.0); const Vector col2 = Vector(1.0); // Declare (but don't construct) sample position Vector pos(DC); if(cd->vd) { // Sample from 3D space pos = cd->vd->p * cd->vd->tex->im; // Get the sample position } else { // Sample from UV space pos = cd->p; // Get the sample position } // The number of times to tile the shader pos.x *= tileX; pos.y *= tileY; // Get local coordinates inside tile pos.x -= (LONG)Floor(pos.x); pos.y -= (LONG)Floor(pos.y); // Decide color if(pos.x < offset != pos.y < offset) return col1; else return col2; }
Changes from your code:
-
Hard-coded parameters: I moved all relevant parameters to variables to make the code more readable.
-
cd- >vd->p vs. cd->p: the shader can now sample both, 2D and 3D space. That way, the 2D shader preview will work. Depending on if cd->vd is NULL or not, only the way how I retrieve 'pos' is changed. The rest of the code is the same for both cases.
-
Vector pos(DC) : I declare the variable, but (via the DC parameter) I don't construct it yet. Wouldn't make sense, as I copy values into it later, anyway.
-
Where is 'Vector colors'? It's gone, you don't need it. Instead of copying the resulting color into 'colors' and then returning 'colors' at the end of the function, I simply return the resulting color immediately.
-
Where are 'Real r, s, t'? Also gone, you don't need those. You didn't even use them in your code.
-
Color decision: I changed that back to the shorter form. It's less comparison work, and you can achieve exactly the same with it.
-
offset: You used the 'LONG offset' from the Mandelbrot example to compare it to the local tile coordinates (which are Reals between 0.0 and 1.0). Does not make much sense. It's now a const Real variable, along with the other parameters. Using a value of 0.5 will create even tiles, other values will change the look.
Now some general notes...
So please guys. I really do thank you for the help. But these kinds of things do not help me at all:
-Renderman references.
-Broad generalizations like: Mix this color with that color vector. And then invert it's matrix.If you can't provide C4D specific code. Please don't waste your time trying to explain it to me in a lecture form. Because I won't understand how to write it.
The thing is, you will never find for exactly your problem. At least the probability is very low. And if you limit your search to C4D specific code, you decrease your changes even more. You need to learn the methodology of all this: Finding *any* example code that is easy to understand (and it won't get any easier than those 80s Renderman examples), use it to learn the maths behind it (because is is always the same), look up the functions you don't know, and then write your own shader code as a Cinema plugin. That is how we all learned it.
And if someone tells you, as you say, to "Mix this color with that color vector" or "And then invert it's matrix", then those things are not at all broad generalizations, but precise descriptions of what you have to do. If you don't know how to mix two vectors, then search for it on the web, or take a look into the SDK documentation. It's just math, there's nothing application specific to it. But you can't expect people to write your shader for you.
Long story short: You have to do research yourself. If you don't know the maths, and don't know the functions you need, and you are not willing to use Google, read code from other systems, use the documentation and generally suck up knowledge like a sponge, you won't get very far.
I know the whole topic of shader programming is not the easiest to grasp. It is complex, and it involves quite some background knowledge that you have to look up and learn. But you must have the patience to do it.
Cheers,
Frank -
-
On 24/04/2013 at 07:43, xxxxxxxx wrote:
Thanks again Frank,
I just put hard values in my example because it's a lot shorter to post a working example that way.
In a normal project I wouldn't hard them like that.
But thanks for pointing it out. Because that brings up another SDK method I'm having trouble with.You removed GetRS() from my example. And I'm still not sure exactly how that method works.
It's not explained properly enough in the SDK for me to understand how it works.This is what the SDK says:
Bool GetRS(const `RayHitID`[URL-REMOVED]& hitid, const `LVector`[URL-REMOVED]& p, Real* r, Real* s) -Calculate the R/S parameters for a point _<_h5_>_ _Parameters_ <_h5_>_/h5> \> _const`RayHitID`[URL-REMOVED]& hitid_ \> \>> The global `RayHitID`[URL-REMOVED]. \> \> _const`LVector`[URL-REMOVED]& p_ \> \>> The point. \> \> _Real* r_ \> \>> The returned R parameter. The caller owns the pointed value. \> \> _Real* s_ \> \>> The returned S parameter. The caller owns the pointed value.
>>
>> Based on my prior experiences with GeData. I think what this method does is do some kind of ray sending. And then stores the some data found from the ray into the variable r &s?
> But what the heck is r&s?!Confused
[URL-REMOVED]
> The SDK never says what theses data values are. And what they can be used for.
>
> This is why I need to stay talking in C4D SDK terms only as much as possible. Because in addtion to being brand new at shaders theory. I also don't understand many of the methods Maxon is providing to us to use to make them. Once I have the SDK decoded, those Renderman examples will probably come in handy.
>
> -ScottA
>
[URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.