processModelMaterialsCommon.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. import defaultValue from "../Core/defaultValue.js";
  2. import defined from "../Core/defined.js";
  3. import WebGLConstants from "../Core/WebGLConstants.js";
  4. import webGLConstantToGlslType from "../Core/webGLConstantToGlslType.js";
  5. import addToArray from "./GltfPipeline/addToArray.js";
  6. import ForEach from "./GltfPipeline/ForEach.js";
  7. import usesExtension from "./GltfPipeline/usesExtension.js";
  8. import ModelUtility from "./ModelUtility.js";
  9. /**
  10. * @private
  11. */
  12. function processModelMaterialsCommon(gltf, options) {
  13. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  14. if (!defined(gltf)) {
  15. return;
  16. }
  17. if (!usesExtension(gltf, "KHR_materials_common")) {
  18. return;
  19. }
  20. if (!usesExtension(gltf, "KHR_techniques_webgl")) {
  21. if (!defined(gltf.extensions)) {
  22. gltf.extensions = {};
  23. }
  24. gltf.extensions.KHR_techniques_webgl = {
  25. programs: [],
  26. shaders: [],
  27. techniques: [],
  28. };
  29. gltf.extensionsUsed.push("KHR_techniques_webgl");
  30. gltf.extensionsRequired.push("KHR_techniques_webgl");
  31. }
  32. const techniquesWebgl = gltf.extensions.KHR_techniques_webgl;
  33. lightDefaults(gltf);
  34. const lightParameters = generateLightParameters(gltf);
  35. const primitiveByMaterial = ModelUtility.splitIncompatibleMaterials(gltf);
  36. const techniques = {};
  37. let generatedTechniques = false;
  38. ForEach.material(gltf, function (material, materialIndex) {
  39. if (
  40. defined(material.extensions) &&
  41. defined(material.extensions.KHR_materials_common)
  42. ) {
  43. const khrMaterialsCommon = material.extensions.KHR_materials_common;
  44. const primitiveInfo = primitiveByMaterial[materialIndex];
  45. const techniqueKey = getTechniqueKey(khrMaterialsCommon, primitiveInfo);
  46. let technique = techniques[techniqueKey];
  47. if (!defined(technique)) {
  48. technique = generateTechnique(
  49. gltf,
  50. techniquesWebgl,
  51. primitiveInfo,
  52. khrMaterialsCommon,
  53. lightParameters,
  54. options.addBatchIdToGeneratedShaders
  55. );
  56. techniques[techniqueKey] = technique;
  57. generatedTechniques = true;
  58. }
  59. const materialValues = {};
  60. const values = khrMaterialsCommon.values;
  61. let uniformName;
  62. for (const valueName in values) {
  63. if (
  64. values.hasOwnProperty(valueName) &&
  65. valueName !== "transparent" &&
  66. valueName !== "doubleSided"
  67. ) {
  68. uniformName = `u_${valueName.toLowerCase()}`;
  69. materialValues[uniformName] = values[valueName];
  70. }
  71. }
  72. material.extensions.KHR_techniques_webgl = {
  73. technique: technique,
  74. values: materialValues,
  75. };
  76. material.alphaMode = "OPAQUE";
  77. if (khrMaterialsCommon.transparent) {
  78. material.alphaMode = "BLEND";
  79. }
  80. if (khrMaterialsCommon.doubleSided) {
  81. material.doubleSided = true;
  82. }
  83. }
  84. });
  85. if (!generatedTechniques) {
  86. return gltf;
  87. }
  88. // If any primitives have semantics that aren't declared in the generated
  89. // shaders, we want to preserve them.
  90. ModelUtility.ensureSemanticExistence(gltf);
  91. return gltf;
  92. }
  93. function generateLightParameters(gltf) {
  94. const result = {};
  95. let lights;
  96. if (
  97. defined(gltf.extensions) &&
  98. defined(gltf.extensions.KHR_materials_common)
  99. ) {
  100. lights = gltf.extensions.KHR_materials_common.lights;
  101. }
  102. if (defined(lights)) {
  103. // Figure out which node references the light
  104. const nodes = gltf.nodes;
  105. for (const nodeName in nodes) {
  106. if (nodes.hasOwnProperty(nodeName)) {
  107. const node = nodes[nodeName];
  108. if (
  109. defined(node.extensions) &&
  110. defined(node.extensions.KHR_materials_common)
  111. ) {
  112. const nodeLightId = node.extensions.KHR_materials_common.light;
  113. if (defined(nodeLightId) && defined(lights[nodeLightId])) {
  114. lights[nodeLightId].node = nodeName;
  115. }
  116. delete node.extensions.KHR_materials_common;
  117. }
  118. }
  119. }
  120. // Add light parameters to result
  121. let lightCount = 0;
  122. for (const lightName in lights) {
  123. if (lights.hasOwnProperty(lightName)) {
  124. const light = lights[lightName];
  125. const lightType = light.type;
  126. if (lightType !== "ambient" && !defined(light.node)) {
  127. delete lights[lightName];
  128. continue;
  129. }
  130. const lightBaseName = `light${lightCount.toString()}`;
  131. light.baseName = lightBaseName;
  132. let ambient;
  133. let directional;
  134. let point;
  135. let spot;
  136. switch (lightType) {
  137. case "ambient":
  138. ambient = light.ambient;
  139. result[`${lightBaseName}Color`] = {
  140. type: WebGLConstants.FLOAT_VEC3,
  141. value: ambient.color,
  142. };
  143. break;
  144. case "directional":
  145. directional = light.directional;
  146. result[`${lightBaseName}Color`] = {
  147. type: WebGLConstants.FLOAT_VEC3,
  148. value: directional.color,
  149. };
  150. if (defined(light.node)) {
  151. result[`${lightBaseName}Transform`] = {
  152. node: light.node,
  153. semantic: "MODELVIEW",
  154. type: WebGLConstants.FLOAT_MAT4,
  155. };
  156. }
  157. break;
  158. case "point":
  159. point = light.point;
  160. result[`${lightBaseName}Color`] = {
  161. type: WebGLConstants.FLOAT_VEC3,
  162. value: point.color,
  163. };
  164. if (defined(light.node)) {
  165. result[`${lightBaseName}Transform`] = {
  166. node: light.node,
  167. semantic: "MODELVIEW",
  168. type: WebGLConstants.FLOAT_MAT4,
  169. };
  170. }
  171. result[`${lightBaseName}Attenuation`] = {
  172. type: WebGLConstants.FLOAT_VEC3,
  173. value: [
  174. point.constantAttenuation,
  175. point.linearAttenuation,
  176. point.quadraticAttenuation,
  177. ],
  178. };
  179. break;
  180. case "spot":
  181. spot = light.spot;
  182. result[`${lightBaseName}Color`] = {
  183. type: WebGLConstants.FLOAT_VEC3,
  184. value: spot.color,
  185. };
  186. if (defined(light.node)) {
  187. result[`${lightBaseName}Transform`] = {
  188. node: light.node,
  189. semantic: "MODELVIEW",
  190. type: WebGLConstants.FLOAT_MAT4,
  191. };
  192. result[`${lightBaseName}InverseTransform`] = {
  193. node: light.node,
  194. semantic: "MODELVIEWINVERSE",
  195. type: WebGLConstants.FLOAT_MAT4,
  196. useInFragment: true,
  197. };
  198. }
  199. result[`${lightBaseName}Attenuation`] = {
  200. type: WebGLConstants.FLOAT_VEC3,
  201. value: [
  202. spot.constantAttenuation,
  203. spot.linearAttenuation,
  204. spot.quadraticAttenuation,
  205. ],
  206. };
  207. result[`${lightBaseName}FallOff`] = {
  208. type: WebGLConstants.FLOAT_VEC2,
  209. value: [spot.fallOffAngle, spot.fallOffExponent],
  210. };
  211. break;
  212. }
  213. ++lightCount;
  214. }
  215. }
  216. }
  217. return result;
  218. }
  219. function generateTechnique(
  220. gltf,
  221. techniquesWebgl,
  222. primitiveInfo,
  223. khrMaterialsCommon,
  224. lightParameters,
  225. addBatchIdToGeneratedShaders
  226. ) {
  227. if (!defined(khrMaterialsCommon)) {
  228. khrMaterialsCommon = {};
  229. }
  230. addBatchIdToGeneratedShaders = defaultValue(
  231. addBatchIdToGeneratedShaders,
  232. false
  233. );
  234. const techniques = techniquesWebgl.techniques;
  235. const shaders = techniquesWebgl.shaders;
  236. const programs = techniquesWebgl.programs;
  237. const lightingModel = khrMaterialsCommon.technique.toUpperCase();
  238. let lights;
  239. if (
  240. defined(gltf.extensions) &&
  241. defined(gltf.extensions.KHR_materials_common)
  242. ) {
  243. lights = gltf.extensions.KHR_materials_common.lights;
  244. }
  245. const parameterValues = khrMaterialsCommon.values;
  246. const jointCount = defaultValue(khrMaterialsCommon.jointCount, 0);
  247. let skinningInfo;
  248. let hasSkinning = false;
  249. let hasVertexColors = false;
  250. if (defined(primitiveInfo)) {
  251. skinningInfo = primitiveInfo.skinning;
  252. hasSkinning = skinningInfo.skinned;
  253. hasVertexColors = primitiveInfo.hasVertexColors;
  254. }
  255. let vertexShader = "precision highp float;\n";
  256. let fragmentShader = "precision highp float;\n";
  257. const hasNormals = lightingModel !== "CONSTANT";
  258. // Add techniques
  259. const techniqueUniforms = {
  260. u_modelViewMatrix: {
  261. semantic: usesExtension(gltf, "CESIUM_RTC")
  262. ? "CESIUM_RTC_MODELVIEW"
  263. : "MODELVIEW",
  264. type: WebGLConstants.FLOAT_MAT4,
  265. },
  266. u_projectionMatrix: {
  267. semantic: "PROJECTION",
  268. type: WebGLConstants.FLOAT_MAT4,
  269. },
  270. };
  271. if (hasNormals) {
  272. techniqueUniforms.u_normalMatrix = {
  273. semantic: "MODELVIEWINVERSETRANSPOSE",
  274. type: WebGLConstants.FLOAT_MAT3,
  275. };
  276. }
  277. if (hasSkinning) {
  278. techniqueUniforms.u_jointMatrix = {
  279. count: jointCount,
  280. semantic: "JOINTMATRIX",
  281. type: WebGLConstants.FLOAT_MAT4,
  282. };
  283. }
  284. // Add material values
  285. let uniformName;
  286. let hasTexCoords = false;
  287. for (const name in parameterValues) {
  288. //generate shader parameters for KHR_materials_common attributes
  289. //(including a check, because some boolean flags should not be used as shader parameters)
  290. if (
  291. parameterValues.hasOwnProperty(name) &&
  292. name !== "transparent" &&
  293. name !== "doubleSided"
  294. ) {
  295. const uniformType = getKHRMaterialsCommonValueType(
  296. name,
  297. parameterValues[name]
  298. );
  299. uniformName = `u_${name.toLowerCase()}`;
  300. if (!hasTexCoords && uniformType === WebGLConstants.SAMPLER_2D) {
  301. hasTexCoords = true;
  302. }
  303. techniqueUniforms[uniformName] = {
  304. type: uniformType,
  305. };
  306. }
  307. }
  308. // Give the diffuse uniform a semantic to support color replacement in 3D Tiles
  309. if (defined(techniqueUniforms.u_diffuse)) {
  310. techniqueUniforms.u_diffuse.semantic = "_3DTILESDIFFUSE";
  311. }
  312. // Copy light parameters into technique parameters
  313. if (defined(lightParameters)) {
  314. for (const lightParamName in lightParameters) {
  315. if (lightParameters.hasOwnProperty(lightParamName)) {
  316. uniformName = `u_${lightParamName}`;
  317. techniqueUniforms[uniformName] = lightParameters[lightParamName];
  318. }
  319. }
  320. }
  321. // Add uniforms to shaders
  322. for (uniformName in techniqueUniforms) {
  323. if (techniqueUniforms.hasOwnProperty(uniformName)) {
  324. const uniform = techniqueUniforms[uniformName];
  325. const arraySize = defined(uniform.count) ? `[${uniform.count}]` : "";
  326. if (
  327. (uniform.type !== WebGLConstants.FLOAT_MAT3 &&
  328. uniform.type !== WebGLConstants.FLOAT_MAT4) ||
  329. uniform.useInFragment
  330. ) {
  331. fragmentShader += `uniform ${webGLConstantToGlslType(
  332. uniform.type
  333. )} ${uniformName}${arraySize};\n`;
  334. delete uniform.useInFragment;
  335. } else {
  336. vertexShader += `uniform ${webGLConstantToGlslType(
  337. uniform.type
  338. )} ${uniformName}${arraySize};\n`;
  339. }
  340. }
  341. }
  342. // Add attributes with semantics
  343. let vertexShaderMain = "";
  344. if (hasSkinning) {
  345. vertexShaderMain +=
  346. " mat4 skinMatrix =\n" +
  347. " a_weight.x * u_jointMatrix[int(a_joint.x)] +\n" +
  348. " a_weight.y * u_jointMatrix[int(a_joint.y)] +\n" +
  349. " a_weight.z * u_jointMatrix[int(a_joint.z)] +\n" +
  350. " a_weight.w * u_jointMatrix[int(a_joint.w)];\n";
  351. }
  352. // Add position always
  353. const techniqueAttributes = {
  354. a_position: {
  355. semantic: "POSITION",
  356. },
  357. };
  358. vertexShader += "attribute vec3 a_position;\n";
  359. vertexShader += "varying vec3 v_positionEC;\n";
  360. if (hasSkinning) {
  361. vertexShaderMain +=
  362. " vec4 pos = u_modelViewMatrix * skinMatrix * vec4(a_position,1.0);\n";
  363. } else {
  364. vertexShaderMain +=
  365. " vec4 pos = u_modelViewMatrix * vec4(a_position,1.0);\n";
  366. }
  367. vertexShaderMain += " v_positionEC = pos.xyz;\n";
  368. vertexShaderMain += " gl_Position = u_projectionMatrix * pos;\n";
  369. fragmentShader += "varying vec3 v_positionEC;\n";
  370. // Add normal if we don't have constant lighting
  371. if (hasNormals) {
  372. techniqueAttributes.a_normal = {
  373. semantic: "NORMAL",
  374. };
  375. vertexShader += "attribute vec3 a_normal;\n";
  376. vertexShader += "varying vec3 v_normal;\n";
  377. if (hasSkinning) {
  378. vertexShaderMain +=
  379. " v_normal = u_normalMatrix * mat3(skinMatrix) * a_normal;\n";
  380. } else {
  381. vertexShaderMain += " v_normal = u_normalMatrix * a_normal;\n";
  382. }
  383. fragmentShader += "varying vec3 v_normal;\n";
  384. }
  385. // Add texture coordinates if the material uses them
  386. let v_texcoord;
  387. if (hasTexCoords) {
  388. techniqueAttributes.a_texcoord_0 = {
  389. semantic: "TEXCOORD_0",
  390. };
  391. v_texcoord = "v_texcoord_0";
  392. vertexShader += "attribute vec2 a_texcoord_0;\n";
  393. vertexShader += `varying vec2 ${v_texcoord};\n`;
  394. vertexShaderMain += ` ${v_texcoord} = a_texcoord_0;\n`;
  395. fragmentShader += `varying vec2 ${v_texcoord};\n`;
  396. }
  397. if (hasSkinning) {
  398. techniqueAttributes.a_joint = {
  399. semantic: "JOINTS_0",
  400. };
  401. techniqueAttributes.a_weight = {
  402. semantic: "WEIGHTS_0",
  403. };
  404. vertexShader += "attribute vec4 a_joint;\n";
  405. vertexShader += "attribute vec4 a_weight;\n";
  406. }
  407. if (hasVertexColors) {
  408. techniqueAttributes.a_vertexColor = {
  409. semantic: "COLOR_0",
  410. };
  411. vertexShader += "attribute vec4 a_vertexColor;\n";
  412. vertexShader += "varying vec4 v_vertexColor;\n";
  413. vertexShaderMain += " v_vertexColor = a_vertexColor;\n";
  414. fragmentShader += "varying vec4 v_vertexColor;\n";
  415. }
  416. if (addBatchIdToGeneratedShaders) {
  417. techniqueAttributes.a_batchId = {
  418. semantic: "_BATCHID",
  419. };
  420. vertexShader += "attribute float a_batchId;\n";
  421. }
  422. const hasSpecular =
  423. hasNormals &&
  424. (lightingModel === "BLINN" || lightingModel === "PHONG") &&
  425. defined(techniqueUniforms.u_specular) &&
  426. defined(techniqueUniforms.u_shininess) &&
  427. techniqueUniforms.u_shininess > 0.0;
  428. // Generate lighting code blocks
  429. let hasNonAmbientLights = false;
  430. let hasAmbientLights = false;
  431. let fragmentLightingBlock = "";
  432. for (const lightName in lights) {
  433. if (lights.hasOwnProperty(lightName)) {
  434. const light = lights[lightName];
  435. const lightType = light.type.toLowerCase();
  436. const lightBaseName = light.baseName;
  437. fragmentLightingBlock += " {\n";
  438. const lightColorName = `u_${lightBaseName}Color`;
  439. if (lightType === "ambient") {
  440. hasAmbientLights = true;
  441. fragmentLightingBlock += ` ambientLight += ${lightColorName};\n`;
  442. } else if (hasNormals) {
  443. hasNonAmbientLights = true;
  444. const varyingDirectionName = `v_${lightBaseName}Direction`;
  445. const varyingPositionName = `v_${lightBaseName}Position`;
  446. if (lightType !== "point") {
  447. vertexShader += `varying vec3 ${varyingDirectionName};\n`;
  448. fragmentShader += `varying vec3 ${varyingDirectionName};\n`;
  449. vertexShaderMain += ` ${varyingDirectionName} = mat3(u_${lightBaseName}Transform) * vec3(0.,0.,1.);\n`;
  450. if (lightType === "directional") {
  451. fragmentLightingBlock += ` vec3 l = normalize(${varyingDirectionName});\n`;
  452. }
  453. }
  454. if (lightType !== "directional") {
  455. vertexShader += `varying vec3 ${varyingPositionName};\n`;
  456. fragmentShader += `varying vec3 ${varyingPositionName};\n`;
  457. vertexShaderMain += ` ${varyingPositionName} = u_${lightBaseName}Transform[3].xyz;\n`;
  458. fragmentLightingBlock += ` vec3 VP = ${varyingPositionName} - v_positionEC;\n`;
  459. fragmentLightingBlock += " vec3 l = normalize(VP);\n";
  460. fragmentLightingBlock += " float range = length(VP);\n";
  461. fragmentLightingBlock += ` float attenuation = 1.0 / (u_${lightBaseName}Attenuation.x + `;
  462. fragmentLightingBlock += `(u_${lightBaseName}Attenuation.y * range) + `;
  463. fragmentLightingBlock += `(u_${lightBaseName}Attenuation.z * range * range));\n`;
  464. } else {
  465. fragmentLightingBlock += " float attenuation = 1.0;\n";
  466. }
  467. if (lightType === "spot") {
  468. fragmentLightingBlock += ` float spotDot = dot(l, normalize(${varyingDirectionName}));\n`;
  469. fragmentLightingBlock += ` if (spotDot < cos(u_${lightBaseName}FallOff.x * 0.5))\n`;
  470. fragmentLightingBlock += " {\n";
  471. fragmentLightingBlock += " attenuation = 0.0;\n";
  472. fragmentLightingBlock += " }\n";
  473. fragmentLightingBlock += " else\n";
  474. fragmentLightingBlock += " {\n";
  475. fragmentLightingBlock += ` attenuation *= max(0.0, pow(spotDot, u_${lightBaseName}FallOff.y));\n`;
  476. fragmentLightingBlock += " }\n";
  477. }
  478. fragmentLightingBlock += ` diffuseLight += ${lightColorName}* max(dot(normal,l), 0.) * attenuation;\n`;
  479. if (hasSpecular) {
  480. if (lightingModel === "BLINN") {
  481. fragmentLightingBlock += " vec3 h = normalize(l + viewDir);\n";
  482. fragmentLightingBlock +=
  483. " float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess)) * attenuation;\n";
  484. } else {
  485. // PHONG
  486. fragmentLightingBlock +=
  487. " vec3 reflectDir = reflect(-l, normal);\n";
  488. fragmentLightingBlock +=
  489. " float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess)) * attenuation;\n";
  490. }
  491. fragmentLightingBlock += ` specularLight += ${lightColorName} * specularIntensity;\n`;
  492. }
  493. }
  494. fragmentLightingBlock += " }\n";
  495. }
  496. }
  497. if (!hasAmbientLights) {
  498. // Add an ambient light if we don't have one
  499. fragmentLightingBlock += " ambientLight += vec3(0.2, 0.2, 0.2);\n";
  500. }
  501. if (!hasNonAmbientLights && lightingModel !== "CONSTANT") {
  502. fragmentShader += "#ifdef USE_CUSTOM_LIGHT_COLOR \n";
  503. fragmentShader += "uniform vec3 gltf_lightColor; \n";
  504. fragmentShader += "#endif \n";
  505. fragmentLightingBlock += "#ifndef USE_CUSTOM_LIGHT_COLOR \n";
  506. fragmentLightingBlock += " vec3 lightColor = czm_lightColor;\n";
  507. fragmentLightingBlock += "#else \n";
  508. fragmentLightingBlock += " vec3 lightColor = gltf_lightColor;\n";
  509. fragmentLightingBlock += "#endif \n";
  510. fragmentLightingBlock += " vec3 l = normalize(czm_lightDirectionEC);\n";
  511. const minimumLighting = "0.2"; // Use strings instead of values as 0.0 -> 0 when stringified
  512. fragmentLightingBlock += ` diffuseLight += lightColor * max(dot(normal,l), ${minimumLighting});\n`;
  513. if (hasSpecular) {
  514. if (lightingModel === "BLINN") {
  515. fragmentLightingBlock += " vec3 h = normalize(l + viewDir);\n";
  516. fragmentLightingBlock +=
  517. " float specularIntensity = max(0., pow(max(dot(normal, h), 0.), u_shininess));\n";
  518. } else {
  519. // PHONG
  520. fragmentLightingBlock += " vec3 reflectDir = reflect(-l, normal);\n";
  521. fragmentLightingBlock +=
  522. " float specularIntensity = max(0., pow(max(dot(reflectDir, viewDir), 0.), u_shininess));\n";
  523. }
  524. fragmentLightingBlock +=
  525. " specularLight += lightColor * specularIntensity;\n";
  526. }
  527. }
  528. vertexShader += "void main(void) {\n";
  529. vertexShader += vertexShaderMain;
  530. vertexShader += "}\n";
  531. fragmentShader += "void main(void) {\n";
  532. let colorCreationBlock = " vec3 color = vec3(0.0, 0.0, 0.0);\n";
  533. if (hasNormals) {
  534. fragmentShader += " vec3 normal = normalize(v_normal);\n";
  535. if (khrMaterialsCommon.doubleSided) {
  536. fragmentShader += " if (czm_backFacing())\n";
  537. fragmentShader += " {\n";
  538. fragmentShader += " normal = -normal;\n";
  539. fragmentShader += " }\n";
  540. }
  541. }
  542. let finalColorComputation;
  543. if (lightingModel !== "CONSTANT") {
  544. if (defined(techniqueUniforms.u_diffuse)) {
  545. if (techniqueUniforms.u_diffuse.type === WebGLConstants.SAMPLER_2D) {
  546. fragmentShader += ` vec4 diffuse = texture2D(u_diffuse, ${v_texcoord});\n`;
  547. } else {
  548. fragmentShader += " vec4 diffuse = u_diffuse;\n";
  549. }
  550. fragmentShader += " vec3 diffuseLight = vec3(0.0, 0.0, 0.0);\n";
  551. colorCreationBlock += " color += diffuse.rgb * diffuseLight;\n";
  552. }
  553. if (hasSpecular) {
  554. if (techniqueUniforms.u_specular.type === WebGLConstants.SAMPLER_2D) {
  555. fragmentShader += ` vec3 specular = texture2D(u_specular, ${v_texcoord}).rgb;\n`;
  556. } else {
  557. fragmentShader += " vec3 specular = u_specular.rgb;\n";
  558. }
  559. fragmentShader += " vec3 specularLight = vec3(0.0, 0.0, 0.0);\n";
  560. colorCreationBlock += " color += specular * specularLight;\n";
  561. }
  562. if (defined(techniqueUniforms.u_transparency)) {
  563. finalColorComputation =
  564. " gl_FragColor = vec4(color * diffuse.a * u_transparency, diffuse.a * u_transparency);\n";
  565. } else {
  566. finalColorComputation =
  567. " gl_FragColor = vec4(color * diffuse.a, diffuse.a);\n";
  568. }
  569. } else if (defined(techniqueUniforms.u_transparency)) {
  570. finalColorComputation =
  571. " gl_FragColor = vec4(color * u_transparency, u_transparency);\n";
  572. } else {
  573. finalColorComputation = " gl_FragColor = vec4(color, 1.0);\n";
  574. }
  575. if (hasVertexColors) {
  576. colorCreationBlock += " color *= v_vertexColor.rgb;\n";
  577. }
  578. if (defined(techniqueUniforms.u_emission)) {
  579. if (techniqueUniforms.u_emission.type === WebGLConstants.SAMPLER_2D) {
  580. fragmentShader += ` vec3 emission = texture2D(u_emission, ${v_texcoord}).rgb;\n`;
  581. } else {
  582. fragmentShader += " vec3 emission = u_emission.rgb;\n";
  583. }
  584. colorCreationBlock += " color += emission;\n";
  585. }
  586. if (defined(techniqueUniforms.u_ambient) || lightingModel !== "CONSTANT") {
  587. if (defined(techniqueUniforms.u_ambient)) {
  588. if (techniqueUniforms.u_ambient.type === WebGLConstants.SAMPLER_2D) {
  589. fragmentShader += ` vec3 ambient = texture2D(u_ambient, ${v_texcoord}).rgb;\n`;
  590. } else {
  591. fragmentShader += " vec3 ambient = u_ambient.rgb;\n";
  592. }
  593. } else {
  594. fragmentShader += " vec3 ambient = diffuse.rgb;\n";
  595. }
  596. colorCreationBlock += " color += ambient * ambientLight;\n";
  597. }
  598. fragmentShader += " vec3 viewDir = -normalize(v_positionEC);\n";
  599. fragmentShader += " vec3 ambientLight = vec3(0.0, 0.0, 0.0);\n";
  600. // Add in light computations
  601. fragmentShader += fragmentLightingBlock;
  602. fragmentShader += colorCreationBlock;
  603. fragmentShader += finalColorComputation;
  604. fragmentShader += "}\n";
  605. // Add shaders
  606. const vertexShaderId = addToArray(shaders, {
  607. type: WebGLConstants.VERTEX_SHADER,
  608. extras: {
  609. _pipeline: {
  610. source: vertexShader,
  611. extension: ".glsl",
  612. },
  613. },
  614. });
  615. const fragmentShaderId = addToArray(shaders, {
  616. type: WebGLConstants.FRAGMENT_SHADER,
  617. extras: {
  618. _pipeline: {
  619. source: fragmentShader,
  620. extension: ".glsl",
  621. },
  622. },
  623. });
  624. // Add program
  625. const programId = addToArray(programs, {
  626. fragmentShader: fragmentShaderId,
  627. vertexShader: vertexShaderId,
  628. });
  629. const techniqueId = addToArray(techniques, {
  630. attributes: techniqueAttributes,
  631. program: programId,
  632. uniforms: techniqueUniforms,
  633. });
  634. return techniqueId;
  635. }
  636. function getKHRMaterialsCommonValueType(paramName, paramValue) {
  637. let value;
  638. // Backwards compatibility for COLLADA2GLTF v1.0-draft when it encoding
  639. // materials using KHR_materials_common with explicit type/value members
  640. if (defined(paramValue.value)) {
  641. value = paramValue.value;
  642. } else if (defined(paramValue.index)) {
  643. value = [paramValue.index];
  644. } else {
  645. value = paramValue;
  646. }
  647. switch (paramName) {
  648. case "ambient":
  649. return value.length === 1
  650. ? WebGLConstants.SAMPLER_2D
  651. : WebGLConstants.FLOAT_VEC4;
  652. case "diffuse":
  653. return value.length === 1
  654. ? WebGLConstants.SAMPLER_2D
  655. : WebGLConstants.FLOAT_VEC4;
  656. case "emission":
  657. return value.length === 1
  658. ? WebGLConstants.SAMPLER_2D
  659. : WebGLConstants.FLOAT_VEC4;
  660. case "specular":
  661. return value.length === 1
  662. ? WebGLConstants.SAMPLER_2D
  663. : WebGLConstants.FLOAT_VEC4;
  664. case "shininess":
  665. return WebGLConstants.FLOAT;
  666. case "transparency":
  667. return WebGLConstants.FLOAT;
  668. // these two are usually not used directly within shaders,
  669. // they are just added here for completeness
  670. case "transparent":
  671. return WebGLConstants.BOOL;
  672. case "doubleSided":
  673. return WebGLConstants.BOOL;
  674. }
  675. }
  676. function getTechniqueKey(khrMaterialsCommon, primitiveInfo) {
  677. let techniqueKey = "";
  678. techniqueKey += `technique:${khrMaterialsCommon.technique};`;
  679. const values = khrMaterialsCommon.values;
  680. const keys = Object.keys(values).sort();
  681. const keysCount = keys.length;
  682. for (let i = 0; i < keysCount; ++i) {
  683. const name = keys[i];
  684. if (values.hasOwnProperty(name)) {
  685. techniqueKey += `${name}:${getKHRMaterialsCommonValueType(
  686. name,
  687. values[name]
  688. )}`;
  689. techniqueKey += ";";
  690. }
  691. }
  692. const jointCount = defaultValue(khrMaterialsCommon.jointCount, 0);
  693. techniqueKey += `${jointCount.toString()};`;
  694. if (defined(primitiveInfo)) {
  695. const skinningInfo = primitiveInfo.skinning;
  696. if (jointCount > 0) {
  697. techniqueKey += `${skinningInfo.type};`;
  698. }
  699. techniqueKey += primitiveInfo.hasVertexColors;
  700. }
  701. return techniqueKey;
  702. }
  703. function lightDefaults(gltf) {
  704. const khrMaterialsCommon = gltf.extensions.KHR_materials_common;
  705. if (!defined(khrMaterialsCommon) || !defined(khrMaterialsCommon.lights)) {
  706. return;
  707. }
  708. const lights = khrMaterialsCommon.lights;
  709. const lightsLength = lights.length;
  710. for (let lightId = 0; lightId < lightsLength; lightId++) {
  711. const light = lights[lightId];
  712. if (light.type === "ambient") {
  713. if (!defined(light.ambient)) {
  714. light.ambient = {};
  715. }
  716. const ambientLight = light.ambient;
  717. if (!defined(ambientLight.color)) {
  718. ambientLight.color = [1.0, 1.0, 1.0];
  719. }
  720. } else if (light.type === "directional") {
  721. if (!defined(light.directional)) {
  722. light.directional = {};
  723. }
  724. const directionalLight = light.directional;
  725. if (!defined(directionalLight.color)) {
  726. directionalLight.color = [1.0, 1.0, 1.0];
  727. }
  728. } else if (light.type === "point") {
  729. if (!defined(light.point)) {
  730. light.point = {};
  731. }
  732. const pointLight = light.point;
  733. if (!defined(pointLight.color)) {
  734. pointLight.color = [1.0, 1.0, 1.0];
  735. }
  736. pointLight.constantAttenuation = defaultValue(
  737. pointLight.constantAttenuation,
  738. 1.0
  739. );
  740. pointLight.linearAttenuation = defaultValue(
  741. pointLight.linearAttenuation,
  742. 0.0
  743. );
  744. pointLight.quadraticAttenuation = defaultValue(
  745. pointLight.quadraticAttenuation,
  746. 0.0
  747. );
  748. } else if (light.type === "spot") {
  749. if (!defined(light.spot)) {
  750. light.spot = {};
  751. }
  752. const spotLight = light.spot;
  753. if (!defined(spotLight.color)) {
  754. spotLight.color = [1.0, 1.0, 1.0];
  755. }
  756. spotLight.constantAttenuation = defaultValue(
  757. spotLight.constantAttenuation,
  758. 1.0
  759. );
  760. spotLight.fallOffAngle = defaultValue(spotLight.fallOffAngle, 3.14159265);
  761. spotLight.fallOffExponent = defaultValue(spotLight.fallOffExponent, 0.0);
  762. spotLight.linearAttenuation = defaultValue(
  763. spotLight.linearAttenuation,
  764. 0.0
  765. );
  766. spotLight.quadraticAttenuation = defaultValue(
  767. spotLight.quadraticAttenuation,
  768. 0.0
  769. );
  770. }
  771. }
  772. }
  773. export default processModelMaterialsCommon;