6面に分離されたキューブマップテクスチャをファイル出力したい (スクリプト)

WebGLなどでHDRを使ったIBL用の映り込みや背景の表示を行う場合、
6面の分離されたキューブマップテクスチャを使用します。
別途、WebGLのフレームワークによってはパノラマの1枚の画像を使用してShaderで表現する手段もあります。
ここではスクリプトを使用し、
カメラを中心にバーティカルクロス形式でレンダリングした後に
そのテクスチャより6枚分のキューブマップテクスチャをファイル出力します。

以下のスクリプトを実行すると、
カメラを中心にレンダリングが行われます。
このとき、元のレンダリングサイズとレンダリングイメージは上書きされるためご注意くださいませ。
レンダリング後、指定のフォルダにキューブマップテクスチャに展開されたイメージが出力されます。


scene = xshade.scene()

# ------------------------------------------------------------.
# レンダリングを行う.
# 左右上下1ピクセルごとのマージンを設けてレンダリング.
# @param[in] cubeSize  キューブマップのサイズ (512, 1024 など).
# ------------------------------------------------------------.
def doRendering (cubeSize):
    # レンダリングサイズ.
    renderWidth  = (cubeSize + 2) * 3
    renderHeight = (cubeSize + 2) * 4
    scene.rendering.image_size = [renderWidth, renderHeight]

    # バーティカルクロスのパノラマ投影.
    scene.rendering.panorama_projection = 5

    # レンダリング.
    # レンダリングが完了するまで待つ.
    scene.rendering.render()

# ------------------------------------------------------------.
# 指定の名前のマスターイメージを取得.
# ------------------------------------------------------------.
def findMasterImageByName (nameStr):
    # マスターイメージパートを取得.
    shape = scene.shape
    masterImagePart = None
    if shape.has_son:
        s = shape.son
        while s.has_bro:
            s = s.bro
            if s.type == 2 and s.part_type == 102:  # マスターイメージパート.
                masterImagePart = s
                break

    if masterImagePart == None or masterImagePart.has_son == False:
        return None
    
    # マスターイメージ内で指定の名称があるか.
    targetShape = None
    s = masterImagePart.son
    while s.has_bro:
        s = s.bro
        if s.name == nameStr:
            targetShape = s
            break

    return targetShape

# ------------------------------------------------------------.
# キューブマップレンダリング結果を6つのマスターイメージとして分離.
# @param[in] outputPath         ファイルにキューブマップ画像を出力する場合のパス.
# @param[in] outputFileType     ファイル拡張子.
# ------------------------------------------------------------.
def createCubeTextures (outputPath, outputFileType):
    if scene.rendering.image == None or scene.rendering.image.has_image == False:
        return
    
    width  = scene.rendering.image.size[0]
    height = scene.rendering.image.size[1]
    orgTexWidth  = width / 3
    orgTexHeight = height / 4
    cubeTexWidth  = orgTexWidth - 2
    cubeTexHeight = orgTexHeight - 2

    cubeTexName = ['cubeTex_px','cubeTex_nx','cubeTex_py','cubeTex_ny','cubeTex_pz','cubeTex_nz']
    cubePosA = [ (orgTexWidth * 2, orgTexHeight), (0, orgTexHeight),
                 (orgTexWidth, 0), (orgTexWidth, orgTexHeight * 2),
                 (orgTexWidth, orgTexHeight * 3), (orgTexWidth, orgTexHeight) ]

    # 色補正を行うか.
    useColorCorrection = False
    if outputFileType != 'exr':
        useColorCorrection = True

    for i in range(6):
        xPos = cubePosA[i][0] + 1
        yPos = cubePosA[i][1] + 1

        # cubeTexName[i]の名前のマスターイメージが存在するか.
        masterImage = findMasterImageByName(cubeTexName[i])
        if masterImage == None:
            masterImage = scene.create_master_image(cubeTexName[i])

        masterImage.image = xshade.create_image((cubeTexWidth, cubeTexHeight), 128)

        for y in range(cubeTexHeight):
            for x in range(cubeTexWidth):
                # レンダリング画像上のピクセル色を取得.
                if i == 4:      # pzの場合は左右と上下を逆にする.
                    col = scene.rendering.image.get_pixel_rgba(cubeTexWidth + xPos - x, cubeTexHeight + yPos - y)
                else:
                    col = scene.rendering.image.get_pixel_rgba(x + xPos, y + yPos)

                # 色補正を行う.
                col2 = col
                if useColorCorrection:
                    col2 = scene.correction.correct([col[0], col[1], col[2]])

                # 色をmasterImage.imageに指定.
                masterImage.image.set_pixel_rgba(x, y, [col2[0], col2[1], col2[2], col[3]])

        # ファイル出力.
        if outputPath != '':
            fName = outputPath + '/' + cubeTexName[i] + '.' + outputFileType
            masterImage.image.save(fName)

# ----------------------------------------------------------.

# ダイアログボックスの作成.
dlg = xshade.create_dialog_with_uuid('51cbd867-c363-47f9-9741-7afa5a4e48ed')

cubeTexSize_id = dlg.append_selection('キューブマップサイズ : /256/512/1024/2048/4096', '')
outputImageFile_id = dlg.append_bool('イメージをファイル出力')
outputFilePath_id = dlg.append_path('出力フォルダ : ')
outputFileType_id = dlg.append_selection('出力イメージ形式 : /jpg/png/exr', '')

# デフォルトボタンを追加.
dlg.append_default_button()

# 値を指定.
dlg.set_value(cubeTexSize_id, 1)
dlg.set_value(outputImageFile_id, True)
dlg.set_value(outputFileType_id, 0)

# デフォルト値を指定.
dlg.set_default_value(cubeTexSize_id, 1)
dlg.set_default_value(outputImageFile_id, True)
dlg.set_default_value(outputFileType_id, 0)

# ダイアログボックスを表示.
if dlg.ask('キューブマップテクスチャを出力'):
    # ダイアログボックスでの値を取得.
    tSizeA = [256, 512, 1024, 2048, 4096]
    cubeTexSize = tSizeA[dlg.get_value(cubeTexSize_id)]

    outputImageFile = dlg.get_value(outputImageFile_id)

    fTypeA = ['jpg', 'png', 'exr']
    outputFileType = fTypeA[dlg.get_value(outputFileType_id)]

    outputPath = dlg.get_value(outputFilePath_id)
    if outputImageFile == False:
        outputPath = ''

    if outputImageFile and outputPath == '':
        print 'キューブマップテクスチャを出力するパスを指定してください。'
    else:
        # キューブマップレンダリングを行う.
        doRendering(cubeTexSize)

        # キューブマップレンダリング結果を6つのマスターイメージとして分離.
        createCubeTextures(outputPath, outputFileType)

        print 'キューブマップテクスチャを出力しました。'
        if outputPath != '':
            print 'キューブマップテクスチャを [' + outputPath + '] に出力しました。'

使い方

あらかじめ背景とするイメージや形状をシーンに配置します。

上記のスクリプトをコピーし、Shade3Dのスクリプトウィンドウにペーストします。
実行すると以下のようなダイアログボックスが表示されます。

「キューブマップサイズ」は、出力するキューブマップの解像度です。
256/512/1024/2048/4096から選択できます。
「イメージをファイル出力」チェックボックスをオンにすると、指定のフォルダにキューブマップテクスチャを出力します。
オフにすると、マスターイメージとしてシーンにキューブマップテクスチャが保持されるだけになります。
「出力フォルダ」の「参照」ボタンを押し、キューブマップテクスチャを出力するフォルダを指定します。
「出力イメージ形式」は、jpg/png/exrから選択できます。
jpg/png形式で出力する場合は色補正が反映されます。exrの場合は、そのままのレンダリング色が反映されます。

「OK」ボタンを押すと、
レンダリングとキューブマップテクスチャのマスターイメージ展開、ファイル出力が行われます。
これはキューブマップサイズが大きい場合は時間がかかります。
マスターイメージは以下のようにイメージパートに展開されます。

「イメージをファイル出力」チェックボックスをオンにしている場合、以下のように指定のフォルダにファイルが出力されます。

出力されたキューブマップテクスチャの名前

6枚のテクスチャは以下のような内容になっています。

出力名 説明
cubeTex_nx -X
cubeTex_px +X
cubeTex_ny -Y
cubeTex_py +Y
cubeTex_nz -Z
cubeTex_pz +Z

出力するキューブマップサイズの注意事項

Shade3Dのグレードにより、レンダリングできる解像度の上限が異なります。
レンダリング画像サイズの最大がBasicは2500 x 2500、Standardは4500 x 4500、Professionalは22528 x 22528となります。
そのため、このキューブマップ出力スクリプトを使ったときのキューブマップサイズは
Basic版では最大512、Standard版では最大1024、となります。
Professional版の場合は4096も指定可能です。
キューブマップの場合は6面で構成されるため、1024を指定した場合は4K解像度の背景(1024 x 4で周囲ぐるっと360度分)に相当します。

WebGL(three.js)で使用する場合

出力されたキューブマップテクスチャをWebGLで使用する場合は、環境によっては少し調整が必要になります。
three.js ( https://threejs.org/ )で使用する場合は、
キューブマップのnzとpzは逆にするようにしてください。
以下はWebGL(three.js)でのブラウザでの表示例です。

Translate »