Get linear shaders

On 14/11/2014 at 18:36, xxxxxxxx wrote:

Using shader.initRender() I am rendering out a shader, however I can't get the InitRenderStruct() to be linear. In the docs I have found InitRenderStruct.linear_workflow but this feature is read only. how do I set this value to True?

On 17/11/2014 at 06:34, xxxxxxxx wrote:

Hello,

the Python implementation expects the IRS to be initiated by Cinema. So I'm afraid it is not possible to edit an ad hoc created IRS.

Best wishes,
Sebastian

On 08/01/2015 at 10:35, xxxxxxxx wrote:

Finally got the chance to pick this project back up. It seems that I may not be facing a linear issue. Here are some examples of output:

Original (How the cinema shader preview sees it) :
_<_img src="http://shawnfrueh.com/references/c4d/original_cinemaoutput.png" height="512" width="512" border="0" /_>_

This is what I get when I use initRender() :
<_<_img src="http://shawnfrueh.com/references/c4d/original_initoutput.png" height="512" width="512" border="0" /_>_" />

and here is what I get when I add a Gamma correction:_r="0" />

So as you mentioned before I am guessing the IRS is not respecting the project color-space. Is there a way around this? How is the shader preview being rendered?

On 09/01/2015 at 07:23, xxxxxxxx wrote:

Hi Shawn,

InitRenderStruct.linear_workflow is passed to InitRender() so that shaders can check if they have to perform some color conversion to InitRenderStruct.document_colorprofile.
c4d.utils.TransformColor() can then be called to return the expected color from Output().

On 09/01/2015 at 08:23, xxxxxxxx wrote:

I really appreciate the help Yannick, worked like a charm! Thank you.

On 09/01/2015 at 12:54, xxxxxxxx wrote:

Looks like I spoke to soon. The scene I was testing in had the color profile disabled. This is what it looks like in a default scene:

COLORSPACETRANSFORMATION_NONE: 
_<_img src="http://shawnfrueh.com/references/c4d/colorspace_nOne.gif" height="278" width="633" border="0" /_>_

COLORSPACETRANSFORMATION_LINEAR_TO_SRGB:
<_<_img src="http://shawnfrueh.com/references/c4d/colorspace_linear_to_srgb.gif.gif" height="278" width="633" border="0" /_>_" />

COLORSPACETRANSFORMATION_SRGB_TO_LINEAR:_r="0" />

COLORSPACETRANSFORMATION_LINEAR_TO_VIEW_<_img src="http://shawnfrueh.com/references/c4d/colorspace_linear_to_view.gif" height="278" width="633" border="0" /_>_order="0" />

COLORSPACETRANSFORMATION_SRGB_TO__<_img src="http://shawnfrueh.com/references/c4d/colorspace_srgb_to_view.gif" height="278" width="633" border="0" /_>_3" border="0" />

So, I guess I'm Back to the drawing board. Am I missing something?

On 09/01/2015 at 12:59, xxxxxxxx wrote:

This is how I am getting the color:

s = shader.Sample(cd)
s = c4d.utils.TransformColor(s,profile)
r = int(s.x * 255)
g = int(s.y * 255)
b = int(s.z * 255)
bmp.SetPixel(x, y, r, g, b)

On 12/01/2015 at 01:39, xxxxxxxx wrote:

Hi Shawn,

The Cinema 4D shaders usually transform their input colors (in InitRender()) using irs.TransformColor() that calls the global TransformColor() according to the irs.linear_workflow state and irs.document_colorprofile.
InitRenderStruct.TransformColor() isn't defined in the Python API but its code is simple and included in the C++ API. Following is its code converted to Python:

def TransformColor(irs, input) :
  if irs.linear_workflow and irs.document_colorprofile is c4d.DOCUMENT_COLORPROFILE_SRGB:
    return c4d.utils.TransformColor(input, c4d.COLORSPACETRANSFORMATION_SRGB_TO_LINEAR);
  elif not irs.linear_workflow and irs.document_colorprofile is c4d.DOCUMENT_COLORPROFILE_LINEAR:
    return c4d.utils.TransformColor(input, c4d.COLORSPACETRANSFORMATION_LINEAR_TO_SRGB);
  
  return input;

You should use this TransformColor() for the color sampled in your shader.
Note that c4d.utils.TransformColor() expects a COLORSPACETRANSFORMATION value as second parameter, not a DOCUMENT_COLORPROFILE value.
Also be sure that the bitmap in your shader uses the appropriate color profile.

On 12/01/2015 at 14:50, xxxxxxxx wrote:

I am not sure I follow. Using your code above I get the same result: the output is not the same. So when you say that the bitmap must use the correct profile. Does that mean that the bitmap I am creating must also have a profile attached to it?

playing with c4d.bitmaps.ColorProfile.GetDefaultLinearRGB() and adding it to the bitmap has no influence on the output.

here is the code I have for better understanding:

from __future__ import division
import c4d,timeit
from c4d import Vector, bitmaps, gui
from c4d.bitmaps import ShowBitmap
from c4d.modules.render import ChannelData, InitRenderStruct
# Bitmap maker created by Shawn Frueh
  
class BasicDialog(gui.GeDialog) :
    
    RENDER = 10000
    SHADERLINK = 10001
    GROUP_SIZE = 10002
    WIDTH_TEXT = 10003
    WIDTH = 10004
    HEIGHT_TEXT = 10005
    HEIGHT = 10006
    GROUPFIT = 10007
    PROGRESS = 10008
    GROUP_PERCENT = 10009
    PROGRESS_SLIDER = 10010
    GROUP_OF_GROUPS = 10011
    RENDER_BIT = 10012
    GROUP_ALL = 10013
    PROFILESELECT = 10014
    RENDERINFO = 10015
    
    CS_NONE = c4d.COLORSPACETRANSFORMATION_NONE
    CS_L_TO_S = c4d.COLORSPACETRANSFORMATION_LINEAR_TO_SRGB
    CS_S_TO_L = c4d.COLORSPACETRANSFORMATION_SRGB_TO_LINEAR
    CS_L_TO_V = c4d.COLORSPACETRANSFORMATION_LINEAR_TO_VIEW
    CS_S_TO_V = c4d.COLORSPACETRANSFORMATION_SRGB_TO_VIEW
    
    def CreateLayout(self) :
        
        self.SetTitle("Shader Renderer")
        
        self.GroupBegin(self.GROUPFIT,c4d.BFH_SCALEFIT, cols = 2)
        
        bc = c4d.BaseContainer()
        bc.SetLong(c4d.BITMAPBUTTON_ICONID1, c4d.RESOURCEIMAGE_MOVE)
        bc.SetBool(c4d.BITMAPBUTTON_BUTTON, True)
        self.myBitButton=self.AddCustomGui(self.RENDER_BIT, c4d.CUSTOMGUI_BITMAPBUTTON, "Bend", c4d.BFH_CENTER | c4d.BFV_CENTER, 64, 64, bc)
        bmp = bitmaps.BaseBitmap()
        bmp.Init(120,120,24)
        self.myBitButton.SetImage(bmp)
        
        self.GroupBegin(self.GROUP_ALL, c4d.BFH_SCALEFIT, cols = 1)
        self.GroupBorderSpace(4,4,4,4)
        self.AddComboBox(self.PROFILESELECT, c4d.BFH_SCALEFIT)
        self.AddCustomGui(self.SHADERLINK, pluginid=c4d.CUSTOMGUI_LINKBOX, name="Link", flags=c4d.BFH_SCALEFIT, minw=200, minh=0) 
        self.GroupBegin(self.GROUP_SIZE,c4d.BFH_SCALEFIT, cols = 4, title = "Render Size")
        
        self.AddStaticText(self.WIDTH_TEXT, c4d.BFH_LEFT, name = "Width")
        self.AddEditNumber(self.WIDTH, c4d.BFH_SCALEFIT)
        self.AddStaticText(self.HEIGHT_TEXT, c4d.BFH_LEFT, name = "Height")
        self.AddEditNumber(self.HEIGHT, c4d.BFH_SCALEFIT)
        self.GroupEnd()
        
        self.GroupBegin(self.GROUP_PERCENT,c4d.BFH_SCALEFIT, cols = 3)
        self.GroupBorderSpace(4,4,4,4)
        self.AddButton(self.RENDER, c4d.BFH_LEFT, name = "Render")
        self.AddStaticText(self.PROGRESS,c4d.BFH_LEFT,name = " 100%!")
        self.AddSlider(self.PROGRESS_SLIDER, c4d.BFH_SCALEFIT)
  
        self.GroupEnd()
        
        self.AddStaticText(self.RENDERINFO, c4d.BFH_LEFT, name = "Please add a shader.", initw = 500)
        
        self.GroupEnd()
        self.GroupEnd()
        return True
    
    def InitValues(self) :
        self.SetString(self.PROGRESS," 0%")
        self.SetReal(self.PROGRESS_SLIDER, 0.0,min = 0, max = 100.0)
        self.SetInt32(self.WIDTH, 512, max = c4d.MAXLONGl)
        self.SetInt32(self.HEIGHT, 512, max = c4d.MAXLONGl)
        self.AddChild(self.PROFILESELECT, self.CS_NONE,"None")
        self.AddChild(self.PROFILESELECT, self.CS_L_TO_S,"Linear to SRGB")
        self.AddChild(self.PROFILESELECT, self.CS_S_TO_L,"SRGB to Linear")
        self.AddChild(self.PROFILESELECT, self.CS_L_TO_V,"Linear to View")
        self.AddChild(self.PROFILESELECT, self.CS_S_TO_V,"SRGB to View")
        return True
    
    
    def Command(self, id, msg) :
        link = self.FindCustomGui(self.SHADERLINK,c4d.CUSTOMGUI_LINKBOX)
        sh = link.GetLink(doc,0)
        
        def GetProfile(p) :
            if p == 0:
                return self.CS_NONE
            if p == 1:
                return self.CS_L_TO_S
            if p == 2:
                return self.CS_S_TO_L
            if p == 10:
                return self.CS_L_TO_V
            if p == 11:
                return self.CS_S_TO_V
            
        def RenderShader(w,h,sh) :
            global percent
            global Pget
            
            shader = sh
            start = timeit.default_timer()
            irs = InitRenderStruct()
            #####
            shader.InitRender(irs)
            #####
            cd = ChannelData()
            cd.p = Vector(.5,.5,0)
            bmp = bitmaps.BaseBitmap()
            bmp.Init(w,h,24)
            c4d.StatusSetBar(0.0)
            pget = 100/w
            percent = 0.0
            profile = self.GetInt32(self.PROFILESELECT)
            profile = GetProfile(profile)# possibly not neccesarry?
            c4d.StatusSetText("Rendering: "+shader.GetName()+" shader")
            for x in xrange(w) :
                c4d.StatusSetBar(percent)
                self.SetReal(self.PROGRESS_SLIDER, percent,min = 0, max = 100.0)
                self.SetString(self.PROGRESS," "+str(percent)+"%")
                for y in xrange(h) :
                    PX = x/w
                    PY = y/h
                    cd.p = Vector(PX,PY,0)
                    s = shader.Sample(cd)
                    s = c4d.utils.TransformColor(s,profile)
                    r = int(s.x * 255)
                    g = int(s.y * 255)
                    b = int(s.z * 255)
                    
                    bmp.SetPixel(x, y, r, g, b)
                percent = percent + pget
                
            shader.FreeRender()
            
            stop = timeit.default_timer()
            
            c4d.StatusClear()
            
            self.SetString(self.PROGRESS," 100%!")
            self.SetReal(self.PROGRESS_SLIDER, 100,min = 0, max = 100.0)
            OutInfo = shader.GetName()+" shader rendered in "+str(stop - start)+" seconds"
            self.SetString(self.RENDERINFO, OutInfo)
            
            return bmp
        
        if id == self.RENDER:
            w = self.GetInt32(self.WIDTH)
            h = self.GetInt32(self.HEIGHT)
            Abmp = RenderShader(w,h,sh)
            ShowBitmap(Abmp)
            
        if id == self.RENDER_BIT:
            Ibmp = RenderShader(120,120,sh)
            self.myBitButton.SetImage(Ibmp)
            self.SetString(self.PROGRESS,"0%")
            self.SetReal(self.PROGRESS_SLIDER, 0,min = 0, max = 100.0)
            self.myBitButton.SetImage(Ibmp)
            
        if id == self.SHADERLINK:
            Ibmp = RenderShader(120,120,sh)
            self.myBitButton.SetImage(Ibmp)
            self.SetString(self.PROGRESS,"0%")
            self.SetReal(self.PROGRESS_SLIDER, 0,min = 0, max = 100.0)
            self.myBitButton.SetImage(Ibmp)
            
        if id == self.PROFILESELECT:
            Ibmp = RenderShader(120,120,sh)
            self.myBitButton.SetImage(Ibmp)
            self.SetString(self.PROGRESS,"0%")
            self.SetReal(self.PROGRESS_SLIDER, 0,min = 0, max = 100.0)
            self.myBitButton.SetImage(Ibmp)
            
        return True
  
dlg = BasicDialog()
dlg.Open(dlgtype=c4d.DLG_TYPE_ASYNC, xpos=-1, ypos=-1)

On 14/01/2015 at 00:43, xxxxxxxx wrote:

Hi Shawn,

Thanks for posting your code. I should have asked in which context you where calling InitRender().
I thought you were invoking InitRender() from a ShaderData plugin.
Unfortunately InitRenderStruct.linear_workflow and InitRenderStruct.document_colorprofile are read-only in the Python API and can't be set.
I'll report this limitation to the development team.