motion blur with yafray!

well, if you’ve got the cpu cycles to spare, that is. i’m new to blender and this was my first attempt at a script, so any suggestions/comments are much appreciated. basically this script renders a set of “in-between” frames using an oject’s time-offset and then composits them together. this has 2 consequences: first, you must make sure every object (including the camera) who’s motion you want included in the effect has the “offs ob” button selected in its animation settings panel. the second, and perhaps more annoying caveat is that since the bpython api provides no direct link to the rendered image, i have chosen to access it through the “python image library” which can be downloaded from http://www.pythonware.com/products/pil/

i hope some people give my script a shot, because i’d really like to give back to the community that brought about such an amazing program in the first place. :slight_smile:

here she be:

#!BPY
"""
Name: 'MotionBlur'
Blender: 234
Group: 'Render'
Tooltip: 'Extended motion blur.'
"""

######################################################################
# motionblur.py														 #
#																	 #
# author: steve miller												 #
# email: [email protected]											 #
#																	 #
# this script simulates motion blur by rendering intermediate frames #
# and compositing them together.  its benefits are the ability to	 #
# use an arbitrary number of samples to achieve the desired quality	 #
# and the ability to add motion blur to yafray.						 #
#																	 #
# note that while this approach is fully automated, its efficiency	 #
# suffers from an inability to directly access the render buffer.	 #
# if anyone knows how to do this, feel free to contact me or		 #
# modify the code.													 #
######################################################################

import os
from Blender import *
from Blender.Scene import Render
import PIL
from PIL import Image
import random
#
# initialize default variables
#
duration = 0.5
samples = 2
jitter = 0.003
fileName = "output"
imageType = 1
save = 0
show = 1

def event(evt, val):
	if evt == Draw.ESCKEY:
		Draw.Exit()
		return

def button_event(evt):
	global duration, samples, jitter, fileName, imageType, save, show
	#
	# update variables as modified by the script
	#
	if evt == 1:
		duration = B1.val
	elif evt == 2:
		samples = B2.val
	elif evt == 3:
		jitter = B3.val
	elif evt == 4:
		fileName = B4.val
		pos = fileName.find(".")
		if pos != -1:
			fileName = fileName[:pos]
	elif evt == 5:
		imageType = B5.val
	elif evt == 6:
		save = B6.val
	elif evt == 7:
		show = B7.val
	elif evt == 88 or evt == 9:
		#
		# sync relevant information with blender before performing the render
		#
		objects = Object.Get()
		scene = Scene.GetCurrent()
		camera = scene.getCurrentCamera()
		renderingContext = scene.getRenderingContext()
		animStart = renderingContext.startFrame()
		animEnd = renderingContext.endFrame()
		renderingContext.enableExtensions(0)
		renderingContext.enableMotionBlur(0)
		renderPath = renderingContext.getRenderPath()
		renderingContext.setRenderWinSize(100)
		sizeX = renderingContext.imageSizeX()
		sizeY = renderingContext.imageSizeY()
		if evt == 88:
			startFrame = endFrame = Get('curframe')
		else:
			startFrame = animStart
			endFrame = animEnd
		timeOffset = range(samples)
		if imageType == 1:
			renderingContext.setImageType(Render.BMP)
			renderingContext.enableRGBColor()
			format = "BMP"
			mode = "RGB"
		elif imageType == 2:
			renderingContext.setImageType(Render.PNG)
			renderingContext.enableRGBAColor()
			format = "PNG"
			mode = "RGBA"
		elif imageType == 3:
			renderingContext.setImageType(Render.JPEG)
			renderingContext.enableRGBColor()
			format = "JPG"
			mode = "RGB"
		#
		# loop calculate the time offset for each subsample
		#
		for sampleNum in range(samples):
			timeOffset[sampleNum] = duration/2 - sampleNum*duration/(samples-1)
		#
		# outer loop sets frame to be rendered
		#
		for frame in range(startFrame, endFrame + 1):
			output = Image.new(mode, [sizeX, sizeY])
			#
			# inner loop sets subsample to be rendered
		    #
			for sampleNum in range(samples):
				for objNum in range(len(objects)):
					object = objects[objNum]
					object.setTimeOffset(timeOffset[sampleNum])
				renderingContext.startFrame(frame)
				renderingContext.endFrame(frame)
				camera.dRotX = jitter*random.random()
				camera.dRotZ = jitter*random.random()
				renderingContext.renderAnim()
				camera.dRotX = 0
				os.rename(renderPath + str(frame).zfill(4), renderPath + "f" + str(frame) + "s" + str(sampleNum))
				blend = 1.0/(sampleNum+1)
				file = Image.open(renderPath + "f" + str(frame) + "s" + str(sampleNum))

				output = Image.blend(output, file, blend)
				os.remove(renderPath + "f" + str(frame) + "s" + str(sampleNum))
			if show:
				output.show()
				Render.CloseRenderWindow()
			if save:
				output.save(renderPath + fileName + str(frame).zfill(4) + "." + format)
			renderingContext.startFrame(animStart)
			renderingContext.endFrame(animEnd)
	elif evt == 10:
		Draw.Exit()
		return
	Draw.Redraw()

#
# this defines the appearance of the script to the end user
#
def gui():
	global B1, B2, B3, B4, B5, B6, B7, B8, B9, B10
	BGL.glClearColor(0.7,0.7,0.7,1)
	BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)

	#Boxes
	BGL.glColor3f(0.75, 0.75, 0.75)
	BGL.glRecti(5, 5, 195, 55)
	BGL.glRecti(5, 60, 195, 115)
	BGL.glRecti(5, 140, 195, 220)

	BGL.glColor3f(0.65, 0.65, 0.65)
	BGL.glRecti(5, 115, 195, 135)
	BGL.glRecti(5, 220, 195, 240)

	#Text
	BGL.glColor3f(1,1,1)
	BGL.glRasterPos2i(10, 250)
	Draw.Text("MOTIONBLUR by shteeve")
	BGL.glRasterPos2i(10, 120)
	Draw.Text("Output Options")
	BGL.glRasterPos2i(10, 225)
	Draw.Text("Effect Options")

	#Buttons
	B1 = Draw.Number("Shutter: ", 1, 10, 195, 180, 20, duration, 0.01, 100.0, "Sets the duration of the blur in frames.")
	B2 = Draw.Number("Samples: ", 2, 10, 170, 180, 20, samples, 2, 200, "Sets the number of samples to render between frames.")
	B3 = Draw.Number("Jitter: ", 3, 10, 145, 180, 20, jitter, 0.0, 0.1, "Non-zero values create an antialiasing effect.")	
	B4 = Draw.String("Name: ", 4, 10, 90, 180, 20, fileName, 25, "Sets the file name prefix.")
	B5 = Draw.Menu("Format%t|BMP%x1|PNG%x2|JPG%x3", 5, 10, 65, 60, 20, imageType, "Sets the file type of the output.")
	B6 = Draw.Toggle("Save", 6, 135, 65, 55, 20, save, "Sets whether the output should be saved")
	B7 = Draw.Toggle("Show", 7, 80, 65, 50, 20, show, "Sets whether to view composited frames.")
	B8 = Draw.PushButton("Render Current", 88, 10, 30, 115, 20, "Renders current frame.")
	B9 = Draw.PushButton("Render Animation", 9, 10, 10, 115, 20, "Renders the sequence as set in the Render panel.")
	B10 = Draw.PushButton("Exit", 10, 135, 10, 55, 40, "Exits the script.")

Draw.Register(gui, event, button_event)


print "all is well!
"

So bad none commented…i wish i could try this script but atm i am not in the position to…anyone daring and then commenting?

shteeve, I will try your script, but I want to know first if a do have to install this “python image library” or it is used only to show the image.

Edit: Just realize I will have to install PIL… :wink:

It works!!! Great stuff! Here is the test image I creat using the script. :smiley: :smiley: :smiley:

http://img.photobucket.com/albums/v202/gudi3d/yafraytest.jpg
I used 16 samples in this image. The default value (2) doesn’t generate a nice result, however it renders faster.

haha, i’d figured this thing had slipped into obscurity. i really do feel bad that it has to use the python image library since all the file operations really make it inefficient for real work. but hey, if you’re in a pinch where you really want motion blur in a yafray scene, it gets the job done. i haven’t checked lately, maybe there has been some development of the bpython api to allow direct access to the render buffer. anyone know?

anyway, i’m glad someone is enjoying it :slight_smile: thanks for your comments!

steve

Actually I disagree with you, Steve. The only problem of using the PIL in my opinion is the fact that, after render a sequence o BMP images, I have to batch process them to make TGA with RLE compression (that are far more convenient). Great job, dude! I am working on an animation and I want to render it using YAFRay, the only thing that was worring me was the fact that I had no motion blur. Now its solved! Thanks a lot!

Glad to see my reply had good effects :slight_smile:
Hope more people find this usefull, keep it up guys!

Thanks shteeve :wink:

P.D: May be you can code the other mising feature in yafray: Render fields :wink:

nice work :smiley: I rendered a nice test!

Very nice work! I like these results much better than the mBlur that is created using the internal renderer. What method do you use to do this?