cesium
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
909 lines (824 loc) • 42.2 kB
JavaScript
define([
'./addToArray',
'./ForEach',
'./numberOfComponentsForType',
'./techniqueParameterForSemantic',
'./webGLConstantToGlslType',
'./glslTypeToWebGLConstant',
'../../Core/clone',
'../../Core/defined',
'../../Core/defaultValue',
'../../Core/WebGLConstants'
], function(
addToArray,
ForEach,
numberOfComponentsForType,
techniqueParameterForSemantic,
webGLConstantToGlslType,
glslTypeToWebGLConstant,
clone,
defined,
defaultValue,
WebGLConstants) {
'use strict';
/**
* @private
*/
function processPbrMetallicRoughness(gltf, options) {
options = defaultValue(options, {});
var hasPbrMetallicRoughness = false;
ForEach.material(gltf, function(material) {
if (isPbrMaterial(material)) {
hasPbrMetallicRoughness = true;
}
});
if (hasPbrMetallicRoughness) {
if (!defined(gltf.programs)) {
gltf.programs = [];
}
if (!defined(gltf.shaders)) {
gltf.shaders = [];
}
if (!defined(gltf.techniques)) {
gltf.techniques = [];
}
// Pre-processing to address incompatibilities between primitives using the same materials.
splitIncompatibleMaterials(gltf);
ForEach.material(gltf, function(material, materialIndex) {
if (isPbrMaterial(material)) {
var values = {};
var technique = generateTechnique(gltf, material, materialIndex, values, options);
gltf.materials[materialIndex] = {
values : values,
technique : technique
};
}
});
// If any primitives have semantics that aren't declared in the generated
// shaders, we want to preserve them.
ensureSemanticExistence(gltf);
}
return gltf;
}
function isPbrMaterial(material) {
return defined(material.pbrMetallicRoughness) ||
defined(material.normalTexture) ||
defined(material.occlusionTexture) ||
defined(material.emissiveTexture) ||
defined(material.emissiveFactor) ||
defined(material.alphaMode) ||
defined(material.alphaCutoff) ||
defined(material.doubleSided);
}
function generateTechnique(gltf, material, materialIndex, values, options) {
var optimizeForCesium = defaultValue(options.optimizeForCesium, false);
var hasCesiumRTCExtension = defined(gltf.extensions) && defined(gltf.extensions.CESIUM_RTC);
var addBatchIdToGeneratedShaders = defaultValue(options.addBatchIdToGeneratedShaders, false);
var techniques = gltf.techniques;
var shaders = gltf.shaders;
var programs = gltf.programs;
var parameterValues = values;
var pbrMetallicRoughness = material.pbrMetallicRoughness;
if (defined(pbrMetallicRoughness)) {
for (var parameterName in pbrMetallicRoughness) {
if (pbrMetallicRoughness.hasOwnProperty(parameterName)) {
parameterValues[parameterName] = pbrMetallicRoughness[parameterName];
}
}
}
for (var additional in material) {
if (material.hasOwnProperty(additional) && ((additional.indexOf('Texture') >= 0) || additional.indexOf('Factor') >= 0) || additional === 'doubleSided') {
parameterValues[additional] = material[additional];
}
}
var vertexShader = 'precision highp float;\n';
var fragmentShader = 'precision highp float;\n';
var skin;
if (defined(gltf.skins)) {
skin = gltf.skins[0];
}
var joints = (defined(skin)) ? skin.joints : [];
var jointCount = joints.length;
var primitiveInfo = material.extras._pipeline.primitive;
var skinningInfo;
var hasSkinning = false;
var hasVertexColors = false;
var hasMorphTargets = false;
var hasNormals = false;
var hasTangents = false;
var hasTexCoords = false;
if (defined(primitiveInfo)) {
skinningInfo = primitiveInfo.skinning;
hasSkinning = skinningInfo.skinned && (joints.length > 0);
hasVertexColors = primitiveInfo.hasVertexColors;
hasMorphTargets = primitiveInfo.hasMorphTargets;
hasNormals = primitiveInfo.hasNormals;
hasTangents = primitiveInfo.hasTangents;
hasTexCoords = primitiveInfo.hasTexCoords;
}
var morphTargets;
if (hasMorphTargets) {
ForEach.mesh(gltf, function(mesh) {
ForEach.meshPrimitive(mesh, function(primitive) {
if (primitive.material === materialIndex) {
var targets = primitive.targets;
if (defined(targets)) {
morphTargets = targets;
}
}
});
});
}
// Add techniques
var techniqueParameters = {
// Add matrices
modelViewMatrix : {
semantic : hasCesiumRTCExtension ? 'CESIUM_RTC_MODELVIEW' : 'MODELVIEW',
type : WebGLConstants.FLOAT_MAT4
},
projectionMatrix : {
semantic : 'PROJECTION',
type : WebGLConstants.FLOAT_MAT4
}
};
if (hasNormals) {
techniqueParameters.normalMatrix = {
semantic : 'MODELVIEWINVERSETRANSPOSE',
type : WebGLConstants.FLOAT_MAT3
};
}
if (hasSkinning) {
techniqueParameters.jointMatrix = {
count : jointCount,
semantic : 'JOINTMATRIX',
type : WebGLConstants.FLOAT_MAT4
};
}
if (hasMorphTargets) {
techniqueParameters.morphWeights = {
count : morphTargets.length,
semantic : 'MORPHWEIGHTS',
type : WebGLConstants.FLOAT
};
}
// Add material parameters
for (var name in parameterValues) {
if (parameterValues.hasOwnProperty(name)) {
techniqueParameters[name] = {
type : getPBRValueType(name)
};
}
}
// Generate uniforms object before attributes are added
var techniqueUniforms = {};
for (var paramName in techniqueParameters) {
if (techniqueParameters.hasOwnProperty(paramName) && paramName !== 'extras') {
var param = techniqueParameters[paramName];
techniqueUniforms['u_' + paramName] = paramName;
var arraySize = defined(param.count) ? '[' + param.count + ']' : '';
if (((param.type !== WebGLConstants.FLOAT_MAT3) && (param.type !== WebGLConstants.FLOAT_MAT4) && (paramName !== 'morphWeights')) ||
param.useInFragment) {
fragmentShader += 'uniform ' + webGLConstantToGlslType(param.type) + ' u_' + paramName + arraySize + ';\n';
delete param.useInFragment;
} else {
vertexShader += 'uniform ' + webGLConstantToGlslType(param.type) + ' u_' + paramName + arraySize + ';\n';
}
}
}
// Add attributes with semantics
var vertexShaderMain = '';
if (hasSkinning) {
var i, j;
var numberOfComponents = numberOfComponentsForType(skinningInfo.type);
var matrix = false;
if (skinningInfo.type.indexOf('MAT') === 0) {
matrix = true;
numberOfComponents = Math.sqrt(numberOfComponents);
}
if (!matrix) {
for (i = 0; i < numberOfComponents; i++) {
if (i === 0) {
vertexShaderMain += ' mat4 skinMatrix = ';
} else {
vertexShaderMain += ' skinMatrix += ';
}
vertexShaderMain += 'a_weight[' + i + '] * u_jointMatrix[int(a_joint[' + i + '])];\n';
}
} else {
for (i = 0; i < numberOfComponents; i++) {
for (j = 0; j < numberOfComponents; j++) {
if (i === 0 && j === 0) {
vertexShaderMain += ' mat4 skinMatrix = ';
} else {
vertexShaderMain += ' skinMatrix += ';
}
vertexShaderMain += 'a_weight[' + i + '][' + j + '] * u_jointMatrix[int(a_joint[' + i + '][' + j + '])];\n';
}
}
}
}
// Add position always
var techniqueAttributes = {
a_position : 'position'
};
techniqueParameters.position = {
semantic : 'POSITION',
type : WebGLConstants.FLOAT_VEC3
};
vertexShader += 'attribute vec3 a_position;\n';
if (hasNormals) {
vertexShader += 'varying vec3 v_positionEC;\n';
if (optimizeForCesium) {
vertexShader += 'varying vec3 v_positionWC;\n';
}
}
// Morph Target Weighting
vertexShaderMain += ' vec3 weightedPosition = a_position;\n';
if (hasNormals) {
vertexShaderMain += ' vec3 weightedNormal = a_normal;\n';
}
if (hasTangents) {
vertexShaderMain += ' vec4 weightedTangent = a_tangent;\n';
}
if (hasMorphTargets) {
for (var k = 0; k < morphTargets.length; k++) {
var targetAttributes = morphTargets[k];
for (var targetAttribute in targetAttributes) {
if (targetAttributes.hasOwnProperty(targetAttribute) && targetAttribute !== 'extras') {
var attributeLower = targetAttribute.toLowerCase() + '_' + k;
techniqueAttributes['a_' + attributeLower] = attributeLower;
techniqueParameters[attributeLower] = {
semantic : targetAttribute + '_' + k,
type : WebGLConstants.FLOAT_VEC3
};
vertexShader += 'attribute vec3 a_' + attributeLower + ';\n';
if (targetAttribute === 'POSITION') {
vertexShaderMain += ' weightedPosition += u_morphWeights[' + k + '] * a_' + attributeLower + ';\n';
} else if (targetAttribute === 'NORMAL') {
vertexShaderMain += ' weightedNormal += u_morphWeights[' + k + '] * a_' + attributeLower + ';\n';
} else if (hasTangents && targetAttribute === 'TANGENT') {
vertexShaderMain += ' weightedTangent.xyz += u_morphWeights[' + k + '] * a_' + attributeLower + ';\n';
}
}
}
}
}
// Final position computation
if (hasSkinning) {
vertexShaderMain += ' vec4 position = skinMatrix * vec4(weightedPosition, 1.0);\n';
} else {
vertexShaderMain += ' vec4 position = vec4(weightedPosition, 1.0);\n';
}
if (hasNormals && optimizeForCesium) {
vertexShaderMain += ' v_positionWC = (czm_model * position).xyz;\n';
}
vertexShaderMain += ' position = u_modelViewMatrix * position;\n';
if (hasNormals) {
vertexShaderMain += ' v_positionEC = position.xyz;\n';
}
vertexShaderMain += ' gl_Position = u_projectionMatrix * position;\n';
// Final normal computation
if (hasNormals) {
techniqueAttributes.a_normal = 'normal';
techniqueParameters.normal = {
semantic : 'NORMAL',
type : WebGLConstants.FLOAT_VEC3
};
vertexShader += 'attribute vec3 a_normal;\n';
vertexShader += 'varying vec3 v_normal;\n';
if (hasSkinning) {
vertexShaderMain += ' v_normal = u_normalMatrix * mat3(skinMatrix) * weightedNormal;\n';
} else {
vertexShaderMain += ' v_normal = u_normalMatrix * weightedNormal;\n';
}
fragmentShader += 'varying vec3 v_normal;\n';
fragmentShader += 'varying vec3 v_positionEC;\n';
if (optimizeForCesium) {
fragmentShader += 'varying vec3 v_positionWC;\n';
}
}
// Read tangents if available
if (hasTangents) {
techniqueAttributes.a_tangent = 'tangent';
techniqueParameters.tangent = {
semantic : 'TANGENT',
type : WebGLConstants.FLOAT_VEC4
};
vertexShader += 'attribute vec4 a_tangent;\n';
vertexShader += 'varying vec4 v_tangent;\n';
vertexShaderMain += ' v_tangent.xyz = u_normalMatrix * weightedTangent.xyz;\n';
vertexShaderMain += ' v_tangent.w = weightedTangent.w;\n';
fragmentShader += 'varying vec4 v_tangent;\n';
}
// Add texture coordinates if the material uses them
var v_texcoord;
if (hasTexCoords) {
techniqueAttributes.a_texcoord_0 = 'texcoord_0';
techniqueParameters.texcoord_0 = {
semantic : 'TEXCOORD_0',
type : WebGLConstants.FLOAT_VEC2
};
v_texcoord = 'v_texcoord_0';
vertexShader += 'attribute vec2 a_texcoord_0;\n';
vertexShader += 'varying vec2 ' + v_texcoord + ';\n';
vertexShaderMain += ' ' + v_texcoord + ' = a_texcoord_0;\n';
fragmentShader += 'varying vec2 ' + v_texcoord + ';\n';
}
// Add skinning information if available
if (hasSkinning) {
techniqueAttributes.a_joint = 'joint';
var attributeType = getShaderVariable(skinningInfo.type);
var webGLConstant = glslTypeToWebGLConstant(attributeType);
techniqueParameters.joint = {
semantic : 'JOINTS_0',
type : webGLConstant
};
techniqueAttributes.a_weight = 'weight';
techniqueParameters.weight = {
semantic : 'WEIGHTS_0',
type : webGLConstant
};
vertexShader += 'attribute ' + attributeType + ' a_joint;\n';
vertexShader += 'attribute ' + attributeType + ' a_weight;\n';
}
if (hasVertexColors) {
techniqueAttributes.a_vertexColor = 'vertexColor';
techniqueParameters.vertexColor = {
semantic: 'COLOR_0',
type: WebGLConstants.FLOAT_VEC4
};
vertexShader += 'attribute vec4 a_vertexColor;\n';
vertexShader += 'varying vec4 v_vertexColor;\n';
vertexShaderMain += ' v_vertexColor = a_vertexColor;\n';
fragmentShader += 'varying vec4 v_vertexColor;\n';
}
if (addBatchIdToGeneratedShaders) {
techniqueAttributes.a_batchId = 'batchId';
techniqueParameters.batchId = {
semantic: '_BATCHID',
type: WebGLConstants.FLOAT
};
vertexShader += 'attribute float a_batchId;\n';
}
vertexShader += 'void main(void) \n{\n';
vertexShader += vertexShaderMain;
vertexShader += '}\n';
// Fragment shader lighting
if (hasNormals) {
fragmentShader += 'const float M_PI = 3.141592653589793;\n';
fragmentShader += 'vec3 lambertianDiffuse(vec3 diffuseColor) \n' +
'{\n' +
' return diffuseColor / M_PI;\n' +
'}\n\n';
fragmentShader += 'vec3 fresnelSchlick2(vec3 f0, vec3 f90, float VdotH) \n' +
'{\n' +
' return f0 + (f90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);\n' +
'}\n\n';
fragmentShader += 'vec3 fresnelSchlick(float metalness, float VdotH) \n' +
'{\n' +
' return metalness + (vec3(1.0) - metalness) * pow(1.0 - VdotH, 5.0);\n' +
'}\n\n';
fragmentShader += 'float smithVisibilityG1(float NdotV, float roughness) \n' +
'{\n' +
' float k = (roughness + 1.0) * (roughness + 1.0) / 8.0;\n' +
' return NdotV / (NdotV * (1.0 - k) + k);\n' +
'}\n\n';
fragmentShader += 'float smithVisibilityGGX(float roughness, float NdotL, float NdotV) \n' +
'{\n' +
' return smithVisibilityG1(NdotL, roughness) * smithVisibilityG1(NdotV, roughness);\n' +
'}\n\n';
fragmentShader += 'float GGX(float roughness, float NdotH) \n' +
'{\n' +
' float roughnessSquared = roughness * roughness;\n' +
' float f = (NdotH * roughnessSquared - NdotH) * NdotH + 1.0;\n' +
' return roughnessSquared / (M_PI * f * f);\n' +
'}\n\n';
}
fragmentShader += 'vec3 SRGBtoLINEAR3(vec3 srgbIn) \n' +
'{\n' +
' return pow(srgbIn, vec3(2.2));\n' +
'}\n\n';
fragmentShader += 'vec4 SRGBtoLINEAR4(vec4 srgbIn) \n' +
'{\n' +
' vec3 linearOut = pow(srgbIn.rgb, vec3(2.2));\n' +
' return vec4(linearOut, srgbIn.a);\n' +
'}\n\n';
fragmentShader += 'vec3 LINEARtoSRGB(vec3 linearIn) \n' +
'{\n' +
' return pow(linearIn, vec3(1.0/2.2));\n' +
'}\n\n';
fragmentShader += 'void main(void) \n{\n';
// Add normal mapping to fragment shader
if (hasNormals) {
fragmentShader += ' vec3 ng = normalize(v_normal);\n';
if (defined(parameterValues.normalTexture)) {
if (hasTangents) {
// Read tangents from varying
fragmentShader += ' vec3 t = normalize(v_tangent.xyz);\n';
fragmentShader += ' vec3 b = normalize(cross(ng, t) * v_tangent.w);\n';
fragmentShader += ' mat3 tbn = mat3(t, b, ng);\n';
fragmentShader += ' vec3 n = texture2D(u_normalTexture, ' + v_texcoord + ').rgb;\n';
fragmentShader += ' n = normalize(tbn * (2.0 * n - 1.0));\n';
} else {
// Add standard derivatives extension
fragmentShader = '#ifdef GL_OES_standard_derivatives\n' +
'#extension GL_OES_standard_derivatives : enable\n' +
'#endif\n' +
fragmentShader;
// Compute tangents
fragmentShader += '#ifdef GL_OES_standard_derivatives\n';
fragmentShader += ' vec3 pos_dx = dFdx(v_positionEC);\n';
fragmentShader += ' vec3 pos_dy = dFdy(v_positionEC);\n';
fragmentShader += ' vec3 tex_dx = dFdx(vec3(' + v_texcoord + ',0.0));\n';
fragmentShader += ' vec3 tex_dy = dFdy(vec3(' + v_texcoord + ',0.0));\n';
fragmentShader += ' vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);\n';
fragmentShader += ' t = normalize(t - ng * dot(ng, t));\n';
fragmentShader += ' vec3 b = normalize(cross(ng, t));\n';
fragmentShader += ' mat3 tbn = mat3(t, b, ng);\n';
fragmentShader += ' vec3 n = texture2D(u_normalTexture, ' + v_texcoord + ').rgb;\n';
fragmentShader += ' n = normalize(tbn * (2.0 * n - 1.0));\n';
fragmentShader += '#else\n';
fragmentShader += ' vec3 n = ng;\n';
fragmentShader += '#endif\n';
}
} else {
fragmentShader += ' vec3 n = ng;\n';
}
if (parameterValues.doubleSided) {
fragmentShader += ' if (!gl_FrontFacing)\n';
fragmentShader += ' {\n';
fragmentShader += ' n = -n;\n';
fragmentShader += ' }\n';
}
}
// Add base color to fragment shader
if (defined(parameterValues.baseColorTexture)) {
fragmentShader += ' vec4 baseColorWithAlpha = SRGBtoLINEAR4(texture2D(u_baseColorTexture, ' + v_texcoord + '));\n';
if (defined(parameterValues.baseColorFactor)) {
fragmentShader += ' baseColorWithAlpha *= u_baseColorFactor;\n';
}
} else {
if (defined(parameterValues.baseColorFactor)) {
fragmentShader += ' vec4 baseColorWithAlpha = u_baseColorFactor;\n';
} else {
fragmentShader += ' vec4 baseColorWithAlpha = vec4(1.0);\n';
}
}
if (hasVertexColors) {
fragmentShader += ' baseColorWithAlpha *= v_vertexColor;\n';
}
fragmentShader += ' vec3 baseColor = baseColorWithAlpha.rgb;\n';
if (hasNormals) {
// Add metallic-roughness to fragment shader
if (defined(parameterValues.metallicRoughnessTexture)) {
fragmentShader += ' vec3 metallicRoughness = texture2D(u_metallicRoughnessTexture, ' + v_texcoord + ').rgb;\n';
fragmentShader += ' float metalness = clamp(metallicRoughness.b, 0.0, 1.0);\n';
fragmentShader += ' float roughness = clamp(metallicRoughness.g, 0.04, 1.0);\n';
if (defined(parameterValues.metallicFactor)) {
fragmentShader += ' metalness *= u_metallicFactor;\n';
}
if (defined(parameterValues.roughnessFactor)) {
fragmentShader += ' roughness *= u_roughnessFactor;\n';
}
} else {
if (defined(parameterValues.metallicFactor)) {
fragmentShader += ' float metalness = clamp(u_metallicFactor, 0.0, 1.0);\n';
} else {
fragmentShader += ' float metalness = 1.0;\n';
}
if (defined(parameterValues.roughnessFactor)) {
fragmentShader += ' float roughness = clamp(u_roughnessFactor, 0.04, 1.0);\n';
} else {
fragmentShader += ' float roughness = 1.0;\n';
}
}
fragmentShader += ' vec3 v = -normalize(v_positionEC);\n';
// Generate fragment shader's lighting block
if (optimizeForCesium) {
// The Sun is brighter than your average light source, and has a yellowish tint balanced by the Earth's ambient blue.
fragmentShader += ' vec3 lightColor = vec3(1.5, 1.4, 1.2);\n';
fragmentShader += ' vec3 l = normalize(czm_sunDirectionEC);\n';
} else {
fragmentShader += ' vec3 lightColor = vec3(1.0, 1.0, 1.0);\n';
fragmentShader += ' vec3 l = vec3(0.0, 0.0, 1.0);\n';
}
fragmentShader += ' vec3 h = normalize(v + l);\n';
if (optimizeForCesium) {
fragmentShader += ' vec3 r = normalize(czm_inverseViewRotation * normalize(reflect(v, n)));\n';
// Figure out if the reflection vector hits the ellipsoid
fragmentShader += ' czm_ellipsoid ellipsoid = czm_getWgs84EllipsoidEC();\n';
fragmentShader += ' float vertexRadius = length(v_positionWC);\n';
fragmentShader += ' float horizonDotNadir = 1.0 - min(1.0, ellipsoid.radii.x / vertexRadius);\n';
fragmentShader += ' float reflectionDotNadir = dot(r, normalize(v_positionWC));\n';
// Flipping the X vector is a cheap way to get the inverse of czm_temeToPseudoFixed, since that's a rotation about Z.
fragmentShader += ' r.x = -r.x;\n';
fragmentShader += ' r = -normalize(czm_temeToPseudoFixed * r);\n';
fragmentShader += ' r.x = -r.x;\n';
} else {
fragmentShader += ' vec3 r = normalize(reflect(v, n));\n';
}
fragmentShader += ' float NdotL = clamp(dot(n, l), 0.001, 1.0);\n';
fragmentShader += ' float NdotV = abs(dot(n, v)) + 0.001;\n';
fragmentShader += ' float NdotH = clamp(dot(n, h), 0.0, 1.0);\n';
fragmentShader += ' float LdotH = clamp(dot(l, h), 0.0, 1.0);\n';
fragmentShader += ' float VdotH = clamp(dot(v, h), 0.0, 1.0);\n';
fragmentShader += ' vec3 f0 = vec3(0.04);\n';
fragmentShader += ' float alpha = roughness * roughness;\n';
fragmentShader += ' vec3 diffuseColor = baseColor * (1.0 - metalness) * (1.0 - f0);\n';
fragmentShader += ' vec3 specularColor = mix(f0, baseColor, metalness);\n';
fragmentShader += ' float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\n';
fragmentShader += ' vec3 r90 = vec3(clamp(reflectance * 25.0, 0.0, 1.0));\n';
fragmentShader += ' vec3 r0 = specularColor.rgb;\n';
fragmentShader += ' vec3 F = fresnelSchlick2(r0, r90, VdotH);\n';
fragmentShader += ' float G = smithVisibilityGGX(alpha, NdotL, NdotV);\n';
fragmentShader += ' float D = GGX(alpha, NdotH);\n';
fragmentShader += ' vec3 diffuseContribution = (1.0 - F) * lambertianDiffuse(diffuseColor);\n';
fragmentShader += ' vec3 specularContribution = F * G * D / (4.0 * NdotL * NdotV);\n';
fragmentShader += ' vec3 color = NdotL * lightColor * (diffuseContribution + specularContribution);\n';
if (optimizeForCesium) {
fragmentShader += ' float inverseRoughness = 1.04 - roughness;\n';
fragmentShader += ' inverseRoughness *= inverseRoughness;\n';
fragmentShader += ' vec3 sceneSkyBox = textureCube(czm_environmentMap, r).rgb * inverseRoughness;\n';
fragmentShader += ' float atmosphereHeight = 0.05;\n';
fragmentShader += ' float blendRegionSize = 0.1 * ((1.0 - inverseRoughness) * 8.0 + 1.1 - horizonDotNadir);\n';
fragmentShader += ' float blendRegionOffset = roughness * -1.0;\n';
fragmentShader += ' float farAboveHorizon = clamp(horizonDotNadir - blendRegionSize * 0.5 + blendRegionOffset, 1.0e-10 - blendRegionSize, 0.99999);\n';
fragmentShader += ' float aroundHorizon = clamp(horizonDotNadir + blendRegionSize * 0.5, 1.0e-10 - blendRegionSize, 0.99999);\n';
fragmentShader += ' float farBelowHorizon = clamp(horizonDotNadir + blendRegionSize * 1.5, 1.0e-10 - blendRegionSize, 0.99999);\n';
fragmentShader += ' float smoothstepHeight = smoothstep(0.0, atmosphereHeight, horizonDotNadir);\n';
fragmentShader += ' vec3 belowHorizonColor = mix(vec3(0.1, 0.15, 0.25), vec3(0.4, 0.7, 0.9), smoothstepHeight);\n';
fragmentShader += ' vec3 nadirColor = belowHorizonColor * 0.5;\n';
fragmentShader += ' vec3 aboveHorizonColor = mix(vec3(0.9, 1.0, 1.2), belowHorizonColor, roughness * 0.5);\n';
fragmentShader += ' vec3 blueSkyColor = mix(vec3(0.18, 0.26, 0.48), aboveHorizonColor, reflectionDotNadir * inverseRoughness * 0.5 + 0.75);\n';
fragmentShader += ' vec3 zenithColor = mix(blueSkyColor, sceneSkyBox, smoothstepHeight);\n';
fragmentShader += ' vec3 blueSkyDiffuseColor = vec3(0.7, 0.85, 0.9);\n';
fragmentShader += ' float diffuseIrradianceFromEarth = (1.0 - horizonDotNadir) * (reflectionDotNadir * 0.25 + 0.75) * smoothstepHeight;\n';
fragmentShader += ' float diffuseIrradianceFromSky = (1.0 - smoothstepHeight) * (1.0 - (reflectionDotNadir * 0.25 + 0.25));\n';
fragmentShader += ' vec3 diffuseIrradiance = blueSkyDiffuseColor * clamp(diffuseIrradianceFromEarth + diffuseIrradianceFromSky, 0.0, 1.0);\n';
fragmentShader += ' float notDistantRough = (1.0 - horizonDotNadir * roughness * 0.8);\n';
fragmentShader += ' vec3 specularIrradiance = mix(zenithColor, aboveHorizonColor, smoothstep(farAboveHorizon, aroundHorizon, reflectionDotNadir) * notDistantRough);\n';
fragmentShader += ' specularIrradiance = mix(specularIrradiance, belowHorizonColor, smoothstep(aroundHorizon, farBelowHorizon, reflectionDotNadir) * inverseRoughness);\n';
fragmentShader += ' specularIrradiance = mix(specularIrradiance, nadirColor, smoothstep(farBelowHorizon, 1.0, reflectionDotNadir) * inverseRoughness);\n';
fragmentShader += ' vec2 brdfLut = texture2D(czm_brdfLut, vec2(NdotV, 1.0 - roughness)).rg;\n';
fragmentShader += ' vec3 IBLColor = (diffuseIrradiance * diffuseColor) + (specularIrradiance * SRGBtoLINEAR3(specularColor * brdfLut.x + brdfLut.y));\n';
fragmentShader += ' color += IBLColor;\n';
}
} else {
fragmentShader += ' vec3 color = baseColor;\n';
}
if (defined(parameterValues.occlusionTexture)) {
fragmentShader += ' color *= texture2D(u_occlusionTexture, ' + v_texcoord + ').r;\n';
}
if (defined(parameterValues.emissiveTexture)) {
fragmentShader += ' vec3 emissive = SRGBtoLINEAR3(texture2D(u_emissiveTexture, ' + v_texcoord + ').rgb);\n';
if (defined(parameterValues.emissiveFactor)) {
fragmentShader += ' emissive *= u_emissiveFactor;\n';
}
fragmentShader += ' color += emissive;\n';
}
else {
if (defined(parameterValues.emissiveFactor)) {
fragmentShader += ' color += u_emissiveFactor;\n';
}
}
// Final color
fragmentShader += ' color = LINEARtoSRGB(color);\n';
var alphaMode = material.alphaMode;
if (defined(alphaMode)) {
if (alphaMode === 'MASK') {
var alphaCutoff = defaultValue(material.alphaCutoff, 0.5);
fragmentShader += ' gl_FragColor = vec4(color, int(baseColorWithAlpha.a >= ' + alphaCutoff + '));\n';
} else if (alphaMode === 'BLEND') {
fragmentShader += ' gl_FragColor = vec4(color, baseColorWithAlpha.a);\n';
} else {
fragmentShader += ' gl_FragColor = vec4(color, 1.0);\n';
}
} else {
fragmentShader += ' gl_FragColor = vec4(color, 1.0);\n';
}
fragmentShader += '}\n';
var techniqueStates;
if (defined(alphaMode) && alphaMode !== 'OPAQUE') {
techniqueStates = {
enable: parameterValues.doubleSided ? [
WebGLConstants.DEPTH_TEST,
WebGLConstants.BLEND
]: [
WebGLConstants.CULL_FACE,
WebGLConstants.DEPTH_TEST,
WebGLConstants.BLEND
],
functions: {
depthMask : [false],
blendEquationSeparate: [
WebGLConstants.FUNC_ADD,
WebGLConstants.FUNC_ADD
],
blendFuncSeparate: [
WebGLConstants.ONE,
WebGLConstants.ONE_MINUS_SRC_ALPHA,
WebGLConstants.ONE,
WebGLConstants.ONE_MINUS_SRC_ALPHA
]
}
};
} else if (parameterValues.doubleSided) {
techniqueStates = {
enable : [
WebGLConstants.DEPTH_TEST
]
};
} else {
techniqueStates = {
enable : [
WebGLConstants.CULL_FACE,
WebGLConstants.DEPTH_TEST
]
};
}
// Add shaders
var vertexShaderId = addToArray(shaders, {
type : WebGLConstants.VERTEX_SHADER,
extras : {
_pipeline : {
source : vertexShader,
extension : '.glsl'
}
}
});
var fragmentShaderId = addToArray(shaders, {
type : WebGLConstants.FRAGMENT_SHADER,
extras : {
_pipeline : {
source : fragmentShader,
extension : '.glsl'
}
}
});
// Add program
var programAttributes = Object.keys(techniqueAttributes);
var programId = addToArray(programs, {
attributes : programAttributes,
fragmentShader : fragmentShaderId,
vertexShader : vertexShaderId
});
var techniqueId = addToArray(techniques, {
attributes : techniqueAttributes,
parameters : techniqueParameters,
program : programId,
states : techniqueStates,
uniforms : techniqueUniforms
});
return techniqueId;
}
function getPBRValueType(paramName) {
switch (paramName) {
case 'baseColorFactor':
return WebGLConstants.FLOAT_VEC4;
case 'metallicFactor':
return WebGLConstants.FLOAT;
case 'roughnessFactor':
return WebGLConstants.FLOAT;
case 'baseColorTexture':
return WebGLConstants.SAMPLER_2D;
case 'metallicRoughnessTexture':
return WebGLConstants.SAMPLER_2D;
case 'normalTexture':
return WebGLConstants.SAMPLER_2D;
case 'occlusionTexture':
return WebGLConstants.SAMPLER_2D;
case 'emissiveTexture':
return WebGLConstants.SAMPLER_2D;
case 'emissiveFactor':
return WebGLConstants.FLOAT_VEC3;
case 'doubleSided':
return WebGLConstants.BOOL;
}
}
function getShaderVariable(type) {
if (type === 'SCALAR') {
return 'float';
}
return type.toLowerCase();
}
function ensureSemanticExistenceForPrimitive(gltf, primitive) {
var accessors = gltf.accessors;
var materials = gltf.materials;
var techniques = gltf.techniques;
var programs = gltf.programs;
var shaders = gltf.shaders;
var targets = primitive.targets;
var attributes = primitive.attributes;
for (var target in targets) {
if (targets.hasOwnProperty(target)) {
var targetAttributes = targets[target];
for (var attribute in targetAttributes) {
if (attribute !== 'extras') {
attributes[attribute + '_' + target] = targetAttributes[attribute];
}
}
}
}
var material = materials[primitive.material];
var technique = techniques[material.technique];
var program = programs[technique.program];
var vertexShader = shaders[program.vertexShader];
for (var semantic in attributes) {
if (attributes.hasOwnProperty(semantic)) {
if (!defined(techniqueParameterForSemantic(technique, semantic))) {
var accessorId = attributes[semantic];
var accessor = accessors[accessorId];
var lowerCase = semantic.toLowerCase();
if (lowerCase.charAt(0) === '_') {
lowerCase = lowerCase.slice(1);
}
var attributeName = 'a_' + lowerCase;
technique.parameters[lowerCase] = {
semantic: semantic,
type: accessor.componentType
};
technique.attributes[attributeName] = lowerCase;
program.attributes.push(attributeName);
var pipelineExtras = vertexShader.extras._pipeline;
var shaderText = pipelineExtras.source;
shaderText = 'attribute ' + getShaderVariable(accessor.type) + ' ' + attributeName + ';\n' + shaderText;
pipelineExtras.source = shaderText;
}
}
}
}
function ensureSemanticExistence(gltf) {
ForEach.mesh(gltf, function(mesh) {
ForEach.meshPrimitive(mesh, function(primitive) {
ensureSemanticExistenceForPrimitive(gltf, primitive);
});
});
}
function splitIncompatibleMaterials(gltf) {
var accessors = gltf.accessors;
var materials = gltf.materials;
ForEach.mesh(gltf, function(mesh) {
ForEach.meshPrimitive(mesh, function(primitive) {
var materialId = primitive.material;
var material = materials[materialId];
var jointAccessorId = primitive.attributes.JOINTS_0;
var componentType;
var type;
if (defined(jointAccessorId)) {
var jointAccessor = accessors[jointAccessorId];
componentType = jointAccessor.componentType;
type = jointAccessor.type;
}
var isSkinned = defined(jointAccessorId);
var hasVertexColors = defined(primitive.attributes.COLOR_0);
var hasMorphTargets = defined(primitive.targets);
var hasNormals = defined(primitive.attributes.NORMAL);
var hasTangents = defined(primitive.attributes.TANGENT);
var hasTexCoords = defined(primitive.attributes.TEXCOORD_0);
var primitiveInfo = material.extras._pipeline.primitive;
if (!defined(primitiveInfo)) {
material.extras._pipeline.primitive = {
skinning : {
skinned : isSkinned,
componentType : componentType,
type : type
},
hasVertexColors : hasVertexColors,
hasMorphTargets : hasMorphTargets,
hasNormals : hasNormals,
hasTangents : hasTangents,
hasTexCoords : hasTexCoords
};
} else if ((primitiveInfo.skinning.skinned !== isSkinned) ||
(primitiveInfo.skinning.type !== type) ||
(primitiveInfo.hasVertexColors !== hasVertexColors) ||
(primitiveInfo.hasMorphTargets !== hasMorphTargets) ||
(primitiveInfo.hasNormals !== hasNormals) ||
(primitiveInfo.hasTangents !== hasTangents) ||
(primitiveInfo.hasTexCoords !== hasTexCoords)) {
// This primitive uses the same material as another one that either:
// * Isn't skinned
// * Uses a different type to store joints and weights
// * Doesn't have vertex colors, morph targets, normals, tangents, or texCoords
var clonedMaterial = clone(material, true);
clonedMaterial.extras._pipeline.primitive = {
skinning : {
skinned : isSkinned,
componentType : componentType,
type : type
},
hasVertexColors : hasVertexColors,
hasMorphTargets : hasMorphTargets,
hasNormals : hasNormals,
hasTangents : hasTangents,
hasTexCoords : hasTexCoords
};
// Split this off as a separate material
materialId = addToArray(materials, clonedMaterial);
primitive.material = materialId;
}
});
});
}
return processPbrMetallicRoughness;
});