【第6回MMD杯予選】 Bad Apple!! - [E]xchange 【立体視】

D
どうにか予選間に合ったよ。あとは本選が間に合うか、それが問題。
(YouTube にも上げました - つちのこ、のこのこ。(はてな番外地))
使用した立体視 VMD 作成 Python スクリプトですが以前のものから改良して

  • 回転に対応
  • 視差固定モード追加

したので貼っておきます。
使い方:

  1. カメラモーションの VMD を camera.vmd という名前でセーブする
  2. VMDConverter でそれを camera.csv にする
  3. 以下の Python スクリプトをそれと同じフォルダに作って実行する
  4. camera_L.csv, camera_R.csv の 2つのファイルが出来るのでそれを VMDConverter で camera_L.vmd, camera_r.vmd にする

あとはそれを MMD に読みこめばOK。

ちなみに左右動画を結合するには

が便利。

バグってました! 新版→【立体視】立体レアさんの紹介動画のような何か。(たこうさ入り) - つちのこ、のこのこ。(はてな番外地)

parallax.py

# -*- coding: utf-8 -*-
u"""parallax カメラの PMD→CSV から立体視用データを作る

Python 2.6.5
"""
from __future__ import division
import math

__author__ = 'kadotanimitsuru'
__version__ = '1.1.0'

##PARALLAX_FIX = None  # 視差固定モード。None なら距離に応じた視差。
PARALLAX_FIX = 1.0 / 2  # 数値でその角度の視差で固定(視差の 1/2 を指定)

BETWEEN_EYES = 6.5 / 8 / 2  # 1mmd = 8cm 換算での瞳孔間隔標準(6.5cm) / 2
FILENAME = 'camera'  # csv の主ファイル名
MAGNIFICATION_LIMIT = 3  # 角度補正の拡大率限界

def parallax(distance):  # 視差の半分
    return math.degrees(
        math.atan2(BETWEEN_EYES, distance))

def main(csvfilename):
    f = open(csvfilename + '.csv', 'rb')
    data = f.readlines()
    f.close()
    keyframes = int(data[4])
    print 'camera: %s, light: %s' % (keyframes, data[5 + keyframes])
    RIGHT = data[:5]
    LEFT = data[:5]
    for i in range(keyframes):
        line = data[5 + i]
        (frame_no, distance,
         pos_x, pos_y, pos_z,
         axis_x, axis_y, axis_z,
         curve) = line.strip().split(',')
        x, y, z = [float(n) for n in axis_x, axis_y, axis_z]
        p = parallax(abs(float(distance))) if PARALLAX_FIX is None else PARALLAX_FIX
        print 'frame:%s, x:%6.2f, y:%6.2f, z:%6.2f, distance:%s, parallax/2:%.2f' % (
            frame_no, x, y, z, distance, p)
        size_rate = abs(math.cos(math.radians(x)))
        magnification = min(1 / size_rate, 3)
        x_rate = abs(math.sin(math.radians(z))) * size_rate * magnification
        y_rate = abs(math.cos(math.radians(z))) * magnification
        a = math.fmod(x, 360)
        if a >= 0:
            if a > 180:
                x_sign = -1
            else:
                x_sign = 1
        else:
            if a < -180:
                x_sign = 1
            else:
                x_sign = -1
        a = math.fmod(z, 360)
        if a >= 0:
            if a > 180:
                zx_sign = -1
            else:
                zx_sign = 1
        else:
            if a < -180:
                zx_sign = -1
            else:
                zx_sign = 1
        if a >= 0:
            if 270 >= a > 90:
                zy_sign = -1
            else:
                zy_sign = 1
        else:
            if -270 <= a < -90:
                zy_sign = -1
            else:
                zy_sign = 1

##        print 'x:%.2f, y:%.2f, z:%.2f, magnification:%.2f' % (
##            x_rate, y_rate, (1 - size_rate), magnification)
        RIGHT.append(','.join(
            (frame_no, distance, pos_x, pos_y, pos_z,
             str(x + zx_sign * p * x_rate),
             str(y + zy_sign * p * y_rate),
             str(z - x_sign * abs(p * y_rate) * (1 - size_rate)),
             curve)) + '\n')
        LEFT.append(','.join(
            (frame_no, distance, pos_x, pos_y, pos_z,
             str(x - zx_sign * p * x_rate),
             str(y - zy_sign * p * y_rate),
             str(z + x_sign * abs(p * y_rate) * (1 - size_rate)),
             curve)) + '\n')
    RIGHT.extend(data[5 + keyframes:])
    LEFT.extend(data[5 + keyframes:])
    f = open(csvfilename + '_R.csv', 'wb')
    f.write(''.join(RIGHT))
    f.close()
    f = open(csvfilename + '_L.csv', 'wb')
    f.write(''.join(LEFT))
    f.close()


if __name__ == '__main__':
    main(FILENAME)

## Public Domain. 好きに流用してください。

但し、回転対応の方は不完全なもので、

  • ±70度以上の X回転を入れると視差が少なくなって行く(90度で視差が 0になります)
  • X回転の絶対値が大きい時に Z回転を入れるとキー間の補完が狂う

等の欠点が。X回転が少ない(つまり水平に近い視線)時以外はまともに動作しないと思ってもらった方が無難です。
(X回転が±70度以内であれば、頻繁にキーを打てばそのフレームについては正確になりますが)



MikuMikuDance 自体が立体視対応してるのに今さらこれを必要とする人は居ないとは思いますが。立体視プログラムのサンプル、くらいのものとして了解ねがいます。