diff --git a/Source/Scene/Billboard.js b/Source/Scene/Billboard.js index 0baeef037daf..4f36c5b63ba1 100644 --- a/Source/Scene/Billboard.js +++ b/Source/Scene/Billboard.js @@ -161,6 +161,7 @@ define([ this._labelDimensions = undefined; this._labelHorizontalOrigin = undefined; + this._labelTranslate = undefined; var image = options.image; var imageId = options.imageId; diff --git a/Source/Scene/BillboardCollection.js b/Source/Scene/BillboardCollection.js index 89bcb04985c7..42b114b284db 100644 --- a/Source/Scene/BillboardCollection.js +++ b/Source/Scene/BillboardCollection.js @@ -17,6 +17,7 @@ define([ '../Core/WebGLConstants', '../Renderer/Buffer', '../Renderer/BufferUsage', + '../Renderer/ContextLimits', '../Renderer/DrawCommand', '../Renderer/Pass', '../Renderer/RenderState', @@ -52,6 +53,7 @@ define([ WebGLConstants, Buffer, BufferUsage, + ContextLimits, DrawCommand, Pass, RenderState, @@ -101,7 +103,7 @@ define([ scaleByDistance : 6, pixelOffsetScaleByDistance : 7, compressedAttribute3 : 8, - textureCoordinateBounds : 9, + textureCoordinateBoundsOrLabelTranslate : 9, a_batchId : 10 }; @@ -116,7 +118,7 @@ define([ scaleByDistance : 7, pixelOffsetScaleByDistance : 8, compressedAttribute3 : 9, - textureCoordinateBounds : 10, + textureCoordinateBoundsOrLabelTranslate : 10, a_batchId : 11 }; @@ -744,7 +746,7 @@ define([ componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[DISTANCE_DISPLAY_CONDITION_INDEX] }, { - index : attributeLocations.textureCoordinateBounds, + index : attributeLocations.textureCoordinateBoundsOrLabelTranslate, componentsPerAttribute : 4, componentDatatype : ComponentDatatype.FLOAT, usage : buffersUsage[TEXTURE_COORDINATE_BOUNDS] @@ -1249,13 +1251,35 @@ define([ } } - function writeTextureCoordinateBounds(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { + function writeTextureCoordinateBoundsOrLabelTranslate(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard) { if (billboard.heightReference === HeightReference.CLAMP_TO_GROUND) { billboardCollection._shaderClampToGround = billboardCollection._scene.context.depthTexture; } var i; - var writer = vafWriters[attributeLocations.textureCoordinateBounds]; + var writer = vafWriters[attributeLocations.textureCoordinateBoundsOrLabelTranslate]; + + if (ContextLimits.maximumVertexTextureImageUnits > 0) { + //write _labelTranslate, used by depth testing in the vertex shader + var translateX = 0; + var translateY = 0; + if (defined(billboard._labelTranslate)) { + translateX = billboard._labelTranslate.x; + translateY = billboard._labelTranslate.y; + } + if (billboardCollection._instanced) { + i = billboard._index; + writer(i, translateX, translateY, 0.0, 0.0); + } else { + i = billboard._index * 4; + writer(i + 0, translateX, translateY, 0.0, 0.0); + writer(i + 1, translateX, translateY, 0.0, 0.0); + writer(i + 2, translateX, translateY, 0.0, 0.0); + writer(i + 3, translateX, translateY, 0.0, 0.0); + } + return; + } + //write texture coordinate bounds, used by depth testing in fragment shader var minX = 0; var minY = 0; var width = 0; @@ -1320,7 +1344,7 @@ define([ writeScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writePixelOffsetScaleByDistance(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeCompressedAttribute3(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); - writeTextureCoordinateBounds(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); + writeTextureCoordinateBoundsOrLabelTranslate(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); writeBatchId(billboardCollection, context, textureAtlasCoordinates, vafWriters, billboard); } @@ -1521,7 +1545,7 @@ define([ } if (properties[IMAGE_INDEX_INDEX] || properties[POSITION_INDEX]) { - writers.push(writeTextureCoordinateBounds); + writers.push(writeTextureCoordinateBoundsOrLabelTranslate); } var numWriters = writers.length; @@ -1632,6 +1656,8 @@ define([ var fs; var vertDefines; + var supportVSTextureReads = ContextLimits.maximumVertexTextureImageUnits > 0; + if (blendOptionChanged || (this._shaderRotation !== this._compiledShaderRotation) || (this._shaderAlignedAxis !== this._compiledShaderAlignedAxis) || @@ -1681,7 +1707,11 @@ define([ vs.defines.push('DISABLE_DEPTH_DISTANCE'); } if (this._shaderClampToGround) { - vs.defines.push('CLAMP_TO_GROUND'); + if (supportVSTextureReads) { + vs.defines.push('VERTEX_DEPTH_CHECK'); + } else { + vs.defines.push('FRAGMENT_DEPTH_CHECK'); + } } var vectorFragDefine = defined(this._batchTable) ? 'VECTOR_TILE' : ''; @@ -1692,7 +1722,11 @@ define([ sources : [fsSource] }); if (this._shaderClampToGround) { - fs.defines.push('CLAMP_TO_GROUND'); + if (supportVSTextureReads) { + fs.defines.push('VERTEX_DEPTH_CHECK'); + } else { + fs.defines.push('FRAGMENT_DEPTH_CHECK'); + } } this._sp = ShaderProgram.replaceCache({ context : context, @@ -1707,7 +1741,11 @@ define([ sources : [fsSource] }); if (this._shaderClampToGround) { - fs.defines.push('CLAMP_TO_GROUND'); + if (supportVSTextureReads) { + fs.defines.push('VERTEX_DEPTH_CHECK'); + } else { + fs.defines.push('FRAGMENT_DEPTH_CHECK'); + } } this._spTranslucent = ShaderProgram.replaceCache({ context : context, @@ -1724,7 +1762,11 @@ define([ sources : [fsSource] }); if (this._shaderClampToGround) { - fs.defines.push('CLAMP_TO_GROUND'); + if (supportVSTextureReads) { + fs.defines.push('VERTEX_DEPTH_CHECK'); + } else { + fs.defines.push('FRAGMENT_DEPTH_CHECK'); + } } this._sp = ShaderProgram.replaceCache({ context : context, @@ -1741,7 +1783,11 @@ define([ sources : [fsSource] }); if (this._shaderClampToGround) { - fs.defines.push('CLAMP_TO_GROUND'); + if (supportVSTextureReads) { + fs.defines.push('VERTEX_DEPTH_CHECK'); + } else { + fs.defines.push('FRAGMENT_DEPTH_CHECK'); + } } this._spTranslucent = ShaderProgram.replaceCache({ context : context, diff --git a/Source/Scene/LabelCollection.js b/Source/Scene/LabelCollection.js index e6ac85dab71a..2c2ea2c277bc 100644 --- a/Source/Scene/LabelCollection.js +++ b/Source/Scene/LabelCollection.js @@ -11,6 +11,7 @@ define([ '../Core/writeTextToCanvas', './BillboardCollection', './BlendOption', + './HeightReference', './HorizontalOrigin', './Label', './LabelStyle', @@ -29,6 +30,7 @@ define([ writeTextToCanvas, BillboardCollection, BlendOption, + HeightReference, HorizontalOrigin, Label, LabelStyle, @@ -250,6 +252,7 @@ define([ collection : labelCollection }); billboard._labelDimensions = new Cartesian2(); + billboard._labelTranslate = new Cartesian2(); } glyph.billboard = billboard; } @@ -303,7 +306,7 @@ define([ var maxGlyphDescent = Number.NEGATIVE_INFINITY; var maxGlyphY = 0; var numberOfLines = 1; - var glyphIndex = 0; + var glyphIndex; var glyphLength = glyphs.length; var backgroundBillboard = label._backgroundBillboard; @@ -384,7 +387,7 @@ define([ glyph.billboard._labelHorizontalOrigin = horizontalOrigin; } - //Compute the next x offset taking into acocunt the kerning performed + //Compute the next x offset taking into account the kerning performed //on both the current letter as well as the next letter to be drawn //as well as any applied scale. if (glyphIndex < glyphLength - 1) { @@ -419,6 +422,17 @@ define([ backgroundBillboard.width = totalLineWidth; backgroundBillboard.height = totalLineHeight; backgroundBillboard._setTranslate(glyphPixelOffset); + backgroundBillboard._labelTranslate = Cartesian2.clone(glyphPixelOffset, backgroundBillboard._labelTranslate); + } + + if (label.heightReference === HeightReference.CLAMP_TO_GROUND) { + for (glyphIndex = 0; glyphIndex < glyphLength; ++glyphIndex) { + glyph = glyphs[glyphIndex]; + var billboard = glyph.billboard; + if (defined(billboard)) { + billboard._labelTranslate = Cartesian2.clone(glyphPixelOffset, billboard._labelTranslate); + } + } } } diff --git a/Source/Shaders/BillboardCollectionFS.glsl b/Source/Shaders/BillboardCollectionFS.glsl index e306c2735cf5..853a448d94a9 100644 --- a/Source/Shaders/BillboardCollectionFS.glsl +++ b/Source/Shaders/BillboardCollectionFS.glsl @@ -8,7 +8,7 @@ varying vec2 v_textureCoordinates; varying vec4 v_pickColor; varying vec4 v_color; -#ifdef CLAMP_TO_GROUND +#ifdef FRAGMENT_DEPTH_CHECK varying vec4 v_textureCoordinateBounds; // the min and max x and y values for the texture coordinates varying vec4 v_originTextureCoordinateAndTranslate; // texture coordinate at the origin, billboard translate (used for label glyphs) varying vec4 v_dimensionsAndImageSize; // dimensions of the bounding rectangle and the size of the image. The values will only be different for label glyphs @@ -79,8 +79,8 @@ void main() czm_writeLogDepth(); -#ifdef CLAMP_TO_GROUND - if (v_eyeDepthDistanceAndApplyTranslate.x > -v_eyeDepthDistanceAndApplyTranslate.y) { +#ifdef FRAGMENT_DEPTH_CHECK + if (v_eyeDepthDistanceAndApplyTranslate.y != 0.0) { vec2 adjustedST = v_textureCoordinates - v_textureCoordinateBounds.xy; adjustedST = adjustedST / vec2(v_textureCoordinateBounds.z - v_textureCoordinateBounds.x, v_textureCoordinateBounds.w - v_textureCoordinateBounds.y); diff --git a/Source/Shaders/BillboardCollectionVS.glsl b/Source/Shaders/BillboardCollectionVS.glsl index 857d7368b59f..01e36c3dfb5a 100644 --- a/Source/Shaders/BillboardCollectionVS.glsl +++ b/Source/Shaders/BillboardCollectionVS.glsl @@ -10,15 +10,15 @@ attribute vec4 eyeOffset; // eye offset in mete attribute vec4 scaleByDistance; // near, nearScale, far, farScale attribute vec4 pixelOffsetScaleByDistance; // near, nearScale, far, farScale attribute vec4 compressedAttribute3; // distance display condition near, far, disableDepthTestDistance, dimensions -#ifdef CLAMP_TO_GROUND -attribute vec4 textureCoordinateBounds; // the min and max x and y values for the texture coordinates +#if defined(VERTEX_DEPTH_CHECK) || defined(FRAGMENT_DEPTH_CHECK) +attribute vec4 textureCoordinateBoundsOrLabelTranslate; // the min and max x and y values for the texture coordinates #endif #ifdef VECTOR_TILE attribute float a_batchId; #endif varying vec2 v_textureCoordinates; -#ifdef CLAMP_TO_GROUND +#ifdef FRAGMENT_DEPTH_CHECK varying vec4 v_textureCoordinateBounds; varying vec4 v_originTextureCoordinateAndTranslate; varying vec4 v_dimensionsAndImageSize; @@ -48,6 +48,76 @@ const float SHIFT_RIGHT3 = 1.0 / 8.0; const float SHIFT_RIGHT2 = 1.0 / 4.0; const float SHIFT_RIGHT1 = 1.0 / 2.0; +vec4 addScreenSpaceOffset(vec4 positionEC, vec2 imageSize, float scale, vec2 direction, vec2 origin, vec2 translate, vec2 pixelOffset, vec3 alignedAxis, bool validAlignedAxis, float rotation, bool sizeInMeters, out mat2 rotationMatrix, out float mpp) +{ + // Note the halfSize cannot be computed in JavaScript because it is sent via + // compressed vertex attributes that coerce it to an integer. + vec2 halfSize = imageSize * scale * czm_resolutionScale * 0.5; + halfSize *= ((direction * 2.0) - 1.0); + + vec2 originTranslate = origin * abs(halfSize); + +#if defined(ROTATION) || defined(ALIGNED_AXIS) + if (validAlignedAxis || rotation != 0.0) + { + float angle = rotation; + if (validAlignedAxis) + { + vec4 projectedAlignedAxis = czm_modelViewProjection * vec4(alignedAxis, 0.0); + angle += sign(-projectedAlignedAxis.x) * acos(sign(projectedAlignedAxis.y) * (projectedAlignedAxis.y * projectedAlignedAxis.y) / + (projectedAlignedAxis.x * projectedAlignedAxis.x + projectedAlignedAxis.y * projectedAlignedAxis.y)); + } + + float cosTheta = cos(angle); + float sinTheta = sin(angle); + rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta); + halfSize = rotationMatrix * halfSize; + } + else + { + rotationMatrix = mat2(1.0, 0.0, 0.0, 1.0); + } +#endif + + if (sizeInMeters) + { + positionEC.xy += halfSize; + } + + mpp = czm_metersPerPixel(positionEC); + + if (!sizeInMeters) + { + originTranslate *= mpp; + } + + positionEC.xy += originTranslate; + if (!sizeInMeters) + { + positionEC.xy += halfSize * mpp; + } + + positionEC.xy += translate * mpp; + positionEC.xy += (pixelOffset * czm_resolutionScale) * mpp; + return positionEC; +} + +#ifdef VERTEX_DEPTH_CHECK +float getGlobeDepth(vec4 positionEC) +{ + vec4 posWC = czm_eyeToWindowCoordinates(positionEC); + + float globeDepth = czm_unpackDepth(texture2D(czm_globeDepthTexture, posWC.xy / czm_viewport.zw)); + + if (globeDepth == 0.0) + { + return 0.0; // not on the globe + } + + vec4 eyeCoordinate = czm_windowToEyeCoordinates(posWC.xy, globeDepth); + return eyeCoordinate.z / eyeCoordinate.w; +} +#endif void main() { // Modifying this shader may also require modifications to Billboard._computeScreenSpacePosition @@ -77,7 +147,7 @@ void main() origin.y = floor(compressed * SHIFT_RIGHT3); compressed -= origin.y * SHIFT_LEFT3; -#ifdef CLAMP_TO_GROUND +#ifdef FRAGMENT_DEPTH_CHECK vec2 depthOrigin = origin.xy; #endif origin -= vec2(1.0); @@ -114,7 +184,7 @@ void main() vec2 imageSize = vec2(floor(temp), temp2); -#ifdef CLAMP_TO_GROUND +#ifdef FRAGMENT_DEPTH_CHECK float labelHorizontalOrigin = floor(compressedAttribute2.w - (temp2 * SHIFT_LEFT2)); float applyTranslate = 0.0; if (labelHorizontalOrigin != 0.0) // is a billboard, so set apply translate to false @@ -125,9 +195,11 @@ void main() } depthOrigin = vec2(1.0) - (depthOrigin * 0.5); - +#endif +#if defined(VERTEX_DEPTH_CHECK) || defined(FRAGMENT_DEPTH_CHECK) temp = compressedAttribute3.w; temp = temp * SHIFT_RIGHT12; + vec2 dimensions; dimensions.y = (temp - floor(temp)) * SHIFT_LEFT12; dimensions.x = floor(temp); @@ -185,9 +257,8 @@ void main() vec4 p = czm_translateRelativeToEye(positionHigh, positionLow); vec4 positionEC = czm_modelViewRelativeToEye * p; -#ifdef CLAMP_TO_GROUND +#if defined(FRAGMENT_DEPTH_CHECK) || defined(VERTEX_DEPTH_CHECK) float eyeDepth = positionEC.z; - v_rotationMatrix = mat2(1.0, 0.0, 0.0, 1.0); #endif positionEC = czm_eyeOffset(positionEC, eyeOffset.xyz); @@ -244,55 +315,38 @@ void main() } #endif - // Note the halfSize cannot be computed in JavaScript because it is sent via - // compressed vertex attributes that coerce it to an integer. - vec2 halfSize = imageSize * scale * czm_resolutionScale * 0.5; - halfSize *= ((direction * 2.0) - 1.0); - - vec2 originTranslate = origin * abs(halfSize); - -#if defined(ROTATION) || defined(ALIGNED_AXIS) - if (validAlignedAxis || rotation != 0.0) - { - float angle = rotation; - if (validAlignedAxis) - { - vec4 projectedAlignedAxis = czm_modelViewProjection * vec4(alignedAxis, 0.0); - angle += sign(-projectedAlignedAxis.x) * acos( sign(projectedAlignedAxis.y) * (projectedAlignedAxis.y * projectedAlignedAxis.y) / - (projectedAlignedAxis.x * projectedAlignedAxis.x + projectedAlignedAxis.y * projectedAlignedAxis.y) ); - } - - float cosTheta = cos(angle); - float sinTheta = sin(angle); - mat2 rotationMatrix = mat2(cosTheta, sinTheta, -sinTheta, cosTheta); - halfSize = rotationMatrix * halfSize; + mat2 rotationMatrix; + float mpp; - #ifdef CLAMP_TO_GROUND - v_rotationMatrix = rotationMatrix; - #endif - } +#ifdef DISABLE_DEPTH_DISTANCE + float disableDepthTestDistance = compressedAttribute3.z; #endif - if (sizeInMeters) - { - positionEC.xy += halfSize; - } - - float mpp = czm_metersPerPixel(positionEC); +#ifdef VERTEX_DEPTH_CHECK +if (lengthSq < disableDepthTestDistance) { + vec2 labelTranslate = textureCoordinateBoundsOrLabelTranslate.xy; + vec4 pEC1 = addScreenSpaceOffset(positionEC, dimensions, scale, vec2(0.0), origin, labelTranslate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp); + float globeDepth1 = getGlobeDepth(pEC1); - if (!sizeInMeters) + if (globeDepth1 != 0.0 && pEC1.z < globeDepth1) { - originTranslate *= mpp; - } + vec4 pEC2 = addScreenSpaceOffset(positionEC, dimensions, scale, vec2(0.0, 1.0), origin, labelTranslate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp); + float globeDepth2 = getGlobeDepth(pEC2); - positionEC.xy += originTranslate; - if (!sizeInMeters) - { - positionEC.xy += halfSize * mpp; + if (globeDepth2 != 0.0 && pEC2.z < globeDepth2) + { + vec4 pEC3 = addScreenSpaceOffset(positionEC, dimensions, scale, vec2(1.0), origin, labelTranslate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp); + float globeDepth3 = getGlobeDepth(pEC3); + if (globeDepth3 != 0.0 && pEC3.z < globeDepth3) + { + positionEC.xyz = vec3(0.0); + } + } } +} +#endif - positionEC.xy += translate * mpp; - positionEC.xy += (pixelOffset * czm_resolutionScale) * mpp; + positionEC = addScreenSpaceOffset(positionEC, imageSize, scale, direction, origin, translate, pixelOffset, alignedAxis, validAlignedAxis, rotation, sizeInMeters, rotationMatrix, mpp); gl_Position = czm_projection * positionEC; v_textureCoordinates = textureCoordinates; @@ -301,8 +355,6 @@ void main() #endif #ifdef DISABLE_DEPTH_DISTANCE - float disableDepthTestDistance = compressedAttribute3.z; - if (disableDepthTestDistance == 0.0 && czm_minimumDisableDepthTestDistance != 0.0) { disableDepthTestDistance = czm_minimumDisableDepthTestDistance; @@ -324,18 +376,33 @@ void main() } #endif -#ifdef CLAMP_TO_GROUND +#ifdef FRAGMENT_DEPTH_CHECK if (sizeInMeters) { translate /= mpp; dimensions /= mpp; imageSize /= mpp; } + +#if defined(ROTATION) || defined(ALIGNED_AXIS) + v_rotationMatrix = rotationMatrix; +#else + v_rotationMatrix = mat2(1.0, 0.0, 0.0, 1.0); +#endif + v_eyeDepthDistanceAndApplyTranslate.x = eyeDepth; - v_eyeDepthDistanceAndApplyTranslate.y = disableDepthTestDistance; + if (lengthSq < disableDepthTestDistance) + { + v_eyeDepthDistanceAndApplyTranslate.y = 1.0; + } + else + { + v_eyeDepthDistanceAndApplyTranslate.y = 0.0; + } + v_eyeDepthDistanceAndApplyTranslate.z = applyTranslate; v_originTextureCoordinateAndTranslate.xy = depthOrigin; v_originTextureCoordinateAndTranslate.zw = translate; - v_textureCoordinateBounds = textureCoordinateBounds; + v_textureCoordinateBounds = textureCoordinateBoundsOrLabelTranslate; v_dimensionsAndImageSize.xy = dimensions * scale; v_dimensionsAndImageSize.zw = imageSize * scale; #endif