自定义 Material 参考文档

Sceneform 提供了默认 Material 定义 (.sfm),让开发者可以轻松获得出色的外观。如果开发者希望深度定制其资产的外观,可以创建自己的材料定义(*.mat 文件),并通过在资产定义中指定 source 属性将这些定义应用于其资产。

核心概念

Material
材质决定了表面的视觉外观。为了完整描述和渲染 Surface,Material 提供以下信息:
  • Material 模型
  • 一组由用户控制的命名参数
  • 光栅状态(混合模式、后端剔除等)
  • 顶点着色器代码
  • fragment 着色器代码
Material 模型
Material 模型也称为着色模型光照模型,用于定义表面的固有属性。这些属性会直接影响光照的计算方式,从而影响表面的外观。
Material 定义
描述资料所需的所有信息的文本文件。本页介绍了 (*.mat) 材料定义文件的结构和格式。

Material 定义

材料定义是一个文本文件,用于描述材料需要的所有信息:

  • 名称
  • 用户参数
  • Material 模型
  • 必需属性
  • 插值(称为变量)
  • 光栅状态(混合模式等)
  • 着色器代码(片段着色器,可选择顶点着色器)

格式

Material 定义格式是一种松散地基于 JSON 的格式,我们称之为 JSONish。在顶层,Material 定义由 3 个使用 JSON 对象表示法的不同块组成:

material {
    // material properties
}

vertex {
    // vertex shader, optional
}

fragment {
    // fragment shader
}

最简可行材料定义必须包含一个 material 代码块和一个 fragment 代码块。vertex 代码块是可选的。

与 JSON 的区别

在 JSON 中,对象由键值对构成。JSON 对具有以下语法:

"key" : value

其中,值可以是字符串、数字、对象、数组或字面量(truefalsenull)。虽然此语法在 Material 定义中完全有效,但 JSONish 中还接受字符串(不带引号)的变体:

key : value

当字符串包含空格时,引号仍是必需的。

vertexfragment 代码块包含未转义且不带英文引号的 GLSL 代码,该代码在 JSON 中无效。

允许使用单行 C++ 样式注释。

密钥对的键区分大小写。

键值对的值不区分大小写。

示例

以下代码列表展示了一个有效的 Material 定义示例。此定义使用照明材料模型,使用默认的不透明混合模式,要求在渲染的网格中显示一组 UV 坐标并定义 3 个用户参数。本文档的以下部分详细介绍了 materialfragment 代码块。

material {
    name : "Textured material",
    parameters : [
        {
           type : sampler2d,
           name : texture
        },
        {
           type : float,
           name : metallic
        },
        {
            type : float,
            name : roughness
        }
    ],
    requires : [
        uv0
    ],
    shadingModel : lit,
    blending : opaque
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = texture(materialParams_texture, getUV0());
        material.metallic = materialParams.metallic;
        material.roughness = materialParams.roughness;
    }
}

Material 块

Material 块是必需块,包含用于描述所有非着色器数据的属性对列表。

name

类型
string
任何字符串。如果名称包含空格,则必须添加英文双引号。
说明
设置材质的名称。系统会在运行时保留该名称,以便用于调试。
material {
    name : stone
}

material {
    name : "Wet pavement"
}

shadingModel

类型
string
litclothunlit 中的任意一项。 默认值为 lit
说明
选择 Material 模型部分中所述的 Material 模型。
material {
    shadingModel : unlit
}

形参

类型
参数对象数组

每个条目都是具有 nametype 属性的对象,且二者均为 string 类型。名称必须是有效的 GLSL 标识符。该类型必须是下表中所述的类型之一。

类型 说明
bool 单个布尔值
布尔值 2 2 个布尔值的矢量
布尔值 3 3 个布尔值的矢量
布尔值 4 4 个布尔值的矢量
float 单浮点数
浮点数 2 2 个浮点数的矢量
浮点数 3 3 个浮点数的矢量
浮点数 4 4 个浮点数的矢量
int 单个整数
整数 2 2 个整数的向量
整数 3 3 个整数的矢量
整数 4 4 个整数的矢量
sampler2d 2D 纹理
samplerExternal 外部纹理。如需了解详情,请参阅 ExternalTexturesetExternalTexture()
采样器

采样器类型还可以指定 format(默认为 float)和 precision(默认为 default)。格式为 intfloat。精度可以是 default(平台的最佳精度,一般为桌面设备上的 high、桌面设备上的 medium 或移动设备中的精确度)、lowmediumhigh 之一。

说明

列出您的材料所需的参数。可以在运行时使用 Sceneform 的 Material API 设置这些参数。从着色器访问参数因参数类型而异:

  • Samplers type:使用以 materialParams_ 为前缀的参数名称。例如 materialParams_myTexture
  • 其他类型:使用参数名称作为名为 materialParams 的结构体的字段。例如 materialParams.myColor
material {
    parameters : [
        {
           type : float4,
           name : albedo
        },
        {
           type      : sampler2d,
           format    : float,
           precision : high,
           name      : roughness
        },
        {
            type : float2,
            name : metallicReflectance
        }
    ],
    requires : [
        uv0
    ],
    shadingModel : lit,
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = materialParams.albedo;
        material.roughness = texture(materialParams_roughness, getUV0());
        material.metallic = materialParams.metallicReflectance.x;
        material.reflectance = materialParams.metallicReflectance.y;
    }
}

需要

类型
string 数组
每个条目必须是以下任何一项:uv0uv1colortangents
说明
列出材质所需的顶点属性。系统会自动添加 position 属性,您无需指定此属性。选择 unlit 以外的任何着色模型时,系统会自动指定 tangents 属性。如需详细了解如何从着色器访问这些属性,请参阅本文档的着色器部分。
material {
    parameters : [
        {
           type : sampler2d,
           name : texture
        },
    ],
    requires : [
        uv0
    ],
    shadingModel : lit,
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = texture(materialParams_texture, getUV0());
    }
}

variables

类型
string 数组
最多 4 个字符串,每个字符串都必须是有效的 GLSL 标识符。
说明
定义由材料的顶点着色器输出的自定义插值(或变量)。该数组的每个条目都定义了一个插值的名称。片段着色器中的全名是带有 variable_ 前缀的插值的名称。例如,如果您声明一个名为 eyeDirection 的变量,则可以使用 variable_eyeDirection 在 fragment 着色器中访问该变量。在顶点着色器中,插值名称只是 MaterialVertexInputs 结构(在本例中为 material.eyeDirection)的成员。在着色器中,每个插值的类型都是 float4 (vec4)。
material {
    name : Skybox,
    parameters : [
        {
           type : sampler2d,
           name : skybox
        }
    ],
    variables : [
         eyeDirection
    ],
    vertexDomain : device,
    depthWrite : false,
    shadingModel : unlit
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        float theta = acos(variable_eyeDirection.y);
        float phi = atan(variable_eyeDirection.z / variable_eyeDirection.x) +
            (variable_eyeDirection.x > 0.0 ? 0.0 : PI);
        material.baseColor = texture(materialParams_skybox,
            vec2((phi + PI / 2.0) / (2.0 * PI), theta / PI));
    }
}

vertex {
    void materialVertex(inout MaterialVertexInputs material) {
        float3 p = getPosition().xyz;
        float3 u = mulMat4x4Float3(getViewFromClipMatrix(), p).xyz;
        material.eyeDirection.xyz = mulMat3x3Float3(getWorldFromViewMatrix(), u);
    }
}

混合

类型
string
opaquetransparentfadeaddmasked 中的任意一个。默认值为 opaque
说明

定义渲染对象如何与渲染目标的内容混合。可能的混合模式包括:

  • 不透明:停用混合,材料输出的 alpha 通道被忽略。
  • 透明:已启用混合模式。Material 的输出使用 Porter-Duff 的 source over 规则与渲染目标进行 alpha 合成。此混合模式假设预乘 alpha。
  • 淡化:充当 transparent,但透明度也会应用于镜面光照。在 transparent 模式下,材质的 alpha 值仅适用于漫射光。此混合模式对于淡入和淡出对象很有用。
  • 添加:已启用混合模式。材料的输出会添加到渲染目标的内容中。
  • 遮罩:混合模式已停用。此混合模式可启用 Alpha 遮罩。材料输出的 alpha 通道定义是否舍弃某个 fragment。如需了解详情,请参阅 maskThreshold 部分。
material {
    blending : transparent
}

顶点域

类型
string
objectworldviewdevice 中的任意一项。默认值为 object
说明

定义所渲染网格的域(或坐标空间)。该网域会影响顶点在顶点着色器中的转换方式。可能的网域包括:

  • 对象:顶点在对象(或模型)坐标空间中定义。顶点使用渲染对象的转换矩阵进行转换
  • 世界:顶点在世界坐标空间中定义。顶点不使用渲染对象的转换进行转换。
  • 视图:顶点在视图(或眼睛或相机)坐标空间中定义。顶点不使用渲染对象的转换进行转换。
  • 设备:顶点在标准化设备(或裁剪)坐标空间中定义。顶点不使用渲染对象的转换进行转换。
material {
    vertexDomain : device
}

插值

类型
string
smoothflat 中的任意一个。默认值为 smooth
说明
定义插值(或变量)在顶点之间的插值方式。 当此属性设置为 smooth 时,系统会对每个插值执行正确的视角插值。设置为 flat 时,系统不会执行插值,并且给定三角形中的所有 fragment 都将使用相同的阴影。
material {
    interpolation : flat
}

剔除行为

类型
string
nonefrontbackfrontAndBack 中的任意一项。默认值为 back
说明
定义应剔除哪些三角形:无、正面三角形、向后三角形或全部。
material {
    culling : none
}

colorWrite

类型
boolean
truefalse。默认值为 true
说明
启用或停用对颜色缓冲区的写入。
material {
    colorWrite : false
}

深度写入

类型
boolean
truefalse。默认值为 true
说明
启用或停用对深度缓冲区的写入。
material {
    depthWrite : false
}

深度深度

类型
boolean
truefalse。默认值为 true
说明
启用或停用深度测试。停用深度测试后,使用此材质渲染的对象将始终显示在其他不透明对象之上。
material {
    depthCulling : false
}

双面

类型
boolean
truefalse。默认值为 false
说明
启用或停用双面渲染。设置为 true 时,culling 会自动设置为 none;如果三角形朝后,三角形的法线会自动翻转,变成朝前。
material {
    doubleSided : true
}

透明度

类型
string
defaulttwoPassesOneSidetwoPassesTwoSides 中的任意一项。默认值为 default
说明
控制透明对象的渲染方式。仅当 blending 模式不是 opaque 时有效。这些方法都无法准确渲染凹形几何形状,但在实践中,它们通常足够好。

三种可能的透明度模式为:

  • default:透明对象会正常呈现,并服从 culling 模式等。

  • twoPassesOneSide:透明对象会先在深度缓冲区中渲染,然后再在颜色缓冲区中呈现,并服从 cullling 模式。这实际上只会渲染透明对象的一半,如下所示。

  • twoPassesTwoSides:透明对象会在颜色缓冲区中渲染两次:先是其背面,然后是正面。使用此模式,您可以渲染两组人脸,同时减少或消除排序问题,如下所示。twoPassesTwoSides 可以与 doubleSided 结合使用,以获得更好的效果。

material {
    transparency : twoPassesOneSide
}

MaskThreshold

类型
number
介于 0.01.0 之间的值。默认值为 0.4
说明
设置当 blending 模式设置为 masked 时,不得舍弃 Fragment 的最小 Alpha 值。如果混合模式不是 masked,此值将被忽略。此值可用于控制 alpha 遮罩对象的外观。
material {
    blending : masked,
    maskThreshold : 0.5
}

shadowMultiplier

类型
boolean
truefalse。默认值为 false
说明
仅在unlit着色模型中提供。如果启用了此属性,则材质计算的最终颜色将乘以阴影系数(或可见性)。这样即可创建透明的阴影接收对象(例如,AR 中不可见的地平面)。
material {
    name : "Invisible shadow plane",
    shadingModel : unlit,
    shadowMultiplier : true,
    blending : transparent
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        // baseColor defines the color and opacity of the final shadow
        material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);
    }
}

VariantFilter

类型
string 数组
每个条目必须是 dynamicLightingdirectionalLightingshadowReceiverskinning 中的任何一个。
说明
用于指定应用不需要的着色器变体列表。在代码生成阶段,系统会跳过这些着色器变体,从而缩减材质的整体大小。请注意,系统可能会自动过滤掉某些变体。例如,在编译 unlit 材质时,系统会过滤掉所有与光线相关的变体(directionalLighting 等)。请谨慎使用变体过滤器,在运行时滤除所需的变体可能会导致崩溃。

变体的说明:- directionalLighting,当场景中存在定向光时使用;- dynamicLighting;当场景中存在非定向光(点、点等)时使用;- shadowReceiver,当对象可以接收阴影时使用;- skinning,当对象使用 GPU 皮肤动画添加动画时使用

material {
    name : "Invisible shadow plane",
    shadingModel : unlit,
    shadowMultiplier : true,
    blending : transparent,
    variantFilter : [ skinning ]
}

顶点块

顶点块是可选组件,可用于控制材质的顶点着色阶段。顶点块必须包含有效的 ESSL 3.0 代码(OpenGL ES 3.0 中支持的 GLSL 版本)。您可以在顶点块中创建多个函数,但必须声明 materialVertex 函数:

vertex {
    void materialVertex(inout MaterialVertexInputs material) {
        // vertex shading code
    }
}

此函数将在运行时由着色系统自动调用,并让您能够使用 MaterialVertexInputs 结构读取和修改 Material 属性。此结构的完整定义可在 Material 顶点输入部分中找到。

您可以使用此结构计算自定义变量/插值或修改属性的值。例如,以下顶点块会随着时间的推移修改顶点的颜色和 UV 坐标:

material {
    requires : [uv0, color]
}
vertex {
    void materialVertex(inout MaterialVertexInputs material) {
        material.color *= sin(getTime());
        material.uv0 *= sin(frameUniforms.time);
    }
}

除了 MaterialVertexInputs 结构之外,您的顶点着色代码还可以使用着色器公共 API 部分中列出的所有公共 API。

Material 顶点输入

struct MaterialVertexInputs {
    float4 color;         // if the color attribute is required
    float2 uv0;           // if the uv0 attribute is required
    float2 uv1;           // if the uv1 attribute is required
    float3 worldNormal;   // only if the shading model is not unlit
    float4 worldPosition; // always available
    // variable* names are replaced with actual names
    float4 variable0;     // if 1 or more variables is defined
    float4 variable1;     // if 2 or more variables is defined
    float4 variable2;     // if 3 or more variables is defined
    float4 variable3;     // if 4 or more variables is defined
};

fragment 块

Fragment 块必须用于控制材料的 Fragment 着色阶段。fragment 代码块必须包含有效的 ESSL 3.0 代码(OpenGL ES 3.0 中支持的 GLSL 版本)。您可以在顶点块中创建多个函数,但必须声明 material 函数:

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        // fragment shading code
    }
}

此函数将在运行时由着色系统自动调用,并让您能够使用 MaterialInputs 结构读取和修改 Material 属性。如需了解此结构的完整定义,请参阅 Material Fragment 输入部分。您可以在本文的 Material 模型部分找到结构各个成员的完整定义。

material() 函数的目标是计算特定于所选着色模型的材质属性。例如,以下 fragment 块可以使用标准的照亮着色模型创建一个有光泽的红色金属:

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor.rgb = vec3(1.0, 0.0, 0.0);
        material.metallic = 1.0;
        material.roughness = 0.0;
    }
}

prepareMaterial 函数

请注意,在退出 material() 函数之前,您必须调用 prepareMaterial(material)。此 prepareMaterial 函数可设置 Material 模型的内部状态。Fragment API 部分中所述的某些 API(例如 shading_normal)只能在调用 prepareMaterial() 后访问。

请务必注意,normal 属性(如 Material fragment 输入部分中所述)仅在调用 prepareMaterial() 之前进行修改时才有效。以下是一个 fragment 着色器示例,它正确修改了 normal 属性,以实现带触碰映射的光泽红色塑料:

fragment {
    void material(inout MaterialInputs material) {
        // fetch the normal in tangent space
        vec3 normal = texture(materialParams_normalMap, getUV0()).xyz;
        material.normal = normal * 2.0 - 1.0;

        // prepare the material
        prepareMaterial(material);

        // from now on, shading_normal, etc. can be accessed
        material.baseColor.rgb = vec3(1.0, 0.0, 0.0);
        material.metallic = 0.0;
        material.roughness = 1.0;
    }
}

Material Fragment 输入

struct MaterialInputs {
    float4 baseColor;           // default: float4(1.0)
    float4 emissive;            // default: float4(0.0)

    // no other field is available with the unlit shading model
    float  roughness;           // default: 1.0
    float  metallic;            // default: 0.0, not available with cloth
    float  reflectance;         // default: 0.5, not available with cloth
    float  ambientOcclusion;    // default: 0.0

    // not available when the shading model is cloth
    float  clearCoat;           // default: 1.0
    float  clearCoatRoughness;  // default: 0.0
    float3 clearCoatNormal;     // default: float3(0.0, 0.0, 1.0)
    float  anisotropy;          // default: 0.0
    float3 anisotropyDirection; // default: float3(1.0, 0.0, 0.0)


    // only available when the shading model is cloth
    float3 sheenColor;         // default: sqrt(baseColor)
    float3 subsurfaceColor;    // default: float3(0.0)

    // not available when the shading model is unlit
    // must be set before calling prepareMaterial()
    float3 normal;             // default: float3(0.0, 0.0, 1.0)
}

着色器公共 API

类型

虽然 GLSL 类型可以直接使用(vec4mat4),但我们建议使用以下类型的别名:

名称 GLSL 类型 说明
布尔值 2 BVEC2 包含 2 个布尔值的矢量
布尔值 3 BVEC3 包含 3 个布尔值的矢量
布尔值 4 BVEC4 包含 4 个布尔值的矢量
整数 2 ivec2 2 个整数的向量
整数 3 ivec3 包含 3 个整数的向量
整数 4 ivec4 4 个整数的矢量
uint2 vevec2 2 个无符号整数的矢量
uint3 Uvec3 由 3 个无符号整数组成的矢量
uint4 Uvec4 4 个无符号整数的矢量
浮点数 2 浮点数 2 2 个浮点数的矢量
浮点数 浮点数 3 3 个浮点数的矢量
浮点数 浮点数 4 4 个浮点数的矢量
浮点数 4 垫 4 4x4 浮点矩阵
浮点 33 垫 3 3x3 浮点矩阵

Math

名称 类型 说明
PI float 表示 \(\pi\)的常量
HALF_PI 浮点数 表示\(\frac{\pi}{2}\)的常量
saturate(float x) 浮点数 将指定的值限制在 0.0 和 1.0 之间
pow5(浮点 x) float 计算 \(x^5\)
sq(浮点数 x) float 计算 \(x^2\)
max3(float3 v) 浮点数 返回指定的 float3 的最大值
mulMat4x4Float3(float4x4 m, float3 v) 浮点数 4 退货 \(m * v\)
mulMat3x3Float3(float4x4 m, float3 v) 浮点数 4 退货 \(m * v\)

矩阵

名称 类型 说明
getViewFromWorldMatrix() 浮点数 x 4 从现实世界空间转换为视图/眼睛空间的矩阵
getWorldFromViewMatrix() 浮点数 x 4 从视图/眼睛空间转换到世界空间的矩阵
getClipFromViewMatrix() 浮点数 x 4 从视图/眼睛空间转换为剪辑 (NDC) 空间的矩阵
getViewFromClipMatrix() 浮点数 x 4 从裁剪 (NDC) 空间转换为视图/眼睛空间的矩阵
getClipFromWorldMatrix() 浮点数 x 4 从世界转换为剪辑 (NDC) 空间的矩阵

帧常量

名称 类型 说明
getResolution() 浮点数 4 视图分辨率(以像素为单位):widthheight1 / width1 / height
getWorldCameraPosition() 浮点数 3 相机/眼睛在现实世界中的位置
getTime() float 自 Sceneform 引擎初始化以来的时间(以秒为单位),可以定期重置以避免精确损失
getExposure() float 相机的光度曝光
getEV100() float 相机的 ISO 100 曝光值

仅顶点

以下 API 只能通过顶点块使用:

名称 类型 说明
getPosition() 浮点数 4 由材料定义的网域中的顶点位置(默认值:对象/模型空间)
getWorldFromModelMatrix() 浮点数 x4 从模型(对象)空间转换为世界空间的矩阵
getWorldFromModelnormalMatrix() 浮点数 3 将法线从模型(对象)空间转换为世界空间的矩阵

仅限 Fragment

以下 API 只能通过 fragment 块使用:

名称 类型 说明
getWorldTangentFrame() float3x3 每列包含世界空间中顶点的 tangent (frame[0])、bi-tangent (frame[1]) 和 normal (frame[2]) 的矩阵。如果材质没有为碰撞映射计算切线空间法线或者着色不是各向异性,则只有 normal 在此矩阵中有效。
getWorldPosition() 浮点数 3 fragment 在真实空间中的位置
getWorldViewVector() 浮点数 3 从片段位置到眼睛的真实空间中的归一化向量
getWorldnormalVector() 浮点数 3 分区映射后,在真实空间中进行归一化(必须在 prepareMaterial() 之后使用)
getWorldReflectedVector() 浮点数 3 关于法线的视图向量的反射(必须在 prepareMaterial() 之后使用)
getNdotV() 浮点数 dot(normal, view) 的结果,始终严格大于 0(必须在 prepareMaterial() 后使用)
getColor() 浮点数 4 fragment 的插值颜色(如果需要颜色属性)
getUV0() 浮点数 2 第一组插值 UV 坐标(如果需要 uv0 属性)
getUV1() 浮点数 2 第一组插值 UV 坐标(如果需要 uv1 属性)
inverseTonemap(float3) 浮点数 3 将反向色调映射运算符应用于指定的线性 SRGB 颜色。此操作可能为近似值
inverseTonemapSRGB(float3) 浮点数 3 将反向色调映射运算符应用于指定的非线性 sRGB 颜色。此操作可能为近似值
亮度(float3) 浮点数 计算指定的线性 sRGB 颜色的亮度

材料模型

Sceneform 材料可以使用以下材料模型之一:

  • 照明(或标准)
  • 布料
  • 未亮灯

Lit 模型

照亮模型是 Sceneform 的标准材料模型。这种基于物理的着色模型旨在提供与其他常用工具和引擎(例如 Unity 5Unreal Engine 4Submaterial DesignerMarmoset Toolbag)的良好互操作性。

此材料模型可用于描述大量非金属表面(电介质)或金属表面(导体)。

使用标准模型的材料的外观使用下表中所述的属性控制。

标准模型的属性

属性 定义
基色 非金属表面的漫反射反照率,金属表面的反射颜色
金属 表面是电介质 (0.0) 还是导体 (1.0)。通常用作二进制值(0 或 1)
粗糙度 表面的感知平滑度 (1.0) 或粗糙度 (0.0)。光滑的表面会呈现清晰的反光
反射率 电介质表面正常入射的菲涅尔反射率。这会直接控制反射的强度
clearCoat 透明涂层层的强度
clearCoatRoughness 透明涂层层的光滑度或粗糙度
各向异性 正切或双切线方向上的各向异性量
anisotropyDirection 局部表面方向
环境光遮蔽 定义表面点可以访问的环境光量。它是一个介于 0.0 和 1.0 之间的逐像素阴影系数
正常 使用地图绘制法线贴图)扰乱表面的详细信息法线
clearCoatnormal 一种使用 Bump 映射扰乱透明涂层层的详细法线(法线映射
emissive 其他漫反射反照率可以模拟发光表面(例如霓虹灯等)此属性在具有泛光传递的 HDR 流水线中最为有用

下表介绍了每种媒体资源的类型和范围。

属性 类型 Range 注意
底色 浮点数 4 [0..1] 预乘线性 RGB
Metallic float [0..1] 应为 0 或 1
粗糙度 float [0..1]
反射率 float [0..1] 首选值 > 0.35
clearCoat float [0..1] 应为 0 或 1
clearCoatRoughness float [0..1] 重新映射到 [0..0.6]
各向异性 浮点数 [-1..1] 当此值为正数时,各向异性会经历切线方向
anisotropyDirection 浮点数 3 [0..1] 线性 RGB,对切线空间中的方向向量进行编码
环境光遮蔽 float [0..1]
正常 浮点数 3 [0..1] 线性 RGB,对切线空间中的方向向量进行编码
clearCoatnormal 浮点数 3 [0..1] 线性 RGB,对切线空间中的方向向量进行编码
emissive 浮点数 4 rgb=[0..1],a=[-n..n] Alpha 是曝光补偿

基本颜色

baseColor 属性定义对象的感知颜色(有时称为反照率)。baseColor 的效果取决于 Surface 的性质,它由 Metallic 部分介绍的 metallic 属性控制。

非金属(电介质)

定义表面的漫射色。现实世界值通常位于 [10.240] 的范围内(如果值的编码范围为 0 至 255),或者位于 [0.04..0.94] 的范围内(范围为 0 至 1)。下表提供了非金属表面的多个基本颜色示例。

金属 sRGB 十六进制 颜色
煤炭 0.19、0.19、0.19 #323232
 
橡胶 0.21、0.21、0.21 #353535
 
泥浆 0.33、0.24、0.19 #553d31
 
木材 0.53、0.36、0.24 #875c3c
 
植物 0.48、0.51、0.31 #7b824e
 
Brick 0.58、0.49、0.46 #947d75
 
沙色 0.69、0.66、0.52 #b1a884
 
Concrete 0.75、0.75、0.73 #c0bfbb
 
金属(导体)

定义表面的镜面反射颜色。真实值通常位于 [170..255] 的范围内(如果值编码在 0 到 255 之间),或位于 [0.66..1.0](介于 0 到 1)之间。下表显示了金属表面的基本颜色的一些示例。

金属 sRGB 十六进制 颜色
银色 0.98、0.98、0.96 #faf9f5
 
0.96、0.96、0.96 #f4f5f5
 
0.81、0.78、0.76 #cec8c2
 
熨斗 0.76、0.74、0.73 #c0bdba
 
白金级 0.84、0.82、0.79 #d6d1c8
 
黄金 1.00、0.87、0.62 #fedc9d
 
布拉斯 0.96、0.89、0.68 #f4e4ad
 
Copper 0.98、0.85、0.72 #fbd8b8
 

金属

metallic 属性定义表面是金属(导体)还是非金属(电介质)。此属性应用作二进制值(设置为 0 或 1)。只有在使用纹理时,中间值才能在不同类型的表面之间创建过渡。

此属性可以显著改变表面的外观。非金属表面具有多色漫反射和消色镜面反射(反射光不会改变颜色)。金属表面没有任何漫反射和多色镜面反射(反射光会采用表面的颜色,如 baseColor 所定义)。

metallic 的效果如下所示(点击图片可查看大图)。

粗糙度

roughness 属性用于控制表面的感知光滑度。当 roughness 设置为 0 时,表面非常光滑,并且很亮。表面越粗糙,反射越“模糊”。在其他引擎和工具中,此属性通常称为“光泽度”,与粗糙度相对 (roughness = 1 - glossiness)。

非金属

roughness 在非金属表面上的效果如下所示(点击图片可查看放大的版本)。

金属

roughness 对金属表面的效果如下所示(点击图片可查看放大的版本)。

反射

reflectance 属性仅影响非金属表面。此属性可用于控制镜面反射强度。此值定义的范围为 0 至 1,表示反射百分比的重新映射。例如,默认值 0.5 对应于 4% 的反射率。应避免使用低于 0.35 (2% 反射率) 的值,因为没有任何真实材料具有如此低的反射率。

reflectance 在非金属表面上的效果如下所示(点击图片可查看放大的版本)。

下图显示了常用值及其与映射函数的关系。

下表介绍了各种类型的材料的可接受反射率值(没有任何真实材料的反射率低于 2%)。

Material 反射 属性值
水域 2% 0.35
织物 4% 至 5.6% 0.5 至 0.59
常用液体 2% 至 4% 0.35 至 0.5
常见宝石 5% 至 16% 0.56 至 1.0
塑料、玻璃 4% 到 5% 0.5 至 0.56
其他介电材料 2% 到 5% 0.35 至 0.56
眼睛 2.5% 0.39 欧元
皮肤 2.8% 0.42
美发 4.6% 0.54
牙齿 5.8% 0.6
默认值 4% 0.5

透明涂层

多层材料非常常见,尤其是在底层上具有半透明层的材料。现实世界中的此类材料包括车漆、易拉罐、漆木和亚克力。

clearCoat 属性可用于描述包含两个图层的材质。透明涂层层始终是各向同性和介电的。下图比较了标准材料模型(左侧)和透明涂层模型(右侧)下的碳纤维材料。

clearCoat 属性用于控制透明涂层层的强度。此值应被视为二进制值(设置为 0 或 1)。中间值对于控制具有透明涂层层的表面部分和不具有透明涂层层的部分之间的过渡非常有用。

clearCoat 对粗糙金属的影响如下所示(点击图片可查看大图)。

透明涂层粗糙度

clearCoatRoughness 属性与 roughness 属性类似,但仅适用于透明涂层层。此外,由于透明涂层层从不完全粗糙,介于 0 和 1 之间的值在内部会重新映射到 0 到 0.6 之间的实际粗糙度。

clearCoatRoughness 对粗糙金属的效果如下所示(点击图片可查看大图)。

各向异性

许多真实的材料(例如拉丝金属)都只能使用各向异性反射模型来复制。材料可以使用 anisotropy 属性从默认各向异性模型更改为各向异性模型。下图比较了各向异性材料(左侧)和各向异性材料(右侧)。

anisotropy 从 0.0(左)到 1.0(右)在粗糙金属上的效果如下所示(点击图片可查看放大的版本)。

下图显示了如何使用正值或负值来控制各向异性突出显示的方向:正值(左)定义切线方向上的各向异性,负值(右)定义双切线方向上的各向异性。

各向异性方向

anisotropyDirection 属性定义表面在给定点的方向,因此可控制镜面高光的形状。它被指定为包含 3 个值的矢量(通常来自纹理),对表面的局部编码方向。

使用方向图在金属上渲染 anisotropyDirection 的效果如下所示(点击图片可查看放大的版本)。

用于渲染上述图像的方向图如下所示。

环境光遮蔽

ambientOcclusion 属性定义表面点可访问的环境光的程度。它是一个介于 0.0(完全覆盖)和 1.0(完全照明)之间的逐像素阴影系数。此属性仅影响漫射间接光照(基于图片的光照),而不影响定向光、点光源和聚光灯等直射光,也不影响镜面光。下图比较了没有漫射环境光遮蔽的材料(左侧)和含有漫射环境光遮蔽的材料(右侧)。

正常

normal 属性定义表面在给定点的法线。它通常来自法线贴图纹理,因而可以因像素而异属性。法线在切线空间中提供,这意味着 +Z 点位于表面外。

例如,假设我们要渲染一件用簇绒皮覆盖的家具。对几何图形进行建模以准确表示簇绒图案将需要太多三角形,因此我们改为在法线贴图中烘焙高聚网格。然后,您可以将基本地图应用到简化的网格。下图比较了没有法线贴图(左侧)和有法线贴图(右侧)的简单网格。

请注意,normal 属性会影响基本层,而不会影响透明涂层层。

透明涂层

clearCoatNormal 属性定义给定点透明涂层层的法线。它的行为类似于 normal 属性。

发光

emissive 属性可用于模拟 Surface 发出的其他光线。它定义为包含 RGB 颜色(线性空间)和曝光补偿值(Alpha 通道)的 float4 值。

虽然曝光值实际上表示相机设置的组合,但通常由摄影师用它来描述光强度。正因为如此,相机可让摄影师对曝光过度或曝光不足的图片进行曝光补偿。此设置既可用于艺术控制,也可用于实现适当的曝光(例如,雪将被曝光为 18% 的中灰度)。

发光属性的曝光补偿值可用于强制发光颜色比当前曝光更亮(正值)或更暗(负值)。如果启用了泛光效果,使用正曝光补偿可以强制表面泛光。

布料模型

前面介绍的所有材料模型旨在从宏观和微观层面模拟密集的表面。不过,服装和面料通常是由松散连接的线组成,这些线会吸收和散射入射光。与硬质表面相比,布料具有更柔和的镜面叶瓣,并且衰减更大,并且存在由前向/后向散射引起的模糊照明。某些面料还会显示双色调镜面反射颜色(例如,天鹅绒)。

下图比较了使用标准模型(左侧)和布料模型(右侧)渲染的牛仔面料。请注意标准材料模型为何无法捕获牛仔面料示例(左侧)的外观。表面看起来具有刚性(几乎像塑料一样),更像是油布而不是布料。这也显示了吸收和散射引起的更柔和镜面叶片对面料的忠实重新创建有多重要。

天鹅绒是布料材料模型的一个有趣用例。如下图所示,由于前向和后向散射,这种面料会呈现强烈的边缘光照。这些散射事件是由直立在面料表面的纤维引起的。当入射光来自与视图方向相反的方向时,纤维将向前散射光。同样,当入射光与视图方向相同时,光纤会向后散射光。

值得注意的是,某些类型的面料最好通过硬质表面材料模型建模。例如,皮革、丝绸和缎子可以使用标准或各向异性材料模型进行重新创建。

金属反射外,布料材料模型包含之前为标准材料模式定义的所有参数。您也可以使用下表中介绍的两个额外参数。

参数 定义
sheenColor 通过创建高色调色调创建双色调高光面料(默认为 \(\sqrt{baseColor}\))
subsurfaceColor 通过材料散射和吸收后漫射颜色的着色

下表介绍了每种媒体资源的类型和范围。

属性 类型 Range 注意
sheenColor 浮点数 3 [0..1] 线性 RGB
subsurfaceColor 浮点数 3 [0..1] 线性 RGB

如需制作丝绒样材质,可以将基本颜色设置为黑色(或深色)。应在有光泽的颜色上设置色度信息。如需创建牛仔布、棉布等更常见的面料,请使用基色提升色度并使用默认的有光泽颜色,或将有光泽的颜色设为基色的亮度。

有光泽的颜色

sheenColor 属性可用于直接修改镜面反射。它可以更好地控制布料的外观,并且能够创建双色调高光材料。

下图比较了带有和不带光泽的蓝色织物(左侧)和带有光泽(蓝色)的织物(点击图片可查看大图)。

子表面颜色

subsurfaceColor 属性并非基于物理材料,而是可用于模拟某些类型的面料中的散射、部分吸收和重新发射光的功能。这在创建较柔软的面料时特别有用。

下图展示了 subsurfaceColor 的效果。它显示了白色布料(左列)与带有棕色子表面散射的白色布料(右列)。点击图片可查看大图。

非照明模型

非照明材料模型可用于关闭所有照明计算。它的主要用途是渲染立方体贴图、外部内容(如视频或摄像头数据流)、界面、可视化/调试等预照亮元素。非照亮模型仅公开了下表中所述的两个属性。

属性 定义
底色 表面漫射色
emissive 用于模拟发光表面的其他漫射色。此属性在具有泛光传递的 HDR 流水线中最为有用

下表介绍了每种媒体资源的类型和范围。

属性 类型 Range 注意
底色 浮点数 4 [0..1] 预乘线性 RGB
emissive 浮点数 4 rgb=[0..1], a=N/A 预乘线性 RGB,忽略 Alpha 值

只需将 emissive 的值添加到 baseColor(如果存在)即可。emissive 的主要用途是在 HDR 流水线配置了泛光传递时,强制非照亮表面泛光。

下图显示了用于渲染调试信息的非照亮 Material 模型(点击此图片可查看大图)。

处理颜色

线性颜色

如果颜色数据来自纹理,只需确保使用 sRGB 纹理,即可受益于从 sRGB 到线性的自动硬件转换。如果将颜色数据作为参数传递给材质,您可以在每个颜色通道上运行以下算法,从 sRGB 转换为线性:

float sRGB_to_linear(float color) {
    return color <= 0.04045 ? color / 12.92 : pow((color + 0.055) / 1.055, 2.4);
}

或者,您也可以使用下方所示两个更便宜但准确度较低的版本之一:

// Cheaper
linearColor = pow(color, 2.2);
// Cheapest
linearColor = color * color;

预乘 alpha

如果颜色的 RGB 分量乘以 alpha 通道,则颜色将使用预乘 alpha:

// Compute pre-multiplied color
color.rgb *= color.a;

如果颜色是从纹理采样的,您只需确保纹理数据提前预乘即可。在 Android 上,从 Bitmap 上传的任何纹理在默认情况下都会预先乘法。