Tile rendering questions



  • On 10/09/2016 at 07:43, xxxxxxxx wrote:

    Dear C4D users,

    For our plugin we like to intergrade tile rendering for our farm. All jobs are sent to the command line of cinema 4d. At the moment we can only render single (full) frames. So for example, when you have 10 frame on animation every server will render out a single image.

    But what we like to add is that servers can work together to create the final image. (tile rendering) For that we need to tell cinema only to render a part of the image. And later on we stitch them together. 
    With team render this is possible, but how to do this with the command line or python/c++ code.

    Every tip or suggestion is welcome!

    Thanks!



  • On 12/09/2016 at 07:40, xxxxxxxx wrote:

    Hi,

    while it is not possible to add command line options that alter the behavior of built-in functionality (like in your case the rendering process), there are quite a few options. Which fits best, depends of course highly on your situation and the plugin you already seem to have.

    a) If your plugin is running on the server side (i.e. being responsible for distributing the data to the render nodes) you could modify the render settings before sending the scene to a node. For a hint on how to do this, see my Python example code below.

    b) Somewhat the compromise of a) and c), you could write a plugin to run on the render nodes. This plugin would add a new command line option (see either C++ Plugin Functions Manual or Plugin Messages in Python docs (a bit down the page)), which loads the scene (that will later be rendered by C4D), modifies the render settings, saves the scene again. Further down the line C4D would then take over and render.

    c) I once did an example for a Dev Kitchen, which partly already does what you are asking for. It adds a command line option for tiled rendering.
    The downside: You will need to take over the rendering part, e.g. saving to the correct format...
    Note the quotation marks enclosing the entire command line option, needed by this example:
    See here:

    # Dev Kitchen 2015 - Python Commandline #5 - Rendering Tiles
    import sys, os, ntpath, math
    import c4d
    from c4d import bitmaps, documents
      
    # Renders a tile of the current camera
    # Syntax: cinema4d.exe "-rendertiled numtiles tileidx file"
    # with numtiles the number of tiles in x and y direction (numtiles=2 => 4 tiles total)
    # and tileidx either all or between 0 and (numtiles*numtiles)-1
    def CommandRenderTiled(arg) :
      # Parse arguments
      argComponents = arg.split(' ')
      numTiles = int(argComponents[1])
      fNumTiles = float(numTiles)
      if argComponents[2] == "all":
        tileIdxMin = 0
        tileIdxMax = (numTiles * numTiles)
      else:
        tileIdxMin = int(argComponents[2])
        tileIdxMax = tileIdxMin + 1
      fname = argComponents[3]
      
      # Load the scene without inserting into C4D's document list
      doc = documents.LoadDocument(fname, c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS)
      if doc is None:
        print "Failed to load file: %s" % fname
        return
      
      bd = doc.GetRenderBaseDraw()
      if bd is None:
        return
      
      # Get the active camera
      cam = bd.GetSceneCamera(doc)
      if cam is None:
        cam = bd.GetEditorCamera(doc)
        if cam is None:
          return
      
      # Get the active render settings and
      rd = doc.GetActiveRenderData()
      filepath, filename = ntpath.split(rd[c4d.RDATA_PATH])
      
      for tileIdx in range(tileIdxMin, tileIdxMax) :
        # Prefix the output filename with tile index
        outputPath = filepath + "/" + str(tileIdx) + "_" + filename
        rd[c4d.RDATA_PATH] = outputPath
      
        # Delete previous render
        if ntpath.isfile(outputPath) is True:
          os.remove(outputPath)
      
        # Create a new camera and configure it for tiled render
        camOffset = c4d.BaseObject(c4d.Ocamera)
        camOffset.SetMg(cam.GetMg())
        camOffset[c4d.CAMERA_PROJECTION] = cam[c4d.CAMERA_PROJECTION]
        camOffset[c4d.CAMERAOBJECT_APERTURE] = cam[c4d.CAMERAOBJECT_APERTURE]
        camOffset[c4d.CAMERAOBJECT_SHOW] = cam[c4d.CAMERAOBJECT_SHOW]
        camOffset[c4d.CAMERA_FOCUS] = cam[c4d.CAMERA_FOCUS] * fNumTiles
        camOffset[c4d.CAMERA_ZOOM] = cam[c4d.CAMERA_ZOOM] * fNumTiles
      
        biasX = math.fmod(float(tileIdx), fNumTiles) - (fNumTiles - 1.0) * 0.5
        biasY = float(tileIdx / numTiles) - (fNumTiles - 1.0) * 0.5
      
        camOffset[c4d.CAMERAOBJECT_FILM_OFFSET_X] = fNumTiles * cam[c4d.CAMERAOBJECT_FILM_OFFSET_X] + biasX
        camOffset[c4d.CAMERAOBJECT_FILM_OFFSET_Y] = fNumTiles * cam[c4d.CAMERAOBJECT_FILM_OFFSET_Y] + biasY
      
        # Insert the new camera into the scene and set it for rendering
        doc.InsertObject(camOffset)
        bd.SetSceneCamera(camOffset)
      
        # Initialize a bitmap with the result size (must match output size of render settings)
        bmp = bitmaps.BaseBitmap()
        bmp.Init(x=rd[c4d.RDATA_XRES], y=rd[c4d.RDATA_YRES], depth=24)
      
        # Render the file (and assure saving of rendered image)
        documents.RenderDocument(doc, rd.GetData(), bmp, c4d.RENDERFLAGS_EXTERNAL)
        if rd[c4d.RDATA_SAVEIMAGE] is False: # if saving is enabled in render settings it will be saved automatically
          if bmp.Save(outputPath, c4d.FILTER_TIF, None, c4d.SAVEBIT_0) is not IMAGERESULT_OK:
            print "An error occurred, when saving the image %s" % outputPath
      
      
    def ParseCommandline(argv) :
      for arg in argv:
        if arg.find("-rendertiled") == 0:
          CommandRenderTiled(arg)
      
      
    def PluginMessage(id, data) :
      if id == c4d.C4DPL_COMMANDLINEARGS:
        # Extend command line parsing, here
        # This is the last plugin message on Cinema 4D's start process
        ParseCommandline(sys.argv)
      return True
    

Log in to reply