Average color of a polygon from texture



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 10/08/2012 at 06:38, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R12-R13 
    Platform:   Windows  ;   Mac OSX  ; 
    Language(s) :     C++  ;

    ---------
    As a way to allow users to use a texture as a control mask on the sequence of unfolding polygons for my Unfurl plugin, I'd like to let them add a Material/Texturetag and sample from a Custom channel data (so as not to affect rendered materials) that is used to influence the sequence.  A couple of issues exist in my mind:

    1. There is very little information about sampling a material channel when not involved in the render process.  It seems to be hit or miss, may not always work (incorrect values returned), and information is vague about handling volumetric shaders and Texturetag alterations (tiling, psr, etc.).  Anybody have code that covers all of this they could share?

    2. Since Unfurl is a polygon-centric plugin, the texture is only relevant at the polygon level (the texture is affecting each polygon discreetly).  That is, it needs the average value/color of the texture bounded by the polygon.  This means, obviously, almost implementing a raytracing algorithm to get all of the pixels of the texture bounded by the polygon and then averaging them to get the value to use. That is quite a tall order for something seemingly simple.  Is there anything in the SDK that might help?  Is there an approximation method that would yield reasonable results instead of the brute-force raytracing scenario?  Would it were that each UVW-mapped polygon was a nice rectangle but this will seldom be the case.

    Addition: Could the MIP sample radius (Vector* delta) for BaseChannel::Sample() be 'misused' to get an average by setting it to the circumscribing radius about the polygon.  Evil, but if it works, I'm game. :)

    Thanks!



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 10/08/2012 at 09:20, xxxxxxxx wrote:

    Hi Robert,

    here is the code I use times ago in my DiTools.
    Not sure if it still work well in current C4D version.
    Please let me know if you find some Problem with it.

    Also the trick with MIP could work I think.

      
        
        
        #ifndef _SAMPLER_REMO_H_
        #define _SAMPLER_REMO_H_
        //=================================================================================================
        //	Remotion (Igor Schulz) 2003 - 2012
        //	BaseMaterial/Shader Sampler for C4D
        //	First Created:: 17.12.03
        //=================================================================================================
        #include "c4d_raytrace.h"
        //=================================================================================================
        class Sampler
        //=================================================================================================
        {
        public:
        	 Sampler();
        	~Sampler();
         
        	LONG Init(BaseMaterial *mat ,LONG chnr,Real time,BaseDocument *doc,BaseObject *op=NULL);
        	LONG Init(TextureTag *textag,LONG chnr,Real time,BaseDocument *doc,BaseObject *op=NULL);
         
        	inline Bool IsInit(){ return TexInit; };
        	Vector	SampleUV(const Vector &uv, Real time=0.0);
        	Vector	Sample3D(const Vector &p = 0.0, const Vector &uv = 0.0, Real time=0.0);
         
        	void Free();
        private:
        	BaseShader			*texShader;
        	TexData				*tex;
        	RayObject			*rop;
        	InitRenderStruct	*irs;
        	ChannelData			cd;
        	Matrix		omg;
        	Real		offsetX;
        	Real		offsetY;
        	Real		lenX;
        	Real		lenY;
        	Bool		TexInit;
        };
        //-------------------------------------------------------------------------------------------------
        inline Vector Sampler::SampleUV(const Vector &uv, Real time)
        {
        	cd.t		= time;
        	cd.p		= uv;
        	cd.p.x		= (uv.x-offsetX) / lenX; 
        	cd.p.y		= (uv.y-offsetY) / lenY;
        	return texShader->Sample(&cd);	
        }
        //-------------------------------------------------------------------------------------------------
        inline Vector Sampler::Sample3D(const Vector &p, const Vector &uv, Real time)
        {
        	cd.t			= time;
        	cd.vd->p		= p;
        	cd.vd->back_p	= p;
        	cd.p			= uv;
        	return texShader->Sample(&cd);
        }
        //-------------------------------------------------------------------------------------------------
        inline LONG Sampler::Init(TextureTag *textag,LONG chnr,Real time,BaseDocument *doc,BaseObject *op)
        {
        	if(!textag) return 1;
        	BaseMaterial *mat = textag->GetMaterial();if(!mat) return 2;//no Material
        	return Init(mat,chnr,time,doc,op);
        }
        //-------------------------------------------------------------------------------------------------
        LONG Sampler::Init(BaseMaterial *mat,LONG chnr, Real time,BaseDocument *doc, BaseObject *op)
        {
        	if (!doc || !mat || chnr < 0 || chnr > 12)	return 1;
        	if (!cd.vd	 || !tex || !irs  || !rop)		return 2;
        	TexInit = FALSE;
        	LONG			err = 0;
        	BaseContainer	chandata;
        	LONG			fps		= doc->GetFps();
        	Filename		dpath	= doc->GetDocumentPath();
        	BaseChannel *texChan1   = mat->GetChannel(chnr);	if (!texChan1) return 3;//no Channel
        	texShader = texChan1->GetShader(); if(!texShader) return 4;
        	if (op){ //Object
        		rop->link		= op;
        		rop->mg			= op->GetMg();
        		rop->mp			= op->GetMp()*op->GetMg();
        		rop->rad		= op->GetRad();
        		if (op->IsInstanceOf(Opolygon)){
        			rop->pcnt	= ToPoly(op)->GetPointCount();
        			rop->padr	= GetPointW(ToPoly(op));
        			rop->vcnt	= ToPoly(op)->GetPolygonCount();
        			rop->vadr	= (RayPolygon* )GetPolygonW(ToPoly(op));
        			rop->type   = O_POLYGON;
        		}
        	}
        	//----------------- TexData -------------------
        	tex->mp			= mat;// Set Material
        	//------------ InitRenderStruct ----------------
        	irs->fps		= fps;
        	irs->time		= BaseTime(time);
        	irs->doc		= doc;
        	irs->docpath	= dpath;
        	irs->vd			= cd.vd;
        	//---------------- Sampling ---------------------
        	chandata	= texChan1->GetData();
        	cd.off		= chandata.GetReal(BASECHANNEL_BLUR_OFFSET,0.0);
        	cd.scale	= 0.0;
        	cd.d		= 0.0;
        	cd.n		= Vector(0.0,1.0,0.0);
        	cd.texflag	= TEX_TILE;
        	if (texShader->InitRender(*irs)==LOAD_OK) { TexInit = TRUE; return 0;} 
        	else err = 5;
        Error:
        	texShader->FreeRender();
        	return err;
        }
        //-------------------------------------------------------------------------------------------------
        Sampler::Sampler()
        {
        	texShader		= NULL;
        	TexInit			= FALSE;
        	offsetX			= 1.0;
        	offsetY			= 1.0;
        	lenX			= 1.0;
        	lenY			= 1.0;
        	//--------------- VolumeData -----------------
        	cd.vd =	 VolumeData::Alloc(); if (!cd.vd) {VolumeData::Free(cd.vd);  return;}
        	cd.vd->version		= 1;  //LONG
        	cd.vd->fps			= 25; //LONG
        	cd.vd->ambient		= Vector(0.5);//0.0;
        	cd.vd->time			= 0;  //Real
        	cd.vd->bumpn		= Vector(0.0,1.0,0.0);
        	cd.vd->back_delta	= Vector(0.0);
        	cd.vd->global_mip	= 1.0; //Real
        	cd.vd->orign		= Vector(0.0,1.0,0.0);
        	cd.vd->n			= Vector(0.0,1.0,0.0);
        	cd.vd->dispn		= Vector(0.0,1.0,0.0);
        	cd.vd->delta		= Vector(0.0);
        	cd.vd->dist			= 0.0; //Real
        	cd.vd->lhit			= RayHitID();
        	cd.vd->cosc			= 1.0; //Real Angle
        	cd.vd->ddu			= Vector(1.0,0.0,0.0);
        	cd.vd->ddv			= Vector(0.0,1.0,0.0);
        	cd.vd->tray			= NULL; //TRay
        	cd.vd->rray			= NULL; //RRay
        	cd.vd->ray			= NULL; //Ray
        	cd.vd->xlight		= NULL; //RayLight
        	//---------------- TexData ------------------
        	tex	= TexData::Alloc(); if (!tex) {VolumeData::Free(cd.vd);TexData::Free(tex);  return;}
        	tex->Init();	
        	tex->proj		= P_UVW;
        	tex->texflag	= TEX_TILE;
        	tex->lenx		= lenX;
        	tex->leny		= lenY;
        	tex->ox			= offsetX;
        	tex->oy			= offsetY;
        	cd.vd->tex		= tex;
        	//---------------- RayObject -----------------
        	rop = AllocRayObject(0); if (!rop) return; 
        	rop->link		  = NULL;
        	rop->texture_link = NULL;
        	rop->visibility	= 1.0;
        	rop->pcnt		= 0;
        	rop->padr		= NULL;
        	rop->vcnt		= 0;
        	rop->vadr		= NULL;
        	rop->texcnt		= 0;
        	rop->texadr		= NULL;
        	rop->uvwadr		= NULL;
        	rop->rsadr		= NULL;
        	cd.vd->op		= rop;
        	//---------------- RenderStruct -----------------
        	irs = gNew(InitRenderStruct);
        }
        //-------------------------------------------------------------------------------------------------
        void Sampler::Free()
        {
        	if (texShader){  if (TexInit) texShader->FreeRender();  }
        	TexInit = FALSE;
        }
        //-------------------------------------------------------------------------------------------------
        Sampler::~Sampler(void)
        {
        	Free();
        	if (tex) TexData::Free(tex);
        	if (cd.vd)  VolumeData::Free(cd.vd);
        	FreeRayObject(rop);
        	gDelete(irs);
        }
        #endif//_SAMPLER_REMO_H_
      
    


  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 11/08/2012 at 06:00, xxxxxxxx wrote:

    Thanks much, Remotion4D!  I will let you know how it works and if there are any changes needed for later versions (I'm building for R12 currently).



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 16/08/2012 at 02:51, xxxxxxxx wrote:

    Not many changes at all, just a few constant definitions are different.

    The MIP delta radius doesn't appear to be helping (set in ChannelData and VolumeData, combined with just getting the color at the UV polygon centroid).  What should I do to get an average value of the texels within the UV polygon?  Should I literally do a sample set of random points that fall within the polygon?  Maybe add a user setting for the number of samples?  I don't know.



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 20/08/2012 at 04:17, xxxxxxxx wrote:

    To complete this topic, I ended up using a random sampling method within the polygon to get the average value.  The user can select the number of samples and it works very well.  If anyone needs to know how to get random points within a triangle/quadrangle, the code is very simple and I would be very happy to provide it. :)



  • THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

    On 20/08/2012 at 08:16, xxxxxxxx wrote:

    I would kill to see a working a example on texture sampling Robert using a ray.
    I'm still trying to figure out how to sample the texture colors on objects.
    Every time the subject comes up here. The complete solution is never posted.

    I tried to use the code posted here a while back. But I couldn't manage to convert it to R12+.
    Some kind of working example would be wonderful. If it's something you are willing to provide.

    -ScottA



  • On 05/04/2013 at 08:44, xxxxxxxx wrote:

    Hi Remo,

    I've downloaded the code from GitHub. And found that I could not compile it for R13.
    I had to change some things in the Init() function to make it compile:

    LONG Sampler::Init(BaseMaterial *mat,LONG chnr, Real time,BaseDocument *doc, BaseObject *op)  
    {  
      if (!doc || !mat || chnr < 0 || chnr > 12)    return 1;  
      if (!cd.vd     || !tex || !irs  || !rop)        return 2;  
      TexInit = FALSE;  
      LONG            err = 0;  
      BaseContainer    chandata;  
      LONG            fps        = doc->GetFps();  
      Filename        dpath    = doc->GetDocumentPath();  
      BaseChannel *texChan1   = mat->GetChannel(chnr);    if (!texChan1) return 3;//no Channel  
      texShader = texChan1->GetShader(); if(!texShader) return 4;  
      if (op){ //Object  
          rop->link        = op;  
          rop->mg            = op->GetMg();  
          rop->mp            = op->GetMp()*op->GetMg();  
          rop->rad        = op->GetRad();  
          if (op->IsInstanceOf(Opolygon))  
          {  
              rop->pcnt    = ToPoly(op)->GetPointCount();  
              rop->padr    = ToPoly(op)->GetPointW();  
              rop->vcnt    = ToPoly(op)->GetPolygonCount();  
              rop->vadr    = (RayPolygon* )ToPoly(op)->GetPolygonW();  
              rop->type   = O_POLYGON;  
          }  
      }  
      //----------------- TexData -------------------  
      tex->mp            = mat;// Set Material  
      //------------ InitRenderStruct ----------------  
      irs->fps        = fps;  
      irs->time        = BaseTime(time);  
      irs->doc        = doc;  
      irs->docpath    = dpath;  
      irs->vd            = cd.vd;  
      //---------------- Sampling ---------------------  
      chandata    = texChan1->GetData();  
      cd.off        = chandata.GetReal(BASECHANNEL_BLUR_OFFSET,0.0);  
      cd.scale    = 0.0;  
      cd.d        = 0.0;  
      cd.n        = Vector(0.0,1.0,0.0);  
      cd.texflag    = TEX_TILE;  
      if (texShader->InitRender(*irs)==INITRENDERRESULT_OK) { TexInit = TRUE; return 0;}   
      else err = 5;  
      
      
    //Error:                              //The Complier doesn't like this for some reason  
    //    texShader->FreeRender();  
    //    return err;  
    }
    

    There's a problem with the way you wrote your "Error" code that my V.S. 2010 compiler does not like. So I just commented it out for now.

    I also have some basic questions on how to use the code:

    1. I assume I'm supposed to use this as an .h file in my project? (that's how I've been using it)

    2. Suppose for example, I wanted to sample the shader in the color channel of the first material in the material manager.
      I would normally create the code to get the material..Get the channel..Get the shader. But the way you have your constructor set up with the channel numbers and such. I'm a bit confused how to use it.

    If you don't mind too much. It would be nice to see a very, very  small example of using the class so I can see how to use the constructor. Because it's a little bit foreign to me from the normal way I code for materials.

    Thanks,
    -ScottA



  • On 05/04/2013 at 12:54, xxxxxxxx wrote:

    Just remove Error:  but do not remove texShader->FreeRender(); and return.

    1)  yes of course.

    1. it is already in Init() of course you can create another Init() with the Shader as parameter.

    I need to search in my code old to find some example for this.



  • On 05/04/2013 at 18:46, xxxxxxxx wrote:

    Let me try to help you... help me. If I may.
    I've been able to figure out how to use it. But I'm not sure what it is you've given me. 😊

    I have your code in my project as an .h file.
    And this is how I'm using it to get a result from the Sample3D() function:

        BaseDocument *doc = GetActiveDocument();  
      BaseObject *obj = doc->GetActiveObject();  
      if (!obj) return FALSE;  
      
      BaseMaterial *mat = doc->GetFirstMaterial();          //The material to target  
      if (!mat) return FALSE;  
      
      BaseContainer *mdata = mat->GetDataInstance();       //The material's container data  
      
      Sampler smpl;  
      smpl.Init(mat,CHANNEL_COLOR, 0.0, doc, obj);         //Initialize an instance of the Sample class with these items  
      
      smpl.Sample3D(0, 0, 0.0);                            //<--- What is this that I have now?
    

    What I don't understand is. How do I use this result to get the texture colors from the shader?
    I'm completely lost about how this whole "getting the textures on a specific area of an object" thing works.
    All this stuff about ChannelData, VolumeData, Text Data, cd.p, cd.uv, etc...
    I'm trying to get a basic understanding of it all. And how to use it all. But there's precious few posts about this subject.
     
    -ScottA



  • On 05/04/2013 at 20:32, xxxxxxxx wrote:

    Sample3D() returns a Vector which represents the color (0-1 for RGB) at that sample coordinate.



  • On 05/04/2013 at 21:18, xxxxxxxx wrote:

    Oh. I see.
    Thanks Robert.

    That gives me a way to get the color values. But what about the UV values for it?
    There's only one param. But I need two(U&V) to be able to sample a specific position on the object.
    One param. for both severely limits where I can sample the UV's:

        Sampler smpl;  
      smpl.Init(mat,CHANNEL_COLOR, 0.0, doc, obj);  
      Vector color = smpl.Sample3D(0, .5, 0.0);   //<--- Gets the center of the UV's...But why do I only have one UV value param instead of two?  
                                                  //Having only one limits where I can target the U&V coords.  
      
      Real r = color.x;  
      Real g = color.y;  
      Real b = color.z;  
      GePrint( RealToString(r) + " " +RealToString(g) + " " +RealToString(b) );
    

    Also:
    What about all the VolumeData and RayObject stuff in that class?
    Can I use that code in some manner? Or is all of that code just there to make the  SampleUV() & Sample3D() functions work?

    Sorry for all the questions guys.
    But like I said. I have lots questions about this subject and I'm trying to learn how everything works.

    -ScottA



  • On 05/04/2013 at 21:36, xxxxxxxx wrote:

    The first two arguments for Sample3D() are vectors.  He is just using a shortcut to create a vector with all three values being equal (0.0 = Vector(0.0, 0.0, 0.0) for instance).  If you want to sample other locations, you will need to pass both the 3D point and the UVW vector associated with it.  Unfortunately, Remotion4D has not provided an example of how he uses his Sampler class to get or calculate this information.  Like me, you will need to determine how to sample, how many samples for each polygon.  I went for a random sampling since I only needed the average color of the polygon but for something more detailed like a scanline sampling or marching cubes or subpixel sampling you will need to research the methods required.



  • On 06/04/2013 at 04:33, xxxxxxxx wrote:

    Please lock here:
    https://github.com/PluginCafe/Cinema-4D-Code-Collection/blob/master/C++/SamplerRemo.h

    Hope this help.



  • On 06/04/2013 at 09:05, xxxxxxxx wrote:

    Thanks Remo,

    Your new notes make it more clear how to sample a specific UV coord now:

        BaseDocument *doc = GetActiveDocument();  
      BaseObject *obj = doc->GetActiveObject();  if (!obj) return FALSE;  
      BaseMaterial *mat = doc->GetFirstMaterial(); if (!mat) return FALSE;  //The material to be sampled  
      Sampler smpl;  
      const LONG init_res = smpl.Init(mat,CHANNEL_COLOR, 0.0, doc); if(init_res != 0) return FALSE;  
      Vector pos3d;                                                        //3d coordinates for 3D shader.  
      Vector uv;                                                           //UVW coordinates for all other shaders.  
        
      //Control the focus point where to sample the color from  
      uv.x = .5;   
      uv.y = .1;  
        
      //set here pos3d and uv !!!  
      const Vector color = smpl.Sample3D(pos3d, uv);  
      
      GePrint( RealToString(color.x) + " " +RealToString(color.y) + " " +RealToString(color.z) );  //prints the RGB color values
    

    I will take a look at the other two functions in your class later today and try to understand what they also do.
    I just wish there was more information about the whole subject. Things like: how and why to use Volume and Ray objects. Stuff like that.

    Thanks for posting the code,
    -ScottA


Log in to reply