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
其中,值可以是字符串、数字、对象、数组或字面量(true
、false
或 null
)。虽然此语法在 Material 定义中完全有效,但 JSONish 中还接受字符串(不带引号)的变体:
key : value
当字符串包含空格时,引号仍是必需的。
vertex
和 fragment
代码块包含未转义且不带英文引号的 GLSL 代码,该代码在 JSON 中无效。
允许使用单行 C++ 样式注释。
密钥对的键区分大小写。
键值对的值不区分大小写。
示例
以下代码列表展示了一个有效的 Material 定义示例。此定义使用照明材料模型,使用默认的不透明混合模式,要求在渲染的网格中显示一组 UV 坐标并定义 3 个用户参数。本文档的以下部分详细介绍了 material
和 fragment
代码块。
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
- 值
lit
、cloth
、unlit
中的任意一项。 默认值为lit
。- 说明
- 选择 Material 模型部分中所述的 Material 模型。
material {
shadingModel : unlit
}
形参
- 类型
- 参数对象数组
- 值
每个条目都是具有
name
和type
属性的对象,且二者均为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 外部纹理。如需了解详情,请参阅 ExternalTexture 和 setExternalTexture() - 采样器
采样器类型还可以指定
format
(默认为float
)和precision
(默认为default
)。格式为int
、float
。精度可以是default
(平台的最佳精度,一般为桌面设备上的high
、桌面设备上的medium
或移动设备中的精确度)、low
、medium
、high
之一。- 说明
列出您的材料所需的参数。可以在运行时使用 Sceneform 的 Material API 设置这些参数。从着色器访问参数因参数类型而异:
- Samplers type:使用以
materialParams_
为前缀的参数名称。例如materialParams_myTexture
。 - 其他类型:使用参数名称作为名为
materialParams
的结构体的字段。例如materialParams.myColor
。
- Samplers type:使用以
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
数组- 值
- 每个条目必须是以下任何一项:
uv0
、uv1
、color
、tangents
。 - 说明
- 列出材质所需的顶点属性。系统会自动添加
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
- 值
opaque
、transparent
、fade
、add
、masked
中的任意一个。默认值为opaque
。- 说明
定义渲染对象如何与渲染目标的内容混合。可能的混合模式包括:
- 不透明:停用混合,材料输出的 alpha 通道被忽略。
- 透明:已启用混合模式。Material 的输出使用 Porter-Duff 的 source over 规则与渲染目标进行 alpha 合成。此混合模式假设预乘 alpha。
- 淡化:充当
transparent
,但透明度也会应用于镜面光照。在transparent
模式下,材质的 alpha 值仅适用于漫射光。此混合模式对于淡入和淡出对象很有用。 - 添加:已启用混合模式。材料的输出会添加到渲染目标的内容中。
- 遮罩:混合模式已停用。此混合模式可启用 Alpha 遮罩。材料输出的 alpha 通道定义是否舍弃某个 fragment。如需了解详情,请参阅 maskThreshold 部分。
material {
blending : transparent
}
顶点域
- 类型
string
- 值
object
、world
、view
、device
中的任意一项。默认值为object
。- 说明
定义所渲染网格的域(或坐标空间)。该网域会影响顶点在顶点着色器中的转换方式。可能的网域包括:
- 对象:顶点在对象(或模型)坐标空间中定义。顶点使用渲染对象的转换矩阵进行转换
- 世界:顶点在世界坐标空间中定义。顶点不使用渲染对象的转换进行转换。
- 视图:顶点在视图(或眼睛或相机)坐标空间中定义。顶点不使用渲染对象的转换进行转换。
- 设备:顶点在标准化设备(或裁剪)坐标空间中定义。顶点不使用渲染对象的转换进行转换。
material {
vertexDomain : device
}
插值
- 类型
string
- 值
smooth
、flat
中的任意一个。默认值为smooth
。- 说明
- 定义插值(或变量)在顶点之间的插值方式。
当此属性设置为
smooth
时,系统会对每个插值执行正确的视角插值。设置为flat
时,系统不会执行插值,并且给定三角形中的所有 fragment 都将使用相同的阴影。
material {
interpolation : flat
}
剔除行为
- 类型
string
- 值
none
、front
、back
、frontAndBack
中的任意一项。默认值为back
。- 说明
- 定义应剔除哪些三角形:无、正面三角形、向后三角形或全部。
material {
culling : none
}
colorWrite
- 类型
boolean
- 值
true
或false
。默认值为true
。- 说明
- 启用或停用对颜色缓冲区的写入。
material {
colorWrite : false
}
深度写入
- 类型
boolean
- 值
true
或false
。默认值为true
。- 说明
- 启用或停用对深度缓冲区的写入。
material {
depthWrite : false
}
深度深度
- 类型
boolean
- 值
true
或false
。默认值为true
。- 说明
- 启用或停用深度测试。停用深度测试后,使用此材质渲染的对象将始终显示在其他不透明对象之上。
material {
depthCulling : false
}
双面
- 类型
boolean
- 值
true
或false
。默认值为false
。- 说明
- 启用或停用双面渲染。设置为
true
时,culling
会自动设置为none
;如果三角形朝后,三角形的法线会自动翻转,变成朝前。
material {
doubleSided : true
}
透明度
- 类型
string
- 值
default
、twoPassesOneSide
或twoPassesTwoSides
中的任意一项。默认值为default
。- 说明
- 控制透明对象的渲染方式。仅当
blending
模式不是opaque
时有效。这些方法都无法准确渲染凹形几何形状,但在实践中,它们通常足够好。
三种可能的透明度模式为:
default
:透明对象会正常呈现,并服从culling
模式等。twoPassesOneSide
:透明对象会先在深度缓冲区中渲染,然后再在颜色缓冲区中呈现,并服从cullling
模式。这实际上只会渲染透明对象的一半,如下所示。twoPassesTwoSides
:透明对象会在颜色缓冲区中渲染两次:先是其背面,然后是正面。使用此模式,您可以渲染两组人脸,同时减少或消除排序问题,如下所示。twoPassesTwoSides
可以与doubleSided
结合使用,以获得更好的效果。
material {
transparency : twoPassesOneSide
}
MaskThreshold
- 类型
number
- 值
- 介于
0.0
和1.0
之间的值。默认值为0.4
。 - 说明
- 设置当
blending
模式设置为masked
时,不得舍弃 Fragment 的最小 Alpha 值。如果混合模式不是masked
,此值将被忽略。此值可用于控制 alpha 遮罩对象的外观。
material {
blending : masked,
maskThreshold : 0.5
}
shadowMultiplier
- 类型
boolean
- 值
true
或false
。默认值为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
数组- 值
- 每个条目必须是
dynamicLighting
、directionalLighting
、shadowReceiver
或skinning
中的任何一个。 - 说明
- 用于指定应用不需要的着色器变体列表。在代码生成阶段,系统会跳过这些着色器变体,从而缩减材质的整体大小。请注意,系统可能会自动过滤掉某些变体。例如,在编译
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 类型可以直接使用(vec4
或 mat4
),但我们建议使用以下类型的别名:
名称 | 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) 空间的矩阵 |
帧常量
仅顶点
以下 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 5、Unreal Engine 4、Submaterial Designer 或 Marmoset 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 上传的任何纹理在默认情况下都会预先乘法。