From da3593d859acbe09225c32f8346fe3dc189aa081 Mon Sep 17 00:00:00 2001 From: hpinkos Date: Tue, 12 Jun 2018 11:02:26 -0400 Subject: [PATCH] Better billboard depth test against terrain for clamped to ground --- Source/Scene/Billboard.js | 6 +- Source/Scene/BillboardCollection.js | 154 +++++++++++++++++++++- Source/Scene/LabelCollection.js | 19 ++- Source/Shaders/BillboardCollectionFS.glsl | 54 +++++++- Source/Shaders/BillboardCollectionVS.glsl | 53 ++++++++ 5 files changed, 272 insertions(+), 14 deletions(-) diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index df8ad223d024..9fe78e54580c 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -159,6 +159,8 @@ define([ this._imageWidth = undefined; this._imageHeight = undefined; + this._labelDimensions = undefined; + var image = options.image; var imageId = options.imageId; if (defined(image)) { @@ -210,7 +212,9 @@ define([ var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = 13; var DISTANCE_DISPLAY_CONDITION = Billboard.DISTANCE_DISPLAY_CONDITION = 14; var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE = 15; - Billboard.NUMBER_OF_PROPERTIES = 16; + Billboard.TEXTURE_OFFSET = 16; + Billboard.DIMENSIONS = 17; + Billboard.NUMBER_OF_PROPERTIES = 18; function makeDirty(billboard, propertyChanged) { var billboardCollection = billboard._billboardCollection; diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index 2382486d757a..2e9c17688580 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -84,8 +84,10 @@ define([ var SCALE_BY_DISTANCE_INDEX = Billboard.SCALE_BY_DISTANCE_INDEX; var TRANSLUCENCY_BY_DISTANCE_INDEX = Billboard.TRANSLUCENCY_BY_DISTANCE_INDEX; var PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX = Billboard.PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX; - var DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION_INDEX; + var DISTANCE_DISPLAY_CONDITION_INDEX = Billboard.DISTANCE_DISPLAY_CONDITION; var DISABLE_DEPTH_DISTANCE = Billboard.DISABLE_DEPTH_DISTANCE; + var TEXTURE_OFFSET = Billboard.TEXTURE_OFFSET; + var DIMENSIONS = Billboard.DIMENSIONS; var NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES; var attributeLocations; @@ -100,7 +102,9 @@ define([ scaleByDistance : 6, pixelOffsetScaleByDistance : 7, distanceDisplayConditionAndDisableDepth : 8, - a_batchId : 9 + textureOffset : 9, + dimensions : 10, + a_batchId : 11 }; var attributeLocationsInstanced = { @@ -114,7 +118,9 @@ define([ scaleByDistance : 7, pixelOffsetScaleByDistance : 8, distanceDisplayConditionAndDisableDepth : 9, - a_batchId : 10 + textureOffset : 10, + dimensions : 11, + a_batchId : 12 }; /** @@ -208,6 +214,9 @@ define([ this._shaderDisableDepthDistance = false; this._compiledShaderDisableDepthDistance = false; + this._shaderClampToGround = false; + this._compiledShaderClampToGround = false; + this._propertiesChanged = new Uint32Array(NUMBER_OF_PROPERTIES); this._maxSize = 0.0; @@ -302,7 +311,9 @@ define([ BufferUsage.STATIC_DRAW, // SCALE_BY_DISTANCE_INDEX BufferUsage.STATIC_DRAW, // TRANSLUCENCY_BY_DISTANCE_INDEX BufferUsage.STATIC_DRAW, // PIXEL_OFFSET_SCALE_BY_DISTANCE_INDEX - BufferUsage.STATIC_DRAW // DISTANCE_DISPLAY_CONDITION_INDEX + BufferUsage.STATIC_DRAW, // DISTANCE_DISPLAY_CONDITION_INDEX + BufferUsage.STATIC_DRAW, // TEXTURE_OFFSET + BufferUsage.STATIC_DRAW // DIMENSIONS ]; this._highlightColor = Color.clone(Color.WHITE); // Only used by Vector3DTilePoints @@ -736,6 +747,16 @@ define([ componentsPerAttribute : 3, componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX] + }, { + index : attributeLocations.textureOffset, + componentsPerAttribute : 4, + componentDatatype : ComponentDatatype.FLOAT, + usage : buffersUsage[TEXTURE_OFFSET] + }, { + index : attributeLocations.dimensions, + componentsPerAttribute : 2, + componentDatatype : ComponentDatatype.FLOAT, + usage : buffersUsage[DIMENSIONS] }]; // Instancing requires one non-instanced attribute. @@ -1176,6 +1197,10 @@ define([ } var disableDepthTestDistance = billboard.disableDepthTestDistance; + if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND && disableDepthTestDistance === 0.0) { + disableDepthTestDistance = 2000.0; + } + disableDepthTestDistance *= disableDepthTestDistance; if (disableDepthTestDistance > 0.0) { billboardCollection._shaderDisableDepthDistance = true; @@ -1196,6 +1221,96 @@ define([ } } + function writeTextureOffset(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) { + billboardCollection._shaderClampToGround = true; + } + var i; + var writer = vafWriters[attributeLocations.textureOffset]; + + var minX = 0; + var minY = 0; + var width = 0; + var height = 0; + var index = billboard._imageIndex; + if (index !== -1) { + var imageRectangle = textureAtlasCoordinates[index]; + + //>>includeStart('debug', pragmas.debug); + if (!defined(imageRectangle)) { + throw new DeveloperError('Invalid billboard image index: ' + index); + } + //>>includeEnd('debug'); + + minX = imageRectangle.x; + minY = imageRectangle.y; + width = imageRectangle.width; + height = imageRectangle.height; + } + var maxX = minX + width; + var maxY = minY + height; + + if (billboardCollection._instanced) { + i = billboard._index; + writer(i, minX, minY, maxX, maxY); + } else { + i = billboard._index * 4; + writer(i + 0, minX, minY, maxX, maxY); + writer(i + 1, minX, minY, maxX, maxY); + writer(i + 2, minX, minY, maxX, maxY); + writer(i + 3, minX, minY, maxX, maxY); + } + } + + function writeDimensions(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) { + billboardCollection._shaderClampToGround = true; + } + var i; + var writer = vafWriters[attributeLocations.dimensions]; + + var imageHeight; + var imageWidth; + + if (!defined(billboard._labelDimensions)) { + var height = 0; + var width = 0; + var index = billboard._imageIndex; + if (index !== -1) { + var imageRectangle = textureAtlasCoordinates[index]; + + //>>includeStart('debug', pragmas.debug); + if (!defined(imageRectangle)) { + throw new DeveloperError('Invalid billboard image index: ' + index); + } + //>>includeEnd('debug'); + + height = imageRectangle.height; + width = imageRectangle.width; + } + + var dimensions = billboardCollection._textureAtlas.texture.dimensions; + imageHeight = Math.round(defaultValue(billboard.height, dimensions.y * height)); + + var textureWidth = billboardCollection._textureAtlas.texture.width; + imageWidth = Math.round(defaultValue(billboard.width, textureWidth * width)); + } else { + imageWidth = billboard._labelDimensions.x; + imageHeight = billboard._labelDimensions.y; + } + + if (billboardCollection._instanced) { + i = billboard._index; + writer(i, imageWidth, imageHeight); + } else { + i = billboard._index * 2; + writer(i + 0, imageWidth, imageHeight); + writer(i + 1, imageWidth, imageHeight); + writer(i + 2, imageWidth, imageHeight); + writer(i + 3, imageWidth, imageHeight); + } + } + function writeBatchId(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { if (!defined(billboardCollection._batchTable)) { return; @@ -1226,6 +1341,8 @@ define([ writeScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writePixelOffsetScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeDistanceDisplayConditionAndDepthDisable(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeTextureOffset(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeDimensions(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeBatchId(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); } @@ -1421,10 +1538,18 @@ define([ writers.push(writePixelOffsetScaleByDistance); } - if (properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE]) { + if (properties[DISTANCE_DISPLAY_CONDITION_INDEX] || properties[DISABLE_DEPTH_DISTANCE] || properties[POSITION_INDEX]) { writers.push(writeDistanceDisplayConditionAndDepthDisable); } + if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) { + writers.push(writeTextureOffset); + } + + if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) { + writers.push(writeDimensions); + } + var numWriters = writers.length; vafWriters = this._vaf.writers; @@ -1540,7 +1665,8 @@ define([ (this._shaderTranslucencyByDistance !== this._compiledShaderTranslucencyByDistance) || (this._shaderPixelOffsetScaleByDistance !== this._compiledShaderPixelOffsetScaleByDistance) || (this._shaderDistanceDisplayCondition !== this._compiledShaderDistanceDisplayCondition) || - (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance)) { + (this._shaderDisableDepthDistance !== this._compiledShaderDisableDepthDistance) || + (this._shaderClampToGround !== this._compiledShaderClampToGround)) { vsSource = BillboardCollectionVS; fsSource = BillboardCollectionFS; @@ -1580,6 +1706,9 @@ define([ if (this._shaderDisableDepthDistance) { vs.defines.push('DISABLE_DEPTH_DISTANCE'); } + if (this._shaderClampToGround) { + vs.defines.push('CLAMP_TO_GROUND'); + } var vectorFragDefine = defined(this._batchTable) ? 'VECTOR_TILE' : ''; @@ -1588,6 +1717,9 @@ define([ defines : ['OPAQUE', vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._sp = ShaderProgram.replaceCache({ context : context, shaderProgram : this._sp, @@ -1600,6 +1732,9 @@ define([ defines : ['TRANSLUCENT', vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._spTranslucent = ShaderProgram.replaceCache({ context : context, shaderProgram : this._spTranslucent, @@ -1614,6 +1749,9 @@ define([ defines : [vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._sp = ShaderProgram.replaceCache({ context : context, shaderProgram : this._sp, @@ -1628,6 +1766,9 @@ define([ defines : [vectorFragDefine], sources : [fsSource] }); + if (this._shaderClampToGround) { + fs.defines.push('CLAMP_TO_GROUND'); + } this._spTranslucent = ShaderProgram.replaceCache({ context : context, shaderProgram : this._spTranslucent, @@ -1644,6 +1785,7 @@ define([ this._compiledShaderPixelOffsetScaleByDistance = this._shaderPixelOffsetScaleByDistance; this._compiledShaderDistanceDisplayCondition = this._shaderDistanceDisplayCondition; this._compiledShaderDisableDepthDistance = this._shaderDisableDepthDistance; + this._compiledShaderClampToGround = this._shaderClampToGround; } var commandList = frameState.commandList; diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index 5cdfa70bfee0..049810abdf32 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -249,6 +249,7 @@ define([ billboard = labelCollection._billboardCollection.add({ collection : labelCollection }); + billboard._labelDimensions = new Cartesian2(); } glyph.billboard = billboard; } @@ -306,10 +307,9 @@ define([ var glyphLength = glyphs.length; var backgroundBillboard = label._backgroundBillboard; - var backgroundPadding = scratchBackgroundPadding; - Cartesian2.clone( + var backgroundPadding = Cartesian2.clone( (defined(backgroundBillboard) ? label._backgroundPadding : Cartesian2.ZERO), - backgroundPadding); + scratchBackgroundPadding); for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { if (text.charAt(glyphIndex) === '\n') { @@ -341,6 +341,13 @@ define([ var widthOffset = calculateWidthOffset(lineWidth, horizontalOrigin, backgroundPadding); var lineSpacing = defaultLineSpacingPercent * maxLineHeight; var otherLinesHeight = lineSpacing * (numberOfLines - 1); + var totalLineWidth = maxLineWidth; + var totalLineHeight = maxLineHeight + otherLinesHeight; + + if (defined(backgroundBillboard)) { + totalLineWidth += (backgroundPadding.x * 2); + totalLineHeight += (backgroundPadding.y * 2); + } glyphPixelOffset.x = widthOffset * scale * resolutionScale; glyphPixelOffset.y = 0; @@ -371,6 +378,8 @@ define([ if (defined(glyph.billboard)) { glyph.billboard._setTranslate(glyphPixelOffset); + glyph.billboard._labelDimensions.x = totalLineWidth; + glyph.billboard._labelDimensions.y = totalLineHeight; } //Compute the next x offset taking into acocunt the kerning performed @@ -405,8 +414,8 @@ define([ } glyphPixelOffset.y = glyphPixelOffset.y * scale * resolutionScale; - backgroundBillboard.width = maxLineWidth + (backgroundPadding.x * 2); - backgroundBillboard.height = maxLineHeight + otherLinesHeight + (backgroundPadding.y * 2); + backgroundBillboard.width = totalLineWidth; + backgroundBillboard.height = totalLineHeight; backgroundBillboard._setTranslate(glyphPixelOffset); } } diff --git a/Source/Shaders/BillboardCollectionFS.glsl b/Source/Shaders/BillboardCollectionFS.glsl index 6d9a66cdcb7d..6fd371c897f0 100644 --- a/Source/Shaders/BillboardCollectionFS.glsl +++ b/Source/Shaders/BillboardCollectionFS.glsl @@ -5,10 +5,40 @@ uniform vec4 u_highlightColor; #endif varying vec2 v_textureCoordinates; - varying vec4 v_pickColor; varying vec4 v_color; +#ifdef CLAMP_TO_GROUND +varying vec4 v_textureOffset; +varying vec2 v_originTextureCoordinate; +varying vec2 v_leftTextureCoordinate; +varying vec2 v_rightTextureCoordinate; +varying vec2 v_dimensions; +varying vec2 v_imageSize; +varying vec2 v_translate; +varying float v_eyeDepth; +varying float v_disableDepthTestDistance; + +float getGlobeDepth(vec2 adjustedST, vec2 depthLookupST) +{ + vec2 a = v_imageSize.xy * (depthLookupST - adjustedST); + vec2 Dd = v_dimensions - v_imageSize; + vec2 px = v_translate.xy + (v_dimensions * v_originTextureCoordinate * vec2(0, -1)); // this is only needed for labels + + vec2 st = ((a - px + (depthLookupST * Dd)) + gl_FragCoord.xy) / czm_viewport.zw; + + float logDepthOrDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, st)); + + if (logDepthOrDepth == 0.0) + { + return 0.0; + } + + vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, logDepthOrDepth); + return eyeCoordinate.z / eyeCoordinate.w; +} +#endif + void main() { vec4 color = texture2D(u_atlas, v_textureCoordinates) * v_color; @@ -38,7 +68,27 @@ void main() #ifdef VECTOR_TILE color *= u_highlightColor; #endif - gl_FragColor = color; + czm_writeLogDepth(); + +#ifdef CLAMP_TO_GROUND + if (v_eyeDepth > -v_disableDepthTestDistance) { + vec2 adjustedST = v_textureCoordinates - v_textureOffset.xy; + adjustedST = adjustedST / vec2(v_textureOffset.z - v_textureOffset.x, v_textureOffset.w - v_textureOffset.y); + + float globeDepth1 = getGlobeDepth(adjustedST, v_originTextureCoordinate); + float globeDepth2 = getGlobeDepth(adjustedST, v_leftTextureCoordinate); + float globeDepth3 = getGlobeDepth(adjustedST, v_rightTextureCoordinate); + + float epsilonEyeDepth = v_eyeDepth + czm_epsilon5; + + // negative values go into the screen + if (globeDepth1 > epsilonEyeDepth && globeDepth2 > epsilonEyeDepth && globeDepth3 > epsilonEyeDepth) + { + discard; + } + } +#endif + } diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index 9c6f537e5856..e0f10233e967 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -10,11 +10,26 @@ attribute vec4 eyeOffset; // eye offset in mete attribute vec4 scaleByDistance; // near, nearScale, far, farScale attribute vec4 pixelOffsetScaleByDistance; // near, nearScale, far, farScale attribute vec3 distanceDisplayConditionAndDisableDepth; // near, far, disableDepthTestDistance +#ifdef CLAMP_TO_GROUND +attribute vec4 textureOffset; // the min and max x and y values for the texture coordinates +attribute vec2 dimensions; +#endif #ifdef VECTOR_TILE attribute float a_batchId; #endif varying vec2 v_textureCoordinates; +#ifdef CLAMP_TO_GROUND +varying vec4 v_textureOffset; +varying vec2 v_originTextureCoordinate; +varying vec2 v_leftTextureCoordinate; +varying vec2 v_rightTextureCoordinate; +varying vec2 v_dimensions; +varying vec2 v_imageSize; +varying vec2 v_translate; +varying float v_eyeDepth; +varying float v_disableDepthTestDistance; +#endif varying vec4 v_pickColor; varying vec4 v_color; @@ -116,6 +131,9 @@ void main() origin.y = floor(compressed * SHIFT_RIGHT3); compressed -= origin.y * SHIFT_LEFT3; +#ifdef CLAMP_TO_GROUND + vec2 depthOrigin = origin.xy * 0.5; +#endif origin -= vec2(1.0); float show = floor(compressed * SHIFT_RIGHT2); @@ -145,10 +163,34 @@ void main() translate.y += (temp - floor(temp)) * SHIFT_LEFT8; translate.y -= UPPER_BOUND; + v_translate = translate.xy; + temp = compressedAttribute1.x * SHIFT_RIGHT8; vec2 imageSize = vec2(floor(temp), compressedAttribute2.w); +#ifdef CLAMP_TO_GROUND + v_textureOffset = textureOffset; + v_originTextureCoordinate = vec2(1.0) - depthOrigin; //the origin + if (v_originTextureCoordinate.y == 1.0) //vertical origin is top + { + v_leftTextureCoordinate = vec2(0.0, 0.0); //bottom left + v_rightTextureCoordinate = vec2(1.0, 0.0); //bottom right + } + else + { + if (v_originTextureCoordinate.y == 0.0) + { + v_originTextureCoordinate.y = 0.1; + } + + v_leftTextureCoordinate = vec2(0.0, 1.0); //top left + v_rightTextureCoordinate = vec2(1.0, 1.0); //top right + } + v_dimensions = dimensions.xy; + v_imageSize = imageSize.xy; +#endif + #ifdef EYE_DISTANCE_TRANSLUCENCY vec4 translucencyByDistance; translucencyByDistance.x = compressedAttribute1.z; @@ -200,6 +242,11 @@ void main() vec4 p = czm_translateRelativeToEye(positionHigh, positionLow); vec4 positionEC = czm_modelViewRelativeToEye * p; + +#ifdef CLAMP_TO_GROUND + v_eyeDepth = positionEC.z; +#endif + positionEC = czm_eyeOffset(positionEC, eyeOffset.xyz); positionEC.xyz *= show; @@ -264,6 +311,11 @@ void main() #ifdef DISABLE_DEPTH_DISTANCE float disableDepthTestDistance = distanceDisplayConditionAndDisableDepth.z; + + #ifdef CLAMP_TO_GROUND + v_disableDepthTestDistance = disableDepthTestDistance; + #endif + if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0) { disableDepthTestDistance = czm_minimumDisableDepthTestDistance; @@ -289,4 +341,5 @@ void main() v_color = color; v_color.a *= translucency; + }