import {
|
Cartesian2,
|
Cartesian3,
|
Matrix2,
|
CustomShader,
|
CustomShaderMode,
|
LightingModel,
|
TextureUniform,
|
UniformType,
|
VaryingType,
|
} from "../../../Source/Cesium.js";
|
import createScene from "../../createScene.js";
|
import pollToPromise from "../../pollToPromise.js";
|
|
describe("Scene/ModelExperimental/CustomShader", function () {
|
var emptyVertexShader =
|
"void vertexMain(VertexInput vsInput, inout vec3 positionMC) {}";
|
var emptyFragmentShader =
|
"void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {}";
|
|
it("constructs with default values", function () {
|
var customShader = new CustomShader();
|
|
expect(customShader.mode).toBe(CustomShaderMode.MODIFY_MATERIAL);
|
expect(customShader.lightingModel).not.toBeDefined();
|
expect(customShader.uniforms).toEqual({});
|
expect(customShader.varyings).toEqual({});
|
expect(customShader.vertexShaderText).not.toBeDefined();
|
expect(customShader.fragmentShaderText).not.toBeDefined();
|
expect(customShader.uniformMap).toEqual({});
|
});
|
|
it("constructs", function () {
|
var customShader = new CustomShader({
|
mode: CustomShaderMode.REPLACE_MATERIAL,
|
lightingModel: LightingModel.PBR,
|
vertexShaderText: emptyVertexShader,
|
fragmentShaderText: emptyFragmentShader,
|
});
|
|
expect(customShader.mode).toBe(CustomShaderMode.REPLACE_MATERIAL);
|
expect(customShader.lightingModel).toBe(LightingModel.PBR);
|
expect(customShader.uniforms).toEqual({});
|
expect(customShader.varyings).toEqual({});
|
expect(customShader.vertexShaderText).toBe(emptyVertexShader);
|
expect(customShader.fragmentShaderText).toBe(emptyFragmentShader);
|
expect(customShader.uniformMap).toEqual({});
|
});
|
|
it("defines uniforms", function () {
|
var uniforms = {
|
u_time: {
|
value: 0,
|
type: UniformType.FLOAT,
|
},
|
u_offset: {
|
value: new Cartesian2(1, 2),
|
type: UniformType.VEC2,
|
},
|
};
|
|
var customShader = new CustomShader({
|
uniforms: uniforms,
|
});
|
|
expect(customShader.uniforms).toBe(uniforms);
|
expect(customShader.uniformMap.u_time()).toBe(uniforms.u_time.value);
|
expect(customShader.uniformMap.u_offset()).toBe(uniforms.u_offset.value);
|
});
|
|
it("setUniform throws for undefined uniformName", function () {
|
var customShader = new CustomShader();
|
expect(function () {
|
return customShader.setUniform(undefined, 45);
|
}).toThrowDeveloperError();
|
});
|
|
it("setUniform throws for undefined value", function () {
|
var customShader = new CustomShader();
|
expect(function () {
|
return customShader.setUniform("u_time", undefined);
|
}).toThrowDeveloperError();
|
});
|
|
it("setUniform throws for undeclared uniform", function () {
|
var customShader = new CustomShader();
|
expect(function () {
|
return customShader.setUniform("u_time", 10);
|
}).toThrowDeveloperError();
|
});
|
|
it("setUniform updates uniform values", function () {
|
var uniforms = {
|
u_time: {
|
value: 0,
|
type: UniformType.FLOAT,
|
},
|
u_offset: {
|
value: new Cartesian2(1, 2),
|
type: UniformType.VEC2,
|
},
|
};
|
|
var customShader = new CustomShader({
|
uniforms: uniforms,
|
});
|
|
expect(customShader.uniformMap.u_time()).toBe(0);
|
customShader.setUniform("u_time", 10);
|
expect(customShader.uniformMap.u_time()).toBe(10);
|
});
|
|
it("setUniform clones vectors", function () {
|
var uniforms = {
|
u_vector: {
|
type: UniformType.VEC3,
|
value: new Cartesian3(),
|
},
|
};
|
|
var customShader = new CustomShader({
|
uniforms: uniforms,
|
});
|
|
var value = new Cartesian3(1, 0, 0);
|
customShader.setUniform("u_vector", value);
|
var result = customShader.uniformMap.u_vector();
|
expect(result).toEqual(value);
|
expect(result).not.toBe(value);
|
});
|
|
it("setUniform clones matrices", function () {
|
var uniforms = {
|
u_matrix: {
|
type: UniformType.MAT2,
|
value: new Matrix2(),
|
},
|
};
|
|
var customShader = new CustomShader({
|
uniforms: uniforms,
|
});
|
|
var value = new Matrix2(2, 0, 0, 2);
|
customShader.setUniform("u_matrix", value);
|
var result = customShader.uniformMap.u_matrix();
|
expect(result).toEqual(value);
|
expect(result).not.toBe(value);
|
});
|
|
it("declares varyings", function () {
|
var varyings = {
|
v_dist_from_center: VaryingType.FLOAT,
|
v_computedMatrix: VaryingType.MAT4,
|
};
|
|
var customShader = new CustomShader({
|
varyings: varyings,
|
});
|
|
expect(customShader.varyings).toBe(varyings);
|
});
|
|
it("detects input variables in the shader text", function () {
|
var customShader = new CustomShader({
|
vertexShaderText: [
|
"void vertexMain(VertexInput vsInput, inout vec3 positionMC)",
|
"{",
|
" positionMC += vsInput.attributes.expansion * vsInput.attributes.normalMC;",
|
"}",
|
].join("\n"),
|
fragmentShaderText: [
|
"void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)",
|
"{",
|
" material.normalEC = normalize(fsInput.attributes.normalEC);",
|
" material.diffuse = fsInput.attributes.color_0;",
|
" material.specular = fsInput.attributes.positionWC / 1.0e6;",
|
"}",
|
].join("\n"),
|
});
|
|
var expectedVertexVariables = {
|
attributeSet: {
|
expansion: true,
|
normalMC: true,
|
},
|
};
|
var expectedFragmentVariables = {
|
attributeSet: {
|
normalEC: true,
|
color_0: true,
|
positionWC: true,
|
},
|
materialSet: {
|
normalEC: true,
|
diffuse: true,
|
specular: true,
|
},
|
};
|
|
expect(customShader.usedVariablesVertex).toEqual(expectedVertexVariables);
|
expect(customShader.usedVariablesFragment).toEqual(
|
expectedFragmentVariables
|
);
|
});
|
|
describe("variable validation", function () {
|
function makeSingleVariableVS(variableName) {
|
return new CustomShader({
|
vertexShaderText: [
|
"void vertexMain(VertexInput vsInput, inout vec3 positionMC)",
|
"{",
|
" positionMC = vsInput.attributes." + variableName + ";",
|
"}",
|
].join("\n"),
|
});
|
}
|
|
function makeSingleVariableFS(variableName) {
|
return new CustomShader({
|
fragmentShaderText: [
|
"void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)",
|
"{",
|
" material.diffuse = fsInput.attributes." + variableName + ";",
|
"}",
|
].join("\n"),
|
});
|
}
|
|
it("validates position", function () {
|
expect(function () {
|
return makeSingleVariableVS("position");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("positionMC");
|
}).not.toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("positionWC");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("positionEC");
|
}).toThrowDeveloperError();
|
|
expect(function () {
|
return makeSingleVariableFS("position");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("positionMC");
|
}).not.toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("positionWC");
|
}).not.toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("positionEC");
|
}).not.toThrowDeveloperError();
|
});
|
|
it("validates normal", function () {
|
expect(function () {
|
return makeSingleVariableVS("normal");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("normalMC");
|
}).not.toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("normalEC");
|
}).toThrowDeveloperError();
|
|
expect(function () {
|
return makeSingleVariableFS("normal");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("normalMC");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("normalEC");
|
}).not.toThrowDeveloperError();
|
});
|
|
it("validates tangent", function () {
|
expect(function () {
|
return makeSingleVariableVS("tangent");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("tangentMC");
|
}).not.toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("tangentEC");
|
}).toThrowDeveloperError();
|
|
expect(function () {
|
return makeSingleVariableFS("tangent");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("tangentMC");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("tangentEC");
|
}).not.toThrowDeveloperError();
|
});
|
|
it("validates bitangent", function () {
|
expect(function () {
|
return makeSingleVariableVS("bitangent");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("bitangentMC");
|
}).not.toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableVS("bitangentEC");
|
}).toThrowDeveloperError();
|
|
expect(function () {
|
return makeSingleVariableFS("bitangent");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("bitangentMC");
|
}).toThrowDeveloperError();
|
expect(function () {
|
return makeSingleVariableFS("bitangentEC");
|
}).not.toThrowDeveloperError();
|
});
|
});
|
|
// asynchronous code is only needed if texture uniforms are used.
|
describe(
|
"texture uniforms",
|
function () {
|
var scene;
|
|
beforeAll(function () {
|
scene = createScene();
|
});
|
|
afterAll(function () {
|
scene.destroyForSpecs();
|
});
|
|
var shaders = [];
|
afterEach(function () {
|
for (var i = 0; i < shaders.length; i++) {
|
var shader = shaders[i];
|
if (!shader.isDestroyed()) {
|
shader.destroy();
|
}
|
}
|
shaders.length = 0;
|
});
|
|
var blueUrl = "Data/Images/Blue2x2.png";
|
var greenUrl = "Data/Images/Green1x4.png";
|
|
function waitForTextureLoad(customShader, textureId) {
|
var textureManager = customShader._textureManager;
|
var oldValue = textureManager.getTexture(textureId);
|
return pollToPromise(function () {
|
scene.renderForSpecs();
|
customShader.update(scene.frameState);
|
|
// Check that the texture changed. This allows waitForTextureLoad()
|
// to be called multiple times in one promise chain, which is needed
|
// for testing setUniform()
|
return textureManager.getTexture(textureId) !== oldValue;
|
}).then(function () {
|
return textureManager.getTexture(textureId);
|
});
|
}
|
|
it("supports texture uniforms", function () {
|
var customShader = new CustomShader({
|
vertexShaderText: emptyVertexShader,
|
fragmentShaderText: emptyFragmentShader,
|
uniforms: {
|
u_blue: {
|
type: UniformType.SAMPLER_2D,
|
value: new TextureUniform({
|
url: blueUrl,
|
}),
|
},
|
},
|
});
|
shaders.push(customShader);
|
expect(customShader.uniformMap.u_blue).toBeDefined();
|
expect(customShader.uniformMap.u_blue()).not.toBeDefined();
|
|
return waitForTextureLoad(customShader, "u_blue").then(function (
|
texture
|
) {
|
expect(customShader.uniformMap.u_blue()).toBe(texture);
|
expect(texture.width).toBe(2);
|
expect(texture.height).toBe(2);
|
});
|
});
|
|
it("can change texture uniform value", function () {
|
var customShader = new CustomShader({
|
vertexShaderText: emptyVertexShader,
|
fragmentShaderText: emptyFragmentShader,
|
uniforms: {
|
u_testTexture: {
|
type: UniformType.SAMPLER_2D,
|
value: new TextureUniform({
|
url: blueUrl,
|
}),
|
},
|
},
|
});
|
shaders.push(customShader);
|
|
return waitForTextureLoad(customShader, "u_testTexture").then(function (
|
blueTexture
|
) {
|
expect(customShader.uniformMap.u_testTexture()).toBe(blueTexture);
|
expect(blueTexture.width).toBe(2);
|
expect(blueTexture.height).toBe(2);
|
customShader.setUniform(
|
"u_testTexture",
|
new TextureUniform({
|
url: greenUrl,
|
})
|
);
|
return waitForTextureLoad(customShader, "u_testTexture").then(
|
function (greenTexture) {
|
expect(customShader.uniformMap.u_testTexture()).toBe(
|
greenTexture
|
);
|
expect(greenTexture.width).toBe(1);
|
expect(greenTexture.height).toBe(4);
|
}
|
);
|
});
|
});
|
|
it("destroys", function () {
|
var customShader = new CustomShader({
|
vertexShaderText: emptyVertexShader,
|
fragmentShaderText: emptyFragmentShader,
|
uniforms: {
|
u_blue: {
|
type: UniformType.SAMPLER_2D,
|
value: new TextureUniform({
|
url: blueUrl,
|
}),
|
},
|
},
|
});
|
shaders.push(customShader);
|
|
return waitForTextureLoad(customShader, "u_blue").then(function (
|
texture
|
) {
|
expect(customShader.isDestroyed()).toBe(false);
|
expect(texture.isDestroyed()).toBe(false);
|
|
customShader.destroy();
|
|
expect(customShader.isDestroyed()).toBe(true);
|
expect(texture.isDestroyed()).toBe(true);
|
});
|
});
|
},
|
"WebGL"
|
);
|
});
|