import {
|
_shadersMaterialStageFS,
|
AlphaMode,
|
combine,
|
GltfLoader,
|
LightingModel,
|
Matrix3,
|
MaterialPipelineStage,
|
ModelAlphaOptions,
|
ModelLightingOptions,
|
Pass,
|
Resource,
|
ResourceCache,
|
ShaderBuilder,
|
Cartesian4,
|
Cartesian3,
|
} from "../../../Source/Cesium.js";
|
import createScene from "../../createScene.js";
|
import waitForLoaderProcess from "../../waitForLoaderProcess.js";
|
|
describe(
|
"Scene/ModelExperimental/MaterialPipelineStage",
|
function () {
|
var scene;
|
var gltfLoaders = [];
|
|
beforeAll(function () {
|
scene = createScene();
|
});
|
|
afterAll(function () {
|
scene.destroyForSpecs();
|
});
|
|
var mockFrameState = {
|
context: {
|
defaultTexture: {},
|
defaultNormalTexture: {},
|
defaultEmissiveTexture: {},
|
},
|
};
|
|
afterEach(function () {
|
var gltfLoadersLength = gltfLoaders.length;
|
for (var i = 0; i < gltfLoadersLength; ++i) {
|
var gltfLoader = gltfLoaders[i];
|
if (!gltfLoader.isDestroyed()) {
|
gltfLoader.destroy();
|
}
|
}
|
gltfLoaders.length = 0;
|
ResourceCache.clearForSpecs();
|
});
|
|
function getOptions(gltfPath, options) {
|
var resource = new Resource({
|
url: gltfPath,
|
});
|
|
return combine(options, {
|
gltfResource: resource,
|
incrementallyLoadTextures: false, // Default to false if not supplied
|
});
|
}
|
|
function loadGltf(gltfPath, options) {
|
var gltfLoader = new GltfLoader(getOptions(gltfPath, options));
|
gltfLoaders.push(gltfLoader);
|
gltfLoader.load();
|
|
return waitForLoaderProcess(gltfLoader, scene);
|
}
|
|
var boomBox = "./Data/Models/PBR/BoomBox/BoomBox.gltf";
|
var boomBoxSpecularGlossiness =
|
"./Data/Models/PBR/BoomBoxSpecularGlossiness/BoomBox.gltf";
|
var boxUnlit = "./Data/Models/GltfLoader/UnlitTest/glTF/UnlitTest.gltf";
|
|
function expectShaderLines(shaderLines, expected) {
|
for (var i = 0; i < expected.length; i++) {
|
expect(shaderLines.indexOf(expected[i])).not.toBe(-1);
|
}
|
}
|
|
function expectUniformMap(uniformMap, expected) {
|
for (var key in expected) {
|
if (expected.hasOwnProperty(key)) {
|
var expectedValue = expected[key];
|
var uniformFunction = uniformMap[key];
|
expect(uniformFunction).toBeDefined();
|
expect(uniformFunction()).toEqual(expectedValue);
|
}
|
}
|
}
|
|
it("adds material uniforms", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: uniformMap,
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expect(shaderBuilder._vertexShaderParts.uniformLines).toEqual([]);
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform sampler2D u_emissiveTexture;",
|
"uniform vec3 u_emissiveFactor;",
|
"uniform sampler2D u_normalTexture;",
|
"uniform sampler2D u_occlusionTexture;",
|
]);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"HAS_EMISSIVE_TEXTURE",
|
"TEXCOORD_EMISSIVE v_texCoord_0",
|
"HAS_EMISSIVE_FACTOR",
|
"HAS_NORMAL_TEXTURE",
|
"TEXCOORD_NORMAL v_texCoord_0",
|
"HAS_OCCLUSION_TEXTURE",
|
"TEXCOORD_OCCLUSION v_texCoord_0",
|
]);
|
|
var material = primitive.material;
|
var expectedUniforms = {
|
u_emissiveTexture: material.emissiveTexture.texture,
|
u_emissiveFactor: material.emissiveFactor,
|
u_normalTexture: material.normalTexture.texture,
|
u_occlusionTexture: material.occlusionTexture.texture,
|
};
|
expectUniformMap(uniformMap, expectedUniforms);
|
});
|
});
|
|
it("adds metallic roughness uniforms", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: uniformMap,
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform sampler2D u_baseColorTexture;",
|
"uniform sampler2D u_metallicRoughnessTexture;",
|
]);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"HAS_BASE_COLOR_TEXTURE",
|
"TEXCOORD_BASE_COLOR v_texCoord_0",
|
"HAS_METALLIC_ROUGHNESS_TEXTURE",
|
"TEXCOORD_METALLIC_ROUGHNESS v_texCoord_0",
|
]);
|
|
var metallicRoughness = primitive.material.metallicRoughness;
|
var expectedUniforms = {
|
u_baseColorTexture: metallicRoughness.baseColorTexture.texture,
|
u_metallicRoughnessTexture:
|
metallicRoughness.metallicRoughnessTexture.texture,
|
};
|
expectUniformMap(uniformMap, expectedUniforms);
|
});
|
});
|
|
it("adds metallic roughness uniforms without defaults", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
|
// Alter PBR parameters so that defaults are not used.
|
var metallicRoughness = primitive.material.metallicRoughness;
|
metallicRoughness.baseColorFactor = new Cartesian4(0.5, 0.5, 0.5, 0.5);
|
metallicRoughness.metallicFactor = 0.5;
|
metallicRoughness.roughnessFactor = 0.5;
|
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: uniformMap,
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform sampler2D u_baseColorTexture;",
|
"uniform vec4 u_baseColorFactor;",
|
"uniform sampler2D u_metallicRoughnessTexture;",
|
"uniform float u_metallicFactor;",
|
"uniform float u_roughnessFactor;",
|
]);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"HAS_BASE_COLOR_TEXTURE",
|
"TEXCOORD_BASE_COLOR v_texCoord_0",
|
"HAS_BASE_COLOR_FACTOR",
|
"HAS_METALLIC_ROUGHNESS_TEXTURE",
|
"TEXCOORD_METALLIC_ROUGHNESS v_texCoord_0",
|
"HAS_METALLIC_FACTOR",
|
"HAS_ROUGHNESS_FACTOR",
|
]);
|
|
var expectedUniforms = {
|
u_baseColorTexture: metallicRoughness.baseColorTexture.texture,
|
u_baseColorFactor: metallicRoughness.baseColorFactor,
|
u_metallicRoughnessTexture:
|
metallicRoughness.metallicRoughnessTexture.texture,
|
u_metallicFactor: metallicRoughness.metallicFactor,
|
u_roughnessFactor: metallicRoughness.roughnessFactor,
|
};
|
expectUniformMap(uniformMap, expectedUniforms);
|
});
|
});
|
|
it("adds specular glossiness uniforms", function () {
|
return loadGltf(boomBoxSpecularGlossiness).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: uniformMap,
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform sampler2D u_diffuseTexture;",
|
"uniform sampler2D u_specularGlossinessTexture;",
|
"uniform float u_glossinessFactor;",
|
]);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"USE_SPECULAR_GLOSSINESS",
|
"HAS_DIFFUSE_TEXTURE",
|
"TEXCOORD_DIFFUSE v_texCoord_0",
|
"HAS_SPECULAR_GLOSSINESS_TEXTURE",
|
"TEXCOORD_SPECULAR_GLOSSINESS v_texCoord_0",
|
"HAS_GLOSSINESS_FACTOR",
|
]);
|
|
var specularGlossiness = primitive.material.specularGlossiness;
|
var expectedUniforms = {
|
u_diffuseTexture: specularGlossiness.diffuseTexture.texture,
|
u_specularGlossinessTexture:
|
specularGlossiness.specularGlossinessTexture.texture,
|
u_glossinessFactor: specularGlossiness.glossinessFactor,
|
};
|
expectUniformMap(uniformMap, expectedUniforms);
|
});
|
});
|
|
it("adds specular glossiness uniforms without defaults", function () {
|
return loadGltf(boomBoxSpecularGlossiness).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
|
// Alter PBR parameters so that defaults are not used.
|
var specularGlossiness = primitive.material.specularGlossiness;
|
specularGlossiness.diffuseFactor = new Cartesian4(0.5, 0.5, 0.5, 0.5);
|
specularGlossiness.specularFactor = new Cartesian3(0.5, 0.5, 0.5);
|
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: uniformMap,
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform sampler2D u_diffuseTexture;",
|
"uniform vec4 u_diffuseFactor;",
|
"uniform sampler2D u_specularGlossinessTexture;",
|
"uniform vec3 u_specularFactor;",
|
"uniform float u_glossinessFactor;",
|
]);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"USE_SPECULAR_GLOSSINESS",
|
"HAS_DIFFUSE_TEXTURE",
|
"TEXCOORD_DIFFUSE v_texCoord_0",
|
"HAS_DIFFUSE_FACTOR",
|
"HAS_SPECULAR_GLOSSINESS_TEXTURE",
|
"TEXCOORD_SPECULAR_GLOSSINESS v_texCoord_0",
|
"HAS_SPECULAR_FACTOR",
|
"HAS_GLOSSINESS_FACTOR",
|
]);
|
|
var expectedUniforms = {
|
u_diffuseTexture: specularGlossiness.diffuseTexture.texture,
|
u_diffuseFactor: specularGlossiness.diffuseFactor,
|
u_specularGlossinessTexture:
|
specularGlossiness.specularGlossinessTexture.texture,
|
u_specularFactor: specularGlossiness.specularFactor,
|
u_glossinessFactor: specularGlossiness.glossinessFactor,
|
};
|
expectUniformMap(uniformMap, expectedUniforms);
|
});
|
});
|
|
it("enables PBR lighting for metallic roughness materials", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var lightingOptions = new ModelLightingOptions();
|
var renderResources = {
|
shaderBuilder: new ShaderBuilder(),
|
uniformMap: {},
|
lightingOptions: lightingOptions,
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
expect(lightingOptions.lightingModel).toBe(LightingModel.PBR);
|
});
|
});
|
|
it("enables PBR lighting for specular glossiness materials", function () {
|
return loadGltf(boomBoxSpecularGlossiness).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var lightingOptions = new ModelLightingOptions();
|
var renderResources = {
|
shaderBuilder: new ShaderBuilder(),
|
uniformMap: {},
|
lightingOptions: lightingOptions,
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
expect(lightingOptions.lightingModel).toBe(LightingModel.PBR);
|
});
|
});
|
|
it("enables unlit lighting when KHR_materials_unlit is present", function () {
|
return loadGltf(boxUnlit).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[1].primitives[0];
|
var lightingOptions = new ModelLightingOptions();
|
var renderResources = {
|
shaderBuilder: new ShaderBuilder(),
|
uniformMap: {},
|
lightingOptions: lightingOptions,
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
expect(lightingOptions.lightingModel).toBe(LightingModel.UNLIT);
|
});
|
});
|
|
it("handles alphaMode = OPAQUE", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: {},
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expect(renderResources.alphaOptions.pass).not.toBeDefined();
|
expect(renderResources.alphaOptions.alphaMode).toBe(AlphaMode.OPAQUE);
|
expect(renderResources.alphaOptions.alphaCutoff).not.toBeDefined();
|
});
|
});
|
|
it("handles alphaMode = MASK", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: uniformMap,
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
var cutoff = 0.6;
|
primitive.material.alphaMode = AlphaMode.MASK;
|
primitive.material.alphaCutoff = cutoff;
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expect(renderResources.alphaOptions.pass).not.toBeDefined();
|
expect(renderResources.alphaOptions.alphaMode).toBe(AlphaMode.MASK);
|
expect(renderResources.alphaOptions.alphaCutoff).toBe(cutoff);
|
});
|
});
|
|
it("handles alphaMode = BLEND", function () {
|
return loadGltf(boomBox).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[0].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: {},
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
pass: Pass.OPAQUE,
|
};
|
|
primitive.material.alphaMode = AlphaMode.BLEND;
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expect(renderResources.alphaOptions.pass).toBe(Pass.TRANSLUCENT);
|
expect(renderResources.alphaOptions.alphaMode).toBe(AlphaMode.BLEND);
|
expect(renderResources.alphaOptions.alphaCutoff).not.toBeDefined();
|
});
|
});
|
|
it("enables back-face culling if material is not double-sided", function () {
|
return loadGltf(boxUnlit).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[1].primitives[0];
|
var renderStateOptions = {};
|
var renderResources = {
|
shaderBuilder: new ShaderBuilder(),
|
uniformMap: {},
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: renderStateOptions,
|
cull: true,
|
};
|
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
expect(renderStateOptions).toEqual({
|
cull: {
|
enabled: true,
|
},
|
});
|
});
|
});
|
|
it("disables back-face culling if material is double-sided", function () {
|
return loadGltf(boxUnlit).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[1].primitives[0];
|
var renderStateOptions = {};
|
var renderResources = {
|
shaderBuilder: new ShaderBuilder(),
|
uniformMap: {},
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: renderStateOptions,
|
cull: true,
|
};
|
|
primitive.material.doubleSided = true;
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expect(renderStateOptions).toEqual({
|
cull: {
|
enabled: false,
|
},
|
});
|
});
|
});
|
|
it("adds material stage functions to the fragment shader", function () {
|
return loadGltf(boxUnlit).then(function (gltfLoader) {
|
var components = gltfLoader.components;
|
var primitive = components.nodes[1].primitives[0];
|
var shaderBuilder = new ShaderBuilder();
|
var renderResources = {
|
shaderBuilder: shaderBuilder,
|
uniformMap: {},
|
lightingOptions: new ModelLightingOptions(),
|
alphaOptions: new ModelAlphaOptions(),
|
renderStateOptions: {},
|
};
|
|
primitive.material.doubleSided = true;
|
MaterialPipelineStage.process(
|
renderResources,
|
primitive,
|
mockFrameState
|
);
|
|
expect(shaderBuilder._vertexShaderParts.shaderLines).toEqual([]);
|
expect(shaderBuilder._fragmentShaderParts.shaderLines).toEqual([
|
_shadersMaterialStageFS,
|
]);
|
});
|
});
|
|
it("_processTextureTransform updates the shader and uniform map", function () {
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var matrix = new Matrix3(0.5, 0, 0.5, 0, 0.5, 0, 0, 0, 1);
|
var textureReader = {
|
transform: matrix,
|
};
|
MaterialPipelineStage._processTextureTransform(
|
shaderBuilder,
|
uniformMap,
|
textureReader,
|
"u_testTexture",
|
"TEST"
|
);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"HAS_TEST_TEXTURE_TRANSFORM",
|
]);
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform mat3 u_testTextureTransform;",
|
]);
|
expectUniformMap(uniformMap, {
|
u_testTextureTransform: matrix,
|
});
|
});
|
|
it("_processTexture processes texture transforms if present", function () {
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var matrix = new Matrix3(0.5, 0, 0.5, 0, 0.5, 0, 0, 0, 1);
|
var mockTexture = {};
|
var textureReader = {
|
transform: matrix,
|
texture: mockTexture,
|
texCoord: 1,
|
};
|
MaterialPipelineStage._processTexture(
|
shaderBuilder,
|
uniformMap,
|
textureReader,
|
"u_testTexture",
|
"TEST",
|
mockFrameState.context.defaultTexture
|
);
|
|
expectShaderLines(shaderBuilder._fragmentShaderParts.defineLines, [
|
"HAS_TEST_TEXTURE",
|
"TEXCOORD_TEST v_texCoord_1",
|
"HAS_TEST_TEXTURE_TRANSFORM",
|
]);
|
expectShaderLines(shaderBuilder._fragmentShaderParts.uniformLines, [
|
"uniform sampler2D u_testTexture;",
|
"uniform mat3 u_testTextureTransform;",
|
]);
|
expectUniformMap(uniformMap, {
|
u_testTextureTransform: matrix,
|
});
|
});
|
|
it("_processTexture creates texture uniforms with a default value", function () {
|
var shaderBuilder = new ShaderBuilder();
|
var uniformMap = {};
|
var matrix = new Matrix3(0.5, 0, 0.5, 0, 0.5, 0, 0, 0, 1);
|
var mockTexture = {};
|
var textureReader = {
|
transform: matrix,
|
texture: mockTexture,
|
texCoord: 1,
|
};
|
MaterialPipelineStage._processTexture(
|
shaderBuilder,
|
uniformMap,
|
textureReader,
|
"u_testTexture",
|
"TEST",
|
mockFrameState.context.defaultTexture
|
);
|
|
expectUniformMap(uniformMap, {
|
u_testTexture: mockTexture,
|
});
|
});
|
},
|
"WEBGL"
|
);
|