Filmes, jogos e outras produções gráficas 3D costumam gerar, armazenar e transmitir uma grande quantidade de dados. Esses dados podem vir de vários softwares (como Unreal Engine, Maya, Houdini ou Blender) do fluxo de arte, cada um com sua própria forma de descrição de cena.
O formato de intercâmbio Universal Scene Description (USD) é um formato de código aberto desenvolvido pela Pixar para resolver a necessidade de intercâmbio e aumento de forma robusta e escalável em cenas 3D arbitrárias que podem ser compostas por muitos ativos elementares. O USD não apenas fornece um conjunto de ferramentas rico para ler, escrever, editar e pré-visualizar rapidamente geometria e sombreamento 3D, mas também permite o intercâmbio de ativos elementares (por exemplo, modelos) ou animações.
Ao contrário de outros pacotes de intercâmbio, o USD também permite a montagem e a organização de qualquer número de ativos em conjuntos, cenas e tomadas virtuais. Você pode transmiti-los de aplicativo para aplicativo e editá-los de forma não destrutiva (como sobreposições) com uma API única e consistente em um único gráfico de cena.
Por que usar o USD?
O USD fornece uma linguagem comum para mover quantidades significativas de dados entre vários aplicativos 3D. Isso fornece flexibilidade na tomada de decisões sobre seu fluxo de arte e facilita a colaboração entre vários artistas 3D (animadores, artistas de iluminação ou sombras, modeladores, artistas de efeitos especiais, entre outros) usando métodos iterativos e não destrutivos.
Suporte ao USD na Unreal Engine
A Unreal Engine oferece suporte ao USD por meio do painel Estágio USD e pelos fluxos de trabalho Python bidirecionais.
A janela Estágio USD. Clique na imagem para ampliar.
Em vez de converter os dados USD em ativos nativos da Unreal Engine, como malhas estáticas e materiais, o fluxo de trabalho do Estágio USD funciona de forma nativa com os dados USD. Isso agiliza a inserção dos dados USD na Unreal Engine, fornecendo uma visão mais clara de como o conteúdo USD foi originalmente estruturado e lida com atualizações em tempo real conforme você faz alterações no arquivo USD de origem no disco.
O Estágio USD fornece a seguinte funcionalidade:
- Representa dados 3D como "primitivas": malha estática, malhas do esqueleto, HISM, materiais, luzes, câmeras, variantes, animação e formas de mesclagem.
- Edição não destrutiva de atributos.
- Visualização de hierarquia e gráfico de cena USD.
- Carregar e descarregar conteúdo usando cargas úteis.
- Suporte a texturas com superfícies de pré-visualização USD.
- Suporte a materiais com PreviewSurface e DisplayColor.
- Suporte a plugins USD, como Alembic e Custom Path Resolver.
- Funcionalidades do USD aceitas no tempo de execução. Para obter mais informações, consulte USD Runtime Support (Suporte ao tempo de execução do USD).
- Carrega os formatos
.usd,.usda,.usdce.usdz.
Para obter mais informações sobre o Universal Scene Description, consulte a Introduction to USD (Introdução ao USD) da Pixar.
Para obter mais informações sobre como trabalhar com o fluxo de trabalho em Python, consulte Programação em Python.
Ações compatíveis
Como importar para a Unreal Engine
O conteúdo exibido no Estágio USD (malha estática, malhas do esqueleto, luzes, folhagem e paisagens) pode ser importado para a Unreal Engine usando os seguintes métodos:
- Usando File, Import Into Level (Arquivo, Importar para o nível). Este processo importará tanto ativos (malha estática, malha do esqueleto, materiais, texturas e assim por diante) quanto atores.
- Usando o botão Add/Import no Navegador de Conteúdo. Esse processo importará apenas os ativos.
- Arrastar e soltar o arquivo no Navegador de Conteúdo. Esse processo importará apenas os ativos.
- Usando a opção Action, Import no Editor de Estágio USD. Esse processo importará ativos e atores. Após a conclusão do processo de importação, os ativos no Estágio USD são substituídos por novos atores do Navegador de Conteúdo.
Como criar e editar animação
As animações armazenadas em um arquivo USD podem ser acessadas pela sequência de nível associada encontrada no painel Properties de USDStageActor.
Abra a sequência de nível selecionando USD Level Stage e clicando duas vezes na sequência no painel Properties. Clique na imagem para ampliá-la.
As animações xform USD são exibidas como faixas Transform na sequência de nível. Outras formas de animação, como floats, booleanos e ossos do esqueleto, são exibidas pela faixa Time. Os dados de animação USD na imagem acima são representados como um par chave-valor em cada código de tempo durante a animação.
Usando a sequência de nível gerada no Estágio USD, você pode vincular a um ator que é gerado no Estágio USD e adicionar animação adicional por meio de uma faixa Transform.
Suporte ao tempo de execução de USD
A Unreal Engine permite o carregamento de arquivos USD no tempo de execução chamando o nó de Blueprint Set Root Layer em um ator de Estágio USD dentro do nível.
O nó Set Root Layer. Clique na imagem para ampliar.
Esse nó cria os ativos necessários e gera atores e componentes no nível, de modo idêntico ao processamento no editor. Outras funções de Blueprint para controlar várias propriedades do ator de Estágio USD incluem:
| Função de Blueprint | Descrição |
|---|---|
| Get Generated Assets | Obtém os ativos gerados para uma primitiva em um caminho de primitiva específico e os posiciona em uma matriz. Usa um ator de Estágio USD e um caminho de primitiva como entradas. |
| Get Generated Components | Obtém o componente gerado para uma primitiva em um caminho de primitiva específico. Usa um ator de Estágio USD e um caminho de primitiva como entradas. |
| Get Source Prim Path | Obtém o caminho da primitiva para um determinado objeto no Estágio USD. Usa um Ator de Estágio USD e uma referência de objeto como entradas. |
| Get Time | Obtém o carimbo de data/hora atual no ator de Estágio USD-alvo. Usa um ator de Estágio USD como alvo. |
| Set Initial Load Set | Define a carga útil inicial a ser carregada. Usa um ator de Estágio USD como entrada e fornece as opções a seguir.
|
| Set Purpose to Load | Define o objetivo inicial a ser carregado. Usa um ator de Estágio USD e um integer como entrada.
|
| Set Render Context | Define o contexto de renderização de um Estágio USD. Toma um ator de Estágio USD como alvo e uma referência a um contexto de renderização como entrada. |
| Set Time | Define o carimbo de data/hora atual de um Estágio USD. Toma um ator de Estágio USD como alvo e um valor de float como entrada. |
Para obter mais informações sobre o atributo Purpose e outros termos do USD, consulte o USD Glossary (Glossário do USD) da Pixar.
Usando esse processo, é possível criar aplicativos capazes de carregar e exibir o conteúdo de um arquivo USD no tempo de execução.
Para habilitar o USD Importer no tempo de execução, adicione a seguinte linha ao seu arquivo Project.Target.cs localizado na pasta UE_(version)\Engine\Source, onde "Project" é o nome do projeto:
GlobalDefinitions.Add("FORCE_ANSI_ALLOCATOR=1");
Por exemplo:
public class YourProjectTarget : TargetRules
{
public YourProjectTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "YourProject" } );
GlobalDefinitions.Add("FORCE_ANSI_ALLOCATOR=1");
}
}
Suporte para MDL da NVIDIA
A Unreal Engine é compatível com materiais de superfície MDL usando o esquema NVIDIA MDL USD. Para obter mais informações sobre o NVIDIA MDL, consulte USD Shader Attributes (Atributos do shader USD) da NVIDIA.
Suporte para edição multiusuário
A edição multiusuário é aceita por muitas operações do Estágio USD, incluindo:
- Adicionar e remover primitivas.
- Renomear primitivas.
- Editar atributos de primitivas.
- Alternar a visibilidade.
- Abrir, fechar ou alterar o estágio atual.
Para habilitar o suporte multiusuário para seu projeto USD, habilite o plugin USD Multi-UserSync. Para saber mais sobre como trabalhar com plugins, consulte Working with Plugins.
A edição USD multiusuário sincroniza a propriedade Root Layer do Estágio USD para cada cliente, garantindo que todos os usuários tenham os mesmos arquivos USD. Para isso, cada cliente abre o mesmo Estágio USD no local, gerando ativos e componentes no próprio sistema e sincronizando apenas as operações realizadas nesses ativos.
Durante uma sessão multiusuário, é importante que todos acessem os arquivos USD usando o mesmo caminho de arquivo. Para garantir que cada cliente possa acessar os mesmos arquivos, recomendamos que os arquivos USD de destino sejam armazenados na pasta do projeto e gerenciados usando o controle de código-fonte.
Para obter mais informações sobre a edição multiusuário na Unreal Engine, consulte Multi-User Editing in Unreal Engine.
Não é possível desfazer a exclusão de primitivas durante uma sessão multiusuário.
Como habilitar o plugin de importação de USD
Para começar a trabalhar com um arquivo USD no Unreal Editor, você precisa habilitar o plugin USD Importer no menu Plugins. Para saber mais sobre como trabalhar com plugins, consulte Working with Plugins.
Após a reinicialização do editor, você verá uma nova opção Estágio USD listada no menu Window, Virtual Production do Editor de Níveis.
O painel Place Actors (Colocar atores) lista um novo Ator de Estágio USD que você pode adicionar ao nível.
Novos atores USD disponíveis no painel Place Actors.
Como usar o USD na Unreal Engine
Trabalhar com conteúdo USD na Unreal Engine começa com o Editor de Estágio USD e o Ator de Estágio USD.
O painel Estágio USD.
| Número | Descrição |
|---|---|
| 1 | Hierarquia |
| 2 | Propriedades |
| 3 | Camadas |
Fluxo de trabalho do Estágio USD
Um Ator de Estágio USD atua como um contêiner para o conteúdo de um arquivo USD carregado e fornece uma âncora para os dados no nível. Os objetos de cena 3D carregados do arquivo USD e vistos na janela de visualização são totalmente compatíveis com a maioria dos outros recursos da Unreal Engine, e você pode tratá-los exatamente como outros atores. Você pode adicionar primitivas adicionais que referenciam o conteúdo encontrado em outros arquivos USD, incluindo malhas do esqueleto animadas.
Salvar as alterações feitas no Estágio USD usando o menu File, Save no Editor de Estágio USD gravará essas alterações novamente no arquivo USD.
Para obter mais informações sobre como trabalhar com o Estágio USD, consulte Guia de início rápido do Editor de Estágio USD.
A Unreal Engine não cria mapas de iluminação automaticamente para ativos carregados no Estágio USD quando um arquivo USD é aberto. Isso pode resultar em uma cena completamente escura quando a iluminação estática for compilada.
Programação em Python
A programação em Python com USD fornece uma maneira flexível de realizar diversas ações, como operações em lote e edições de cena, que podem ser difíceis ou demoradas usando a interface de usuário. Operações como ocultar ou editar os atributos de um grande número de primitivas podem ser automatizadas rapidamente usando um código Python flexível inicializado no painel Log de Saída.
Antes de usar Python, você deve habilitar o plugin Python Editor Script. Para saber mais sobre plugins, consulte Working with Plugins.
Você pode usar o Log de Saída localizado na parte inferior do Editor de Níveis para executar códigos Python. Também é possível abrir o Log de Saída como um painel separado em Window, Log de Saída.
Clique na imagem para ampliá-la.
Para obter mais informações sobre como usar código Python com a Unreal Engine, consulte Scripting the Unreal Editor Using Python.
Caso de uso para usar um código Python
Quando a versão do SDK do USD fornecida com a Unreal Engine foi atualizada para a 21.05, a atualização renomeou vários atributos nos esquemas de iluminação USDLux. Para resolver esse problema, a Unreal Engine inclui um código Python que renomeia os atributos primitivos USDLux para a convenção de nomenclatura 21.05.
from pxr import Usd, Sdf, UsdLux
import argparse
def rename_spec(layer, edit, prim_path, attribute, prefix_before, prefix_after):
path_before = prim_path.AppendProperty(prefix_before + attribute)
path_after = prim_path.AppendProperty(prefix_after + attribute)
# Precisamos verificar sempre, pois adicionar um edição de namespace que não pode ser aplicado cancelará todo o lote.
if layer.GetAttributeAtPath(path_before):
print(f"Tentando renomear '{path_before}' para '{path_after}'")
edit.Add(path_before, path_after)
def rename_specs(layer, edit, prim_path, reverse=False):
prefix_before = 'inputs:' if reverse else ''
prefix_after = '' if reverse else 'inputs:'
# Luz
rename_spec(layer, edit, prim_path, 'intensity', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'exposure', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'diffuse', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'specular', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'normalize', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'color', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'enableColorTemperature', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'colorTemperature', prefix_before, prefix_after)
# ShapingAPI
rename_spec(layer, edit, prim_path, 'shaping:focus', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shaping:focusTint', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shaping:cone:angle', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shaping:cone:softness', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shaping:ies:file', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shaping:ies:angleScale', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shaping:ies:normalize', prefix_before, prefix_after)
# ShadowAPI
rename_spec(layer, edit, prim_path, 'shadow:enable', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shadow:color', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shadow:distance', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shadow:falloff', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'shadow:falloffGamma', prefix_before, prefix_after)
# DistantLight
rename_spec(layer, edit, prim_path, 'angle', prefix_before, prefix_after)
# DiskLight, SphereLight, CylinderLight
# Observação: o método "tractAsPoint" não deve ter o prefixo "inputs:", então vamos ignorá-lo.
rename_spec(layer, edit, prim_path, 'radius', prefix_before, prefix_after)
# RectLight
rename_spec(layer, edit, prim_path, 'width', prefix_before, prefix_after)
rename_spec(layer, edit, prim_path, 'height', prefix_before, prefix_after)
# CylinderLight
rename_spec(layer, edit, prim_path, 'length', prefix_before, prefix_after)
# RectLight, DomeLight
rename_spec(layer, edit, prim_path, 'texture:file', prefix_before, prefix_after)
# DomeLight
rename_spec(layer, edit, prim_path, 'texture:format', prefix_before, prefix_after)
def collect_light_prims(prim_path, prim, traverse_variants, light_prim_paths, visited_paths):
if not prim:
return
if prim_path in visited_paths:
return
visited_paths.add(prim_path)
# Percorrer manualmente, pois podemos alternar entre variantes, o que invalidaria o iterador "stage.Traverse()"
para filho em "prim.GetChildren():".
# Por exemplo: /Root/Prim/Child
child_path = prim_path.AppendChild(child.GetName())
if UsdLux.Light(child):
light_prim_paths.add(child_path)
traversed_grandchildren = False
if traverse_variants:
varsets = child.GetVariantSets()
for varset_name in varsets.GetNames():
varset = varsets.GetVariantSet(varset_name)
original_selection = varset.GetVariantSelection() if varset.HasAuthoredVariantSelection() else None
# Alternar as seleções apenas na camada da sessão.
with Usd.EditContext(prim.GetStage(), prim.GetStage().GetSessionLayer()):
for variant_name in varset.GetVariantNames():
varset.SetVariantSelection(variant_name)
# Por exemplo: /Root/Prim/Child{VarName=Var}
varchild_path = child_path.AppendVariantSelection(varset_name, variant_name)
collect_light_prims(varchild_path, child, traverse_variants, light_prim_paths, visited_paths)
traversed_grandchildren = True
if original_selection:
varset.SetVariantSelection(original_selection)
else:
varset.ClearVariantSelection()
if not traversed_grandchildren:
collect_light_prims(child_path, child, traverse_variants, light_prim_paths, visited_paths)
def update_lights_on_stage(stage_root, traverse_variants=False, reverse=False):
""" Percorrer o estágio com a camada-raiz `stage_root`, atualizando atributos de primitivas de luz de/para USD 21.05.
A abordagem aqui envolve atravessar o estágio composto e coletar caminhos para primitivas que são luzes UsdLux
(passando ou não pelas variantes de acordo com o argumento de entrada) e, depois, percorrer todas as janelas
e renomear todas as especificações de atributos de primitivas de luz para 21.05 (adicionando o prefixo "inputs:") ou para
o esquema anterior à 21.05 (removendo o prefixo "inputs:").
Percorremos o estágio composto para garantir que estamos modificando exclusivamente atributos de primitiva de luz UsdLux,
evitando modificações no atributo "radius" de uma esfera, por exemplo.
"""
stage = Usd.Stage.Open(stage_root, Usd.Stage.LoadAll)
layers_to_traverse = stage.GetUsedLayers(True)
# Colete as primitivas UsdLux no estágio composto.
light_prim_paths = set()
visited_paths = set()
collect_light_prims(Sdf.Path("/"), stage.GetPseudoRoot(), traverse_variants, light_prim_paths, visited_paths)
print("Primitivas de luz coletadas:")
for l in light_prim_paths:
print(f"\t{l}")
# Percorrer todas as camadas e renomear todos os atributos relevantes de primitivas de luz.
visited_paths = set()
for layer in layers_to_traverse:
# Colocar em lote todas as operações de renomeação para esta camada em um único namespace.
edit = Sdf.BatchNamespaceEdit()
def visit_path(path):
attr_spec = layer.GetAttributeAtPath(path)
if attr_spec:
prim_path = attr_spec.owner.path
# Visite cada primitiva apenas uma vez, pois vamos processar todas as propriedades UsdLux de uma só vez.
if prim_path in visited_paths:
return
visited_paths.add(prim_path)
if prim_path in light_prim_paths:
rename_specs(layer, edit, prim_path, reverse)
layer.Traverse("/", visit_path)
if len(edit.edits) == 0:
print(f"Nada a renomear na camada '{layer.identifier}'")
else:
if layer.CanApply(edit):
layer.Apply(edit)
print(f"Alteração aplicada à camada '{layer.identifier}'")
else:
print(f"Falha ao aplicar alteração à camada '{layer.identifier}'")
# Salvar todas as camadas.
for layer in layers_to_traverse:
if not layer.anonymous:
layer.Save()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Atualizar primitivas de luz para USD 21.05')
parser.add_argument('root_layer_path', type=str,
"help='Caminho completo da camada-raiz do estágio a ser atualizada, por exemplo, "C:/MyFolder/MyLevel.usda"')
parser.add_argument('--v', '--traverse_variants', dest='traverse_variants', ação='store_true',
help='Procure também todas as variantes em cada conjunto de variantes encontrado ao procurar as primitivas de luz')
parser.add_argument('--r', '--reverse', dest='reverse', action='store_true',
help='Argumento opcional para fazer a alteração inversa: renomear atributos de luz UsdLux 21.05 para que sigam o esquema anterior a 21.05')
args = parser.parse_args()
update_lights_on_stage(args.root_layer_path, args.traverse_variants, args.reverse)
O código pode ser encontrado nos arquivos de código-fonte do USDImporter, localizados em Engine/Plugins/Importers/USDImporter/Content/Python/usd_unreal/update_lights_to_21_05.py.
Siga estas etapas para executar o código pelo Log de Saída:
-
Abra Log de Saída selecionando Window, Log de Saída.
-
Clique no menu suspenso Cmd à esquerda do campo da linha de comando e selecione Python.
-
Digite o seguinte no campo da linha de comando:
"C:\Program Files\Epic Games\UE_4.27\Engine\Plugins\Importers\USDImporter\Content\Python\usd_unreal\update_lights_to_21_05.py" "C:/path/to/root_layer.usda"Onde
"C:/path/to/root_layer.usda"é o caminho para o seu arquivo USD.A amostra acima contém o caminho padrão para a instalação da Unreal Engine. Certifique-se de atualizar o caminho se não tiver instalado sua versão da Unreal Engine na posição padrão.
-
Pressione Enter para executar o comando.