材质配置文件说明
本页面镜像自 BedrockWiki
根据原始项目协议授权。本文经过AI翻译处理,如有内容遗漏,可以提交PR进行补充。
WARNING
材质系统不适合心理承受能力弱的用户。请做好应对潜在崩溃、内容日志错误和长时间加载的准备。
前言
本文译自网易中国版开发者文档 材质配置说明 ,将详细介绍材质文件的结构与配置方式。
材质文件结构
我们将以微软原生材质文件为例进行说明。该目录下主要包含以".material"为后缀的文件,此外还有三个重要的json文件:common.json、fancy.json和sad.json。
首先观察sad.json和fancy.json,它们用于控制画面质量表现。每个文件都定义了材质文件列表。通常fancy.json会比sad.json多定义几个材质文件,并可能在某些材质文件中添加额外宏定义,着色器可通过判断这些宏进行特殊处理:
[
{"path":"materials/sad.material"},
{"path":"materials/entity.material"},
{"path":"materials/terrain.material"},
{"path":"materials/portal.material"},
{"path":"materials/barrier.material"},
{"path":"materials/wireframe.material"}
]
[
{"path":"materials/fancy.material", "+defines":["FANCY"]},
{"path":"materials/entity.material", "+defines":["FANCY"]},
{"path":"materials/terrain.material", "+defines":["FANCY"]},
{"path":"materials/hologram.material"},
{"path":"materials/portal.material", "+defines":["FANCY"]},
{"path":"materials/barrier.material"},
{"path":"materials/wireframe.material"}
]
可以看出fancy.json比sad.json多定义了fancy.material和hologram.material材质文件,同时还为多个材质文件定义了FANCY宏。游戏设置/视频/精美图像选项的开关就是控制sad与fancy之间的切换。当开启精美图像时,fancy.json中的材质文件会生效;关闭时则使用sad.json中的材质文件。
为了获得更好的表现效果,fancy.json中的材质文件通常包含更复杂的运算,而sad.json中的材质则通过牺牲少许渲染效果换取更佳性能。若开发者需要编写更复杂的着色器,建议同时编写低配版本,分别在fancy和sad中进行定义,让玩家通过游戏中的精美图像选项自行控制是否开启对应效果。
[
{"path":"materials/particles.material"},
{"path":"materials/shadows.material"},
{"path":"materials/sky.material"},
{"path":"materials/ui.material"},
{"path":"materials/ui3D.material"},
{"path":"materials/portal.material"},
{"path":"materials/barrier.material"},
{"path":"materials/wireframe.material"}
]
与可互相切换的sad和fancy不同,common.json中定义的材质文件会在进入游戏后始终加载。除了声明在common.json、sad.json、fancy.json中的材质文件外,其他材质文件不会被加载。
材质语法规范
我们以entity.material为例进行说明。打开文件可以看到文件以materials开头,接着定义了版本号version为1.0.0,这些都是固定格式,标识该材质文件的解析方式,暂时无需修改。
可以看到材质中每个字段的定义都采用键值对形式,例如:
[
"vertexShader": "shaders/entity.vertex",
]
冒号左侧表示键名vertexShader,右侧表示值shaders/entity.vertex;
也存在列表形式的定义:
[
"vertexFields": [
{ "field": "Position" },
{ "field": "Normal" },
{ "field": "UV0" }
],
]
带有[ ]符号的声明就是列表,内部是每个子元素的json定义。
材质属性字段全览
渲染状态
states
配置渲染环境,可选值包括:
EnableAlphaToCoverage
:针对半透明物体的顺序无关渲染方式。该开关仅在支持MSAA的环境中有用。开启后物体边缘会根据透明度更精确地进行柔化过渡。也可用于大量网格重叠的复杂场景。Wireframe
:线框绘制模式Blending
: 启用颜色混合模式,常用于渲染半透明物体。声明该选项后通常需要接着声明混合因子blendSrc、blendDstDisableColorWrite
:不向颜色缓冲区写入颜色值,RGBA通道均不写入DisableAlphaWrite
:不向颜色缓冲区写入透明度alpha值,允许写入RGB值DisableRGBWrite
:不向颜色缓冲区写入RGB值,允许写入alpha值DisableDepthTest
:关闭深度测试DisableDepthWrite
:关闭深度写入DisableCulling
: 同时渲染正反面InvertCulling
:使用正面裁剪。默认为背面裁剪。声明此项后背面会被渲染而正面被裁剪。StencilWrite
: 启用模板掩码写入EnableStencilTest
:启用模板掩码测试
着色器路径
vertexShader
顶点着色器路径,通常为shaders/XXX.vertex。
vrGeometryShader
或 geometryShader
几何着色器路径,通常为shaders/XXX.geometry,移动端不会使用,无需修改。
fragmentShader
片段着色器路径,通常为shaders/XXX.fragment。
着色器宏定义
defines
为使用的着色器定义宏。为了实现代码复用,我们会将同一个着色器用于许多不同的材质。此时若希望根据当前材质在着色器某处执行不同逻辑,可以通过材质defines声明的宏进行判断。以entity_for_skeleton材质为例,可以看到这里定义了USE_SKINNING、USE_OVERLAY和NETEASE_SKINNING三个宏。
"entity_for_skeleton": {
"vertexShader": "shaders/entity.vertex",
"vrGeometryShader": "shaders/entity.geometry",
"fragmentShader": "shaders/entity.fragment",
"+defines": [ "USE_SKINNING", "USE_OVERLAY", "NETEASE_SKINNING" ],
"vertexFields": [
{ "field": "Position" },
{ "field": "Normal" },
{ "field": "BoneId0" },
{ "field": "UV0" }
],
"msaaSupport": "Both",
"+samplerStates": [
{
"samplerIndex": 0,
"textureFilter": "Point"
}
]
}
观察顶点着色器entity.vertex,会发现通过#ifdef、#else、#endif等指令判断宏定义并执行不同逻辑分支。这些宏的判断语句是在编译期处理的,不同于传统着色器中运行时处理的if else逻辑分支,实际运行时不会产生分支性能损耗。此外可以看到宏还能做多层判断,先判断NETEASE_SKINNING宏,再在内部执行逻辑中判断LARGE_VERTEX_SHADER_UNIFORMS宏:
#ifdef NETEASE_SKINNING
MAT4 boneMat = transpose(mat3x4ToMat4(BONES_70[int(BONEID_0)]));
entitySpacePosition = boneMat * POSITION;
entitySpaceNormal = boneMat * NORMAL;
#else
#if defined(LARGE_VERTEX_SHADER_UNIFORMS)
entitySpacePosition = BONES[int(BONEID_0)] * POSITION;
entitySpaceNormal = BONES[int(BONEID_0)] * NORMAL;
#else
entitySpacePosition = BONE * POSITION;
entitySpaceNormal = BONE * NORMAL;
#endif
#endif
运行时状态
深度测试
depthFunc
深度检测通过函数,可使用以下值:
Always
: 总是通过Equal
:深度值等于缓冲值时通过NotEqual
:深度值不等于缓冲值时通过Less
:深度值小于缓冲值时通过Greater
:深度值大于缓冲值时通过GreaterEqual
:深度值大于等于缓冲值时通过LessEqual
:深度值小于等于缓冲值时通过
关联状态渲染环境配置:
DisableDepthTest
:关闭深度测试DisableDepthWrite
:关闭深度写入
模板掩码测试
stencilRef
用于与掩码缓冲比较或写入的值
stencilRefOverride
是否使用缓冲当前值作为stencilRef,支持0或1:
1
:使用配置的stencilRef。若配置了stencilRef,stencilRefOverride会自动取10
:使用缓冲当前值作为stencilRef,此时不要配置stencilRef
stencilReadMask
掩码缓冲值与stencilRef值在比较前会分别与stencilReadMask做位与运算
stencilWriteMask
stencilRef值在写入掩码缓冲前会与stencilWriteMask做位与运算
frontFace
和 backFace
配置在网格正面或背面使用哪种掩码测试函数。此外判断顺序是先掩码检测再深度检测。需要配置以下操作:
stencilFunc
: stencilRef与掩码缓冲比较时使用的方法,支持以下值:Always
: 总是通过Equal
:stencilRef等于缓冲值时通过NotEqual
:stencilRef不等于缓冲值时通过Less
:stencilRef小于缓冲值时通过Greater
:stencilRef大于缓冲值时通过GreaterEqual
:stencilRef大于等于缓冲值时通过LessEqual
:stencilRef小于等于缓冲值时通过
stencilFailOp
:stencilFunc比较函数返回失败时执行的处理,支持以下值:Keep
:保持缓冲原值Replace
:将stencilRef位与stencilWriteMask的值写入缓冲
stencilDepthFailOp
:stencilFunc比较函数返回成功但深度测试失败时执行的处理,支持以下值:Keep
:保持缓冲原值Replace
:将stencilRef位与stencilWriteMask的值写入缓冲
stencilPassOp
: stencilFunc比较函数返回成功且深度测试成功时执行的处理,支持以下值:Keep
:保持缓冲原值Replace
:将stencilRef位与stencilWriteMask的值写入缓冲
关联状态渲染环境配置:
StencilWrite
:启用掩码写入EnableStencilTest
: 启用掩码测试
最后看一个具体示例:
"shadow_back": {
"+states": [
"StencilWrite",
"DisableColorWrite",
"DisableDepthWrite",
"InvertCulling",
"EnableStencilTest"
],
"vertexShader": "shaders/position.vertex",
"vrGeometryShader": "shaders/position.geometry",
"fragmentShader": "shaders/flat_white.fragment",
"frontFace": {
"stencilFunc": "Always",
"stencilFailOp": "Keep",
"stencilDepthFailOp": "Keep",
"stencilPassOp": "Replace"
},
"backFace": {
"stencilFunc": "Always",
"stencilFailOp": "Keep",
"stencilDepthFailOp": "Keep",
"stencilPassOp": "Replace"
},
"stencilRef": 1,
"stencilReadMask": 255,
"stencilWriteMask": 1,
"vertexFields": [
{ "field": "Position" }
],
"msaaSupport": "Both"
}
该示例中,StencilWrite表示支持向掩码缓冲写入,EnableStencilTest表示开启掩码测试,frontFace的配置表示渲染正面时掩码测试总是通过,若深度测试失败则保持缓冲值不变,若也都通过则将stencilRef位与stencilWriteMask的值写入缓冲,即1 & 1 = 1的值。backFace的配置也类似。
半透明物体颜色混合
半透明物体的渲染需要配置混合因子。最终输出的rgb颜色值 = 当前颜色值 * 源混合因子 + 缓冲中的颜色值 * 目标混合因子
blendSrc
源混合因子
blendDst
目标混合因子
alphaSrc
计算alpha时的源混合因子,通常不配置取默认值
alphaDst
计算alpha时的目标混合因子,通常不配置取默认值
混合因子总共可以取以下值:
DestColor
:缓冲颜色值SourceColor
:当前颜色值Zero
: (0,0,0)One
: (1,1,1)OneMinusDestColor
: (1,1,1) - 缓冲颜色值OneMinusSrcColor
: (1,1,1) - 当前颜色值SourceAlpha
:当前颜色中的alpha值DestAlpha
:缓冲颜色中的alpha值OneMinusSrcAlpha
:1 - 当前颜色值中的alpha值
引擎中默认取值为:
blendSrc
:SourceAlphablendDst
:OneMinusSrcAlphaalphaSrc
:OnealphaDst
:OneMinusSrcAlpha
关联状态渲染环境配置:
Blending
: 启用颜色混合模式,常用于渲染半透明物体。声明该选项后通常需要接着声明混合因子blendSrc、blendDstDisableColorWrite
:不向颜色缓冲区写入颜色值,RGBA通道均不写入DisableAlphaWrite
:不向颜色缓冲区写入透明度alpha值,允许写入RGB值DisableRGBWrite
:不向颜色缓冲区写入RGB值,允许写入alpha值
模板测试配置
stencilRef
用于与模板缓冲比较或写入的参考值
stencilRefOverride
是否使用缓冲当前值作为stencilRef,支持0或1:
1
:使用配置的stencilRef(若已配置stencilRef则自动取1)0
:使用缓冲当前值作为stencilRef(此时不应配置stencilRef)
stencilReadMask
模板缓冲值与stencilRef值在比较前会分别与stencilReadMask进行位与运算
stencilWriteMask
stencilRef值在写入模板缓冲前会与stencilWriteMask进行位与运算
frontFace
和 backFace
配置在网格正面/背面使用的模板测试函数。测试顺序为先模板测试后深度测试。需配置以下操作:
stencilFunc
: stencilRef与模板缓冲的比较方式,可选:Always
: 总是通过Equal
:值相等时通过NotEqual
:值不等时通过Less
:小于时通过Greater
:大于时通过GreaterEqual
:大于等于时通过LessEqual
:小于等于时通过
stencilFailOp
:模板测试失败时的处理:Keep
:保持缓冲原值Replace
:将stencilRef写入缓冲
stencilDepthFailOp
:模板测试通过但深度测试失败时的处理:Keep
:保持缓冲原值Replace
:将stencilRef写入缓冲
stencilPassOp
:两项测试均通过时的处理:Keep
:保持缓冲原值Replace
:将stencilRef写入缓冲
关联渲染状态:
StencilWrite
:启用模板写入EnableStencilTest
:启用模板测试
配置示例:
"shadow_back": {
"+states": [
"StencilWrite",
"DisableColorWrite",
"DisableDepthWrite",
"InvertCulling",
"EnableStencilTest"
],
"frontFace": {
"stencilFunc": "Always",
"stencilFailOp": "Keep",
"stencilDepthFailOp": "Keep",
"stencilPassOp": "Replace"
},
"backFace": { /* 相同配置 */ },
"stencilRef": 1,
"stencilReadMask": 255,
"stencilWriteMask": 1
}
半透明物体混合
最终颜色 = 当前颜色 × blendSrc + 缓冲颜色 × blendDst
混合因子
blendSrc
:源混合因子(默认SourceAlpha)blendDst
:目标混合因子(默认OneMinusSrcAlpha)alphaSrc
:alpha源因子(默认One)alphaDst
:alpha目标因子(默认OneMinusSrcAlpha)
可选值:
DestColor
:缓冲颜色SourceColor
:当前颜色Zero
:(0,0,0)One
:(1,1,1)OneMinusDestColor
: 1-缓冲颜色OneMinusSrcColor
: 1-当前颜色SourceAlpha
:当前alpha值DestAlpha
:缓冲alpha值OneMinusSrcAlpha
:1-当前alpha值
关联状态:
Blending
:启用混合DisableColorWrite
:禁用颜色写入DisableAlphaWrite
:禁用alpha写入DisableRGBWrite
:禁用RGB写入
纹理采样
samplerStates
配置采样状态(列表结构,按纹理索引配置):
{
"samplerIndex": 0, // 纹理索引(从0开始)
"textureFilter": "Point", // 过滤模式
"textureWrap": "Repeat" // 环绕模式
}
过滤模式
Point
:点采样Bilinear
:双线性Trilinear
:三线性MipMapBilinear
:MipMap双线性TexelAA
:抗锯齿(部分设备不支持)PCF
:百分比渐近过滤(部分设备不支持)
环绕模式
Repeat
:重复纹理Clamp
:边缘拉伸
顶点属性
vertexFields
声明网格顶点包含的属性:
Position
:模型空间坐标Color
:颜色Normal
:法线UV0
/UV1
/UV2
:纹理坐标BoneId0
:骨骼ID(骨骼模型用)
光栅化配置
msaaSupport
抗锯齿支持模式:
NonMSAA
:非MSAA模式下启用MSAA
:MSAA模式下启用Both
:始终启用(推荐)
深度偏移
解决z-fighting问题:
depthBias
:基础偏移slopeScaledDepthBias
:斜率比例偏移depthBiasOGL
:OpenGL平台偏移slopeScaledDepthBiasOGL
:OpenGL斜率偏移
计算公式: offset = (slopeScaledDepthBias × m) + (depthBias × r)
图元模式
primitiveMode
渲染图元类型:
None
:不渲染QuadList
:四边形列表TriangleList
:三角形列表(每3个顶点构成三角形)TriangleStrip
:三角形带(复用顶点)LineList
:线段列表Line
:线段带
材质变体
variants
快速创建衍生材质:
"base_material": {
"vertexShader": "...",
"variants": [
{
"variant1": { // 变体1
"+defines": ["MACRO_1"],
"vertexFields": [...]
}
},
{
"variant2": { // 变体2
"+states": ["Blending"]
}
}
]
}
使用时通过base_material.variant1
调用变体
材质合并规则
多文件定义同一材质时的合并策略:
- 常规字段:后加载的覆盖先加载的
- 特殊字段(支持
+
/-
操作符):defines
:宏定义states
:渲染状态samplerStates
:采样状态
执行顺序:覆盖 → 添加 → 删除
示例:
// 基础定义
"mat": {"defines": ["A","B"]}
// 扩展定义(添加C,删除B)
"mat": {
"+defines": ["C"],
"-defines": ["B"]
}
// 最终效果:["A","C"]