SOLVED Implementing a watermark on render

Hi all

I've just began plugin development for a couple of days. The documentation is too complex for me. So far, I could only create a sample plugin with dialog and simple shape create , I could only do this by watching @kbar tutorial. (cheers to him)

what I'm tryin to do:
when I start the plugin, I want it to begin the rendering (of a simple shape I created) and at the exact start of rendering i want to implement a watermark. I want the watermark to be a pixelized text and color changing.

been looking ate the documentation for hours. the only thing i know is i need to use VideoPostData but whatever i write turns out to be wrong.
it would be much appreciated if someone provides a brief guide.

Hello @shetal,

thank you for reaching out to us. The reformulation of your question and the conformance with the forum guidelines on tagging is also much appreciated.

About your question: As stated in the forum guidelines, we cannot provide full solutions for questions, but provide answers for specific questions. Which is why I will not show here any example code, the first step would have to be made by you. I will instead roughly line out the purpose of and workflow around VideoPostData, which I assume is what you are looking for anyway.

VideoPostData is derived from NodeData, the base class to implement a node for a classic API scene graph. Node means here 'something that lives inside a document and is an addressable entity', examples for such nodes are materials, shaders, objects, tags, ..., and as such 'video post' node. As mentioned in its class description, VideoPostData is a versatile plugin interface which can be used to intervene a rendering process in multiple ways. The most tangible place for VideoPostData in the app is the render settings where video post plugins can be added as effects for a rendering process as shown below with the built-in water mark video post node.

12dc3981-c9af-4e3b-80a3-a1e5dabc2a42-image.png

VideoPostData is an effect, meaning that you cannot use it to invoke a rendering process and on its own it also cannot forcibly add itself to a rendering and must be included manually with the RenderData, the render settings of a rendering. However, a user could make a render setting which includes such watermark effect part of his or her default render settings. One could also implement another plugin interface, SceneHookData, to automatically add such effect to every active document. We would not encourage that though, as this could be confusing or infuriating for users. Finally, such VideoPostData plugin would be visible by default like all NodeData plugins, i.e., it would appear as something in menus that the user can add and interact with. To prevent this if desired, one would have to register the plugin with the flag PLUGINFLAG_HIDE suppress it from popping up in the 'Effect ...' button menu. I cannot tell you with certainty if it is possible to hide programmatically added effect nodes from the users view in the effect list of a render settings. There are some flags which can be used to hide instances of nodes, but I would have to test myself if this also applies in this list, it is more likely that this will not be possible.

To implement a VideoPostData plugin interface, one can override multiple methods and take different approaches, the most commonly used one is to override VideoPostData::Execute(Link) which will be called multiple times for each rendered frame. The method follows a flag/message logic which is commonly used in Cinema 4D's classic API, where one gets passed in a flag which signalizes in which context the method is being called. Here the context is at which state of the rendering this call is being made, and the chain is:

  • VIDEOPOSTCALL::FRAMESEQUENCE - Series of images starts.
  • VIDEOPOSTCALL::FRAME - Image render starts.
  • VIDEOPOSTCALL::SUBFRAME - Sub-frame starts.
  • VIDEOPOSTCALL::RENDER - Render precalculation.
  • VIDEOPOSTCALL::INNER - Render precalculation.
  • VIDEOPOSTCALL::INNER - Immediately after rendering.
  • VIDEOPOSTCALL::RENDER - Immediately after shader cleanup.
  • VIDEOPOSTCALL::SUBFRAME - Sub-frame rendering done.
  • VIDEOPOSTCALL::FRAME - Frame rendering done.
  • VIDEOPOSTCALL::FRAMESEQUENCE - Complete rendering process finished.

These flags are accompanied by information if the flags denotes the opening or closing of that 'step' in the rendering process. A developer often then restricts its plugin functionality to a certain flag. I.e., in your case you only want to execute some code when the closing VIDEOPOSTCALL::FRAME is being passed, i.e., after a single frame and all its sub-frames have been rendered. Execute() also passes in a pointer to a VideoPostStruct(Link) which carries information about the ongoing rendering. One of its fields is render, a pointer to a Render(Link). This data structure represents a rendering with multiple buffers and provides the method GetBuffer() which returns a pointer to VPBuffer buffer. In your case you would want to retrieve the RGBA buffer for the rendering by requesting the VPBUFFER_RGBA buffer (Link) with GetBuffer().

This buffer is then finally the pixel buffer, the bitmap data you want to modify. The buffer is being read and written in a line wise fashion with VPBuffer::GetLine() and ::SetLine(). Here you would have to superimpose your watermark information onto the frame. I would do this in a shader like fashion, i.e., write a function which I can query for a texture coordinate for every pixel/fragment in every line and it will then return an RBGA value which I could then combine with the RGBA information which is in the buffer at that coordinate. The details on that depend on what you want to do, e.g.,

  • Should the watermark be tiled across the frame or just live in a 'corner'?
  • Should it contain alpha information?
  • Can the user influence it, or is it just a png file on disk?
  • etc...

and the answers to that are mostly algorithmic and not directly connected to our API which limits the amount of support we can provide for them. If this all sounds very confusing to you, it might be helpful to look at our video post examples I did post in the previous thread, e.g., vpreconstructimage.cpp, as this will probably make things less daunting.

If you decide that you do not want to take this route for technical or complexity reasons, you could write a SceneHookData plugin which listens via NodeData::Message for MSG_MULTI_RENDERNOTIFICATION(Link), a message family which is being sent in the context of a rendering. There you would have to evaluate the start field in the RenderNotificationData(Link) accompanying the message, to determine if the call is for the start or end of a rendering. Then you could grab the rendering output file(s) on disk with the help of the render settings from disk and 'manually' superimpose your watermark information. This will come with the drawback that you might have to deal with compressed video files like mpeg or Avi and all the image formats. Some complexity in that can be hidden away with our BaseBitmap type I did mention in my last posting, but not all of it. There is also the fact that you might run into problems when this plugin runs on a render server, where you cannot easily obtain write or even read access to files of the render output.

I hope this give you some desired guidance,
Ferdinand

Hello @shetal,

thank you for reaching out to us. The reformulation of your question and the conformance with the forum guidelines on tagging is also much appreciated.

About your question: As stated in the forum guidelines, we cannot provide full solutions for questions, but provide answers for specific questions. Which is why I will not show here any example code, the first step would have to be made by you. I will instead roughly line out the purpose of and workflow around VideoPostData, which I assume is what you are looking for anyway.

VideoPostData is derived from NodeData, the base class to implement a node for a classic API scene graph. Node means here 'something that lives inside a document and is an addressable entity', examples for such nodes are materials, shaders, objects, tags, ..., and as such 'video post' node. As mentioned in its class description, VideoPostData is a versatile plugin interface which can be used to intervene a rendering process in multiple ways. The most tangible place for VideoPostData in the app is the render settings where video post plugins can be added as effects for a rendering process as shown below with the built-in water mark video post node.

12dc3981-c9af-4e3b-80a3-a1e5dabc2a42-image.png

VideoPostData is an effect, meaning that you cannot use it to invoke a rendering process and on its own it also cannot forcibly add itself to a rendering and must be included manually with the RenderData, the render settings of a rendering. However, a user could make a render setting which includes such watermark effect part of his or her default render settings. One could also implement another plugin interface, SceneHookData, to automatically add such effect to every active document. We would not encourage that though, as this could be confusing or infuriating for users. Finally, such VideoPostData plugin would be visible by default like all NodeData plugins, i.e., it would appear as something in menus that the user can add and interact with. To prevent this if desired, one would have to register the plugin with the flag PLUGINFLAG_HIDE suppress it from popping up in the 'Effect ...' button menu. I cannot tell you with certainty if it is possible to hide programmatically added effect nodes from the users view in the effect list of a render settings. There are some flags which can be used to hide instances of nodes, but I would have to test myself if this also applies in this list, it is more likely that this will not be possible.

To implement a VideoPostData plugin interface, one can override multiple methods and take different approaches, the most commonly used one is to override VideoPostData::Execute(Link) which will be called multiple times for each rendered frame. The method follows a flag/message logic which is commonly used in Cinema 4D's classic API, where one gets passed in a flag which signalizes in which context the method is being called. Here the context is at which state of the rendering this call is being made, and the chain is:

  • VIDEOPOSTCALL::FRAMESEQUENCE - Series of images starts.
  • VIDEOPOSTCALL::FRAME - Image render starts.
  • VIDEOPOSTCALL::SUBFRAME - Sub-frame starts.
  • VIDEOPOSTCALL::RENDER - Render precalculation.
  • VIDEOPOSTCALL::INNER - Render precalculation.
  • VIDEOPOSTCALL::INNER - Immediately after rendering.
  • VIDEOPOSTCALL::RENDER - Immediately after shader cleanup.
  • VIDEOPOSTCALL::SUBFRAME - Sub-frame rendering done.
  • VIDEOPOSTCALL::FRAME - Frame rendering done.
  • VIDEOPOSTCALL::FRAMESEQUENCE - Complete rendering process finished.

These flags are accompanied by information if the flags denotes the opening or closing of that 'step' in the rendering process. A developer often then restricts its plugin functionality to a certain flag. I.e., in your case you only want to execute some code when the closing VIDEOPOSTCALL::FRAME is being passed, i.e., after a single frame and all its sub-frames have been rendered. Execute() also passes in a pointer to a VideoPostStruct(Link) which carries information about the ongoing rendering. One of its fields is render, a pointer to a Render(Link). This data structure represents a rendering with multiple buffers and provides the method GetBuffer() which returns a pointer to VPBuffer buffer. In your case you would want to retrieve the RGBA buffer for the rendering by requesting the VPBUFFER_RGBA buffer (Link) with GetBuffer().

This buffer is then finally the pixel buffer, the bitmap data you want to modify. The buffer is being read and written in a line wise fashion with VPBuffer::GetLine() and ::SetLine(). Here you would have to superimpose your watermark information onto the frame. I would do this in a shader like fashion, i.e., write a function which I can query for a texture coordinate for every pixel/fragment in every line and it will then return an RBGA value which I could then combine with the RGBA information which is in the buffer at that coordinate. The details on that depend on what you want to do, e.g.,

  • Should the watermark be tiled across the frame or just live in a 'corner'?
  • Should it contain alpha information?
  • Can the user influence it, or is it just a png file on disk?
  • etc...

and the answers to that are mostly algorithmic and not directly connected to our API which limits the amount of support we can provide for them. If this all sounds very confusing to you, it might be helpful to look at our video post examples I did post in the previous thread, e.g., vpreconstructimage.cpp, as this will probably make things less daunting.

If you decide that you do not want to take this route for technical or complexity reasons, you could write a SceneHookData plugin which listens via NodeData::Message for MSG_MULTI_RENDERNOTIFICATION(Link), a message family which is being sent in the context of a rendering. There you would have to evaluate the start field in the RenderNotificationData(Link) accompanying the message, to determine if the call is for the start or end of a rendering. Then you could grab the rendering output file(s) on disk with the help of the render settings from disk and 'manually' superimpose your watermark information. This will come with the drawback that you might have to deal with compressed video files like mpeg or Avi and all the image formats. Some complexity in that can be hidden away with our BaseBitmap type I did mention in my last posting, but not all of it. There is also the fact that you might run into problems when this plugin runs on a render server, where you cannot easily obtain write or even read access to files of the render output.

I hope this give you some desired guidance,
Ferdinand

thank you very much for the time you spent for writing this guide. I truly cherish the "helping" attitude.
its going to take a while for me to go through these information...

Hello @shetal,

without any further questions or replies, we will consider this thread as solved by Monday the 20th and flag it accordingly.

Thank you for your understanding,
Ferdinand