Houdini终于从19.0版开始支持MaterialX（1.38版）：在：https://www.sidefx.com/docs/houdini/solaris/materialx所以我想写一些关于MaterialX的东西。目前的Houdini虽然支持导入MaterialX，不支持导出。也许它将来会支持出口。否则无法使用。看H19.0.440的更新MaterialX vops 有一个 RMB > Save > MaterialX... 菜单项，用于将着色器网络保存到 .mtlx 文件。您现在可以导出到 MaterialX。此外，大约有 148 个由 MaterialX 准备的标准节点（在官方 MaterialX 中内联或 OSL 中编写）作为 VOP 实现。当我尝试放置它时...... 看起来 MaterialX 中作为标准提供的大多数节点都是用 VOP 实现的，除了 Lama 节点。 可用的节点不多。有吸引力的节点也很少。 但是你要注意什么？ 此 MaterialX 标准曲面的规格在https://autodesk.github.io/standard-surface/中有详细说明。 Karma 的 MaterialX 标准曲面： Arnold 的 aiStandardSurace 参数： 参数完全相同。 换句话说，Karma 和 Arnold 现在可以使用通用的 Uber 着色器。standard_surface此外，AMD 的 GPUOpen 站点 ( https://matlib.gpuopen.com/main/materials/all ) 发布了大量的 MaterialX 文件。什么是 MaterialX？什么是 MaterialX？我认为有些人会成为这样的人。这里有一些我想介绍的网站。MaterialX官网https://www.materialx.org/MaterialX 的概述、规格和API文档都写在这里。我已经在我的网站上发布了规范的日文翻译，所以如果您有兴趣，请访问。除了MaterialX github https://github.com/materialx/MaterialX源代码，您还可以下载使用下面介绍的 MaterialXViewer。Autodesk 的 github https://github.com/autodesk-forks/MaterialX如果您在 Arnold 中寻找 MaterialX，也请查看此处。MaterialX 很难解释，因为您必须多次阅读规范才能理解它，但这里是我发布的翻译介绍的一部分。1.1. 简介许多 CG 制作工作室为每个制作流程使用涉及多个软件工具的工作流程。此外，由于需要在多个设施之间共享和外包大量工作，因此其他部门和工作室需要使用不同的软件包和渲染系统的完全外观开发模型。必须通过。此外，工作室渲染管道过去使用由经验丰富的程序员开发并由技术总监修改的整体着色器。纹理和着色器之间的预硬化连接和硬编码纹理颜色校正选项现在可用于通过图像处理树和混合运算符将输入纹理图像和程序纹理生成器转换为各种着色器。灵活的基于节点图的着色器网络，可连接和构建任意输入。至少需要四种不同的相关数据关系来指定 CG 对象的完整“外观”：根据图像处理网络是如何使用源、运算符、连接和参数构建的，输出空间可变数据流的数量会有所不同。 不同的贴图类型取决于与纹理文件名或 ID 关联的几何特定信息。 根据空间可变数据流、统一值和 BxDF 着色器输入之间的关系，您可以定义不同数量的材质。 根据材质与特定几何图形的关联方式，您创建的外观数量会有所不同。 据我们所知，没有通用的开放标准来传输上述所有数据关系。每个应用程序都有自己的文件格式来存储这些信息，但这些格式是私有的、专有的，并且没有充分指定和实施以完全打开和重现应用程序。不是... 因此，要指定由着色器网络构造的 CG 对象的“外观”，可以将这种外观（或外观的子组件）从一个软件包传递到另一个软件包或在安装之间传递。我们需要一个定义明确的标准，独立于平台。1.2. 提案我们提出了一个新的 Material Content Schema、MaterialX 和一个基于 XML 的文件，用于读取和写入该 MaterialX 内容。MaterialX 模式定义了一些主要元素类型和一些互补的子元素类型。主要元素类型有：定义数据处理图的标准节点集 使用 BxDF 着色器和自定义处理运算符扩展标准节点集。 定义绑定到特定统一参数值和空间变化输入数据流的着色器实例。 定义可以从节点图中引用的几何属性。 定义附加到几何体的材料和属性的特定组合。MTLX 文件是表示 MaterialX 文档的标准 XML 文件。此处使用的 XML 元素和属性对应于 MaterialX 元素和属性。一个 MTLX 文件可以是一个完全独立的文件，也可以拆分为多个文件以共享和重用组件。粗略地说，它是一种使用 XML 格式文件在不同 DCC 之间交换数据的机制，用于着色器图形定义和材质分配。例如，假设您在 Maya 中为角色建模，在 Arnold 中构建着色图，然后将该材质分配给模型。Maya 场景可以使用 Maya 进行渲染，但是如果您想像使用另一个 DCC（Houdini 或 Katana）进行外观开发的情况，您可以理解为什么需要 MaterialX。当您通过 FBX 或 Alembic 将 Maya 场景传递到另一个 DCC 时会发生什么？几何会通过，但着色器图不会通过，对吧？好吧，在另一个 DCC 中重建着色器图并重新分配材质是一项非常低效的任务。此外，代理（Arnold 术语中的 Stand-In，Maya 术语中的 GPU 缓存）无法直接访问几何体。如果渲染器和Arnold统一，可以不使用MaterialX，使用Arnold标准格式ass或者USD来解决这个问题，但是如果渲染器不同呢？不同的渲染器提供不同的着色器。官方的 MaterialX 提供了基于 OSL 的标准节点，因此如果您创建一个仅包含这些标准节点的着色器图，您可以使用支持 MaterialX 的渲染器按原样渲染。Houdini 的 Karma 在 VOP 中使用其标准节点，而不是在其 OSL 中。使用MaterialXViewer有一个查看器可以查看 MaterialX 着色器图并将着色器从 MaterialX 转换为 OSL/MDL/GLSL，所以让我们使用这个查看器来玩一下 MaterialX。1.首先，根据自己机器的环境，从https://github.com/materialx/MaterialX/releases下载MaterialXViewer 。 2.zip解压到任意位置时，内容如下。执行文件夹中的MaterialXView.exe 启动MaterialXViewer。 3. MaterialXViewer的使用方法请参考以下文档。https://github.com/materialx/MaterialX/blob/main/documents/DeveloperGuide/Viewer.md 4.点击Load Material按钮，选择下面的一个MaterialX示例文件，我们来看看。在查看器中按一个键会将OSL 文件转换为相同的 MaterialX 文件位置，将键转换为 GLSL 文件，将键转换为 MDL 文件。MaterialXViewer安装目录/resources/Materials/Examples/StandardSurfaceMaterialXViewer 可以读取 MaterialX 文件并将其转换为 OSL/GLSL/MDL 文件（最初它是一个名为 ShaderX 的项目，但在我知道之前它已与 MaterialX 项目合并）。此 MaterialXViewer 无法读取任何 MaterialX 文件，并且由于它在内部通过 MaterialXGenGLSL 显示 GLSL，因此无法显示具有光线跟踪处理节点的 MaterialX 文件，例如 Ambient Occlusion。此外，无法显示 MaterialX 标准节点以外的节点，例如带有 Arnold 节点的 MaterialX。作为一个例外，可以显示 aiStandardSurface。这是因为它对应于 MaterialX 标准曲面。在Solaris上导入MaterialX文件要加载 MaterialX 文件，只需在参考 LOP 中指定该文件。现在材质已添加到场景图树中，您可以在“指定材质 LOP”中将其指定给目标 prim 并进行渲染。导出MaterialX文件我想自己导出 MaterialX，而不是标准的 Houdini 导出器。如果在Python shell 中键入，import MaterialXMaterialX 模块可以作为标准使用。 这允许您创建自己的导出器。 由于 MaterialX 文件是 XML 格式的，所以我认为那些处理过 XML 解析器的人可以直观地控制它们。 这次我想编写一个 Python 脚本，将在 Houdini 的 mat 上下文中构建的 MaterialX 节点图导出到 MaterialX 文件中。将Houdini内置的MaterialX节点图导出到MaterialX文件我已经构建了一个 MaterialX 节点图，如下所示。构建 MaterialX 节点图时要记住的一件事是输入和输出类型必须匹配。一种常见的做法是将vector3的输出连接到color3的输入。请注意，这往往与其他节点图一起使用。我想用我自己的 Python 脚本将它导出到 MaterialX 文件中。 以 MaterialX 格式查看，这是一个必需的表面材质元素，其节点是通过该方法添加的。通过该方法添加 单个节点的 Node 元素。 MaterialX 具有与 Houdini 中的子网相对应的 NodeGraph 元素。 所以通过方法添加子网节点。MtlX Surface MaterialaddMaterialaddNodeaddNodeGraph要将Houdini中创建的MaterialX节点图导出到MaterialX，首先需要从MaterialX VOP节点检查MaterialX节点类型和输出类型。我这次做的脚本是根据MaterialX VOP的节点类型名称和签名参数判断创建的。这是我根据以上内容整理的 Python 脚本。/stage/一个 mtlx 文件会自动生成在与直接在 context 下materialx命名的 Material Network 中的MtlX Surface Material每个节点的 hip 文件相同的位置。import hou import MaterialX as mx import re signatureLabeltoValueMap ={ "Boolean (Float)":"boolean_float", "Boolean":"boolean", "BSDF (C)":"bsdfC", "BSDF (F)":"bsdfF", "BSDF":"bsdf", "Color (B)":"color3B", "Color (Color 4)":"color3_color4", "Color (Color Array)":"color3_color3array", "Color (Color)":"color3_color3", "Color (FA)":"color3FA", "Color (Float)":"color3_float", "Color (I)":"color3I", "Color (Vector 2)":"color3_vector2", "Color (Vector 3)":"color3_vector3", "Color (Vector 4)":"color3_vector4", "Color 4 (B)":"color4B", "Color 4 (CF)":"color4CF", "Color 4 (Color 4 Array)":"color4_color4array", "Color 4 (Color 4)":"color4_color4", "Color 4 (Color)":"color4_color3", "Color 4 (FA)":"color4FA", "Color 4 (Float)":"color4_float", "Color 4 (I)":"color4I", "Color 4 (Vector 2)":"color4_vector2", "Color 4 (Vector 3)":"color4_vector3", "Color 4 (Vector 4)":"color4_vector4", "Color 4 Array (Color 4 Array)":"color4array_color4array", "Color 4":"color4", "Color Array (Color Array)":"color3array_color3array", "Color":"color3", "Displacement Shader (F)":"displacementshaderF", "Displacement Shader (V)":"displacementshaderV", "Displacement Shader":"displacementshader", "EDF (C)":"edfC", "EDF (F)":"edfF", "EDF":"edf", "filename":"filename", "Float (B)":"floatB", "Float (Color 4)":"float_color4", "Float (Color)":"float_color3", "Float (Float Array)":"float_floatarray", "Float (I)":"floatI", "Float (Vector 2)":"float_vector2", "Float (Vector 3)":"float_vector3", "Float (Vector 4)":"float_vector4", "Float Array (Float Array)":"floatarray_floatarray", "Float":"float", "Integer (Float)":"integer_float", "Integer (Integer Array)":"integer_integerarray", "Integer Array (Integer Array)":"integerarray_integerarray", "Integer":"integer", "Light Shader":"lightshader", "Matrix 3x3 (FA)":"matrix33FA", "Matrix 3x3":"matrix33", "Matrix 4x4 (FA)":"matrix44FA", "Matrix 4x4":"matrix44", "String (String Array)":"string_stringarray", "String Array (String Array)":"stringarray_stringarray", "String":"string", "Surface Shader (C)":"surfaceshaderC", "Surface Shader (F)":"surfaceshaderF", "Surface Shader":"surfaceshader", "VDF (C)":"vdfC", "VDF (F)":"vdfF", "VDF":"vdf", "Vector 2 (B)":"vector2B", "Vector 2 (Color 4)":"vector2_color4", "Vector 2 (Color)":"vector2_color3", "Vector 2 (FA)":"vector2FA", "Vector 2 (Float)":"vector2_float", "Vector 2 (I)":"vector2I", "Vector 2 (M3)":"vector2M3", "Vector 2 (Vector 2 Array)":"vector2_vector2array", "Vector 2 (Vector 2)":"vector2_vector2", "Vector 2 (Vector 3)":"vector2_vector3", "Vector 2 (Vector 4)":"vector2_vector4", "Vector 2 Array (Vector 2 Array)":"vector2array_vector2array", "Vector 2":"vector2", "Vector 3 (B)":"vector3B", "Vector 3 (Color 4)":"vector3_color4", "Vector 3 (Color)":"vector3_color3", "Vector 3 (FA)":"vector3FA", "Vector 3 (Float)":"vector3_float", "Vector 3 (I)":"vector3I", "Vector 3 (M4)":"vector3M4", "Vector 3 (Vector 2)":"vector3_vector2", "Vector 3 (Vector 3 Array)":"vector3_vector3array", "Vector 3 (Vector 3)":"vector3_vector3", "Vector 3 (Vector 4)":"vector3_vector4", "Vector 3 Array (Vector 3 Array)":"vector3array_vector3array", "Vector 3":"vector3", "Vector 4 (B)":"vector4B", "Vector 4 (Color 4)":"vector4_color4", "Vector 4 (Color)":"vector4_color3", "Vector 4 (FA)":"vector4FA", "Vector 4 (Float)":"vector4_float", "Vector 4 (I)":"vector4I", "Vector 4 (Vector 2)":"vector4_vector2", "Vector 4 (Vector 3)":"vector4_vector3", "Vector 4 (Vector 4 Array)":"vector4_vector4array", "Vector 4 (Vector 4)":"vector4_vector4", "Vector 4 (VF)":"vector4VF", "Vector 4 (VV)":"vector4VV", "Vector 4 Array (Vector 4 Array)":"vector4array_vector4array", "Vector 4":"vector4", "Volume Shader (C)":"volumeshaderC", "Volume Shader (F)":"volumeshaderF", "Volume Shader":"volumeshader", } nosignatureOutputMap={ "surface":"surfaceshader", "dispacement":"displacementshader", "vector2":"vector2", "float":"float", "color":"color3", "vector4":"vector4", "vdf":"vdf", "vector":"vector3", "bsdf":"bsdf", "edf":"edf", } def defineNodeAndNodeGraph(outputNode,doc,ref): for eachVop in outputNode.inputAncestors(): nodeName = eachVop.name() nodeTypeName = eachVop.type().name() #ノードグラフを追加する if nodeTypeName == "subnet": subnetOutput = None for eachChild in eachVop.children(): if eachChild.type().name()=="suboutput": subnetOutput = eachChild break nodeGraph = doc.addNodeGraph(nodeName) defineNodeAndNodeGraph(subnetOutput,nodeGraph,ref) #ノードを追加する else: if nodeTypeName.startswith("mtlx"): nodeTypeName = nodeTypeName.split("mtlx")[-1] else: continue #Separate系のノードにsignatureパラメータがないのでVOPノードタイプ名からMaterialXノードタイプを判断する if nodeTypeName.endswith(("3c","4c","3v","4v")): nodeTypeName = nodeTypeName[:-1] signatureValue = "" signature = eachVop.parm("signature") if signature != None: signatureLabel = signature.menuLabels()[signature.menuItems().index(signature.eval())] if signatureLabel in signatureLabeltoValueMap: signatureValue = signatureLabeltoValueMap[signatureLabel] refNodeDef = None if signatureValue != "": refNodeDef = ref.getNodeDef("ND_"+nodeTypeName+"_"+signatureValue) else: refNodeDef = ref.getNodeDef("ND_"+nodeTypeName) if refNodeDef == None: for outputType in eachVop.outputDataTypes(): if outputType in nosignatureOutputMap: refNodeDef = ref.getNodeDef("ND_"+nodeTypeName+"_"+nosignatureOutputMap[outputType]) if refNodeDef != None: break if refNodeDef == None: continue nodeElement = doc.addNode(refNodeDef.getNodeString(),nodeName) nodeElement.setType(refNodeDef.getType()) for eachInput in refNodeDef.getInputs(): parmType = eachInput.getType() value = eachInput.getValueString() eachParmName = eachInput.getName() if parmType in ["color2","color3","color4","matrix33","matrix44","vector2","vector3","vector4"]: tupleValues = 0 if value != "": tupleValues = tuple([float(i.strip()) for i in value.split(",")]) vopParmName = eachParmName+"_"+signatureValue parmCheck = eachVop.parmTuple(vopParmName) if parmCheck == None: vopParmName = eachParmName evaluatedValues = eachVop.parmTuple(vopParmName).eval() if len(evaluatedValues)==1: evaluatedValues = evaluatedValues*int(re.sub(r"\D","",parmType)) if evaluatedValues != tupleValues: nodeElement.setInputValue(eachParmName, ",".join([str(i) for i in evaluatedValues]), parmType) elif parmType == "integer": vopParmName = eachParmName+"_"+signatureValue parmCheck = eachVop.parm(vopParmName) if parmCheck == None: vopParmName = eachParmName evaluatedValue = eachVop.parm(vopParmName).eval() if evaluatedValue != int(value): nodeElement.setInputValue(eachParmName, str(evaluatedValue), parmType) elif parmType == "float": vopParmName = eachParmName+"_"+signatureValue parmCheck = eachVop.parm(vopParmName) if parmCheck == None: vopParmName = eachParmName evaluatedValue = eachVop.parm(vopParmName).eval() if evaluatedValue != float(value): nodeElement.setInputValue(eachParmName, str(evaluatedValue), parmType) elif parmType in ["string","filename"]: vopParmName = eachParmName+"_"+signatureValue parmCheck = eachVop.parm(vopParmName) if parmCheck == None: vopParmName = eachParmName evaluatedValue = eachVop.parm(vopParmName).eval() if evaluatedValue != value: nodeElement.setInputValue(eachParmName, evaluatedValue, parmType) elif parmType == "boolean": if value == "true": value = True else: value = False vopParmName = eachParmName+"_"+signatureValue parmCheck = eachVop.parm(vopParmName) if parmCheck == None: vopParmName = eachParmName evaluatedValue = eachVop.parm(vopParmName).eval() if evaluatedValue != value: nodeElement.setInputValue(eachParmName, str(evaluatedValue).lower(), parmType) return def connectNodeAndNodeGraph(outputNode,doc): for eachVop in outputNode.inputAncestors(): if eachVop.type().name() == "subnet": eachNodeElement = doc.getNodeGraph(eachVop.name()) suboutputNode = None for i in eachVop.children(): if i.type().name()=="suboutput": suboutputNode = i break connectNodeAndNodeGraph(suboutputNode,eachNodeElement) else: eachNodeElement = doc.getNode(eachVop.name()) for eachConnect in eachVop.inputConnections(): inputNode = eachConnect.inputNode() inputNodeTypeName = inputNode.type().name() connectedParm = eachConnect.outputName() targetParm = eachConnect.inputName() if inputNodeTypeName == "subnet": targetNodeElement = doc.getNodeGraph(eachConnect.inputNode().name()) targetOutput = targetNodeElement.getOutput(targetParm) if not targetOutput: targetOutput = targetNodeElement.addOutput(targetParm) subnetOutputType = eachConnect.outputDataType() if subnetOutputType == "color": targetOutput.setType("color3") elif subnetOutputType == "color2": targetOutput.setType("color2") elif subnetOutputType == "color4": targetOutput.setType("color4") elif subnetOutputType == "float": targetOutput.setType("float") elif subnetOutputType == "vector": targetOutput.setType("vector3") elif subnetOutputType == "vector2": targetOutput.setType("vector2") elif subnetOutputType == "vector4": targetOutput.setType("vector4") elif subnetOutputType == "int": targetOutput.setType("integer") elif subnetOutputType == "bool": targetOutput.setType("boolean") elif subnetOutputType == "surface": targetOutput.setType("surfaceshader") elif subnetOutputType == "displacement": targetOutput.setType("displacementshader") subnetOutputNode = inputNode.subnetTerminalChild(targetParm)[0] for i in subnetOutputNode.inputConnections(): if i.outputName() == targetParm: outputNodeName = i.inputNode().name() break targetOutput.setConnectedNode(targetNodeElement.getNode(outputNodeName)) eachNodeElement.setConnectedOutput(connectedParm,targetOutput) else: targetNodeElement = doc.getNode(eachConnect.inputNode().name()) eachNodeElement.setConnectedNode(connectedParm,targetNodeElement) return searchPath = hou.text.expandString("$HH/materialx") ref = mx.createDocument() mx.loadLibraries(["libraries"],searchPath,ref) materialxNetwork = hou.node("/stage/materialx") if materialxNetwork != None and materialxNetwork.type().name() == "matnet": for eachVop in materialxNetwork.recursiveGlob("*",filter=hou.nodeTypeFilter.Vop): if eachVop.type().name()!="mtlxsurfacematerial": continue outputNodeName = eachVop.name() surfaceshaderName = "" displacementshaderName = "" for eachConnect in eachVop.inputConnections(): if eachConnect.outputName() == "surfaceshader": surfaceshaderName = eachConnect.inputNode().name() elif eachConnect.outputName() == "displacementshader": displacementshaderName = eachConnect.inputNode().name() if surfaceshaderName == "": continue doc = mx.createDocument() defineNodeAndNodeGraph(eachVop,doc,ref) connectNodeAndNodeGraph(eachVop,doc) material = doc.addMaterial(outputNodeName) material.setConnectedNode("surfaceshader", doc.getNode(surfaceshaderName)) if displacementshaderName != "": material.setConnectedNode("displacementshader", doc.getNode(displacementshaderName)) filePath = hou.text.expandString("$HIP") + "/" + hou.text.expandString("$HIPNAME") + "_" +outputNodeName + ".mtlx" mx.writeToXmlFile(doc, filePath) 使用导出的 MaterialX 文件如果在 Maya 的 Arnold 环境中使用 aiMaterialX 读取 MaterialX 文件（Arnold7.0 对应 MaterialX1.38）或使用 MaterialXViewer 读取，则可以像这样轻松传递着色器图。hip文件和python脚本都在这里：https://drive.google.com/file/d/1frXCG4R2Zv2MiFZl0cn2k2RvUgkoEmfc/view?usp=sharing
Cryptomatte默认使用了Asset ，Object以及Material 三种ID，但实际生产中，往往可能需要自定义些其它类型ID，Cryptomatte也支持使用自定义属性来区分ID。创建cryptomatte节点创建一个Arnold shader network，在其中创建AOV Output以及cryptomatte节点，在cryptomatte的user中，添加自定的AOV名称以及ID属性名称：给物体添加string类型属性使用Attribute Wrangle，在primitives上添加字符串属性，属性名称就来自于之前cryptomatte定义的data的名称。在渲染输出中添加对应AOV在arnold output设置中，在AOV Shader接入之前创建的aov节点，并创建AOV输出，选择之前定义的AOV名称选项。
伊利扬 · 格奥尔基耶夫、杰米 · 朴茨茅斯、扎普 · 安德森、阿德里安 · 赫鲁贝尔、艾伦 · 金、大垣慎司、弗雷德里克 · 斯文特版本 1.0.1.1本文档是 uber 表面着色器的规范，旨在提供一种材质表示，能够对实际视觉效果和动画制作中使用的绝大多数材质进行准确建模。它遵循 Arnold 渲染器中标准曲面着色器的设计，其实现相对简单，其用户界面由一小组具有直观含义和范围的参数组成。本文档是版本化的，将随着规范的发展而更新。我们提供 OSL 和 MaterialX 参考实现 [Georgiev2019 ]，其目的是指定足够的细节，以允许在其他渲染器中实现，根据目的将外观与合理的保真度相匹配。今天，不同设施之间计算机图形场景资产的交换仍然是一个重大问题，尤其是在表面外观方面。不同的渲染器和 3D 引擎使用不同的着色系统、着色语言和固定功能的管道。此外，准确地建模表面外观仍然是一个复杂而相关的问题，并且是一个活跃的科学研究主题。然而，_当今_行业迫切需要找到一种标准化的材料模型，既涵盖日常工作流程中的最常见用例，又易于使用。多年来，随着不同供应商达成共识，将材料定义与场景中的光传输模拟分开，某些事实上的标准外观定义框架已经出现。此类通用框架包括 MaterialX、材质定义语言 (MDL) 和开放着色语言 (OSL) [Gritz2010]，允许将材料指定为原始表面反射模型的组合。然而，这些框架本身不足以满足最终用户的外观开发，他们不应该为日常任务从头开始构建表面着色器。需要具有一组明确定义的参数的标准超级着色器参数化，这些参数可以进行调整以表示大多数真实世界（以及想象中的）材质。在本提案中，我们旨在填补这一空白；我们的参考实现之一是用 OSL [Georgiev2019 ]编写的。我们提出的模型紧跟 Arnold 5 标准曲面着色器，它在 Anders Langlands 的 alSurface [Langlands2014 ] 和 Autodesk 3ds Max 的 Physical Material [Andersson2016 ] 中具有强大的精神前辈。alSurface 着色器在一段时间内已成为业界事实上的标准，但不再积极开发。3ds Max 的物理材质又受到 Allegorithmic 的 PBR 着色模型 [McDermott2018 ]、迪斯尼的 Principled Shader [Burley2012 ] 和几个实时 3D 引擎的一般行业趋势的强烈启发，并且在所有主要渲染引擎的支持下都得到了生产证明用于 3ds Max。在这个提议中，我们没有为每个可能的情况提供参数，而是有意尝试将参数集归结为仅在实践中最有用的参数。我们还修复了原始反射模型的组合，以确保用户尽可能在物理上合理的范围内工作。我们的目标是使整体行为简单、合乎逻辑、直观且易于理解，以便该模型涵盖大多数日常用例。对于它没有涵盖的少数几个，可能需要使用特定于渲染器的着色器，或构建定制的着色网络。我们的目标是创建一个表面着色模型，能够忠实地代表各种现实世界的材料。金属、玻璃或墙漆等基本材料表面的光散射特性得到了很好的研究，可以通过简单的分析模型准确地表示出来。其他材料，例如成品木材、布料或皮肤，包括堆叠在彼此顶部的半透明物质层。此类材料更复杂的行为是光在这些层之间的界面处散射并通过它们传播的结果，并且每个此类散射界面通常可以由分析模型表示。此外，许多现实世界的物体由几种不同的材料制成，例如金属和塑料，通过在其表面上对每种材料的类型进行纹理化，可以方便地对此类对象的外观进行建模。虽然材料过渡通常是突然的，但连续混合材料的能力对于艺术目的以及抗锯齿都很有用。两个基本操作——分层和混合——提供了一种语言来对具有复杂外观的不同材料进行建模。对于具有物理意义的堆叠层，它的底部应该有一个不透明（例如金属）或透明（例如玻璃或皮肤）的体块，并在其顶部有一组电介质板。任何未在两层之间的界面处反射的光都会向下传输通过下层，并在其相应的介质内进行选择性吸收。混合操作可以被认为是两种材料的统计混合，即使并非所有组合都具有物理意义。作为线性插值，该操作自动保持能量守恒。我们的着色器对包含十个组件的材质进行建模，这些组件按层次分层和混合，如图 1 所示。单个组件的属性可以在整个表面上变化。图 1：我们的着色器模拟的理想化材质模型的示意图。组件的水平堆叠代表统计混合，垂直堆叠代表分层。标有星号的组件是可选的，如第2.2节所述 。闭合混合表示给定一个着色点和一个视图方向，我们着色器的评估结果是一个_双向散射分布函数_(BSDF)，它描述了表面在那个点和方向上的光散射特性（加上一个发射分量和一个次表面散射分布1） . 由于在材料的整个结构内传播和散射，因此产生的 BSDF 表示在最顶部界面处的聚集光散射行为。准确模拟层状材料内的光传播通常很困难且计算成本很高，并且仍然是一个活跃的研究课题。我们的着色器采用常见的实用方法，即生成的 BSDF 可以表示为“原子”、构建块 BSDF 的简单混合（即线性组合）——图 1 中的每个组件一个。我们仍然尝试通过调整原子 BSDF 的属性和线性组合权重来模拟分层最重要的物理效果——着色、反射/透射模糊和角度相关的图层透明度。每个原子 BSDF，通常非正式地称为“波瓣”，是反射或传输分布函数，即 BRDF 或 BTDF（发射和次表面散射除外）。在我们的实现中，这些 BSDF 由_闭包_表示。闭包是一个“黑盒”对象，它包含用于评估和采样底层 BSDF 2 的渲染器特定例程。它由渲染器定义和使用，但由着色器使用一组基本数据类型的参数（例如浮点数、向量和颜色）进行_实例化_。我们的着色器返回一个闭包对象列表，每个对象都有一个关联的（颜色）权重。用户可以通过一组着色器级参数来控制闭包的实例化参数和权重，以模拟各种物理上合理的材料。加权闭包的总和完全描述了给定表面点的光发射和散射特性。封闭列表，如表 1所示，最终被传递给渲染器，用于集成围绕着色点的光传输。成分关闭描述透明度transparency简单的传递（可以认为是增量 BTDF）涂层specular_brdf介电微面 BRDF (GGX)排放emission漫射金属metal_brdf导体微面 BRDF (GGX)镜面反射specular_brdf介电微面 BRDF (GGX)镜面透射*specular_btdf介电微面 BTDF (GGX)光泽*sheen_brdf逆反射介电微面 BRDF [ Estevez2017 ]次表面散射*subsurface次表面散射（例如扩散或随机游走）漫透射*diffuse_btdf漫反射微面 BTDF (Oren-Nayar)漫反射diffuse_brdf漫反射微面 BRDF (Oren-Nayar)表 1：代表图 1所示材料模型的每个组件的闭包列表。标有星号的闭包是可选的，如第2.2节所述 。闭合权重通过一个公式计算，该公式旨在满足构造能量守恒，同时近似模拟层间光传播的影响。可以说，最突出的这种效果是镜面（电介质）层的方向变化透明度，这是它们的菲涅耳控制的半球方向反射率（又名定向反照率）的结果。我们根据反射率对对应的 BRDF 闭包与底层闭包进行权衡，我们的实现假设有一个reflectance(brdf)函数可以计算它。当反射积分的解析表达式不可用于闭包时，可以对其进行近似，例如通过蒙特卡罗估计。我们着色器的闭包权重计算逻辑最容易被可视化为一棵树，其中每个叶节点都是一个闭包，每个内部节点都是其两个子节点的线性组合，在边缘指定权重。我们在图 2 中说明了这种树结构。每个闭包的最终权重是沿其对应叶节点和根节点之间路径的边权重的乘积。在下面的第3节 中，我们更详细地描述了各个闭包和边权重。图 2：我们的着色器返回根闭包standard_surface，它是“原子”闭包的加权和，显示为树中的叶节点。每个闭包的权重是从其对应的叶子节点到根节点的路径上边权重的乘积。边缘权重由着色器级参数参数化，以粗体显示。兼容模式我们的模型旨在使生产级应用程序相对简单，但是其闭包和参数的数量对于实时引擎来说可能太多而无法处理。对于此类应用程序，我们定义了“预览”级别的兼容性，在典型情况下将我们的着色器简化为合理的视觉相似度。简化相当于归零组合权重参数transmission，sheen以及subsurface，它有效地消除了specular_btdf，sheen_brdf，subsurface，和diffuse_btdf封闭件。在这种兼容模式下，我们也忽略了其他闭包中的某些参数，这些参数在下一节的参数表中用星号标记。在本节中，我们描述了我们的着色器使用的各个闭包，列在表 1 中，它们的参数以及它们如何相互加权。一些参数在闭包中重复使用，因此它们在下面的小节中出现多次。我们用粗体表示着色器级参数，例如opacity。透明度该transparency封闭件具有没有实例化参数，并使得表面在遮光点完全透明，即，在输入射线被发送未偏转的和未着色的。效果如图3所示。所述opacity着色器参数控制所有其他封闭件的相对重量（通过线性“阿尔法”共混物），因此透明度重量1-opacity。通过以下公式的透明度和涂层闭合的组合是产生最终standard_surface闭合组合的顶级混合物：标准表面 = (1 - 不透明度) * 透明度() + 不透明度 * 涂层层 在coat_layer下面的小节中定义了闭包。着色器参数：透明度Name类型默认描述opacity颜色1,1,1表面的（彩色）不透明度（默认情况下完全不透明）图 3：不透明度与透射率。从左到右：仅带透射的球体、（二进制）不透明蒙版、带透射和蒙板不透明度的球体。涂层最上面的散射层是带有 GGX 微面 BRDF 闭合的介电涂层coat_brdf。作为电介质，该 BRDF 不保能（即，其定向反射率通常小于 1），因为它遵循菲涅耳反射定律。该层被假定为无限薄，剩余的未反射光直接传递到下面的层而不发生折射。反射颜色固定为白色，尽管涂层介质颜色可以由用户控制。闭包组合公式为外套层=外套*外套_brdf（...）+ lerp（白色，外套_颜色*（1 - 反射（外套_brdf）），外套）*发射镜面混合 其中lerp(a, b, t) = (1 - t) * a + t * b，emission_specular_mixture闭包在以下小节中定义。在上面的闭包公式中，white表示恒定的白色。然后，涂层实际上是“无操作”直通（当coat参数变为零时）和具有嵌入吸收介质的电介质板之间的统计混合。在真实的物理材料中，被底层散射的光在穿过有色/粗糙涂层时会被着色和模糊。在渲染器中准确捕捉这些效果需要按顺序模拟各个层之间的光相互作用。由于我们的模型是独立评估的闭包的简单线性组合，我们模拟这些效果：对涂层下方的所有层进行着色是通过按coat_color参数缩放它们的闭合来实现的，这可以解释为涂层介质内的吸收效应对来自底层的反射进行着色（即能量被着色涂层吸收）。在基板顶部有介电涂层时，一小部分光在涂层内部表现出内部反射，然后返回基板再次反射，变得更暗和更饱和。这种效果可以通过coat_affect_color参数模拟，该参数用作基材颜色的指数，目前仅限于漫反射和次表面散射：base_color = pow(base_color, 1.0 + (coat *coat_affect_color)) subsurface_color = pow(subsurface_color, 1.0 + (coat *coat_affect_color)) 我们还提供了镀层粗糙的选项来增加其他GGX BSDFs，即粗糙度metal_brdf，specular_brdf和specular_btdf。其余的 BSDF 没有调制，因为它们已经很粗糙了。调制给定的公式roughness是粗糙度 = lerp（粗糙度，1，外套 *coat_affect_roughness *coat_roughness） 着色器参数：涂层Name类型默认描述coat漂浮0反射权重（反射颜色固定为白色）coat_color颜色1,1,1来自下方所有层的光线的色调颜色coat_roughness漂浮0.1涂层反射粗糙度；在传递给 BSDF 之前在内部进行平方以获得更线性的感知响应 [ Burley2012 ]coat_anisotropy*漂浮0的反射各向异性coat_brdf；范围[0,1]coat_rotation*漂浮0各向异性的取向；范围[0,1]（其中1表示 360°）coat_IOR漂浮1.5的折射率 coat_brdfcoat_normal向量0,0,0涂层反射的着色法线；可选，覆盖默认的着色法线；对闭包组合权重没有影响coat_affect_color*漂浮0额外调制漫反射和次表面散射饱和度的程度；范围[0,1]coat_affect_roughness*漂浮0对下面镜面反射层粗糙度的影响有多大；范围[0,1]图 4：通过组合涂层和镜面反射层可以实现的各种效果。从左到右：薄片、雨滴、碳纤维。排放附加的定向均匀（即朗伯）发射闭合emission位于涂层下方，但在其他方面不受应用于其他闭合的组合权重的能量守恒逻辑的影响。我们将发射置于涂层下方，以允许渲染由反射表面（例如荧光棒）界定的低发射材料，而无需对发射器和边界对象进行明确建模。涉及发射的闭包组合是发射镜面混合=发射*发射颜色*发射（）+镜面混合 其中specular_mixture闭包定义如下。着色器参数：发射Name类型默认描述emission漂浮0发射颜色乘数emission_color颜色1,1,1发光颜色图 5：表示连接到 的热熔岩的纹理贴图emission_color。金属涂层下方的金属反射被建模为 GGX 微面导体 BRDF metal_brdf，其吸收系数和复杂的折射率是根据更用户友好的参数base_color和specular_color [Gulbrandsen2014 ] 计算的，定义如下。这允许通过直接指定正常和掠射入射的（可纹理化）颜色来实现所需的外观。请注意，这两个颜色参数也用于非金属（即电介质）镜面反射和漫反射 BRDF，如下所述。这种非透射金属 BRDF 作为统计混合物与非金属（即电介质）镜面反射层模型混合，在下一节中描述，根据metalness参数如下：specular_mixture = metalness * metal_brdf(...) + (1 - metalness) * specular_reflection_layer 其中specular_reflection_layer闭包定义如下。当厚度为光波长数量级的薄折射膜放置在材料顶部时，由于干涉，会出现类似彩虹的彩虹色效应。这些效果包含在微面封闭中，适用于金属或镜面反射和透射层，以存在为准。薄膜在逻辑上位于涂层和底层之间，但由于假定涂层是无限薄的，薄膜外部介质的折射率被认为是真空的折射率，即1.0。假设它的内部是镜面反射层。薄膜模型基于 Belcour 和Barla [Belcour2017 ] 的模型。着色器参数：金属Name类型默认描述base漂浮1标量乘数 base_colorbase_color颜色0.8,0.8,0.8法向入射时的反射颜色（即从正上方看的表面）specular漂浮1标量乘数 specular_colorspecular_color颜色1,1,1掠入射时的反射颜色（即轮廓周围）specular_roughness漂浮0.2反射粗糙度；在传递给 BSDF 之前在内部进行平方，以在参数范围内实现更均匀的粗糙度外观specular_anisotropy*漂浮0的反射各向异性metal_brdf；范围[0,1]specular_rotation*漂浮0各向异性的取向；范围[0,1]（其中1表示 360°）thin_film_thickness*漂浮0薄膜厚度（纳米）thin_film_IOR*漂浮1.5薄膜的折射率图 6：通过设置base_color和specular_color参数由金属封盖生产的金属。从左到右：铝、铜、金。图 7：薄膜干涉对表面的影响。从左到右：多色调车漆、肥皂泡和烧焦的铬。镜面反射该层模拟了 GGX 微面电介质 BRDF specular_brdf，概念上就在涂层下方。与涂层类似，由于菲涅耳定律，这种 BRDF 本身不是能量守恒的，未被反射的能量完全传输到下面的层。我们使用的闭合组合表示反射和透射镜面波瓣之间的能量平衡specular_reflection_layer = specular * specular_color * specular_brdf(...) + (1 - specular_color * specular * 反射率(specular_brdf)) *transmission_sheen_mix 其中transmission_sheen_mix闭包定义如下。着色器参数：镜面反射Name类型默认描述specular漂浮1镜面反射权重specular_color颜色1,1,1镜面反射颜色specular_roughness漂浮0.2反射粗糙度；在传递给 BSDF 之前在内部进行平方，以在参数范围内获得更均匀的粗糙度specular_IOR漂浮1.5的折射率 specular_brdfspecular_anisotropy*漂浮0的反射各向异性specular_brdf；范围[0,1]specular_rotation*漂浮0各向异性的取向；范围[0,1]（1表示 360°）thin_film_thickness*漂浮0薄膜厚度（纳米）thin_film_IOR*漂浮1.5薄膜的折射率图 8：改变镜面反射折射率。从左到右：1.0、1.1、1.52（默认）。镜面透射该层transmission对通过 GGX 微面 BTDF 穿过镜面反射层底部的透射specular_btdf或从漫反射基层 ( base_layer) 散射的统计混合（根据参数）进行建模。闭包组合由下式给出transmission_sheen_mix = 透射 * transm_color * specular_btdf(...) + (1 - 透射) * sheen_layer 在sheen_layer下面的小节中定义了闭包，并且如下所述，transm_color = (transmission_depth == 0) ? 传输颜色：1 传输闭包specular_btdf与 共享许多参数specular_brdf；例如，粗糙度​spec_btdf​默认与 相同specular_roughness。它可以通过​transmission_extra_roughness参数额外增加或减少，尽管这在物理上是不正确的。就像镜面反射 BRDF 一样，这个 BTDF 可以通过重用相同的参数由薄膜调制。如果thin_walled布尔参数为false，则表面被认为是有限尺寸实体对象的边界。因此，根据specular_btdf闭合，光在进入和离开物体时会发生折射。如果thin_walled是true，则表面是双面的，代表无限薄的壳（例如理想化的树叶或纸），因此光在镜面透射到另一侧时不会发生折射。在这种情况下，折射率设置为周围介质的折射率（例如，真空为 1.0），并且色散和薄膜被禁用。我们还允许指定对象内部均匀介质的属性，有或没有散射。这对于建模一系列材料非常有用，从透明或有色（仅吸收）玻璃和液体到具有视觉显着散射的材料，例如蜂蜜、深水体、乳白色玻璃或乳白色玻璃。的transmission_color和transmission_depth参数对是一种常用的艺术家友好的方式来设置的介质的消光系数，而transmission_scatter直接设置介质的散射系数。当transmission_depth为零时，内部介质为零，并transmission_color用于直接在闭合混合物中以恒定量乘法着色折射（参见上面的代码片段）。什么时候transmission_depth 为正，着色由内部介质通过比尔定律控制。着色器参数：镜面透射Name类型默认描述transmission*漂浮0传动重量transmission_color*颜色1,1,1透射颜色，即色调transmission_depth*漂浮0白光在材料transmission_color内部传播的距离，直到它的颜色完全符合比尔定律，决定了内部介质的消光系数；如果为零，则transmission_color作为恒定（表面）透射色调；范围[0,infinity)transmission_scatter*颜色0,0,0内部介质的散射系数transmission_scatter_anisotropy*漂浮0内部介质的 Henyey-Greenstein 相函数的各向异性；范围[-1,1]transmission_dispersion*漂浮0色散阿贝数，描述折射率随波长变化的程度​transmission_extra_roughness*漂浮0附加（正或负）粗糙度 specular_roughnessspecular_roughness漂浮0.2折射粗糙度specular_btdf; 在传递给 BTDF 之前在内部进行平方，以在参数范围内实现更均匀的粗糙度外观[0,1]specular_IOR漂浮1.5的折射率 specular_btdfspecular_anisotropy*漂浮0的反射各向异性specular_btdf；范围[0,1]specular_rotation*漂浮0各向异性的取向；范围[0,1]（其中1表示 360°）thin_film_thickness*漂浮0薄膜厚度thin_film_IOR*漂浮1.5薄膜的折射率thin_walled*布尔值false如果true，物体被认为是无限薄的并且表面是双面的图 9：的效果transmission_scatter_anisotropy。从左到右：-0.5、0.0、0.5。光泽所述sheen_brdf封闭件是基于该埃斯特韦斯和库拉 的微面光泽 BRDF [Estevez2017 ]。它模拟纺织品的外观，其中表面刻面是圆柱形 “纤维”，轴主要平行于表面法线，在掠射角产生镜面高光。从概念上讲，这位于基础基板的顶部，因此传输到基板的能量会因光泽反射率而减少：sheen_layer = sheen * sheen_color * sheen_brdf(...) + (1 - sheen *reflectance(sheen_brdf)) * base_mix 其中base_mix闭包定义如下。着色器参数：sheenName类型默认描述sheen*漂浮0.8反射权重 sheen_brdfsheen_color*颜色1,1,1的反射颜色 sheen_brdfsheen_roughness*漂浮0.3反射粗糙度sheen_brdf；范围[0,1]图 10：使用光泽闭合呈现的各种纺织品。漫反射根据subsurface参数，基础衬底层由 Oren-Nayar 漫反射分量和次表面分量的统计混合组成：base_mix = (1 - subsurface) * base * base_color *diffuse_brdf(...) + subsurface * subsurface_mix 其中subsurface_mix闭包定义如下。着色器参数：漫反射Name类型默认描述base漂浮0.8反射权重 diffuse_brdfbase_color颜色1,1,1的反射颜色 diffuse_brdfdiffuse_roughness*漂浮0反射粗糙度 diffuse_brdf漫透射如果thin_walled是 ，则次表面分量退化为漫透射true。这代表了一个无限薄的外壳（例如一张理想化的粗糙纸），能量通过它传输到漫射瓣中。漫反射通过 Oren-Nayar 微面 BRDF 建模，围绕着色法线翻转，使其成为 BTDF：subsurface_mix = Thin_walled * subsurface_color *diffuse_btdf(...) + (1 - thin_walled) * subsurface(...) 其中subsurface封闭件进行说明。着色器参数：漫反射Name类型默认描述subsurface*漂浮0漫反射权重subsurface_color*颜色1,1,1漫透射色diffuse_roughness*漂浮0漫透射粗糙度图 11：不透明纸平面（左）与通过thin_walled（右）启用的漫反射。次表面散射对于固体对象（其thin_walled是false），表面散射模型的光传播和扩散的表面下的效果，其中，通常离开的射线叶在比入射光线不同的表面位置。这种效果由subsurface闭包表示，对此我们没有规定要使用的特定渲染模型，例如基于漫射轮廓表面照明模糊或强力次表面光线追踪。着色器参数：次表面散射Name类型默认描述subsurface*漂浮0次表面散射权重subsurface_color*颜色1,1,1次表面颜色，由subsurface(...)闭包使用subsurface_radius*颜色1,1,1红色、绿色和蓝色通道的次表面半径（即平均自由程）subsurface_scale*漂浮1标量标度 subsurface_radiussubsurface_anisotropy*漂浮0地下介质相函数的各向异性；范围[-1,1]图 12：改变次表面散射半径的效果 ( subsurface_radius)。在介绍了我们提出的模型之后，在本节中，我们将讨论一些潜在的微妙之处、注意事项和未来改进的领域。标量与 RGB 闭包权重考虑我们的镜面反射层公式，其中我们有闭包组合：specular_reflection_layer = specular * specular_color * specular_brdf(...) + (1 - specular_color * specular * 反射率(specular_brdf)) *transmission_sheen_mix 这里包括了specular_color因子乘法reflectance(specular_brdf)以确保能量_保存_。例如，如果反射是纯红色，则只有互补的青色会透射到底层。这允许防止组合模型中的能量损失并创建具有高反照率的多层材料。但是，虽然它在物理上是正确的，但它也可能使艺术家更难控制底层裂片中互补色的外观。我们考虑的另一种方法（例如在 3ds Max 的物理材料模型中使用）是将乘法闭合因子保持为标量，例如在这种情况下specular_color将从闭合权重中省略。这在技术上违反了能量守恒（即能量以非物理方式消散）并且难以创建具有彩色镜面反射的高反照率材料。但是，这种行为对艺术家来说可能更直观。总的来说，我们更喜欢身体上正确的行为，但这是有争议的。互惠物理上正确的 BSDF 必须满足互易性（即传入和传出方向交换下的对称性）。然而，在我们提出的模型中，即使叶级 BSDF 是互易的，闭包组合_也不是_。这是由于引入了reflectance(...)仅取决于传入方向的函数。如果将着色模型合并到某些光传输算法（例如双向路径跟踪）中，则这可能会出现问题，这些算法通常依赖此属性来保持。然而，强制互惠可能会使我们模型的数学形式显着复杂化，而不会产生质量上更好的视觉外观。对于许多渲染器，包括 Arnold——一种单向路径追踪器，可以违反互易的物理约束，即使在叶 BSDF 中，也不会导致任何实际问题。此外，以真正物理上正确的方式 [Jakob2014 ] 强制实施分层材料的互惠性目前过于复杂和繁琐，无法在生产渲染器中实现。在实际生产使用中实现互惠的模型，例如 Kulla 和 Estevez [Kulla2017 ] 的涂层方案，通过引入可能类似于此处描述的非互惠方法的不准确度的极端近似来实现。因此，目前我们并不认为在我们的模型中加入互惠是绝对必要的。分层模型我们的分层模型通过施工确保节能，并尽可能确保节能。然而，作为一个相对简单的模型，它只是根据近似reflectance(...)函数调整权重的闭包的线性组合，它不是我们描述的层中光传输的物理精确模拟。最近出现了许多对分层介质中全光传输的更准确处理 [Jakob2014 ] [Belcour2018 ] [Zeltner2018 ]。这些模型结合了通过整个层堆叠的各种反射和传输模式的影响，从而生成了最终的 BSDF（或一般的 BSSRDF），它不是每层 BSDF 的简单线性组合。将来，我们希望研究过渡到更准确的模型，例如这样。然而，目前似乎所有可用模型的计算成本都更高，实现起来也更复杂。我们至少在我们的模型中尝试合并一些由于手动层间交互而产生的最重要的影响。例如，我们允许涂层的粗糙度影响（部分）底层的粗糙度。表面取向在透射情况下，光可能从表面法线的上方或下方入射。传输层对此很敏感，并确保光线正确地折射通过界面。但是，其他层的方向与_面向_法线方向一致，因此当对象从外部和内部受到撞击时，散射行为是相同的。这又是一种非物理近似，它在实践中很有用，因为它简化了逻辑而不会引入明显的视觉伪影。用户友好性与灵活性我们的模型目前具有固定数量的层，其中仅包括两个镜面反射层，而其他一些流行模型（例如，皮克斯的 PxrSurface [Hery2017 ]）允许更多可配置的层数。在生产环境中，这种将模型调整到精细程度的自由在某些情况下可能很有用。然而，正如前面提到的，我们这里的一般理念是，最好提供一组相对最小的具有直观意义的用户友好参数，并使用组合方案确保任何给定的参数集都在物理合理性的范围内。在模型无法产生所需结果的边缘情况下，可以编写自定义着色器图。在本文档中，我们描述了一个标准、通用表面着色器的提案，用于各种生产渲染环境。此模型基于 Arnold 渲染器中标准曲面着色器的当前实现。我们详细描述了构成模型的十个闭包，以及它们如何组合以生成最终着色器。我们希望这个提议可以作为更广泛采用的标准表面着色器实现的有用基础。作者要感谢 Mike Farnsworth、Lee Griggs、Arvid Schneider、Milos Hasan、Michael Nickelsky、Henrik Edstrom、Karl Schmidt、Niklas Harrysson、Marcel Reinhard 和 Davide Pesare 提供的有用意见和反馈。[ Andersson2016 ] Zap Andersson。2016. 物理材料 (v1.01)。欧特克白皮书。[ Belcour2017 ] Laurent Belcour 和 Pascal Barla。2017. 微面理论的实际扩展，用于建模不同的虹彩。_ACM 图形交易_, 36, 4.[ Belcour2018 ] Laurent Belcour。2018. 使用带有统计运算符的原子分解高效渲染分层材料。_ACM 图形交易_, 37, 4.[ Burley2012 ] 布伦特 · 伯利。2016. 迪士尼基于物理的着色。在 _ACM SIGGRAPH 2012 课程：电影和游戏制作中基于物理的实用阴影_。[ Estevez2017 ] Alejandro Conty Estevez 和 Christopher Kulla。2017. 生产友好的 Microfacet Sheen BRDF。Sony Pictures Imageworks 技术报告。[ Gritz2010 ] 拉里 · 格里茨、克利福德 · 斯坦、克里斯 · 库拉和亚历杭德罗 · 康蒂。2010. 开放着色语言。在 _ACM SIGGRAPH 2010 会谈中_。[ Gulbrandsen2014 ] Ole Gulbrandsen。2014. 艺术家友好金属菲涅耳。_计算机图形技术杂志_，3、4。[ Hery2017 ] Christophe Hery, Ryusuke Villemin, Junyi Ling. 2017. 皮克斯材料基金会。皮克斯技术报告。[ Jakob2014 ] Wenzel Jakob、Eugene d'Eon、Otto Jakob 和 Steve Marschner。2014. 渲染分层材料的综合框架。_ACM 图形交易_, 33, 4.[ Langlands2014 ] 安德斯 · 朗兰兹。2014. Arnold 中基于物理的着色器设计。在 _ACM SIGGRAPH 2014 会谈中_。[ Kulla2017 ] Christopher Kulla 和 Alejandro Conty Estevez。2017. 在 Imageworks 重新审视基于物理的着色。在 _ACM SIGGRAPH 2017 课程：理论和实践中基于物理的阴影_。[ Zeltner2018 ] Tizian Zeltner 和 Wenzel Jakob。2018. 层实验室：各向异性表面反射率的加法和减法组合的微积分。_ACM 图形交易_, 37, 4.1 为方便起见，允许我们的闭包列表不仅是严格的 BSDF，而且还包括发射分布函数 (EDF) 和双向次表面散射分布函数 (BSSRDF)，这很有用。渲染器被理解为识别这些并适当地处理它们。2 或者确实是 EDF 或 BSSRDF。
PBR material在游戏行业中变得流行，并且CG渲染已经越来越多地发展。也就是说，游戏行业的材料正在慢慢追逐电影和电视的影响，而在过去的两年里，我们已经接近这个现实。CG渲染中使用的材质几乎可以实现游戏PBR材质的所有效果，相反，不会。PBR中使用的参数（此处最流行的金属粗糙度模型）是反照率金属度粗糙度法线，V-Ray material的主要相应参数是漫反射镜面光泽度正常或凸起。Albedo是熟悉的漫反射，即物体的固有颜色。金属度描述的是材料是金属还是电介质，这是一种感性解释，事实上，这个参数真正控制的是F0，即Feyner反射，垂直于相机视线的平面的反射率，当金属度为0时，F0的值为0.04，即IOR为1.5时的对应F0; 当金属度为1时，F0的值为1，当金属度为0-1的中位数时，F0也在0.04和1之间插值，我猜它是线性插值，F90在此过程中保持不变，始终恒定为1.这是实际上是对菲涅耳效应的粗略描述。在V-Ray材质球上，没有与金属相对应的参数，有些人可能认为它是镜面反射，这个参数控制反射强度，实际上不是。因为镜面反射整体上改变了整个材料球的反射率，金属度只会改变F0的正反射率，侧面总是1。F0和折射率，即IOR，有一个对应的关系，它可以通过公式转换。所以在V-Ray中，IOR在某种程度上可以对应于金属性。单词rough = 1-glossiness本身就是反义词。只要属性完全相互对应，V-Ray material就可以完全恢复PBR material的效果。但V-Ray的材质还有很多其他功能，例如透明，半透明，物理算法SSS效果，漫反射本身的粗糙度属性不适用于pbr。此外，一些PBR材料具有镜面特性。例如，UE4的PBR材料，但这里的镜面有点类似于V-Ray中的镜面反射，但不同。UE4材质球的镜面反射率是指F0的反射强度，默认值0.5对应于F0的0.04。F0会略微降低到0.在1音可以略微改善F0。原则上使用正确的方法来链接地图，v-ray纹理球可以获得非常类似于PBR纹理球的视觉效果，但边缘的反射强度的处理是不同的，尽管由于菲涅耳反射的存在，模型边缘的反射率恒定为1，但当物体表面粗糙，光线位于边缘时，容易发生相互反射，遮挡等现象，会使反射率看起来很好在宏观层面上较低。目前，我已经测试了几个渲染器，V-Ray Arnold和Martial Ray在处理边缘模糊反射方面不同于PBR球，而且实际上是错误的，我发现唯一正确的是redshift material。如果您使用的是V-Ray，Arnold或Mental Ray渲染器，并且没有发现问题，那么可以说您调整的大部分材料都是错误的。但遵循PBR材料的想法是对的。
Julius Ihle用OSL编写的jiWindowBox shader，支持PRman,Vray,Arnold,Blender等不同平台渲染器。只需一个简单的平面，就可以模拟出室内的效果，透视完美匹配相机，极适合大型城市场景的制作，因为是利用单张贴图去映射，模拟出的室内并不参与光照反射等的计算，大大减轻了模型工作量和渲染压力。这种方式被称为Interior Mapping或Parallax Mapping。参考链接：http://interiormapping.oogst3d.net/贴图说明除了房间的所有墙壁，还有一个中间层（例如可以是家具）和一个窗帘层，以提供真实的深度感。贴图需要注意以下几点：确保只在中间和窗帘区域有alpha。所有的墙壁，天花板和地板都应该是黑色的。RenderMan和Arnold渲染时，最好使用maketx（Arnold）或txmake（RenderMan）将纹理转换为mipmapped纹理文件，这样可以大大提高渲染速度。属性Shader安装Prman可以直接使用编译后的oso文件或者使用osl。VRray可直接使用编译后的oso文件或者使用osl。Arnold5.0需要使用编译后的oso文件拷入 ARNOLD_PLUGIN_PATH路径下，或是使用osl放入同一路径下，启动MAYA后加载mtoa自动编译。可参考 MtoA2.0使用osl Arnold 5.0刚出不久，其中最让我高兴的是支持了osl，目前世面的上几个主流渲染器，基本都早已支持了osl，... Shader下载http://pan.baidu.com/s/1hseTyYS
不得不说Octane渲染这类东西太爽了，cam上可以添加“特效”和“调色”，合成都省去了。使用最新的OctaneRender for Maya 3.06.4版：
ChaosGroup官方提供了V-Ray的alSurface shader下载，目前版本是v0.2b-alpha，而且一直在更新中。alSurface shader是来自于Anders Langlands的开源项目https://bitbucket.org/anderslanglands/alshaders/wiki/Home目前他们提供了arnold的shader，并且功能上要比ai的更好用，更完善，再加上因为是免费开源，现在许多公司和个人都在使用alshader。ChaosGroup或许是跟随潮流，参考alSurface编译出了vray版本的alSurface。下载地址：https://github.com/ChaosGroup/vray_al_surface/tags可惜目前还不支持折射。
在2015年SIGGRAPH上，Psyop的Jonah Friedman和Andy Jones提出了"Cryptomatte"，它可以自动分配ID，并且可以支持motion blur，Depth of field及transparency。SIGGRAPH上的详细介绍http://www.jonahfriedman.com/wp-content/uploads/psyop_IDmattes_poster.pdf现在他们已经提供了nuke工具的下载，并且将Cryptomatte功能集成到了1.0.0RC17的AlShaders。工具下载地址https://github.com/Psyop/CryptomattealSurface,alHair,alLayer上可以直接输出Cryptomatte AOV。目前alshader上可以分别输出crypto_asset，crypto_material，crypto_object三个类型的ID，全部是自动分配，并且不限ID数量。https://aducg.com/ueditor/php/upload/image/20160810/1470779785511714.pngNnke的工具是一个Cryptomatte节点，这里可以添加选择ID和减选。选择可以使用鼠标点选，也可以直接输入物体名称或者材质名称。还有两个项可以分别创建出所有ID的遮罩和所选择的ID遮罩。效果参考
MatCap在游戏引擎一类的地方很常用，因为计算快，其实就是一张贴图，当然也没有什么可调节的东西。显然AR这类渲染器并不需要MatCap，但是有时候会碰到各类奇葩的效果，比如TOON一类的效果就体现出它的价值了。只要求出物体的法线便可做到，所以方法很多，而且很简单。其它渲染器也都可以完成。但为什么要用arnold做这个事情呢?原因大致有两点：是我太无聊了。同一镜头全部使用一种渲染器完成会更“好”些，尤其MAYA。MatCap我也仅仅只是拿来渲染某个层方便合成师去完成最终效果而已。因为渲染太快了，所以我多渲染了点.........具体制作方法如下