Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Recent
    • Tags
    • Users
    • Register
    • Login

    Tile rendering with Cinema 4D

    Scheduled Pinned Locked Moved Cinema 4D SDK
    python2026
    3 Posts 1 Posters 4 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • K Online
      karthikbp
      last edited by

      Dear community,

      Is there a recommended way to do tile rendering—dividing a large image into smaller pieces that can be rendered quickly and then reassembled into one final image?

      Current Approach:

      We currently use the "Render Tiles" camera to divide an image into smaller pieces, then assemble them using software like FFmpeg or OpenImageIO.
      Here's our detailed step-by-step procedure: https://github.com/aws-deadline/deadline-cloud-for-cinema-4d/blob/mainline/docs/tile_rendering/tile_rendering.md based on this old article.

      Alternative approach:

      But while going over some of the render settings, I stumbled on "RDATA_RENDERREGION_LEFT" (similarly for left, top and bottom).
      Could we use this approach instead—rendering specific regions of the image separately and then assembling them into the final image?

      Has anyone implemented a similar solution or can provide guidance on whether this approach is feasible?

      Thank you for your assistance.

      K 1 Reply Last reply Reply Quote 0
      • K Online
        karthikbp @karthikbp
        last edited by

        I was able to get tile rendering done by running a script like this:

        import c4d
        import os
        
        doc: c4d.documents.BaseDocument
        op: c4d.BaseObject | None
        
        
        def main() -> None:
            # --- Configure these ---
            tiles_x = 2  # columns
            tiles_y = 2  # rows
            output_dir = os.path.join(os.path.expanduser("~"), "Desktop", "tiles")
            # ------------------------
        
            os.makedirs(output_dir, exist_ok=True)
        
            base_rd = doc.GetActiveRenderData()
            full_w = int(base_rd[c4d.RDATA_XRES])
            full_h = int(base_rd[c4d.RDATA_YRES])
            tile_w = full_w // tiles_x
            tile_h = full_h // tiles_y
        
            for ty in range(tiles_y):
                for tx in range(tiles_x):
                    rd = base_rd.GetClone()
        
                    left = tx * tile_w
                    top = ty * tile_h
                    right = left + tile_w
                    bottom = top + tile_h
        
                    # Region at full resolution
                    rd[c4d.RDATA_RENDERREGION] = True
                    rd[c4d.RDATA_RENDERREGION_LEFT] = left
                    rd[c4d.RDATA_RENDERREGION_TOP] = top
                    rd[c4d.RDATA_RENDERREGION_RIGHT] = right
                    rd[c4d.RDATA_RENDERREGION_BOTTOM] = bottom
        
                    # Full-res bitmap — C4D renders region into this
                    bmp = c4d.bitmaps.MultipassBitmap(full_w, full_h, c4d.COLORMODE_RGB)
                    bmp.AddChannel(True, True)
        
                    result = c4d.documents.RenderDocument(
                        doc,
                        rd.GetData(),
                        bmp,
                        c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_SHOWERRORS,
                    )
        
                    if result != c4d.RENDERRESULT_OK:
                        print(f"Tile ({tx}, {ty}) failed with code: {result}")
                        continue
        
                    # Crop the tile region out of the full bitmap
                    tile_bmp = c4d.bitmaps.BaseBitmap()
                    tile_bmp.Init(tile_w, tile_h)
                    for y in range(tile_h):
                        for x in range(tile_w):
                            r, g, b = bmp.GetPixel(left + x, top + y)
                            tile_bmp.SetPixel(x, y, r, g, b)
        
                    path = os.path.join(output_dir, f"tile_{tx}_{ty}.png")
                    tile_bmp.Save(path, c4d.FILTER_PNG)
                    print(f"Saved {path}")
        
            c4d.gui.MessageDialog(
                f"Done! {tiles_x * tiles_y} tiles saved to:\n{output_dir}"
            )
        
        
        if __name__ == "__main__":
            main()
        
        
        K 1 Reply Last reply Reply Quote 0
        • K Online
          karthikbp @karthikbp
          last edited by

          Hmm, I was able to reassemble the tiles within Cinema 4D as well. Here's the script with reassembly.

          Feel free to chime in if we should not be doing this (performance impact, does not work with different renderers etc)

          import c4d
          import os
          
          doc: c4d.documents.BaseDocument
          op: c4d.BaseObject | None
          
          
          def main() -> None:
              """Renders the scene as a grid of tiles, then reassembles into the final image."""
              # --- Configure these ---
              tiles_x = 2
              tiles_y = 2
              output_dir = os.path.join(os.path.expanduser("~"), "Desktop", "tiles")
              final_path = os.path.join(output_dir, "final_assembled.png")
              # ------------------------
          
              os.makedirs(output_dir, exist_ok=True)
          
              base_rd = doc.GetActiveRenderData()
              full_w = int(base_rd[c4d.RDATA_XRES])
              full_h = int(base_rd[c4d.RDATA_YRES])
              tile_w = full_w // tiles_x
              tile_h = full_h // tiles_y
          
              tile_bmps = {}
          
              for ty in range(tiles_y):
                  for tx in range(tiles_x):
                      rd = base_rd.GetClone()
          
                      left = tx * tile_w
                      top_ = ty * tile_h
                      right = left + tile_w
                      bottom = top_ + tile_h
          
                      rd[c4d.RDATA_RENDERREGION] = True
                      rd[c4d.RDATA_RENDERREGION_LEFT] = left
                      rd[c4d.RDATA_RENDERREGION_TOP] = top_
                      rd[c4d.RDATA_RENDERREGION_RIGHT] = right
                      rd[c4d.RDATA_RENDERREGION_BOTTOM] = bottom
          
                      bmp = c4d.bitmaps.MultipassBitmap(full_w, full_h, c4d.COLORMODE_RGB)
                      bmp.AddChannel(True, True)
          
                      print(f"Rendering tile ({tx}, {ty}) region=({left},{top_})-({right},{bottom})")
                      result = c4d.documents.RenderDocument(
                          doc,
                          rd.GetData(),
                          bmp,
                          c4d.RENDERFLAGS_EXTERNAL | c4d.RENDERFLAGS_SHOWERRORS,
                      )
          
                      if result != c4d.RENDERRESULT_OK:
                          print(f"Tile ({tx}, {ty}) failed with code: {result}")
                          return
          
                      # Crop tile — use same coords as region
                      tile_bmp = c4d.bitmaps.BaseBitmap()
                      tile_bmp.Init(tile_w, tile_h)
                      for y in range(tile_h):
                          for x in range(tile_w):
                              r, g, b = bmp.GetPixel(left + x, top_ + y)
                              tile_bmp.SetPixel(x, y, r, g, b)
          
                      tile_path = os.path.join(output_dir, f"tile_{tx}_{ty}.png")
                      tile_bmp.Save(tile_path, c4d.FILTER_PNG)
                      print(f"Saved {tile_path}")
          
                      tile_bmps[(tx, ty)] = tile_bmp
          
              # --- Reassemble: place each tile at its matching position, no flip ---
              print("Assembling final image...")
              final_bmp = c4d.bitmaps.BaseBitmap()
              final_bmp.Init(full_w, full_h)
          
              for ty in range(tiles_y):
                  for tx in range(tiles_x):
                      tile_bmp = tile_bmps[(tx, ty)]
                      dst_x = tx * tile_w
                      dst_y = ty * tile_h
                      for y in range(tile_h):
                          for x in range(tile_w):
                              r, g, b = tile_bmp.GetPixel(x, y)
                              final_bmp.SetPixel(dst_x + x, dst_y + y, r, g, b)
          
              final_bmp.Save(final_path, c4d.FILTER_PNG)
              print(f"Saved assembled image to {final_path}")
              c4d.bitmaps.ShowBitmap(final_bmp)
          
              c4d.gui.MessageDialog(
                  f"Done! {tiles_x * tiles_y} tiles rendered and assembled.\n{final_path}"
              )
          
          
          if __name__ == "__main__":
              main()
          
          
          1 Reply Last reply Reply Quote 0
          • First post
            Last post