Sampling shader to Bitmap



  • On 25/02/2016 at 01:56, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R17 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    Hello.

    I have to sample a shader and create a bitmap based on that shader.
    The problem is that I get a small error in the very first line of pixels.
    I send you 2 images with the correct and the wrong version of the result:
    correct: https://dl.dropboxusercontent.com/u/432275/correct.png
    wrong: https://dl.dropboxusercontent.com/u/432275/wrong.png

    These 2 bitmaps are both 100x100 (for testing).
    For each pixel, i sample the shader using the corresponding texture coordinates.

    Here is the code that I use for sampling:

    unsigned int resolution_x = 100; //Testing
    unsigned int resolution_y = 100;
    			 
    BaseBitmap * bitmap = BaseBitmap::Alloc(); 
    IMAGERESULT init_result = bitmap->Init(resolution_x, resolution_y);
    if (init_result = IMAGERESULT_OK) {
    	InitRenderStruct irs;
    	shader->InitRender(irs);
    	ChannelData channel_data;
    	channel_data.off = 0;
    	channel_data.scale = 0;
    	channel_data.t = 0;
    	channel_data.texflag = TEX_TILE;
    	channel_data.p.z = 0;
    	channel_data.d = Vector(1,1,1);
    	channel_data.n = Vector(0,1,0);
    	channel_data.vd = NULL;
    				
    	for (unsigned int x = 0; x < resolution_x; ++x ) {
    		for (unsigned int y = 0; y < resolution_y; ++y ) {
    			channel_data.p.x = ((LReal)x)/(resolution_x-1) ;
    			channel_data.p.y = ((LReal)y)/(resolution_y-1)  ; //!!!!!!!
    			assert(channel_data.p.x <= 1 && channel_data.p.y <= 1 );
    			Vector color = shader->Sample(&channel_data);
    			bitmap->SetPixel(x, y, 255*color.x , 255*color.y , 255*color.z);				
    		}
    	}
      
    	shader->FreeRender();
    	if (bitmap->Save(Filename(file.c_str()), FILTER_PNG, NULL, SAVEBIT_0) == IMAGERESULT_OK) {
    		//image saved
    	}
    }
    

    Now, in the line i have marked with //!!!!! i add a small error correction value 0.001.
    That way i get the correct result.

    Why is this error correction value  needed and why is this needed only in Y texture coordinates ?

    Thank you for your time.



  • On 25/02/2016 at 03:20, xxxxxxxx wrote:

    Hmm, not sure exactly (don't know the sampling code) but referencing "Blur Offset" and "Blur Scale" in a C4D material, the MIP radius should be something lower (around 0.01 or lower) than 1 (I'm guessing this might be the issue here).
    Also no TEX_TILE required right? Just pass 0.



  • On 25/02/2016 at 07:09, xxxxxxxx wrote:

    Thank you very much for your response.

    I don't use this shader in a C4D material. I have create my own MaterialData. 
    Even with texflag set to 0, the issue persists (I still need to add 0.001 to Y tex coordinate).



  • On 25/02/2016 at 10:01, xxxxxxxx wrote:

    Based on your usage of "assert". It looks like you are using raw C++ code rather than the SDK.
    By using a nested for() loop. You are also using the slowest possible code to handle images.
    If you must use raw C++ for some reason. I can't offer much advice. Because I would need to see the rest of your code.

    If you are open to using the SDK. There are some things I can give advice about.

    First:
    Consider using the GetPixelCnt() function inside of a single for() loop. It is much, much faster than using a nested for() loop.

    Second:
    This is a very simple way to render the shader to a bitmap

    //This is how to render the shader in a material to it's own bitmap image  
    //NOTE: even though it works fine. This is private and Not supported by Maxon(it may change in future releases)  
      
    #include "customgui_matpreview.h"  
      
      
      AutoAlloc<BaseBitmap> bitmap;  
      bitmap->Init(800,600);  
      
      BaseMaterial *mat = doc->GetFirstMaterial();  
      BaseShader *shader = mat->GetFirstShader();  
      
      BaseShader *clone = (BaseShader* )shader->GetClone(COPYFLAGS_0,nullptr);  
      
      RenderShaderPreview(doc->GetDocumentPath(),clone,shader,nullptr,bitmap,nullptr,0,RENDER_PREVIEW_USE_BMP_SIZE);  
      
      ShowBitmap(bitmap);  
      
      BaseShader::Free(clone);
    

    Third:
    I do have a custom method that samples the shader using the SDK and a nested for() loop.
    The code is similar to yours. But slightly different.
    *I did not write this myself. Someone posted it here a long time ago( maybe niklas? )

    //This is a custom method that is used to render the shader to a bitmap image  
      
    Bool RenderShader(BaseShader* shader, BaseBitmap* bmp, Real scale=1.0)  
    {  
      InitRenderStruct irs;  
      if(shader->InitRender(irs) != INITRENDERRESULT_OK) return FALSE;  
      
      LONG w = bmp->GetBw();  
      LONG h = bmp->GetBh();  
      ChannelData cd;  
      cd.p = Vector(0, 0, 0);  
      cd.n = Vector(0, 0, 1);  
      cd.d = Vector(0, 0, 0);  
      cd.t = 0.0;  
      cd.texflag = 0;  
      cd.vd = NULL;  
      cd.off = 0.0;  
      cd.scale = 0.0;  
      
      for (LONG x=0; x < w; x++)   
      {  
          for (LONG y=0; y < h; y++)   
          {  
              cd.p.x = (Real) x * scale;  
              cd.p.y = (Real) y * scale;  
              Vector pixel = shader->Sample(&cd);  
      
              LONG r = pixel.x * 255;  
              LONG g = pixel.y * 255;  
              LONG b = pixel.z * 255;  
              bmp->SetPixel(x, y, r, g, b);  
          }  
      }  
      
      ShowBitmap(bmp);  
      shader->FreeRender();  
      return TRUE;  
    }
    

    -ScottA



  • On 25/02/2016 at 14:35, xxxxxxxx wrote:

    Hello and thank you very much for you help.

    This code works great for single planes but it doesn't for polygons.
    I didn't mention it earlier because I didn't know that was a different case, but the shader is depended on particles' positions.

    How can I generate the bitmap based on that restriction ?

    Thank you for your time.



  • On 26/02/2016 at 01:51, xxxxxxxx wrote:

    Hello,

    what do you mean with "single planes" and "polygons"?

    If a shader depends on the world space position of the sampled fragment surface point (and the world space position of some other scene elements) one cannot bake this shader by just sampling the UV coordinates. For example a cube has the same UV coordinates for each face. But the effect of a shader like ambient occlusion may be different for each surface.

    Best wishes,
    Sebastian



  • On 26/02/2016 at 02:12, xxxxxxxx wrote:

    Hello.

    The most simple case that i have used is a plane (10x10 polygon object).
    The particles are emitted and collided on that.
    If I sample the shader with plane's UV coordinates, I can produce a bitmap that if used as a texture, it will give the same visual result with C4D's renderer.

    Another case is when i use a Cube (10x10x10 polygon object).
    Even if I unwrap it's UV texture (via BP UV Edit) and sample the shader with UV, the result totally wrong.

    So, in every case I want to create a bitmap that will be used as texture and give the same result as in C4D renderer.

    Is there any way to do that ?

    Thank you.



  • On 26/02/2016 at 09:16, xxxxxxxx wrote:

    Hello,

    sampling a shader in UV-space will not get a meaningful result for every shader. Some shaders do not operate in UV space but in world space etc. For example the Proximal shader does not depend on any UV coordinates but on the position of the shaded fragment and the position of the referenced objects. Also a shader and a material could be assigned to any number of objects.

    If you look at the material editor you see the shader preview. This preview is done by sampling the shader in UV space. For some shaders that's enough. But for some shaders not.

    Best wishes,
    Sebastian



  • On 28/02/2016 at 09:10, xxxxxxxx wrote:

    Hello.

    So, is it possible to create a bitmap based on a shader that operates in world space ?

    Thank you.


Log in to reply