©Anton Podvalny

Table of Contents

Introduction


In this chapter we'll cover another type of animations supported by V-Ray - the flythrough animations. This means scenes where the camera "flies through" the scene, e.g. has its position animated. The other name which is used is "walk-through" animations.

Parameters


Light cache

If we use light cache, it's best if we calculate it for the entire walk-through animation, and not for a single frame only. Note that this is not strictly necessary - we can render the animation with the light cache being calculated each frame; however, rendering it only once will save rendering time, especially for long animations.

For this to work, we need to set the light cache settings parameter mode to Fly-through (SettingsLightCache::mode = 1). Make sure that the timeline animation range (set in SettingsOutput) matches the range which you want to render. This is important because the light cache will look at the current timeline animation range when calculating the fly-through cache.

Since all the light cache samples will be distributed among all the animation frames, we will need to increase the light cache Subdivs value (for example SettingsLightCache::subdivs = 2000, the default is 1000). The exact value depends on the quality you want to achieve and on your specific animation. If the camera moves slowly, or the path is relatively short (e.g. just one room of a house) then you can use lower Subdivs value, since most of the samples will fall in the same place anyways. If the camera moves quickly or covers larger parts of the scene, you will need more samples to get adequate coverage everywhere.

Here's an explanation of the Fly-through mode taken from the docs: "Fly-through – Computes a light cache for an entire fly-through animation, assuming that the camera position/orientation is the only thing that changes. The movement of the camera in the active time segment only is taken in consideration. Note that it may be better to set Scale to World (SettingsLightCache::world_scale = true) for fly-through animations. The light cache for the entire animated sequence is computed only at the first rendered frame and is reused without changes for subsequent frames."

We'll cover in depth the light cache parameters in a later chapter about GI.

The preferred method for rendering animation is by using Brute force/Light cache as primary/secondary engine or Brute force/Brute force.

Example

 

The above animation is generated by rendering the file "Animated_Camera.vrscene" from the scene bundle. Check the comments inside the file and the included ones to see how GI can be used more optimally in an animation.

Code Example


This example is similar to the one from the previous chapter, but instead of animating an object, we'll animate the renderView's position. Here, the so called Animation2 API (available in AppSDK 6 and above is used).

Animation2 API is particularly useful, in the case when applying changes to animated parameter values during rendering. In this case, there's no need to change the current time/frame with renderer.setCurrentTime(t).

 

# Compatibility with Python 2.7.
from __future__ import print_function

# The directory containing the vray shared object should be present in the PYTHONPATH environment variable.
# Try to import the vray module from VRAY_SDK/python, if it is not in PYTHONPATH
import sys, os
VRAY_SDK = os.environ.get('VRAY_SDK')
if VRAY_SDK:
    sys.path.append(os.path.join(VRAY_SDK, 'python'))
import vray

startFrame = 1
endFrame = 20
SCENE_PATH = os.path.join(os.environ.get('VRAY_SDK'), 'scenes')
# Change process working directory to SCENE_PATH in order to be able to load relative scene resources.
os.chdir(SCENE_PATH)

def updateNextFrameRenderView(renderer, startFrame):
    # Next frame to be updated
    frameToUpdate = renderer.frame + 1 if renderer.frame > 0 else startFrame + 1
    # t corresponds to the time of the next frame to be rendered
    t = frameToUpdate / renderer.classes.SettingsOutput.getInstanceOrCreate().frames_per_second
    # Obtain a reference to the 'RenderView' plugin.
    renderView = renderer.classes.RenderView.getInstanceOrCreate()
    # Obtain a copy of the renderView transform at time t.
    updatedTransform = renderView.getValue('transform', t)
    # Modify the copy of the renderView transform.
    # The changes do not affect the scene directly since updatedTransform is a copy of the actual transform.
    updatedTransform = updatedTransform.replaceOffset(updatedTransform.offset + vray.Vector(0, 0, 3 * t))
    # Update the transform value in renderView at time t (applying the changes above).
    # NOTE: Animation2 API is particularly useful, in the case when 
    # applying changes to animated parameter values during rendering.
    # In this case, there's no need to change the current time/frame with renderer.time = t
    renderView.setValue('transform', updatedTransform, t)

def onStateChanged(renderer, oldState, newState, instant):
    if newState == vray.RENDERER_STATE_IDLE_FRAME_DONE or newState == vray.RENDERER_STATE_IDLE_DONE:
        print('Image Ready, frame ' + str(renderer.frame) + ' (sequenceEnd = ' + str(renderer.sequenceEnded) + ')')
        if newState == vray.RENDERER_STATE_IDLE_FRAME_DONE:
            # Prepare the renderView of the next frame to be rendered
            updateNextFrameRenderView(renderer, startFrame)
            # If the sequence has NOT finished - continue with next frame.
            renderer.continueSequence()

# Create an instance of VRayRenderer with default options.
# The renderer is automatically closed after the `with` block.
with vray.VRayRenderer() as renderer:
    # Register a simple log callback. Always useful for debugging.
    def dumpMsg(renderer, message, level, instant):
        if level == vray.LOGLEVEL_ERROR:
            print("[ERROR]", message)
        elif level == vray.LOGLEVEL_WARNING:
            print("[Warning]", message)
        elif level == vray.LOGLEVEL_INFO:
            print("[info]", message)
        # Uncomment for testing, but you might want to ignore these in real code
        #else: print("[debug]", message)
    renderer.setOnLogMessage(dumpMsg)
    # Add a listener for the renderer state change event.
    # We will use it to detect when a frame is completed and allow the next one to start.
    renderer.setOnStateChanged(onStateChanged)
    # Load scene from a file.
    renderer.load(os.path.join(SCENE_PATH, 'animation.vrscene'))
    # Maximum paths per pixel for interactive mode. Set a low sample level to complete rendering faster.
    # The default value is unlimited. We have to call this *after* loading the scene.
    renderer.setInteractiveSampleLevel(1)
    # NOTE: 'useAnimatedValues' is not necessary to be set, in case only Animation2 API is used
    # renderer.useAnimatedValues = True
    # Prepare the renderView of the next frame to be rendered
    updateNextFrameRenderView(renderer, startFrame)
    # Set 'start' and 'end' of the sequence. 'step' is omitted and defaults to 1.
    # For all possible use cases of 'renderSequence' check the documentation for VRayRenderer.renderSequence.
    renderer.renderSequence({'start': startFrame, 'end': endFrame})
    # Wait until the entire sequence has finished rendering.
    renderer.waitForSequenceEnd()
#define VRAY_RUNTIME_LOAD_PRIMARY
#include "vraysdk.hpp"
#include "vrayplugins.hpp"
#include "utils.h"

using namespace VRay;
using namespace VRay::Plugins;
using namespace std;

const char *BASE_PATH = getenv("VRAY_SDK");
string SCENE_PATH = (BASE_PATH ? string(BASE_PATH) : string(".")) + PATH_DELIMITER + "scenes";
const int startFrame = 1;
const int endFrame = 20;

void updateNextFrameRenderView(VRayRenderer &renderer, const int startFrame) {
	const int currentFrame = renderer.getCurrentFrame();
	// Next frame to be updated
	const int frameToUpdate = (currentFrame > 0) ? (currentFrame + 1) : (startFrame + 1);
	// t corresponds to the time of the next frame to be rendered
	const double t = frameToUpdate / renderer.getInstanceOrCreate<SettingsOutput>().get_frames_per_second();
	// Obtain a reference to the 'RenderView' plugin.
	RenderView renderView = renderer.getInstanceOrCreate<RenderView>();
	// Obtain a copy of the renderView transform at time t.
	Transform updatedTransform = renderView.get_transform(t);
	// Modify the copy of the renderView transform.
	// The changes do not affect the scene directly since updatedTransform is a copy of the actual transform.
	updatedTransform.offset += Vector(0.0, 0.0, 3 * t);
	// Update the transform value in renderView at time t (applying the changes above).
	// NOTE: Animation2 API is particularly useful, in the case when 
	// applying changes to animated parameter values during rendering.
	// In this case, there's no need to change the current time/frame with renderer.setCurrentTime(t)
	renderView.set_transform(updatedTransform, t);
}

void onStateChanged(VRayRenderer &renderer, RendererState oldState, RendererState newState, double instant, void* userData) {
	if (newState == IDLE_FRAME_DONE || newState == IDLE_DONE) {
		printf("Image Ready, frame %d (sequenceEnded = %d)\n", renderer.getCurrentFrame(), renderer.isSequenceEnded());
		if (newState == IDLE_FRAME_DONE) {
			// Prepare the renderView of the next frame to be rendered
			updateNextFrameRenderView(renderer, startFrame);
			// If the sequence has NOT finished - continue with next frame.
			renderer.continueSequence();
		}
	}
}

int main() {
	// Change process working directory to SCENE_PATH in order to be able to load relative scene resources.
	changeCurrentDir(SCENE_PATH.c_str());
	// Load V-Ray SDK library.
	VRayInit init(NULL, true);
	// Create an instance of VRayRenderer with default options.
	// The renderer is automatically closed at the end of the current scope.
	VRayRenderer renderer;
	// It's recommended to always have a console log
	renderer.setOnLogMessage(logMessage);
	// Add a listener for the renderer state change event.
	// We will use it to detect when a frame is completed and allow the next one to start.
	renderer.setOnStateChanged(onStateChanged);
	// Load scene from a file.
	renderer.load("animation.vrscene");
	// Maximum paths per pixel for interactive mode. Set a low sample level to complete rendering faster.
	// The default value is unlimited. We have to call this *after* loading the scene.
	renderer.setInteractiveSampleLevel(1);
	// NOTE: 'useAnimatedValues' is not necessary to be set, in case only Animation2 API is used
	// renderer.useAnimatedValues(true);
	// Prepare the renderView of the next frame to be rendered
	updateNextFrameRenderView(renderer, startFrame);
	// Set 'start' and 'end' of the sequence.
	// For all possible use cases of 'renderSequence' check the documentation for VRayRenderer.renderSequence.
	SubSequenceDesc subSequenceDescriptions[1];
	subSequenceDescriptions[0].start = startFrame;
	subSequenceDescriptions[0].end = endFrame;
	subSequenceDescriptions[0].step = 1;
	renderer.renderSequence(subSequenceDescriptions, 1);
	// Wait until the entire sequence has finished rendering.
	renderer.waitForSequenceEnd();
	return 0;
}
using System;
using System.IO;
using VRay;
using VRay.Plugins;

namespace _03_animation2_api
{
	class Program
	{
		const int startFrame = 1;
		const int endFrame = 20;
		static void UpdateNextFrameRenderView(VRayRenderer renderer, int startFrame)
		{
			// Next frame to be updated
			int frameToUpdate = (renderer.Frame > 0) ? (renderer.Frame + 1) : (startFrame + 1);
			// t corresponds to the time of the next frame to be rendered
			double t = frameToUpdate / renderer.GetInstanceOrCreate<SettingsOutput>().FramesPerSecond;
			// Obtain a reference to the 'RenderView' plugin.
			RenderView renderView = renderer.GetInstanceOrCreate<RenderView>();
			// Obtain a copy of the renderView transform at time t.
			Transform updatedTransform = renderView.Get_Transform(t);
			// Modify the copy of the renderView transform.
			// The changes do not affect the scene directly since updatedTransform is a copy of the actual transform.
			updatedTransform = updatedTransform.ReplaceOffset(updatedTransform.Offset + new Vector(0, 0, 3 * t));
			// Update the transform value in renderView at time t (applying the changes above).
			// NOTE: Animation2 API is particularly useful, in the case when 
			// applying changes to animated parameter values during rendering.
			// In this case, there's no need to change the current time/frame with renderer.Time = t
			renderView.Set_Transform(updatedTransform, t);
		}

		static void Main(string[] args)
		{
			string SCENE_PATH = Path.Combine(Environment.GetEnvironmentVariable("VRAY_SDK"), "scenes");
			// Change process working directory to SCENE_PATH in order to be able to load relative scene resources.
			Directory.SetCurrentDirectory(SCENE_PATH);
			// Create an instance of VRayRenderer with default options. The renderer is automatically closed after the `using` block.
			using (VRayRenderer renderer = new VRayRenderer())
			{
				// Add a listener for any type of log message.
				renderer.LogMessage += new EventHandler<MessageEventArgs>((source, e) =>
				{
					// You can remove the if for testing, but you might want to ignore Debug in real code
					if (e.LogLevel != LogLevelType.Debug)
					{
						Console.WriteLine(String.Format("[{0}] {1}", e.LogLevel.ToString(), e.Message));
					}
				});
				// Add a listener for state changes.
				// It is invoked when the renderer prepares for rendering, renders, finishes or fails.
				renderer.StateChanged += new EventHandler<StateChangedEventArgs>((source, e) =>
				{
					if (e.NewState == VRayRenderer.RendererState.IDLE_FRAME_DONE || e.NewState == VRayRenderer.RendererState.IDLE_DONE)
					{
						Console.WriteLine("Image Ready, frame " + renderer.Frame + " (sequenceEnded = " + renderer.IsSequenceEnded + ")");

						// Prepare the renderView of the next frame to be rendered
						UpdateNextFrameRenderView(renderer, startFrame);
						// If the sequence has NOT finished - continue with next frame.
						renderer.ContinueSequence();
					}
				});
				// Load scene from a file.
				renderer.Load("animation.vrscene");
				// Maximum paths per pixel for interactive mode. Set a low sample level to complete rendering faster.
				// The default value is unlimited. We have to call this *after* loading the scene.
				renderer.SetInteractiveSampleLevel(1);
				// NOTE: 'UseAnimatedValues' is not necessary to be set, in case only Animation2 API is used
				// renderer.UseAnimatedValues = true;
				// Prepare the renderView of the next frame to be rendered
				UpdateNextFrameRenderView(renderer, startFrame);
				// Set 'start' and 'end' of the sequence. 'step' is omitted and defaults to 1.
				// For all possible use cases of 'renderSequence' check the documentation for VRayRenderer.renderSequence.
				renderer.RenderSequence(new SubSequenceDesc[] { new SubSequenceDesc(startFrame, endFrame) });
				// Wait until the entire sequence has finished rendering.
				renderer.WaitForSequenceEnd();
			}
		}
	}
}
var path = require('path');
var vray = require(path.join(process.env.VRAY_SDK, 'node', 'vray'));
var SCENE_PATH = path.join(process.env.VRAY_SDK, 'scenes');
const startFrame = 1;
const endFrame = 20;

function updateNextFrameRenderView(renderer, startFrame) {
	// Next frame to be updated
	var frameToUpdate = (renderer.frame > 0) ? (renderer.frame + 1) : (startFrame + 1);
	// t corresponds to the time of the next frame to be rendered
	var t = frameToUpdate / renderer.classes.SettingsOutput.getInstanceOrCreate().frames_per_second;
	// Obtain a reference to the 'RenderView' plugin.
	var renderView = renderer.classes.RenderView.getInstanceOrCreate()
	// Obtain a copy of the renderView transform at time t.
	var updatedTransform = renderView.getValue("transform", t);
	// Modify the copy of the renderView transform.
	// The changes do not affect the scene directly since updatedTransform is a copy of the actual transform.
	updatedTransform = updatedTransform.replaceOffset(updatedTransform.offset.add(vray.Vector(0, 0, 3 * t)));
	// Update the transform value in renderView at time t (applying the changes above).
	// NOTE: Animation2 API is particularly useful, in the case when 
	// applying changes to animated parameter values during rendering.
	// In this case, there's no need to change the current time/frame with renderer.time = t
	renderView.setValue("transform", updatedTransform, t);
}

// Change process working directory to SCENE_PATH in order to be able to load relative scene resources.
process.chdir(SCENE_PATH);
// Create an instance of VRayRenderer with default options.
var renderer = vray.VRayRenderer();
// It's recommended to always have a console log callback
renderer.on("logMessage", function (message, level, instant) {
	if (level == vray.LOGLEVEL_ERROR)
		console.log("[ERROR] ", message);
	else if (level == vray.LOGLEVEL_WARNING)
		console.log("[Warning] ", message);
	else if (level == vray.LOGLEVEL_INFO)
		console.log("[info] ", message);
	// Uncomment for testing, but you might want to ignore these in real code
	//else console.log("[debug] ", message);
});
// Add a listener for the renderer state change event.
// We will use it to detect when a frame is completed and allow the next one to start.
renderer.on("stateChanged", function (oldState, newState, instant) {
	if (newState == "idleFrameDone" || newState == "idleDone") {
		console.log("Image Ready, frame " + renderer.frame + " (sequenceEnd = " + renderer.sequenceEnded + ")");
		// Check if the sequence has finished rendering.
		if (newState == "idleDone") {
			// Closes the renderer.
			renderer.close();
		} else {
			// Prepare the renderView of the next frame to be rendered
			updateNextFrameRenderView(renderer, startFrame);
			// If the sequence has NOT finished - continue with next frame.
			renderer.continueSequence();
		}
	} else if (newState.startsWith("idle")) {
		console.log("Unexpected end of render sequence: ", newState);
		renderer.close();
	}
});

// Load scene from a file asynchronously.
renderer.load("animation.vrscene", function (err) {
	if (err) throw err;
	// Maximum paths per pixel for interactive mode. Set a low sample level to complete rendering faster.
	// The default value is unlimited. We have to call this *after* loading the scene.
	renderer.setInteractiveSampleLevel(1);
	// NOTE: "useAnimatedValues" is not necessary to be set, in case only Animation2 API is used
	// renderer.useAnimatedValues = true;
	// Prepare the renderView of the next frame to be rendered
	updateNextFrameRenderView(renderer, startFrame);
	// Set "start" and "end" of the sequence.
	// "step" is omitted and defaults to 1.
	// For all possible use cases of "renderSequence" check the documentation
	renderer.renderSequence({ "start": startFrame, "end": endFrame });
	// Prevent garbage collection and exiting nodejs until the renderer is closed.
	renderer.keepAlive();
});
  • No labels