import { AlphaMode, AttributeType, CustomShader, CustomShaderPipelineStage, LightingModel, ModelAlphaOptions, ModelLightingOptions, Pass, ShaderBuilder, UniformType, VaryingType, _shadersCustomShaderStageVS, _shadersCustomShaderStageFS, } from "../../../Source/Cesium.js"; import ShaderBuilderTester from "../../ShaderBuilderTester.js"; describe("Scene/ModelExperimental/CustomShaderPipelineStage", function () { var primitive = { attributes: [ { semantic: "POSITION", type: AttributeType.VEC3, }, { semantic: "NORMAL", type: AttributeType.VEC3, }, { semantic: "TEXCOORD", setIndex: 0, type: AttributeType.VEC2, }, ], }; var primitiveWithCustomAttributes = { attributes: [ { semantic: "POSITION", type: AttributeType.VEC3, }, { name: "_TEMPERATURE", type: AttributeType.SCALAR, }, ], }; var emptyVertexShader = "void vertexMain(VertexInput vsInput, inout vec3 positionMC) {}"; var emptyFragmentShader = "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {}"; var emptyShader = new CustomShader({ vertexShaderText: emptyVertexShader, fragmentShaderText: emptyFragmentShader, }); it("sets defines in the shader", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: emptyShader, }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [ "HAS_CUSTOM_VERTEX_SHADER", ]); ShaderBuilderTester.expectHasFragmentDefines(shaderBuilder, [ "HAS_CUSTOM_FRAGMENT_SHADER", "CUSTOM_SHADER_MODIFY_MATERIAL", ]); }); it("adds uniforms from the custom shader", function () { var uniforms = { u_time: { type: UniformType.FLOAT, }, u_enableAnimation: { type: UniformType.BOOL, }, }; var customShader = new CustomShader({ uniforms: uniforms, vertexShaderText: emptyVertexShader, fragmentShaderText: emptyFragmentShader, }); var model = { customShader: customShader, }; var uniformMap = {}; var shaderBuilder = new ShaderBuilder(); var renderResources = { shaderBuilder: shaderBuilder, uniformMap: uniformMap, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasVertexUniforms(shaderBuilder, [ "uniform bool u_enableAnimation;", "uniform float u_time;", ]); ShaderBuilderTester.expectHasFragmentUniforms(shaderBuilder, [ "uniform bool u_enableAnimation;", "uniform float u_time;", ]); expect(renderResources.uniformMap).toEqual(customShader.uniformMap); }); it("adds varying declarations from the custom shader", function () { var varyings = { v_distanceFromCenter: VaryingType.FLOAT, v_computedMatrix: VaryingType.MAT3, }; var customShader = new CustomShader({ vertexShaderText: emptyVertexShader, fragmentShaderText: emptyFragmentShader, varyings: varyings, }); var model = { customShader: customShader, }; var shaderBuilder = new ShaderBuilder(); var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasVaryings(shaderBuilder, [ "varying float v_distanceFromCenter;", "varying mat2 v_computedMatrix;", ]); }); it("overrides the lighting model if specified in the custom shader", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: emptyVertexShader, fragmentShaderText: emptyFragmentShader, lightingModel: LightingModel.PBR, }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); expect(renderResources.lightingOptions.lightingModel).toBe( LightingModel.PBR ); }); it("sets alpha options", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: emptyVertexShader, fragmentShaderText: emptyFragmentShader, }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); expect(renderResources.alphaOptions.pass).not.toBeDefined(); expect(renderResources.alphaOptions.alphaMode).toBe(AlphaMode.OPAQUE); }); it("sets alpha options for translucent custom shader", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: emptyVertexShader, fragmentShaderText: emptyFragmentShader, isTranslucent: true, }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); expect(renderResources.alphaOptions.pass).toBe(Pass.TRANSLUCENT); expect(renderResources.alphaOptions.alphaMode).toBe(AlphaMode.BLEND); }); it("generates shader code from built-in attributes", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: [ "void vertexMain(VertexInput vsInput, inout vec3 positionMC)", "{", " vec3 normalMC = vsInput.attributes.normalMC;", " vec2 texCoord = vsInput.attributes.texCoord_0;", " positionMC = vsInput.attributes.positionMC;", "}", ].join("\n"), fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " vec3 positionMC = fsInput.attributes.positionMC;", " vec3 normalEC = fsInput.attributes.normalEC;", " vec2 texCoord = fsInput.attributes.texCoord_0;", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_VS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionMC;", " vec3 normalMC;", " vec2 texCoord_0;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionMC;", " vec3 normalEC;", " vec2 texCoord_0;"] ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_VERTEX_INPUT, "VertexInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasVertexFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_VS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_VS, [ " vsInput.attributes.positionMC = attributes.positionMC;", " vsInput.attributes.normalMC = attributes.normalMC;", " vsInput.attributes.texCoord_0 = attributes.texCoord_0;", ] ); ShaderBuilderTester.expectHasFragmentFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_FS, [ " fsInput.attributes.positionMC = attributes.positionMC;", " fsInput.attributes.normalEC = attributes.normalEC;", " fsInput.attributes.texCoord_0 = attributes.texCoord_0;", ] ); }); it("generates shader code for custom attributes", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: [ "void vertexMain(VertexInput vsInput, inout vec3 positionMC)", "{", " float temperature = vsInput.attributes.temperature;", " positionMC = vsInput.attributes.positionMC;", "}", ].join("\n"), fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " float temperature = fsInput.attributes.temperature;", " vec3 positionMC = fsInput.attributes.positionMC;", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process( renderResources, primitiveWithCustomAttributes ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_VS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionMC;", " float temperature;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionMC;", " float temperature;"] ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_VERTEX_INPUT, "VertexInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasVertexFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_VS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_VS, [ " vsInput.attributes.positionMC = attributes.positionMC;", " vsInput.attributes.temperature = attributes.temperature;", ] ); ShaderBuilderTester.expectHasFragmentFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_FS, [ " fsInput.attributes.positionMC = attributes.positionMC;", " fsInput.attributes.temperature = attributes.temperature;", ] ); }); it("only generates input lines for attributes that are used", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: [ "void vertexMain(VertexInput vsInput, inout vec3 positionMC)", "{", " positionMC = 2.0 * vsInput.attributes.positionMC - 1.0;", "}", ].join("\n"), fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " float temperature = fsInput.attributes.temperature", " material.diffuse = vec3(temperature / 90.0, 0.0, 0.0);", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process( renderResources, primitiveWithCustomAttributes ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_VS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionMC;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" float temperature;"] ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_VERTEX_INPUT, "VertexInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasVertexFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_VS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_VS, [" vsInput.attributes.positionMC = attributes.positionMC;"] ); ShaderBuilderTester.expectHasFragmentFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_FS, [" fsInput.attributes.temperature = attributes.temperature;"] ); }); it("generates the shader lines in the correct order", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: emptyShader, }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); expect(shaderBuilder._vertexShaderParts.structIds).toEqual([ CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_VS, CustomShaderPipelineStage.STRUCT_ID_VERTEX_INPUT, ]); expect(shaderBuilder._fragmentShaderParts.structIds).toEqual([ CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, ]); ShaderBuilderTester.expectVertexLinesEqual(shaderBuilder, [ "#line 0", emptyVertexShader, _shadersCustomShaderStageVS, ]); ShaderBuilderTester.expectFragmentLinesEqual(shaderBuilder, [ "#line 0", emptyFragmentShader, _shadersCustomShaderStageFS, ]); }); it("does not add positions in other coordinate systems if not needed", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: emptyShader, }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); }); it("configures positions in other coordinate systems when present in the shader", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: emptyVertexShader, fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " material.diffuse = fsInput.attributes.positionMC;", " material.specular = fsInput.attributes.positionWC;", " material.normal = fsInput.attributes.positionEC;", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); expect(shaderBuilder._vertexShaderParts.defineLines).toEqual([ "COMPUTE_POSITION_WC", "HAS_CUSTOM_VERTEX_SHADER", ]); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_VS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionMC;", " vec3 positionWC;", " vec3 positionEC;"] ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_VERTEX_INPUT, "VertexInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasVertexFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_VS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_VS, [] ); ShaderBuilderTester.expectHasFragmentFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_FS, [ " fsInput.attributes.positionMC = attributes.positionMC;", " fsInput.attributes.positionWC = attributes.positionWC;", " fsInput.attributes.positionEC = attributes.positionEC;", ] ); }); it("infers default values for built-in attributes", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: [ "void vertexMain(VertexInput vsInput, inout vec3 positionMC)", "{", " vec2 texCoords = vsInput.attributes.texCoord_1;", "}", ].join("\n"), fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " material.diffuse = vec3(fsInput.attributes.tangentEC);", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_VS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec2 texCoord_1;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 tangentEC;"] ); ShaderBuilderTester.expectHasVertexStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_VERTEX_INPUT, "VertexInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); ShaderBuilderTester.expectHasVertexFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_VS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_VS, [" vsInput.attributes.texCoord_1 = vec2(0.0);"] ); ShaderBuilderTester.expectHasFragmentFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_FS, [" fsInput.attributes.tangentEC = vec3(1.0, 0.0, 0.0);"] ); }); it("handles incompatible primitives gracefully", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: [ "void vertexMain(VertexInput vsInput, inout vec3 positionMC)", "{", " vec3 texCoords = vsInput.attributes.notAnAttribute;", "}", ].join("\n"), fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " material.diffuse *= fsInput.attributes.alsoNotAnAttribute;", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; spyOn(CustomShaderPipelineStage, "_oneTimeWarning"); CustomShaderPipelineStage.process(renderResources, primitive); // once for the vertex shader, once for the fragment shader expect(CustomShaderPipelineStage._oneTimeWarning.calls.count()).toBe(2); expect(shaderBuilder._vertexShaderParts.defineLines).toEqual([]); expect(shaderBuilder._fragmentShaderParts.defineLines).toEqual([]); }); it("disables vertex shader if vertexShaderText is not provided", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ fragmentShaderText: emptyFragmentShader, }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), uniformMap: {}, alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); expect(shaderBuilder._vertexShaderParts.defineLines).toEqual([]); expect(shaderBuilder._fragmentShaderParts.defineLines).toEqual([ "HAS_CUSTOM_FRAGMENT_SHADER", "CUSTOM_SHADER_MODIFY_MATERIAL", ]); expect(shaderBuilder._vertexShaderParts.shaderLines).toEqual([]); var fragmentShaderIndex = shaderBuilder._fragmentShaderParts.shaderLines.indexOf( emptyFragmentShader ); expect(fragmentShaderIndex).not.toBe(-1); }); it("disables fragment shader if fragmentShaderText is not provided", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ vertexShaderText: emptyVertexShader, }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), uniformMap: {}, }; CustomShaderPipelineStage.process(renderResources, primitive); expect(shaderBuilder._vertexShaderParts.defineLines).toEqual([ "HAS_CUSTOM_VERTEX_SHADER", ]); expect(shaderBuilder._fragmentShaderParts.defineLines).toEqual([]); var vertexShaderIndex = shaderBuilder._vertexShaderParts.shaderLines.indexOf( emptyVertexShader ); expect(vertexShaderIndex).not.toBe(-1); expect(shaderBuilder._fragmentShaderParts.shaderLines).toEqual([]); }); it("disables custom shader if neither fragmentShaderText nor vertexShaderText are provided", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader(), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), uniformMap: {}, }; CustomShaderPipelineStage.process(renderResources, primitive); // Essentially the shader stage is skipped, so nothing should be updated expect(shaderBuilder).toEqual(new ShaderBuilder()); expect(renderResources.uniformMap).toEqual({}); expect(renderResources.lightingOptions).toEqual(new ModelLightingOptions()); }); it("handles fragment-only custom shader that computes positionWC", function () { var shaderBuilder = new ShaderBuilder(); var model = { customShader: new CustomShader({ fragmentShaderText: [ "void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)", "{", " material.diffuse = fsInput.attributes.positionWC;", "}", ].join("\n"), }), }; var renderResources = { shaderBuilder: shaderBuilder, model: model, lightingOptions: new ModelLightingOptions(), alphaOptions: new ModelAlphaOptions(), }; CustomShaderPipelineStage.process(renderResources, primitive); ShaderBuilderTester.expectHasVertexDefines(shaderBuilder, [ "COMPUTE_POSITION_WC", ]); expect(shaderBuilder._vertexShaderParts.structIds).toEqual([]); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_ATTRIBUTES_FS, CustomShaderPipelineStage.STRUCT_NAME_ATTRIBUTES, [" vec3 positionWC;"] ); ShaderBuilderTester.expectHasFragmentStruct( shaderBuilder, CustomShaderPipelineStage.STRUCT_ID_FRAGMENT_INPUT, "FragmentInput", [" Attributes attributes;"] ); expect(shaderBuilder._vertexShaderParts.functionIds).toEqual([]); ShaderBuilderTester.expectHasFragmentFunction( shaderBuilder, CustomShaderPipelineStage.FUNCTION_ID_INITIALIZE_INPUT_STRUCT_FS, CustomShaderPipelineStage.FUNCTION_SIGNATURE_INITIALIZE_INPUT_STRUCT_FS, [" fsInput.attributes.positionWC = attributes.positionWC;"] ); }); });