Skip to content

材质配置文件说明

本页面镜像自
BedrockWiki

根据原始项目协议授权。本文经过AI翻译处理,如有内容遗漏,可以提交PR进行补充。

WARNING

材质系统不适合心理承受能力弱的用户。请做好应对潜在崩溃、内容日志错误和长时间加载的准备。

前言

本文译自网易中国版开发者文档 材质配置说明 ,将详细介绍材质文件的结构与配置方式。

材质文件结构

我们将以微软原生材质文件为例进行说明。该目录下主要包含以".material"为后缀的文件,此外还有三个重要的json文件:common.json、fancy.json和sad.json。

首先观察sad.json和fancy.json,它们用于控制画面质量表现。每个文件都定义了材质文件列表。通常fancy.json会比sad.json多定义几个材质文件,并可能在某些材质文件中添加额外宏定义,着色器可通过判断这些宏进行特殊处理:

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"}
]
json
[
	{"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中进行定义,让玩家通过游戏中的精美图像选项自行控制是否开启对应效果。

json
[
	{"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,这些都是固定格式,标识该材质文件的解析方式,暂时无需修改。

可以看到材质中每个字段的定义都采用键值对形式,例如:

json
[
	"vertexShader": "shaders/entity.vertex",
]

冒号左侧表示键名vertexShader,右侧表示值shaders/entity.vertex;

也存在列表形式的定义:

json
[
	"vertexFields": [
        { "field": "Position" },
        { "field": "Normal" },
        { "field": "UV0" }
    ],
]

带有[ ]符号的声明就是列表,内部是每个子元素的json定义。

材质属性字段全览

渲染状态

states

配置渲染环境,可选值包括:

  • EnableAlphaToCoverage:针对半透明物体的顺序无关渲染方式。该开关仅在支持MSAA的环境中有用。开启后物体边缘会根据透明度更精确地进行柔化过渡。也可用于大量网格重叠的复杂场景。

  • Wireframe:线框绘制模式

  • Blending: 启用颜色混合模式,常用于渲染半透明物体。声明该选项后通常需要接着声明混合因子blendSrc、blendDst

  • DisableColorWrite:不向颜色缓冲区写入颜色值,RGBA通道均不写入

  • DisableAlphaWrite:不向颜色缓冲区写入透明度alpha值,允许写入RGB值

  • DisableRGBWrite:不向颜色缓冲区写入RGB值,允许写入alpha值

  • DisableDepthTest:关闭深度测试

  • DisableDepthWrite:关闭深度写入

  • DisableCulling: 同时渲染正反面

  • InvertCulling:使用正面裁剪。默认为背面裁剪。声明此项后背面会被渲染而正面被裁剪。

  • StencilWrite: 启用模板掩码写入

  • EnableStencilTest:启用模板掩码测试

着色器路径

vertexShader

顶点着色器路径,通常为shaders/XXX.vertex。

vrGeometryShadergeometryShader

几何着色器路径,通常为shaders/XXX.geometry,移动端不会使用,无需修改。

fragmentShader

片段着色器路径,通常为shaders/XXX.fragment。

着色器宏定义

defines

为使用的着色器定义宏。为了实现代码复用,我们会将同一个着色器用于许多不同的材质。此时若希望根据当前材质在着色器某处执行不同逻辑,可以通过材质defines声明的宏进行判断。以entity_for_skeleton材质为例,可以看到这里定义了USE_SKINNING、USE_OVERLAY和NETEASE_SKINNING三个宏。

json
"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宏:

glsl
#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会自动取1

  • 0:使用缓冲当前值作为stencilRef,此时不要配置stencilRef

stencilReadMask

掩码缓冲值与stencilRef值在比较前会分别与stencilReadMask做位与运算

stencilWriteMask

stencilRef值在写入掩码缓冲前会与stencilWriteMask做位与运算

frontFacebackFace

配置在网格正面或背面使用哪种掩码测试函数。此外判断顺序是先掩码检测再深度检测。需要配置以下操作:

  • 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: 启用掩码测试

最后看一个具体示例:

json
    "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:SourceAlpha

  • blendDst:OneMinusSrcAlpha

  • alphaSrc:One

  • alphaDst:OneMinusSrcAlpha

关联状态渲染环境配置:

  • Blending: 启用颜色混合模式,常用于渲染半透明物体。声明该选项后通常需要接着声明混合因子blendSrc、blendDst

  • DisableColorWrite:不向颜色缓冲区写入颜色值,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进行位与运算

frontFacebackFace

配置在网格正面/背面使用的模板测试函数。测试顺序为先模板测试后深度测试。需配置以下操作:

  • stencilFunc: stencilRef与模板缓冲的比较方式,可选:

    • Always: 总是通过
    • Equal:值相等时通过
    • NotEqual:值不等时通过
    • Less:小于时通过
    • Greater:大于时通过
    • GreaterEqual:大于等于时通过
    • LessEqual:小于等于时通过
  • stencilFailOp:模板测试失败时的处理:

    • Keep:保持缓冲原值
    • Replace:将stencilRef写入缓冲
  • stencilDepthFailOp:模板测试通过但深度测试失败时的处理:

    • Keep:保持缓冲原值
    • Replace:将stencilRef写入缓冲
  • stencilPassOp:两项测试均通过时的处理:

    • Keep:保持缓冲原值
    • Replace:将stencilRef写入缓冲

关联渲染状态:

  • StencilWrite:启用模板写入
  • EnableStencilTest:启用模板测试

配置示例:

json
"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

配置采样状态(列表结构,按纹理索引配置):

json
{
    "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

快速创建衍生材质:

json
"base_material": {
    "vertexShader": "...",
    "variants": [
        {
            "variant1": {  // 变体1
                "+defines": ["MACRO_1"],
                "vertexFields": [...]
            }
        },
        {
            "variant2": {  // 变体2
                "+states": ["Blending"]
            }
        }
    ]
}

使用时通过base_material.variant1调用变体

材质合并规则

多文件定义同一材质时的合并策略:

  1. 常规字段:后加载的覆盖先加载的
  2. 特殊字段(支持+/-操作符):
    • defines:宏定义
    • states:渲染状态
    • samplerStates:采样状态

执行顺序:覆盖 → 添加 → 删除

示例:

json
// 基础定义
"mat": {"defines": ["A","B"]}

// 扩展定义(添加C,删除B)
"mat": {
    "+defines": ["C"],
    "-defines": ["B"] 
}

// 最终效果:["A","C"]