diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index 3f6c292772c0..a772afc586db 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -48,6 +48,13 @@ + + Blend Alpha + + + + + @@ -66,7 +73,8 @@ // The viewModel tracks the state of our mini application. var viewModel = { blendAmount : 0.0, - blendColor : 0.0 + blendColor : 0.0, + blendAlpha : 1.0 }; // Convert the viewModel members into knockout observables. Cesium.knockout.track(viewModel); @@ -83,7 +91,13 @@ Cesium.knockout.getObservable(viewModel, 'blendColor').subscribe( function(newValue) { - entity.model.blendColor = Cesium.Color.fromHsl(newValue, 1.0, 0.5); + entity.model.blendColor = Cesium.Color.fromHsl(newValue, 1.0, 0.5, viewModel.blendAlpha); + } +); + +Cesium.knockout.getObservable(viewModel, 'blendAlpha').subscribe( + function(newValue) { + entity.model.blendColor = Cesium.Color.fromHsl(viewModel.blendColor, 1.0, 0.5, newValue); } ); @@ -106,7 +120,7 @@ minimumPixelSize : 128, maximumScale : 20000, blendAmount : viewModel.blendAmount, - blendColor : Cesium.Color.fromHsl(viewModel.blendColor, 1.0, 0.5) + blendColor : Cesium.Color.fromHsl(viewModel.blendColor, 1.0, 0.5, viewModel.blendAlpha) } }); viewer.trackedEntity = entity; diff --git a/Apps/Sandcastle/gallery/development/3D Models.html b/Apps/Sandcastle/gallery/development/3D Models.html index dbd8409deea8..dbc7680fcd9c 100644 --- a/Apps/Sandcastle/gallery/development/3D Models.html +++ b/Apps/Sandcastle/gallery/development/3D Models.html @@ -48,6 +48,13 @@ + + Blend Alpha + + + + + @@ -65,7 +72,8 @@ // The viewModel tracks the state of our mini application. var viewModel = { blendAmount : 0.0, - blendColor : 0.0 + blendColor : 0.0, + blendAlpha : 1.0 }; // Convert the viewModel members into knockout observables. @@ -83,7 +91,13 @@ Cesium.knockout.getObservable(viewModel, 'blendColor').subscribe( function(newValue) { - model.blendColor = Cesium.Color.fromHsl(newValue, 1.0, 0.5); + entity.model.blendColor = Cesium.Color.fromHsl(newValue, 1.0, 0.5, viewModel.blendAlpha); + } +); + +Cesium.knockout.getObservable(viewModel, 'blendAlpha').subscribe( + function(newValue) { + entity.model.blendColor = Cesium.Color.fromHsl(viewModel.blendColor, 1.0, 0.5, newValue); } ); @@ -106,7 +120,7 @@ model.readyPromise.then(function(model) { model.blendAmount = viewModel.blendAmount; - model.blendColor = Cesium.Color.fromHsl(viewModel.blendColor, 1.0, 0.5); + model.blendColor = Cesium.Color.fromHsl(viewModel.blendColor, 1.0, viewModel.blendAlpha); // Play and loop all animations at half-speed model.activeAnimations.addAll({ speedup : 0.5, diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index 78485264bc56..f9e602d44424 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -1739,15 +1739,28 @@ define([ return shader; } - function modifyShaderForBlendColor(shader) { + function hasPremultipliedAlpha(model) { + var gltf = model.gltf; + return defined(gltf.asset) ? defaultValue(gltf.asset.premultipliedAlpha, false) : false; + } + + function modifyShaderForBlendColor(shader, premultipliedAlpha) { shader = ShaderSource.replaceMain(shader, 'gltf_blend_main'); shader += 'uniform vec4 gltf_blendColor; \n' + 'uniform float gltf_blendAmount; \n' + 'void main() \n' + '{ \n' + - ' gltf_blend_main(); \n' + + ' gltf_blend_main(); \n'; + + // Un-premultiply the alpha so that blending is correct. + if (premultipliedAlpha) { + shader += ' gl_FragColor.rgb /= gl_FragColor.a; \n'; + } + + shader += ' gl_FragColor.rgb = mix(gl_FragColor.rgb, gltf_blendColor.rgb, gltf_blendAmount); \n' + + ' gl_FragColor.a *= gltf_blendColor.a; \n' + '} \n'; return shader; } @@ -1783,7 +1796,8 @@ define([ vs = modifyShaderForQuantizedAttributes(vs, id, model, context); } - var blendFS = modifyShaderForBlendColor(fs); + var premultipliedAlpha = hasPremultipliedAlpha(model); + var blendFS = modifyShaderForBlendColor(fs, premultipliedAlpha); var drawVS = modifyShader(vs, id, model._vertexShaderLoaded); var drawFS = modifyShader(blendFS, id, model._fragmentShaderLoaded); @@ -2319,6 +2333,16 @@ define([ var polygonOffset = defaultValue(statesFunctions.polygonOffset, [0.0, 0.0]); var scissor = defaultValue(statesFunctions.scissor, [0.0, 0.0, 0.0, 0.0]); + // Change the render state to use traditional alpha blending instead of premultiplied alpha blending + if (booleanStates[WebGLConstants.BLEND] && hasPremultipliedAlpha(model)) { + if ((blendFuncSeparate[0] === WebGLConstants.ONE) && (blendFuncSeparate[1] === WebGLConstants.ONE_MINUS_SRC_ALPHA)) { + blendFuncSeparate[0] = WebGLConstants.SRC_ALPHA; + blendFuncSeparate[1] = WebGLConstants.ONE_MINUS_SRC_ALPHA; + blendFuncSeparate[2] = WebGLConstants.ONE; + blendFuncSeparate[3] = WebGLConstants.ONE_MINUS_SRC_ALPHA; + } + } + rendererRenderStates[id] = RenderState.fromCache({ frontFace : defined(statesFunctions.frontFace) ? statesFunctions.frontFace[0] : WebGLConstants.CCW, cull : {