自定义材料参考

Sceneform 提供了默认的材料定义 (.sfm) 来让开发者可以轻松获得出色的外观。 想要深度自定义其 asset 外观的开发者可以创建自己的材料定义(*.mat 文件),并通过在 asset 定义中指定 source 属性的方式将这些定义应用到 asset 中。

本页将介绍 (*.mat) 材料定义文件的结构和格式。

材料定义

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

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

格式

材料定义格式是一种并非严格基于 JSON 的格式,我们称之为 JSONish。 在顶层,材料定义由 3 个不同的块组成,这些块使用 JSON 对象标记:

material {
    // material properties
}

vertex {
    // vertex shader, optional
}

fragment {
    // fragment shader
}

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

与 JSON 的不同之处

在 JSON 中,一个对象由键/值组成。 一个 JSON 对的语法如下所示:

"key" : value

其中,值可以是字符串、数字、对象、数组或字面量(truefalsenull)。 尽管此语法在材料定义中完全有效,但 JSONish 中还接受字符串周围没有引号的不同类型。

key : value

如果字符串包含空格,那么仍必须使用引号。

vertexfragment 块包含非转义、不带引号的 GLSL 代码,此代码在 JSON 中无效。

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

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

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

示例

以下代码详情显示了一个有效的材料定义的示例。 此定义使用照亮的材料模型,使用默认的不透明混合模式,要求渲染的网格中存在一组 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 {
    shadingModel : unlit
}

material {
    shadingModel : "lit"
}

parameters

类型
参数对象数组

每个条目都是一个带有 nametype 属性的对象,这两个属性都是 string 类型。 名称必须是一个有效的 GLSL 标识符。 类型必须是下表中介绍的类型之一。

类型 说明
bool 单个布尔值
bool2 2 个布尔值的矢量
bool3 3 个布尔值的矢量
bool4 4 个布尔值的矢量
float 单个浮点值
float2 2 个浮点值的矢量
float3 3 个浮点值的矢量
float4 4 个浮点值的矢量
int 单个整型值
int2 2 个整型值的矢量
int3 3 个整型值的矢量
int4 4 个整型值的矢量
sampler2d 2D 纹理
samplerExternal 外部纹理。 如需了解详细信息,请参阅 ExternalTexturesetExternalTexture()
取样器

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

说明

列出您的材料需要的参数。 可以在运行时使用 Sceneform 的材料 API 设置这些参数。 从着色器访问参数各不相同,具体取决于参数的类型:

  • 取样器类型:使用带有 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;
    }
}

requires

类型
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 在片段着色器中访问它。 在顶点着色器中,插值名称只是 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);
    }
}

blending

类型
string
opaquetransparentaddmasked。 默认值为 opaque
说明
定义渲染对象与渲染目标内容的混合方式/是否混合。

可能的混合模式包括:

  • 不透明:停用混合,材料输出的 alpha 通道被忽略。
  • 透明:启用混合。 材料的输出是使用 Porter-Duff 的 source over 规则与渲染目标合成的 alpha。 此混合模式假设预乘 alpha。
  • 添加:启用混合。 材料的输出将添加到渲染目标的内容中。
  • 遮罩:停用混合。 此混合模式将启用 alpha 遮罩。 材料输出的 alpha 通道定义是否舍弃某个片段。请参阅 maskThreshold 部分了解详细信息。
material {
    blending : transparent
}

vertexDomain

类型
string
objectworldviewdevice。 默认值为 object
说明
定义渲染网格的域(或坐标空间)。 域影响顶点在顶点着色器中的转换方式。

可能的域包括:

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

interpolation

类型
string
smoothflat。 默认值为 smooth
说明
定义插值(或变量)在顶点之间的插入方式。 如果此属性设为 smooth,将在每个插值上执行透视角度正确的插入。如果设为 flat,将不执行插入,带有给定三角形的所有片段都将使用相同颜色着色。
material {
    interpolation : flat
}

culling

类型
string
nonefrontbackfrontAndBack。 默认值为 back
说明
定义应挑选哪些三角形:无、朝前的三角形、朝后的三角形或全部。
material {
    culling : none
}

colorWrite

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

depthWrite

类型
boolean
truefalse。 默认值为 true
说明
启用或停用向深度缓冲区的写入。 即使此属性设为 true,透明材料(请参阅 blending 部分)也永远不会写入深度缓冲区。
material {
    depthWrite : false
}

depthCulling

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

doubleSided

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

transparency

类型
string
defaulttwoPassesOneSidetwoPassesTwoSides。 默认值为 default
说明
控制透明对象的渲染方式。 只有在 blending 模式不是 opaque 时才有效。 这些函数都无法准确渲染凹形几何形状,但是在实际运用中,它们通常足以满足需求。

三个可能的透明度模式包括:

  • default:透明对象正常渲染,并服从 culling 模式等

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

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

material {
    transparency : twoPassesOneSide
}

maskThreshold

类型
number
介于 0.01.0 之间的值。 默认值为 0.4
说明
设置当 blending 模式设为 masked 时,为了不被舍弃,片段必须具有的最小 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);
    }
}

vertex 块

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

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

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

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

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

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

材料顶点输入

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 块必须包含有效的 ESSL 3.0 代码(OpenGL ES 3.0 中支持的 GLSL 的版本)。 您可以在 vertex 块内部随意创建多个函数,但是必须声明 material 函数:

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

此函数将在运行时由着色系统自动调用,并且让您能够使用 MaterialInputs 结构读取和修改材料属性。 您可以在材料片段输入部分中找到此结构的完整定义。 您可以在本文的材料模型部分中找到各种结构的完整定义。

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 函数设置材料模型的内部状态。 “片段 API”部分中介绍的某些 API(例如 shading_normal)只能在调用 prepareMaterial() 访问。

还需记住的是,normal 属性(如材料片段输入部分中所述)仅在调用 prepareMaterial() 修改时有效。 下面是一个片段着色器示例,它可以正确修改 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;
    }
}

材料片段输入

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
    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 类型 说明
bool2 bvec2 2 个布尔值的矢量
bool3 bvec3 3 个布尔值的矢量
bool4 bvec4 4 个布尔值的矢量
int2 ivec2 2 个整型值的矢量
int3 ivec3 3 个整型值的矢量
int4 ivec4 4 个整型值的矢量
uint2 uvec2 2 个无符号整型值的矢量
uint3 uvec3 3 个无符号整型值的矢量
uint4 uvec4 4 个无符号整型值的矢量
float2 float2 2 个浮点值的矢量
float3 float3 3 个浮点值的矢量
float4 float4 4 个浮点值的矢量
float4x4 mat4 一个 4x4 浮点矩阵
float3x3 mat3 一个 3x3 浮点矩阵

数学

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

矩阵

名称 类型 说明
getViewFromWorldMatrix() float4x4 从世界空间转换成视野/眼睛空间的矩阵
getWorldFromViewMatrix() float4x4 从视野/眼睛空间转换成世界空间的矩阵
getClipFromViewMatrix() float4x4 从视野/眼睛空间转换成裁剪 (NDC) 空间的矩阵
getViewFromClipMatrix() float4x4 从裁剪 (NDC) 空间转换成视野/眼睛空间的矩阵
getClipFromWorldMatrix() float4x4 从世界空间转换成裁剪 (NDC) 空间的矩阵
getWorldFromClipMatrix() float4x4 从裁剪 (NDC) 空间转换成世界空间的矩阵

帧常量

名称 类型 说明
getResolution() float4 视野的分辨率(以像素为单位):widthheight1 / width1 / height
getWorldCameraPosition() float3 摄像头/眼睛在世界空间中的位置
getTime() float 自 Sceneform 引擎初始化以来的时间,可能会定期重置来避免精度损失
getExposure() float 摄像头的曝光量
getEV100() float 摄像头在 ISO 100 下的曝光值

仅顶点

以下 API 仅在 vertex 块中可用:

名称 类型 说明
getPosition() float4 顶点在材料定义的域(默认:对象/模型空间)中的位置
getWorldFromModelMatrix() float4x4 从模型(对象)空间转换成世界空间的矩阵
getWorldFromModelNormalMatrix() float3x3 将法线从模型(对象)空间转换成世界空间的矩阵

仅片段

以下 API 仅在 fragment 块中可用:

名称 类型 说明
getWorldTangentFrame() float3x3 顶点的 tangent (frame[0])、bi-tangent (frame[1]) 和 normal (frame[2]) 每一列在世界空间中包含的矩阵。 如果材料不为过凹凸贴图计算切线空间法线或者着色不是各向异性,则只有 normal 在此语法中有效。
getWorldPosition() float3 片段在世界空间中的位置
getWorldViewVector() float3 世界空间中从片段位置到眼睛的归一化向量
getWorldNormalVector() float3 世界空间中凹凸贴图后的归一化向量(必须在 prepareMaterial() 后使用)
getWorldReflectedVector() float3 视野向量相对于法线的反射(必须在 prepareMaterial() 后使用)
getNdotV() float dot(normal, view) 的结果,始终严格大于 0(必须在 prepareMaterial() 后使用)
getColor() float4 片段的内插色(如果需要颜色属性)
getUV0() float2 第一组内插的 UV 坐标(如果需要 uv0 属性)
getUV1() float2 第一组内插的 UV 坐标(如果需要 uv1 属性)
inverseTonemap(float3) float3 将反向色调贴图运算符应用到指定的线性 sRGB 颜色。 此运算可能是一种近似运算
inverseTonemapSRGB(float3) float3 将反向色调贴图运算符应用到指定的非线性 sRGB 颜色。 此运算可能是一种近似运算
luminance(float3) float 计算指定线性 sRGB 颜色的亮度

材料模型

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

  • 照亮(或标准)
  • 布料
  • 非照亮

照亮模型

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

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

使用标准模型的材料的外观使用下表介绍的属性控制。

标准模型的属性

属性 定义
baseColor 非金属表面的漫反射反照率、金属表面的镜面反射颜色
metallic 表面看起来是介电的 (0.0) 还是导体 (1.0)。 通常用作二进制值(0 或 1)
roughness 表面的感知平滑度 (1.0) 或粗糙度 (0.0)。 光滑的表面展现强烈反射
reflectance 介电表面法线入射处的菲涅耳反射率。 这直接控制反射的强度
clearCoat 透明涂层层的强度
clearCoatRoughness 透明涂层层的感知光滑度或粗糙度
anisotropy 切线或双切线方向上的各向异性量
anisotropyDirection 局部表面方向
ambientOcclusion 定义表面点获得环境光的难易程度。 它是一个介于 0.0 和 1.0 之间的逐像素阴影系数
normal 用于使用凹凸贴图法线贴图)扰乱表面的细节法线
emissive 用于模拟发光表面(例如霓虹灯等)的附加漫反射反照率此属性在带有泛光传递的 HDR 管道中非常有用

每个属性的类型和范围如下表所述。

属性 类型 范围 注释
baseColor float4 [0..1] 预乘线性 RGB
metallic float [0..1] 应为 0 或 1
roughness float [0..1]
reflectance float [0..1] 值最好 > 0.35
clearCoat float [0..1] 应为 0 或 1
clearCoatRoughness float [0..1] 重新映射到 [0..0.6]
anisotropy float [-1..1] 如果此值为正数,则各向异性位于切线方向
anisotropyDirection float3 [0..1] 线性 RGB,在切线空间编码一个方向向量
ambientOcclusion float [0..1]
normal float3 [0..1] 线性 RGB,在切线空间编码一个方向向量
emissive float4 rgb=[0..1], a=[-n..n] Alpha 是曝光补偿

基色

baseColor 属性定义对象的感知颜色(有时称为反照率)。 baseColor 的效果取决于表面的性质,由金属部分中介绍的 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
 
砖块 0.58, 0.49, 0.46 #947d75
 
沙子 0.69, 0.66, 0.52 #b1a884
 
混凝土 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
 
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%)。

材料 反射率 属性值
玻璃 3.5% 0.46
2% 0.35
常见液体 2% 至 4% 0.35 至 0.5
常见宝石 8% 至 16% 0.70 至 1.0
其他介电材料 2% 至 5% 0.35 至 0.56
默认值 4% 0.50

透明涂层

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

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 属性仅影响底层,而不会影响透明涂层层。

发光

emissive 属性可用于模拟表面发射的其他光。 它被定义为一个 float4 值,包含 RGB 颜色(线性空间)和曝光补偿值(alpha 通道)。

即使曝光值实际表示摄像头设置的组合,它仍经常被摄影师用来描述光强度。 这就是为什么摄像头可以让摄影师在过渡曝光或曝光不足的图像上应用曝光补偿的原因。 此设置不仅可用于艺术控制,还能用于实现正确曝光(例如,雪将被曝光为 18% 中灰)。

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

布料模型

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

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

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

请务必注意,仍有很多类型的面料最好通过硬表面材料模型建模。 例如,皮革、丝绸和缎子可以使用标准或各向异性材料模型重建。

metallicreflectance 外,布料材料模型用到了之前为标准材料模型定义的所有参数。 也用到了下表中介绍的两个额外的参数。

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

每个属性的类型和范围如下表所述。

属性 类型 范围 注释
sheenColor float3 [0..1] 线性 RGB
subsurfaceColor float3 [0..1] 线性 RGB

要创建像天鹅绒一样的材料,可以将基色设为黑色(或暗色)。 应在有光泽的颜色上设置色度信息。 要创建牛仔布和棉布等更常见的面料,请执行以下操作: 为色度使用基色并使用默认的有光泽的颜色,或者将有光泽的颜色设为基色的亮度。

有光泽的颜色

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

下图比较了不带光泽(左)和带光泽(右)的蓝色布料(点击图像可以查看放大的版本)。

子表面颜色

subsurfaceColor 属性不基于物理材料,可用于模拟某种面料中的散射、部分吸收和光的再发射。 这在创建比较柔软的面料时特别有用。

下图展现了 subsurfaceColor 的效果。 它显示了白布(左列)与带棕色子表面散射的白布(右)。 点击图像可以查看放大的版本。

非照亮模型

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

属性 定义
baseColor 表面漫射色
emissive 用于模拟发光表面的其他漫射色。 此属性在带有泛光传递的 HDR 管道中非常有用

每个属性的类型和范围如下表所述。

属性 类型 范围 注释
baseColor float4 [0..1] 预乘线性 RGB
emissive float4 rgb=[0..1], a=N/A 预乘线性 RGB,忽略 alpha

emissive 的值将添加到 baseColor(如果存在)中。 emissive 的主要用途是在 HDR 管道带有泛光传递时,强制非照亮表面泛光。

下图显示了用于渲染调试信息的非照亮材料模型的示例(点击图像可以查看放大的版本)。

处理颜色

线性颜色

如果颜色数据来自纹理,只需确保使用 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 上,从位图上传的任何纹理在默认情况下都会预乘。