読者です 読者をやめる 読者になる 読者になる

Blenderでカメラの視錐台を表示する

3D空間にオブジェクトを配置するときにカメラの視錐台が表示できると色々便利なのですがBlenderには視錐台を表示する機能はないようです。なので前回のレンダリング画面にぴったりフィットするプレーンを作る方法を応用して視錐台を表示してみました。

f:id:kaiware-daikon:20150114211127p:plain見やすくするためにカメラと視錐台を選択状態にしています

原理

まず(0, 0, -"Clipping Start")と(0, 0, -"Clipping End")の位置に画面とぴったりフィットするプレーンを作ります。次にその2つのプレーンの対応する頂点同士を結ぶと錐台ができます。錐台をワイヤーフレーム表示にして親をカメラに設定するとカメラの動きに追従する視錐台になります。

スクリプト

Perspectiveタイプにのみ対応してています。画面の解像度を変えたり、カメラのClipping StartやClipping Endを変えた場合、スクリプトを再実行する必要があります。

import bpy
import bmesh

def main():
	name = 'ViewFrustum'
	scene = bpy.context.scene
	camera = scene.camera
	screen_v, screen_f = calc_screen_geometry(
		scene.render.resolution_x,
		scene.render.resolution_y,
		camera.data.lens,
		camera.data.sensor_width)
	frustum_v, frustum_f = calc_frustum_geometry(
		screen_v,
		camera.data.clip_start,
		camera.data.clip_end)
	frustum = bpy.data.objects.get(name, None)
	if frustum:
		set_mesh_data(frustum.data, frustum_v, frustum_f)
	else:
		mesh = bpy.data.meshes.new(name)
		set_mesh_data(mesh, frustum_v, frustum_f)
		frustum = bpy.data.objects.new(name, mesh)
		scene.objects.link(frustum)
		frustum.parent = scene.camera
		frustum.hide_render = True
		frustum.draw_type = 'WIRE'

def calc_frustum_geometry(verts, clip1, clip2):
	verts1 = [[v[0]*clip1, v[1]*clip1, -clip1] for v in verts]
	verts2 = [[v[0]*clip2, v[1]*clip2, -clip2] for v in verts]
	verts = verts1 + verts2 
	faces = [[0,1,2,3],[1,0,4,5],[2,1,5,6],[3,2,6,7],[0,3,7,4],[4,5,6,7]]
	return verts, faces	

def calc_screen_geometry(res_x, res_y, lens, sensor_width):
    base = [[0.5,0.5,0],[-0.5,0.5,0],[-0.5,-0.5,0],[0.5,-0.5,0]]
    res_max = max(res_x, res_y)
    r = sensor_width / lens
    sx = r * res_x / res_max
    sy = r * res_y / res_max
    verts = [[v[0]*sx, v[1]*sy, 0] for v in base]
    faces = [[0,1,2,3]]
    return verts, faces
	
def set_mesh_data(mesh, verts, faces):
	bm = bmesh.new()
	for v in verts:
		bm.verts.new(v)
	bm.verts.ensure_lookup_table()
	for f in faces:
		bm.faces.new([bm.verts[vi] for vi in f])
	bm.faces.ensure_lookup_table()
	bm.to_mesh(mesh)
	mesh.update()
	bm.free()

if __name__ == '__main__':
    main()

動作確認: Blender 2.73