[Scripts]Create camera image plane

Hi!

Working on a scifi live action movie (lots of greenscreen footage), so I’ve created some scripts for things we are doing frequently.

The other script should be run with a camera selected and then creates an imageplane, sets up drivers to automatically scale it by the render aspect ratio and focal length of camera and distance (that would be the neg z of the imageplanes location) so it will cover the entire screen. It sets up a cycles material so it’s ready to load an image sequence (I’m using lowres pngs where the green screen has been keyed out in my workflow).

Limitation: no testing that the selected object is a camera (script will fail if it’s not)

EDIT: Now available as an addon in post 3!


import bpy
from bpy_extras.image_utils import load_image
from mathutils import Vector, Euler
import math


def SetupDriverVariables(driver, imageplane):
    camAngle = driver.variables.new()
    camAngle.name = 'camAngle'
    camAngle.type = 'SINGLE_PROP'
    camAngle.targets[0].id = imageplane.parent
    camAngle.targets[0].data_path="data.angle"
    
    depth = driver.variables.new()
    depth.name = 'depth'
    depth.type = 'TRANSFORMS'
    depth.targets[0].id = imageplane    
    depth.targets[0].data_path = 'location'
    depth.targets[0].transform_type = 'LOC_Z'
    depth.targets[0].transform_space = 'LOCAL_SPACE'

#unfortunately not possible to add driver on scene object    
#    resolution_x = driver.variables.new()
#    resolution_x.name = 'resolution_x'
#    resolution_x.type = 'SINGLE_PROP'
#    resolution_x.targets[0].id =bpy.context.scene
#    resolution_x.targets[0].data_path = 'render.resolution_x'
#    resolution_y = driver.variables.new()
#    resolution_y.name = 'resolution_y'
#    resolution_y.type = 'SINGLE_PROP'
#    resolution_y.targets[0].id =bpy.context.scene
#    resolution_y.targets[0].data_path = 'render.resolution_y'
#    pixel_x = driver.variables.new()
#    pixel_x.name = 'pixel_x'
#    pixel_x.type = 'SINGLE_PROP'
#    pixel_x.targets[0].id =bpy.context.scene
#    pixel_x.targets[0].data_path = 'render.pixel_aspect_x'
#    pixel_y = driver.variables.new()
#    pixel_y.name = 'pixel_y'
#    pixel_y.type = 'SINGLE_PROP'
#    pixel_y.targets[0].id =bpy.context.scene
#    pixel_y.targets[0].data_path = 'render.pixel_aspect_y'
            

def SetupDriversForImagePlane(imageplane):
    driver = imageplane.driver_add('scale',1).driver
    driver.type = 'SCRIPTED'
    SetupDriverVariables( driver, imageplane)
    #driver.expression ="-depth*math.tan(camAngle/2)*resolution_y*pixel_y/(resolution_x*pixel_x)"
    driver.expression ="-depth*tan(camAngle/2)*bpy.context.scene.render.resolution_y * bpy.context.scene.render.pixel_aspect_y/(bpy.context.scene.render.resolution_x * bpy.context.scene.render.pixel_aspect_x)"
    driver = imageplane.driver_add('scale',0).driver
    driver.type= 'SCRIPTED'
    SetupDriverVariables( driver, imageplane)
    driver.expression ="-depth*tan(camAngle/2)"

# get selected camera (might traverse children of selected object until a camera is found?)
# for now just pick the active object
def createImagePlaneForCamera(camera):
    imageplane = None
    try:
        depth = 10
        
        
        #create imageplane
        bpy.ops.mesh.primitive_plane_add()#radius = 0.5)
        imageplane = bpy.context.active_object
        imageplane.name = "imageplane"
        bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
        bpy.ops.object.editmode_toggle()
        bpy.ops.mesh.select_all(action='TOGGLE')
        bpy.ops.transform.resize( value=(0.5,0.5,0.5))
        bpy.ops.uv.smart_project(angle_limit=66,island_margin=0, user_area_weight=0)
        bpy.ops.uv.select_all(action='TOGGLE')
        bpy.ops.transform.rotate(value=1.5708, axis=(0,0,1) )
        bpy.ops.object.editmode_toggle()
    
        
        imageplane.location = (0,0,-depth)
        imageplane.parent = camera
        
        
        #calculate scale
        #REPLACED WITH CREATING EXPRESSIONS
        SetupDriversForImagePlane(imageplane)

        #setup material
        if( len( imageplane.material_slots) == 0 ):
            bpy.ops.object.material_slot_add()
            #imageplane.material_slots.
        bpy.ops.material.new()
        mat_index = len(bpy.data.materials)-1
        imageplane.material_slots[0].material = bpy.data.materials[mat_index]
        material =  imageplane.material_slots[0].material
        # if not returned by new use imgeplane.material_slots[0].material
        material.name = 'mat_imageplane_'+camera.name
        material.use_nodes = True
        nodes = material.node_tree.nodes
        links = material.node_tree.links
        
        nodes.clear()
        emissive = nodes.new('ShaderNodeEmission')
        emissive.location = 0, 0
        transparent = nodes.new('ShaderNodeBsdfTransparent')
        transparent.location = 0,100
        mix = nodes.new('ShaderNodeMixShader')
        mix.location = 400,0
        links.new( emissive.outputs[0], mix.inputs[2] )
        links.new( transparent.outputs[0], mix.inputs[1] )
        outnode = nodes.new('ShaderNodeOutputMaterial')
        outnode.location = 800,0
        links.new( mix.outputs[0], outnode.inputs[0] )
        texture = nodes.new('ShaderNodeTexImage')
        texture.location = -400,0
        links.new( texture.outputs[0], emissive.inputs[0] )    
        links.new( texture.outputs[1], mix.inputs[0] )
        #texture.image = bpy.ops.image.open(filepath="c:\
ova\\keyed\\1\\1_5_1\\1_5_1.00000.png")

    
    except Exception as e:
        imageplane.select=False
        camera.select = True
        raise e    
    return {'FINISHED'}



# main
print("========================")
print("   SCRIPT STARTING")
print("========================")

camera = bpy.context.active_object #bpy.data.objects['Camera']
createImagePlaneForCamera(camera)

By the way, is there some way to get the texture correctly (with alpha that is) shown in the viewport when selecting ‘material’ or ‘textured’ (if so, I haven’t found it, I’m using rendered preview to test placement relative to my backgrounds)? Is there a script available to save an animation from the viewport when it’s played (so I can use the textured mode instead of opengl rendering - difference in speed is large)?

Best wishes and happy blending!
/Torbjörn

4 Likes

Just found this, it looks really cool. I have been wondering how to create an active layout for the compositor, I guess this script would be helpful when trying to line up the footage.

Hi tobbew,
I’m interested in your script, but some screenshots or a video would be helpful to fully understand how it works. Thank you.

Noticing there was an interest for the script I turned it into an addon and attached it.

Install as any other addon, select your camera, tap space and select ‘Create camera image plane’ from the list of operators.

Attachments

create_image_plane.zip (1.99 KB)

1 Like

Oh wow, thanks. I really like it when people go to the trouble of making their script a proper addon. It makes it easier to promote Blender’s cool dev community.

1 Like

@3pointEdit: You’re welcome.
@chloesdad: The script does not do anything magical, it creates a plane and setups some drivers so when the distance from the camera (-z) is changed the plane is automatically scaled to cover the screen and gives it a cycles material so i’ll just have to load the texture. It’s not complicated to do by hand, but I created a script since it’s something I’d otherwise have to do a repeatedly. Of course a possibility might have been to just create it once and keep it in a blend file and then import the camera together with the plane. That would probably have worked as well, but I needed to learn some python to be able to make other repeated tasks faster in the future.

1 Like

Thanks Tobbew for your explanation.

1 Like

tobbew, when i load this script then warning message is shown - auto-run disabled: driver ‘-depth*tan(camAngle/2)’
After that the image plane is in outliner but in the view there’s no image plane - even after loading image sequence or single image to the shader.

I think the problem is in object scale drivers script. I deleted object scale X and Y drivers and then i can manipulate scale of the image plane and after that image plane with texture is shown, but after thant when i move image plane there’s no scaling to fit camera view and i need to do this all the time by hand.

Mayby i do something wrong so can you create short video how to do this correctly?

For drivers and some scripts you must goto user prefs > file > “auto run python scripts” and turn on that preference. It is off by default.

1 Like

So I have been playing with the script. I wonder how you determine the distance to place the plane from camera? How does the script achieve placement along the z axis?

Question-
If I move the plane how would I return it to the initial location relative to the camera?

I guess I can add a constraint after running the script, but would this conflict with whatever you are doing? I cannot see a driver or constraint that performs the rotation lock?
EDIT:
After running the script I added:

  1. Limit location set to -10 Z units min/max (I read that was the figure the script assigned)
  2. Limit rotation on all axis

Then I free the plane by reducing the ‘influence’ to zero on both constraints. To restore the registered initial location you increase the ‘influence’ again.

EDIT2:
I can’t work out how to reduce the influence of the Scale Driver in the script though? It would be nice if I could key frame this too?

EDIT3:
Ok so I just mute the drivers.

1 Like

Thanks! Now everything works!

1 Like

@3pointedit:
I don’t understand your question, to be honest. What are your trying to accomplish?

The scripts sets up drivers with the goal to have it always cover the entire screen no matter what the distance from the camera is. In my own workflow it is not supposed to be moved except for depth (-z if I remember correctly, currently not on my own computer). The idea is to move the camera.

I use it when I have lot’s of angles of green screen footage and a 3d rendered environment.
My workflow (not the only possible one, you may use it however you wish):

  1. film footage and prepare lowpoly background
  2. camera match the footage.
  3. key away the green screen and output a lowres proxy of the keyed footage as a png serie
  4. run the script on the camera, set up the Proxy footage for the material
  5. place the camera and adjust the depth to the camera image plane (since the plane is parented to the camera it will follow and meanwhile I can use the cycles viewport rendering to see through the green screen and see how the placing will be and scrub through the animation)
  6. model details and fix textures on parts visible in the camera angles or for objets outside whose shadows or reflections will affect the scene (if we had done a proper previs we’d already know, but we didn’t)
    7 ) render final footage without the camera image plane and do proper post compositing

The script does not do any placement along z except for a default which bears no specific meaning, but is supposed to be changed to what’s appropriate for your scene. I’m working on (how originally around here :slight_smile: ) a hobby scifi short with all green screen environments.

Oh I see. I wonder where I can look to see parenting? Anyway I like to use it for motion graphics, where I want the image, video or art work to be registered perfectly to the camera as a default position. Then I need the flexibility to animate away from that position and return to it. It’s really cool that the scale driver makes the flat field behave as an ortho camera view, but I can still have 3d accurate perspective.

any chance you could grab a screens to demonstrate your layout?

tobbew,
thanks for the script!

you can see the parenting in the outliner (the image plane). I think you can see it also in the object properties. If you want to turn it on anf off you might want to use a parent constraint instead so you can animate its weight. Then you might also add an empty to the scene and also add a parent constrain with an inverted weight from the one from the camera so uou can animate the position with the empty and by animating the weight. You might want to change the scale equations to mix between the scale of the empty and the current formula based on the weight of the constrain

Hmmm, good ideas. Thanks for the tips.

1 Like

having sevond thoughts just combining current method with a copy transforms, rotations and scale constraints from the empty could do it and might be both easier to setup and use. Haven’t tried though.

OMG!! That is so clean and easy. Thank you!!
EDIT
It’s worth saying again…Thankyou

Hello tobbew, nice script.
I found one ‘gotcha’. If your preferences are set to add objects aligned to ‘view’, it will create a plane at an odd angle.
If the preference is set to add object to ‘world’ it works.
Outside of that, cool script.

@@tobbew
Great script!
I found it very useful :yes:
To make it even more comfortable I added two lines of code to get it in the “Add Menu” (Shift A) :wink:

create_image_plane.py.zip (2.04 KB)

  • Davide