Appearance
RoyMaterialTemplate与RoyMaterialInstance
在RoyEngine中,材质由两部分组成:材质模板和材质实例。
材质模板约定了材质的基础,包括:
- Shader,即GPU执行指令
- Uniform,即材质参数的形式(名称+类型)。
- 渲染参数:是否透明、混合模式、是否双面等
材质实例从材质模板中创建出来,其要做的操作就是把材质参数的数值与材质模板结合到一起,形成完整的可用于RoyObject3D渲染的数据结构体。
举个例子,材质模板中定义了color这样一个材质参数,创建三个材质实例并分别赋给三个球,这三个实例对应color的值是红色、黄色、蓝色,那么这三个球就是红球、黄球、蓝球。
材质模板的生成:
- 材质模板的源文件是一个json形式的问题,目前所有项目中使用到的材质文件位于目录libs/royengine/assets/materials下,使用matc(位于libs/royengine/assets/matc)进行编译,编译的产物放入libs/royengine/libs/materials下面。
- 我们摘一个典型的roymat文件如下并做简要分析。
material {
name : base_${SHADINGMODEL}_${BLENDING},
requires : [ uv0, uv1 ],
normalOnly : true,
shadingModel : ${SHADINGMODEL},
blending : ${BLENDING},
doubleSided : ${DOUBLESIDED},
transparency : ${TRANSPARENCY},
flipUV : false,
draftSupport : true,
specularAmbientOcclusion : simple,
specularAntiAliasing : true,
clearCoatIorChange : false,
reflections : screenspace,
parameters : [
{ type : float4[4], name : clippingPlanes },
{ type : float4, name : clippingInfo },
{ type : float3, name : specularFactor },
{ type : float, name : glossinessFactor },
{ type : int, name : meshID },
// Base Color
{ type : int, name : baseColorMapIndex },
{ type : float4, name : baseColorFactor },
{ type : sampler2d, name : baseColorMap },
{ type : mat3, name : baseColorUvMatrix, precision: high },
// Metallic-Roughness Map
{ type : int, name : metallicRoughnessMapIndex },
{ type : float, name : metallicFactor },
{ type : float, name : roughnessFactor },
{ type : sampler2d, name : metallicRoughnessMap },
// Emissive Map
{ type : int, name : emissiveMapIndex },
{ type : float3, name : emissiveFactor },
{ type : float, name : emissiveStrength },
{ type : sampler2d, name : emissiveMap },
// Reflectance
{ type : float, name : reflectance }
${CUSTOM_PARAMS}
],
}
vertex {
#include "../common/getMeshID_vs.glsl"
void materialVertex(inout MaterialVertexInputs material) {
${CUSTOM_VERTEX}
vertex_meshID.w = getMeshID() * 0.001;
}
}
fragment {
#include "../common/materialClipping_fs.glsl"
void material(inout MaterialInputs material) {
highp float2 uvs[2];
uvs[0] = getUV01();
uvs[1] = getUV1();
prepareMaterial(material);
viewClipping();
materialClipping();
material.kApplyAO = 1.0;
material.baseColor = materialParams.baseColorFactor;
if (materialParams.baseColorMapIndex > -1) {
highp float2 uv = uvs[materialParams.baseColorMapIndex];
uv = (vec3(uv, 1.0) * materialParams.baseColorUvMatrix).xy;
vec4 texColor = texture(materialParams_baseColorMap, uv);
texColor.rgb = rgb_from_srgb(texColor.rgb);
material.baseColor *= texColor;
}
#if defined(BLEND_MODE_TRANSPARENT)
material.baseColor.rgb *= material.baseColor.a;
#endif
// material.baseColor *= getColor();
#if !defined(SHADING_MODEL_UNLIT)
#if defined(SHADING_MODEL_LIT)
material.roughness = materialParams.roughnessFactor;
material.metallic = materialParams.metallicFactor;
#endif
material.emissive = vec4(materialParams.emissiveStrength *
materialParams.emissiveFactor.rgb, 0.0);
#if defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
material.glossiness = materialParams.glossinessFactor;
material.specularColor = materialParams.specularFactor;
#else
material.reflectance = materialParams.reflectance;
#endif
if (materialParams.metallicRoughnessMapIndex > -1) {
highp float2 uv = uvs[materialParams.metallicRoughnessMapIndex];
#if defined(SHADING_MODEL_SPECULAR_GLOSSINESS)
vec4 sg = texture(materialParams_metallicRoughnessMap, uv);
material.specularColor *= rgb_from_srgb(sg.rgb);
material.glossiness *= sg.a;
#else
vec4 mr = texture(materialParams_metallicRoughnessMap, uv);
material.roughness *= mr.g;
material.metallic *= mr.b;
#endif
}
if (materialParams.emissiveMapIndex > -1) {
highp float2 uv = uvs[materialParams.emissiveMapIndex];
material.emissive.rgb *= rgb_from_srgb(texture(materialParams_emissiveMap, uv).rgb);
}
#endif
${CUSTOM_FRAGMENT}
material.meshId = floor(vertex_meshID.w * 1000.0 + 0.5);
}
}
vertexWGSL {
#include "../common/getMeshID_vs.wgsl"
fn materialVertex(material:ptr<function, MaterialVertexInputs>) {
${CUSTOM_VERTEX}
vertex_meshID.w = getMeshID() * 0.001;
}
}
fragmentWGSL {
#include "../common/materialClipping_fs.wgsl"
fn material(material:ptr<function, MaterialInputs>) {
var uvs: array<float2, 2>;
uvs[0] = getUV01();
uvs[1] = getUV1();
prepareMaterial(*material);
viewClipping();
materialClipping();
(*material).kApplyAO = 1.0;
(*material).baseColor = materialParams.baseColorFactor;
if (materialParams.baseColorMapIndex > -1) {
var uv: float2 = uvs[materialParams.baseColorMapIndex];
uv = (vec3f(uv, 1.0) * materialParams.baseColorUvMatrix).xy;
var texColor: vec4f = textureSample(materialParams_baseColorMap, materialParams_baseColorMapSampler, uv);
let temp: vec3f = rgb_from_srgb(texColor.rgb);
texColor.r = temp.r;
texColor.g = temp.g;
texColor.b = temp.b;
(*material).baseColor *= texColor;
}
if (BLEND_MODE_TRANSPARENT) {
let temp: vec3f = (*material).baseColor.rgb * (*material).baseColor.a;
(*material).baseColor.r = temp.r;
(*material).baseColor.g = temp.g;
(*material).baseColor.b = temp.b;
}
// (*material).baseColor *= getColor();
if (!SHADING_MODEL_UNLIT) {
if (SHADING_MODEL_LIT) {
(*material).roughness = materialParams.roughnessFactor;
(*material).metallic = materialParams.metallicFactor;
}
(*material).emissive = vec4f(materialParams.emissiveStrength * materialParams.emissiveFactor.rgb, 0.0);
if (SHADING_MODEL_SPECULAR_GLOSSINESS) {
(*material).glossiness = materialParams.glossinessFactor;
(*material).specularColor = materialParams.specularFactor;
} else {
(*material).reflectance = materialParams.reflectance;
}
if (materialParams.metallicRoughnessMapIndex > -1) {
var uv: float2 = uvs[materialParams.metallicRoughnessMapIndex];
if (SHADING_MODEL_SPECULAR_GLOSSINESS) {
let sg: vec4f = textureSample(materialParams_metallicRoughnessMap, materialParams_metallicRoughnessMapSampler, uv);
(*material).specularColor *= rgb_from_srgb(sg.rgb);
(*material).glossiness *= sg.a;
} else {
let mr: vec4f = textureSample(materialParams_metallicRoughnessMap, materialParams_metallicRoughnessMapSampler, uv);
(*material).roughness *= mr.g;
(*material).metallic *= mr.b;
}
}
if (materialParams.emissiveMapIndex > -1) {
var uv: float2 = uvs[materialParams.emissiveMapIndex];
let temp: vec3f = (*material).emissive.rgb * rgb_from_srgb(textureSample(materialParams_emissiveMap, materialParams_emissiveMapSampler, uv).rgb);
(*material).emissive.r = temp.r;
(*material).emissive.g = temp.g;
(*material).emissive.b = temp.b;
}
}
${CUSTOM_FRAGMENT}
(*material).meshId = floor(vertex_meshID.w * 1000.0 + 0.5);
}
}
其中:
- material项时材质的基本信息
- vertex项是顶点着色器中的用户自定义函数。
- fragment项是像素着色器中的用户自定义函数。
一些简要说明:
- 材质的编译过程是字符串的拼合过程,根据材质模板定义的参数,选择和生成正确的字符串,并配合材质文件中定义的自定义函数,共同生成最终的运行时文件。
- 最终的运行时文件包含由材质参数变体形成的多种shader文件的组合,在实际的RoyObject3D去做绘制时,引擎会选择正确的shader来上传到GPU去执行,这也解释了为什么诸如场景参数或者相机参数变化时,引擎能够立刻渲染,因为在这之前已经生成了所有情况的的shader文件。
在使用上:
- RoyMaterialTemplate需要在onOnit方法中注册给RoyMaterialTemplateMgr,这样就可以在各个插件或组件中,通过名称来获取到材质模板。
- 拿到RoyMaterialTemplate后就可以创建RoyMaterialInstance,主要的方法:
- setUniform: 设置材质参数
- setTexture:设置纹理,纹理只是材质参数的另外一种表达形式而已
- setSampler:设置纹理采样器,决定纹理以何种方式采样(点采样、双线性采样、三线性采样)
- 将RoyMaterialInstance赋值给组件,如RoyMeshComponent,组件会将材质实例本身交给其衍生的RoyObject3D上。
- 具体的使用参考可以参考libs\royframe\royframebase\src\royresource\matgltf\Mat*.ts
对于RoyMaterial的属性,其在RoyProtocal中的结构体对应,摘要如下:
typescript
/**
* Interface for uniform data in a material instance
*/
interface RoyMatUniformData {
/** Name of the uniform */
name: string;
/** Data of the uniform */
type: string;
/** Data of the uniform */
dataFloatArray?: number[];
/** Data of the uniform */
dataFloat?: number;
/** Data of the uniform */
dataInt?: number;
/** Data of the uniform */
dataBool?: boolean;
/** Data of the uniform */
dataStr?: string;
}
/**
* Interface for texture data in a material instance
*/
interface RoyMatTextureData {
/** Name of the texture */
name: string;
/** Data of the texture */
data: string;
/** Sampler information for the texture */
sampler?: RoySamplerInfoData;
}
/**
* Interface for sampler information.
*/
interface RoySamplerInfoData {
/** The minification filter. */
minFilter?: string;
/** The magnification filter. */
magFilter?: string;
/** The wrap mode for the S coordinate. */
wrapModeS?: string;
/** The wrap mode for the T coordinate. */
wrapModeT?: string;
}
/**
* Interface for material data
*/
interface RoyMaterialData extends RoyTransDataInfo {
/** Name of the material template */
templateName: string;
/** Blend mode */
blendMode?: string;
/** Shading mode */
shadingMode?: string;
/** Culling face mode */
cullFaceMode?: string;
/** Depth write enabled flag */
depthWriteEnabled?: boolean;
/** Depth test enabled flag */
depthTestEnabled?: boolean;
/** Double sided flag */
doubleSided?: boolean;
/** Color write flag */
colorWrite?: boolean;
/** Polygon offset */
polygonOffset?: number[];
/** Instance enabled flag */
instanceEnabled?: boolean;
/** Light enabled flag */
lightEnabled?: boolean;
/** List of uniform data */
uniformList?: RoyMatUniformData[];
/** List of texture data */
textureList?: RoyMatTextureData[];
/** Name of material*/
name: string;
}