在 Maya 2018 中使用 Python 2.7 编写一个 **Look Development(LookDev)工具**,可以帮助美术师快速创建材质、分配贴图、设置灯光环境等。我们将实现一个基础但功能完整的 LookDev 工具,包括:
- 创建标准材质(如 Lambert、Blinn、Phong、aiStandardSurface);
- 分配贴图(如颜色、法线、凹凸、置换、高光等);
- 自动连接贴图节点;
- 支持选择多个物体;
- 使用 PySide2 创建 GUI(Maya 2018 支持 PySide2);
- 兼容 Python 2.7。
---
## ✅ 工具功能概述
| 功能 | 描述 |
|------|------|
| 材质选择 | 支持 Lambert、Blinn、Phong、aiStandardSurface(需安装 Arnold) |
| 贴图分配 | 支持 color、normal、bump、displacement、specular 等贴图 |
| 自动连接 | 自动创建 File、Shader、Bump2D、DisplacementShader 等节点并连接 |
| GUI 支持 | 使用 PySide2 创建图形界面 |
| 多选支持 | 支持一次为多个物体分配材质 |
---
## ✅ 示例代码(Python 2.7 兼容)
```python
# -*- coding: utf-8 -*-
from __future__ import print_function
import maya.cmds as cmds
from PySide2 import QtWidgets, QtCore
def create_shader(shader_type='lambert'):
"""
创建指定类型的材质
"""
shader_name = cmds.shadingNode(shader_type, asShader=True)
sg = cmds.sets(renderable=True, noSurfaceShader=True, empty=True, name=shader_name + 'SG')
cmds.connectAttr('%s.outColor' % shader_name, '%s.surfaceShader' % sg)
return shader_name, sg
def assign_shader(shader_sg, objects):
"""
将材质分配给选中的物体
"""
for obj in objects:
shapes = cmds.listRelatives(obj, children=True, shapes=True)
if shapes:
shape = shapes[0]
cmds.sets(shape, e=True, forceElement=shader_sg)
def connect_texture(shader, attr, texture_path):
"""
创建 file 节点并连接到材质属性
"""
file_node = cmds.shadingNode('file', asTexture=True)
cmds.setAttr('%s.fileTextureName' % file_node, texture_path, type='string')
if 'normal' in attr.lower():
bump_node = cmds.shadingNode('bump2d', asUtility=True)
cmds.connectAttr('%s.outAlpha' % file_node, '%s.bumpMap' % bump_node)
cmds.connectAttr('%s.outNormal' % bump_node, '%s.%s' % (shader, attr))
elif 'displace' in attr.lower():
disp_node = cmds.createNode('displacementShader')
cmds.connectAttr('%s.outColor' % file_node, '%s.color' % disp_node)
cmds.connectAttr('%s.displacement' % disp_node, '%s.initialShadingGroup.displacementShader' % cmds.ls(sl=True)[0])
else:
cmds.connectAttr('%s.outColor' % file_node, '%s.%s' % (shader, attr))
def get_selected_objects():
return cmds.ls(selection=True)
class LookDevTool(QtWidgets.QWidget):
def __init__(self):
super(LookDevTool, self).__init__()
self.setWindowTitle("LookDev 工具 - Maya 2018")
self.setFixedSize(400, 400)
self.initUI()
def initUI(self):
layout = QtWidgets.QVBoxLayout()
# 材质类型选择
shader_layout = QtWidgets.QHBoxLayout()
self.shader_combo = QtWidgets.QComboBox()
self.shader_combo.addItems(['lambert', 'blinn', 'phong', 'aiStandardSurface'])
shader_layout.addWidget(QtWidgets.QLabel("选择材质类型:"))
shader_layout.addWidget(self.shader_combo)
layout.addLayout(shader_layout)
# 贴图路径输入
self.texture_inputs = {}
for tex_type in ['color', 'normal', 'bump', 'displacement', 'specular']:
hlayout = QtWidgets.QHBoxLayout()
label = QtWidgets.QLabel("{}.tga:".format(tex_type))
line_edit = QtWidgets.QLineEdit()
btn = QtWidgets.QPushButton("浏览")
btn.clicked.connect(lambda _, le=line_edit: self.browse_texture(le))
hlayout.addWidget(label)
hlayout.addWidget(line_edit)
hlayout.addWidget(btn)
layout.addLayout(hlayout)
self.texture_inputs[tex_type] = line_edit
# 创建材质按钮
create_btn = QtWidgets.QPushButton("创建材质并分配")
create_btn.clicked.connect(self.create_material_and_assign)
layout.addWidget(create_btn)
self.setLayout(layout)
def browse_texture(self, line_edit):
file_path, _ = QtWidgets.QFileDialog.getOpenFileName(self, "选择贴图", "", "Texture Files (*.tga *.jpg *.png *.exr)")
if file_path:
line_edit.setText(file_path)
def create_material_and_assign(self):
shader_type = self.shader_combo.currentText()
textures = {k: v.text() for k, v in self.texture_inputs.items() if v.text()}
selected_objs = get_selected_objects()
if not selected_objs:
cmds.warning("请先选择物体!")
return
# 创建材质
shader, sg = create_shader(shader_type)
# 分配材质
assign_shader(sg, selected_objs)
# 连接贴图
for tex_type, path in textures.items():
if 'color' in tex_type:
connect_texture(shader, 'color', path)
elif 'normal' in tex_type:
connect_texture(shader, 'normalCamera', path)
elif 'bump' in tex_type:
connect_texture(shader, 'bumpMap', path)
elif 'displace' in tex_type:
connect_texture(shader, 'displacement', path)
elif 'specular' in tex_type:
connect_texture(shader, 'specularColor', path)
print("材质创建完成,贴图已连接!")
def show_tool():
global tool_window
try:
tool_window.close()
except:
pass
tool_window = LookDevTool()
tool_window.show()
# 调用方式:在 Maya 脚本编辑器中运行以下命令
# import lookdev_tool
# lookdev_tool.show_tool()
```
---
## ✅ 如何使用该工具?
### 方法 1:直接在 Maya 脚本编辑器中运行
1. 打开 Maya 2018;
2. 打开 Script Editor;
3. 切换到 Python 模式;
4. 粘贴上述代码;
5. 运行:
```python
import lookdev_tool
lookdev_tool.show_tool()
```
### 方法 2:保存为 `.py` 文件并在菜单中注册
1. 保存为 `lookdev_tool.py`;
2. 在 Maya 中打开 `Script Editor`;
3. 输入:
```python
import lookdev_tool
cmds.menuItem(label="LookDev Tool", command="lookdev_tool.show_tool()")
```
---
## ✅ 注意事项
- Maya 2018 默认使用 Python 2.7,所以必须使用兼容 Python 2 的语法;
- 如果你使用了 `f-string` 或 `pathlib` 等 Python 3 特性,请替换为 `+` 拼接字符串或 `os.path`;
- `aiStandardSurface` 材质需要安装 **Arnold 渲染器**;
- GUI 使用 PySide2,Maya 2018 支持;
- 贴图路径请使用绝对路径;
- Maya 中的材质连接方式因材质类型而异,需要根据具体材质调整节点连接逻辑;
---
## ✅ 示例功能扩展建议
| 功能扩展 | 实现方式 |
|----------|----------|
| 支持 USD 材质 | 使用 `mayaUsd` 模块 |
| 支持 Substance 贴图自动加载 | 使用 Substance Python API 或目录结构解析 |
| 添加灯光预设 | 创建灯光节点并设置参数 |
| 支持材质库 | 保存材质为 `.ma` 文件并导入 |
| 支持批量处理 | 多线程或异步处理多个物体 |
---
##