教程 3:VRED 中的脚本使用位置与典型用例

教程 3:VRED 中的脚本使用位置与典型用例视频介绍了以下内容:

下载示例脚本

跳转到示例脚本

下载教程 PDF

视频字幕:大家好,欢迎学习面向 VRED Pro 的 Python 脚本教程。我是主持人 Christopher,今天我将介绍 VRED 中所有不同的 Python 界面,以及如何有效地使用它们。

VRED 提供了许多使用 Python 编写脚本的方法。通常,您可以在多个界面中放置 Python 代码来影响场景或 VRED 的行为。但是,并非每个界面都适用于相同的任务。有些问题可以在一个位置(而不是另一个位置)得到更好地解决。在这里,我想介绍用于放置 Python 脚本的所有不同位置,从简单到复杂,并提供一些示例来帮助您快速入门。

首先,我们来看一下 VRED 中最简单的 Python 界面,即终端。终端既可用于 Python 命令的输入,又可用于 VRED、其他脚本或您自己的代码生成的输出消息的输出。在启动 VRED 后打开终端时,您会看到一系列消息,这些消息基本上都是来自 VRED 的初始化消息,旨在告诉您已加载哪些资源以及是否有任何错误。您或任何其他脚本生成的每条输出消息都会显示在终端中,因此,只有在这里,我们才能在运行脚本时直接查看是否存在错误,或者查看日志消息。

终端还可用于 Python 命令的基本输入,就像交互模式下的 Python 解释器一样。输入命令后,如果运行成功,则收到绿色反馈,如果出现错误,则收到红色反馈。由于终端只是一行输入,因此它主要用于测试 Python 函数。此外,当您重新启动 VRED 时,您在终端中写入的所有内容都将丢失,不会随文件一起保存。在终端中,您可以访问在脚本编辑器、首选项或变量集中全局定义的变量和函数,但是您在终端中定义的所有变量和函数都不能在其他脚本中访问。

在讨论更重要的 Python 脚本接口之前,我们先来了解一下脚本首选项。脚本首选项是存储在 VRED 应用程序首选项中的 Python 脚本,始终可用,与您打开的文件无关。脚本首选项非常适合定义自定义键映射。当首次打开脚本首选项时,在 F 键上已经定义了一些键映射,这些键映射会更改渲染设置或其他内容。

脚本首选项用于存放您始终需要的小型 Python 脚本或变量,与您打开的文件无关。例如,您可以定义一些自定义日志函数,用于自动在您的终端消息中添加时间戳。您还可以定义一个函数,用于截取屏幕快照或进行预览渲染并将其保存在硬盘上。但大多数时候,您可能希望通过额外的函数自定义键映射。例如,在不同渲染质量之间切换。在脚本首选项中定义的所有函数对脚本编辑器、变量集和终端全局可用。举例来说,这意味着您可以直接从变量集调用脚本首选项中定义的函数。

变量集是我们的下一个主题。您可以直接在变量集中编写 Python 脚本。它们有一个“脚本”选项卡,可以包含在触发变量集时执行的 Python 代码。您可以定义变量、函数甚至类,但大多数情况下您会编写执行某项特定操作的脚本。

在本示例中,我实现了一个通过激活变量集触发的数据准备工具。在这种情况下,变量集充当 Python 工具,只需双击即可触发。脚本本身会匹配从另一个 CAD 软件导入时丢失的材质和材质切换。它搜索具有特定名称的材质并生成材质切换。然后,它使用新的材质切换替换损坏的材质。

在这里,您可以看到脚本不太长,并且只解决一个问题。因此,它也很容易维护。在代码和输出消息中有一些注释,这些注释有助于让代码更易读、更易于理解,还有助于用户查看代码是否实现了预期功能,或者是否有任何错误。

在变量集中编写的 Python 脚本只在局部有效。这意味着,相应的变量、函数和类仅在此特定变量集中可用。您不能从变量集 B 中的脚本调用在变量集 A 中定义的函数。这也意味着,如果要在多个变量集中使用一个脚本,则可能应该将其放在脚本编辑器中,在脚本编辑器中该脚本是全局可用的,只需从变量集调用即可。这也可以防止重复代码问题。

最后但同样重要的是,您还应了解在变量集中放入多少代码。上一个示例大约有 40 行,这是正常的。但是,如果您的脚本包含数百行代码,那么基本上可以说这个程序很难维护和理解。您将需要大量资源来扩展此类代码或消除错误。在这种情况下,最好使用插件或在全局可用的脚本编辑器中定义函数,接下来我们将讨论相关内容。

脚本编辑器位于菜单“编辑”>“脚本编辑器”下。脚本编辑器可与直接附加到 VRED 场景的 Python 文件相媲美。在其中,可以定义全局可用的变量、函数和类。此处写入的所有 Python 代码也可以在诸如变量集、终端或 Web 引擎等位置使用。因此,它是全局可用的。

在编辑器的底部,有一个“运行”按钮,用于执行脚本编辑器中的所有代码。但是,在首选项中的“文件 IO”下也有一些设置。VRED 文件,在其中可以指定加载文件时是否应自动执行脚本和编辑器。默认设置为启用,因此,如果不希望自动运行脚本,则需要禁用此选项。

脚本编辑器对于全局定义的函数和变量非常有用。例如,您可以定义一个函数,以从当前摄影机位置创建视点。此函数可以从任何变量集调用,甚至可以远程触发。并且,可以使用一个函数对所有视点自动进行批处理渲染,并将其存储在可以定义的目录中。当然,您也可以在脚本编辑器中编写复杂的脚本。

在 Python 中,可以将多个模块、类和函数放在一个文件中。因此,从理论上讲,您可以在单个脚本编辑器中实现全局函数、多个工具以及用户界面。但在我看来,这确实很混乱,过于复杂,不是很容易维护。如果脚本的代码超过几百行,则可能需要在脚本插件或模块中拆分功能。

我们提供了有关扩展 VRED VR 菜单的教程视频,在其中实现了其他工具。对于此任务,脚本编辑器是不二之选。

如果您有一个包含 Python 脚本的存储库,则使用“导入”功能直接导入脚本会很有用。这就像打开文件一样简单,导入的脚本只附加到脚本编辑器中的内容并执行。但是在这里,您必须要小心。如果编辑器中已存在以某种方式操纵场景的脚本,则每次导入另一个脚本时都会再次执行该脚本。

“导入”功能可用于快速添加自定义用户界面或采用文件形式的其他 Python 工具。在“扩展 VR 菜单”教程中,我们使用这种方法将自定义 VR 工具导入场景中。

在 VRED 中,还可以构建完全自动化的渲染流程。要加载场景并自动渲染图像或影片,接下来的两个命令行选项会派上用场:-prepython-postpython。您可以在加载场景之前使用 -prepython 执行脚本,在加载场景之后使用 -postpython 运行脚本。举例来说,这意味着使用 -prepython 可以运行与场景无关的脚本,例如与模块甚至脚本首选项无关。使用 -postpython 可以运行在脚本编辑器中定义的脚本,或启动存储为预设的渲染作业。基本上,您可以输入亦可在 VRED 终端中输入的所有 Python 命令,用分号分隔。

常见用例是加载场景、导入其他 CAD 模型,然后渲染所有视点或影片。在此之后,您可以终止 VRED 并使用另一个文件开始。要进行自动渲染,建议使用一些渲染预设,这样您就可以使用命令行选择正确的渲染设置。

添加常用 Python 工具和函数的另一种方法是使用自定义 Python 模块。它们将添加到 VRED 应用程序目录的 lib 目录下的 python 中。它们的结构与任何其他模块类似,并且可以包含类、函数以及基本上 Python 模块可以包含的任何内容。您需要注意的是,并非所有 VRED 模块都会自动导入,就像在脚本编辑器中编写脚本一样。这意味着,您必须明确导入所有 API v1 模块,然后才能使用它们。但是,API v2 模块是由 VRED 注入的,不必导入。

不过,虽然如此,模块仍是同时提供多个工具、Python 函数和类的典型方法。您可以为任何 VRED 设计师或开发人员安装这些模块,它们都共享相同的 Python 脚本集。由于可以在存储库中管理这些脚本,更容易保持良好的代码质量,这非常棒。对于存储在 VRED 文件或变量集中的 Python 脚本,这并不容易,因为所有脚本都分散在文件中。

脚本插件可能是向 VRED 实例添加功能的最高级方法。它们基本上是随用户界面一起提供的 VRED 工具,并在 VRED 菜单栏中提供。插件可以只是执行一项小型任务的几行代码,但也可以是数百行代码,并改变您与 VRED 交互和使用 VRED 的各个方面。当然,您可以在脚本编辑器中实现相同的功能。但插件与 VRED 文件无关,通常是使用第三方代码编辑器编写的。

开发您自己的插件的优势在于,插件会集成到 VRED 菜单栏中,可以轻松地与其他设计师和开发人员共享插件,并且插件与场景中的其他 Python 代码是分开的。因此,插件不会意外地更改您正在使用的其他 Python 脚本的状态。

我现在不想过多地介绍脚本插件,因为我们有一个专门介绍这方面内容的完整教程。在该教程中,我将向您展示如何实现自己的脚本插件以及用户界面。一定要看一下。

如您所见,使用一些简单或较为复杂的 Python 脚本,可以通过多种不同的方法来更改场景或您使用 VRED 的方式。至此,您应该对如何在 VRED 中使用 Python 脚本的所有可能性有了比较充分的认识。对于每个问题,都有不止一个解决方案,但是某些 Python 界面会比其他界面更适合实现您的目标。

希望我在本视频中的介绍能够为大家提供一些指导,帮助您应对这些挑战。今天就到这里。希望我们能在接下来的教程中见面。感谢您参加本课程,下次见!


Python 代码示例

教程 3:VRED 中的脚本使用位置与典型用例视频有三个 Python 脚本示例。要下载这些压缩文件,请单击此处

command_line_parameters.txt

REM Example 1
REM -prepython "print('Executed before file has loaded')" -postpython "print('Executed after file has loaded')"
REM Example 2
start "D:\Programme\Autodesk\VREDPro-<internalVersion>\bin\WIN64\VREDPro.exe" "C:\ProgramData\Autodesk\VREDPro-<internalVersion>\examples\Automotive_Genesis.vpb" -prepython "print('---');print('Executed before file has loaded')" -postpython "print('---');print('Executed after file has loaded')"

script_editor_scripts.py

# Example 1.0
# Global function library

import time
from datetime import datetime

def logInfo(message):
    now = datetime.now().time().strftime("%H:%M:%S.%f")
    print(now, '[INFO]', message)


def logWarn(message):
    now = datetime.now().time().strftime("%H:%M:%S.%f")
    print(now, '[WARN]', message)
    

def createViewpointFromCamera():
    now = datetime.now().time().strftime("%H:%M:%S.%f")
    vrCameraService.createViewpoint("vp_{}".format(now))
    
    
def renderViewpoints():
    viewpoints = vrCameraService.getAllViewpoints()

    renderDirectory = vrFileDialog.getExistingDirectory(
                                        "Select a render directory:",
                                        vrFileIO.getFileIOBaseDir()
                                    )

    if not renderDirectory:
        logWarn("No directory where to save the renderings!")
        return

    for viewpoint in viewpoints:
        name = viewpoint.getName()

        if name.startswith('vp_'):
            vrRenderSettings.setRenderFilename("{}.jpg".format(name))
            vrRenderSettings.startRenderToFile(False)

variant_set_scripts.py

# Example 1
# Recreating material switches
print('--- Create Switch Materials ---')
allMaterials = getAllMaterials()
# get all materials which name starts with "switch"
switchMaterials = list(filter(lambda matPtr: matPtr.getName().startswith('switch'), allMaterials))
# iterate all switch materials
for switchMaterial in switchMaterials:
switchPtr = switchMaterial
switchName = switchPtr.getName()
print('Found switch: ' + str(switchName))
# extract material prefix
materialPrefix = '_'.join(switchName.split('_')[1:])
# Get all materials which name starts with the material prefix
childMaterials = list(filter(lambda matPtr: matPtr.getName().startswith(materialPrefix), allMaterials))
childMaterialNames = [x.getName() for x in childMaterials]
print('Found child materials: ' + ', '.join(childMaterialNames))
# Create new material switch
print('Create new material switch...')
newMaterialSwitch = createMaterial("SwitchMaterial")
newMaterialSwitch.setName(switchName)
# Add child materials to switch
for child in childMaterials:
newMaterialSwitch.addMaterial(child)
# swap old switch material with new switch material
print('Assign new material switch...')
nodes = switchPtr.getNodes()
for node in nodes:
node.setMaterial(newMaterialSwitch)
print('')
print('Finished!')
print('---')
# Example 2
# Calling global functions in the script editor
logInfo('This is an info message from a variant set.')
logWarn('This is a warning message from a variant set.')
createViewpointFromCamera()
renderViewpoints()