Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color blend mode support for vertex shaders #5874

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Source/Scene/Batched3DModel3DTileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ define([
var batchTable = content._batchTable;
var gltf = content._model.gltf;
var batchIdAttributeName = getBatchIdAttributeName(gltf);
var callback = batchTable.getVertexShaderCallback(true, batchIdAttributeName);
var diffuseUniformName = getAttributeOrUniformBySemantic(gltf, '_3DTILESDIFFUSE');
var callback = batchTable.getVertexShaderCallback(true, batchIdAttributeName, diffuseUniformName);
return defined(callback) ? callback(vs) : vs;
};
}
Expand Down
62 changes: 46 additions & 16 deletions Source/Scene/Cesium3DTileBatchTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -846,14 +846,14 @@ define([
'} \n';
}

Cesium3DTileBatchTable.prototype.getVertexShaderCallback = function(handleTranslucent, batchIdAttributeName) {
Cesium3DTileBatchTable.prototype.getVertexShaderCallback = function(handleTranslucent, batchIdAttributeName, diffuseUniformName) {
if (this.featuresLength === 0) {
return;
}

var that = this;
return function(source) {
var renamedSource = ShaderSource.replaceMain(source, 'tile_main');
var renamedSource = modifyDiffuse(source, diffuseUniformName, true);
var newMain;

if (ContextLimits.maximumVertexTextureImageUnits > 0) {
Expand All @@ -864,9 +864,9 @@ define([
'varying vec4 tile_featureColor; \n' +
'void main() \n' +
'{ \n' +
' tile_main(); \n' +
' vec2 st = computeSt(' + batchIdAttributeName + '); \n' +
' vec4 featureProperties = texture2D(tile_batchTexture, st); \n' +
' tile_color(featureProperties); \n' +
' float show = ceil(featureProperties.a); \n' + // 0 - false, non-zeo - true
' gl_Position *= show; \n'; // Per-feature show/hide
if (handleTranslucent) {
Expand All @@ -891,11 +891,12 @@ define([
' tile_featureColor = featureProperties; \n' +
'}';
} else {
// When VTF is not supported, color blend mode MIX will look incorrect due to the feature's color not being available in the vertex shader
newMain =
'varying vec2 tile_featureSt; \n' +
'void main() \n' +
'{ \n' +
' tile_main(); \n' +
' tile_color(vec4(1.0)); \n' +
' tile_featureSt = computeSt(' + batchIdAttributeName + '); \n' +
'}';
}
Expand All @@ -904,21 +905,37 @@ define([
};
};

function getHighlightOnlyShader(source) {
function getHighlightOnlyFragmentShader(source) {
source = ShaderSource.replaceMain(source, 'tile_main');
return source +
'uniform float tile_colorBlend; \n' +
'void tile_color(vec4 tile_featureColor) \n' +
'{ \n' +
' tile_main(); \n' +
' gl_FragColor *= tile_featureColor; \n' +
' gl_FragColor.a *= tile_featureColor.a; \n' +
' float highlight = ceil(tile_colorBlend); \n' +
' gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n' +
'} \n';
}

function modifyDiffuse(source, diffuseUniformName) {
function getPassthroughVertexShader(source) {
source = ShaderSource.replaceMain(source, 'tile_main');
return source +
'void tile_color(vec4 tile_featureColor) \n' +
'{ \n' +
' tile_main(); \n' +
'} \n';
}

function getDefaultShader(source, isVertexShader) {
return isVertexShader ? getPassthroughVertexShader(source) : getHighlightOnlyFragmentShader(source);
}

function modifyDiffuse(source, diffuseUniformName, isVertexShader) {
// If the glTF does not specify the _3DTILESDIFFUSE semantic, return a basic highlight shader.
// Otherwise if _3DTILESDIFFUSE is defined prefer the shader below that can switch the color mode at runtime.
if (!defined(diffuseUniformName)) {
return getHighlightOnlyShader(source);
return getDefaultShader(source, isVertexShader);
}

// Find the diffuse uniform. Examples matches:
Expand All @@ -929,7 +946,7 @@ define([

if (!defined(uniformMatch)) {
// Could not find uniform declaration of type vec3, vec4, or sampler2D
return getHighlightOnlyShader(source);
return getDefaultShader(source, isVertexShader);
}

var declaration = uniformMatch[0];
Expand All @@ -943,19 +960,29 @@ define([
// Replace: tile_colorBlend is 1.0 and the tile color is used
// Mix: tile_colorBlend is between 0.0 and 1.0, causing the source color and tile color to mix
var finalDiffuseFunction =
'bool isWhite(vec3 color) \n' +
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this because I noticed the == vec3(1.0) was too exact and started causing lots of aliasing on my desktop.

'{ \n' +
' return all(greaterThan(color, vec3(1.0 - czm_epsilon3))); \n' +
'} \n' +
'vec4 tile_diffuse_final(vec4 sourceDiffuse, vec4 tileDiffuse) \n' +
'{ \n' +
' vec4 blendDiffuse = mix(sourceDiffuse, tileDiffuse, tile_colorBlend); \n' +
' vec4 diffuse = (tileDiffuse.rgb == vec3(1.0)) ? sourceDiffuse : blendDiffuse; \n' +
' vec4 diffuse = isWhite(tileDiffuse.rgb) ? sourceDiffuse : blendDiffuse; \n' +
' return vec4(diffuse.rgb, sourceDiffuse.a); \n' +
'} \n';

// The color blend mode is intended for the RGB channels so alpha is always just multiplied.
// gl_FragColor is multiplied by the tile color only when tile_colorBlend is 0.0 (highlight)
var applyHighlight =
' gl_FragColor.a *= tile_featureColor.a; \n' +
' float highlight = ceil(tile_colorBlend); \n' +
' gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n';
var applyHighlight = '';

if (!isVertexShader) {
// Highlight color is always applied in the fragment shader, from either here or in getHighlightOnlyFragmentShader.
// No need to apply the highlight color in the vertex shader as well.
applyHighlight =
' gl_FragColor.a *= tile_featureColor.a; \n' +
' float highlight = ceil(tile_colorBlend); \n' +
' gl_FragColor.rgb *= mix(tile_featureColor.rgb, vec3(1.0), highlight); \n';
}

var setColor;
if (type === 'vec3' || type === 'vec4') {
Expand All @@ -968,7 +995,10 @@ define([
' tile_diffuse = tile_diffuse_final(source, tile_featureColor); \n' +
' tile_main(); \n';
} else if (type === 'sampler2D') {
regex = new RegExp('texture2D\\(' + diffuseUniformName + '.*?\\)', 'g');
// Regex handles up to one level of nested parentheses:
// E.g. texture2D(u_diffuse, uv)
// E.g. texture2D(u_diffuse, computeUV(index))
regex = new RegExp('texture2D\\(' + diffuseUniformName + '.*?(\\)\\)|\\))', 'g');
source = source.replace(regex, 'tile_diffuse_final($&, tile_diffuse)');
setColor =
' tile_diffuse = tile_featureColor; \n' +
Expand All @@ -995,7 +1025,7 @@ define([
return;
}
return function(source) {
source = modifyDiffuse(source, diffuseUniformName);
source = modifyDiffuse(source, diffuseUniformName, false);
if (ContextLimits.maximumVertexTextureImageUnits > 0) {
// When VTF is supported, per-feature show/hide already happened in the fragment shader
source +=
Expand Down
4 changes: 4 additions & 0 deletions Source/Scene/Cesium3DTileColorBlendMode.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ define([
* When <code>REPLACE</code> or <code>MIX</code> are used and the source color is a glTF material, the technique must assign the
* <code>_3DTILESDIFFUSE</code> semantic to the diffuse color parameter. Otherwise only <code>HIGHLIGHT</code> is supported.
* </p>
* <p>
* A feature whose color evaluates to white (1.0, 1.0, 1.0) is always rendered without color blending, regardless of the
* tileset's color blend mode.
* </p>
* <pre><code>
* "techniques": {
* "technique0": {
Expand Down
8 changes: 6 additions & 2 deletions Source/Scene/ModelInstanceCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,9 @@ define([
vertexShaderCached = instancedSource;

if (usesBatchTable) {
instancedSource = collection._batchTable.getVertexShaderCallback(true, 'a_batchId')(instancedSource);
var gltf = collection._model.gltf;
var diffuseUniformName = getAttributeOrUniformBySemantic(gltf, '_3DTILESDIFFUSE');
instancedSource = collection._batchTable.getVertexShaderCallback(true, 'a_batchId', diffuseUniformName)(instancedSource);
}

return instancedSource;
Expand Down Expand Up @@ -433,7 +435,9 @@ define([
function getVertexShaderNonInstancedCallback(collection) {
return function(vs) {
if (defined(collection._batchTable)) {
vs = collection._batchTable.getVertexShaderCallback(true, 'a_batchId')(vs);
var gltf = collection._model.gltf;
var diffuseUniformName = getAttributeOrUniformBySemantic(gltf, '_3DTILESDIFFUSE');
vs = collection._batchTable.getVertexShaderCallback(true, 'a_batchId', diffuseUniformName)(vs);
// Treat a_batchId as a uniform rather than a vertex attribute
vs = 'uniform float a_batchId\n;' + vs;
}
Expand Down
2 changes: 1 addition & 1 deletion Source/Scene/PointCloud3DTileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,7 @@ define([

if (hasBatchTable) {
// Batched points always use the HIGHLIGHT color blend mode
drawVS = batchTable.getVertexShaderCallback(false, 'a_batchId')(drawVS);
drawVS = batchTable.getVertexShaderCallback(false, 'a_batchId', undefined)(drawVS);
drawFS = batchTable.getFragmentShaderCallback(false, undefined)(drawFS);
}

Expand Down