Skip to content

Commit 2f3a731

Browse files
committed
fix(BillboardCollection): Fix precision loss in billboard image texcoords
1 parent 28213a7 commit 2f3a731

File tree

2 files changed

+45
-59
lines changed

2 files changed

+45
-59
lines changed

packages/engine/Source/Scene/BillboardCollection.js

Lines changed: 32 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ const SDF_INDEX = Billboard.SDF_INDEX;
5757
const SPLIT_DIRECTION_INDEX = Billboard.SPLIT_DIRECTION_INDEX;
5858
const NUMBER_OF_PROPERTIES = Billboard.NUMBER_OF_PROPERTIES;
5959

60+
const scratchTextureSize = new Cartesian2();
61+
6062
let attributeLocations;
6163

6264
const attributeLocationsBatched = {
6365
positionHighAndScale: 0,
6466
positionLowAndRotation: 1,
65-
compressedAttribute0: 2, // pixel offset, translate, horizontal origin, vertical origin, show, direction, texture coordinates
67+
compressedAttribute0: 2, // pixel offset, translate, horizontal origin, vertical origin, show, direction, image coordinates (px)
6668
compressedAttribute1: 3, // aligned axis, translucency by distance, image width
6769
compressedAttribute2: 4, // image height, color, pick color, size in meters, valid aligned axis, 13 bits free
6870
eyeOffset: 5, // 4 bytes free
@@ -78,11 +80,11 @@ const attributeLocationsBatched = {
7880
const attributeLocationsInstanced = {
7981
direction: 0,
8082
positionHighAndScale: 1,
81-
positionLowAndRotation: 2, // texture offset in w
82-
compressedAttribute0: 3,
83+
positionLowAndRotation: 2,
84+
compressedAttribute0: 3, // image lower-left coordinates (px) in w
8385
compressedAttribute1: 4,
8486
compressedAttribute2: 5,
85-
eyeOffset: 6, // texture range in w
87+
eyeOffset: 6,
8688
scaleByDistance: 7,
8789
pixelOffsetScaleByDistance: 8,
8890
compressedAttribute3: 9,
@@ -322,6 +324,10 @@ function BillboardCollection(options) {
322324
u_atlas: () => {
323325
return this.textureAtlas.texture;
324326
},
327+
u_atlasSize: () => {
328+
const { width, height } = this.textureAtlas.texture;
329+
return Cartesian2.fromElements(width, height, scratchTextureSize);
330+
},
325331
u_highlightColor: () => {
326332
return this._highlightColor;
327333
},
@@ -938,8 +944,6 @@ function writePositionScaleAndRotation(
938944
}
939945
}
940946

941-
const scratchCartesian2 = new Cartesian2();
942-
943947
const UPPER_BOUND = 32768.0; // 2^15
944948

945949
const LEFT_SHIFT16 = 65536.0; // 2^16
@@ -1003,22 +1007,22 @@ function writeCompressedAttrib0(
10031007
billboardCollection._allVerticalCenter &&
10041008
verticalOrigin === VerticalOrigin.CENTER;
10051009

1006-
let bottomLeftX = 0;
1007-
let bottomLeftY = 0;
1008-
let width = 0;
1009-
let height = 0;
1010+
// Compute image coordinates and size, in pixels. Coordinates are from lower-left of texture atlas.Z
1011+
let imageX = 0;
1012+
let imageY = 0;
1013+
let imageWidth = 0;
1014+
let imageHeight = 0;
10101015
if (billboard.ready) {
10111016
const imageRectangle = billboard.computeTextureCoordinates(
10121017
scratchBoundingRectangle,
10131018
);
1014-
1015-
bottomLeftX = imageRectangle.x;
1016-
bottomLeftY = imageRectangle.y;
1017-
width = imageRectangle.width;
1018-
height = imageRectangle.height;
1019+
const { width: atlasWidth, height: atlasHeight } =
1020+
billboardCollection.textureAtlas.texture;
1021+
imageX = imageRectangle.x * atlasWidth;
1022+
imageY = imageRectangle.y * atlasHeight;
1023+
imageWidth = imageRectangle.width * atlasWidth;
1024+
imageHeight = imageRectangle.height * atlasHeight;
10191025
}
1020-
const topRightX = bottomLeftX + width;
1021-
const topRightY = bottomLeftY + height;
10221026

10231027
let compressed0 =
10241028
Math.floor(
@@ -1055,52 +1059,44 @@ function writeCompressedAttrib0(
10551059
compressed1 += upperTranslateY;
10561060
compressed2 += lowerTranslateY;
10571061

1058-
scratchCartesian2.x = bottomLeftX;
1059-
scratchCartesian2.y = bottomLeftY;
1060-
const compressedTexCoordsLL =
1061-
AttributeCompression.compressTextureCoordinates(scratchCartesian2);
1062-
scratchCartesian2.x = topRightX;
1063-
const compressedTexCoordsLR =
1064-
AttributeCompression.compressTextureCoordinates(scratchCartesian2);
1065-
scratchCartesian2.y = topRightY;
1066-
const compressedTexCoordsUR =
1067-
AttributeCompression.compressTextureCoordinates(scratchCartesian2);
1068-
scratchCartesian2.x = bottomLeftX;
1069-
const compressedTexCoordsUL =
1070-
AttributeCompression.compressTextureCoordinates(scratchCartesian2);
1062+
const compressedImageLL = imageX * LEFT_SHIFT16 + imageY;
1063+
const compressedImageLR = (imageX + imageWidth) * LEFT_SHIFT16 + imageY;
1064+
const compressedImageUR =
1065+
(imageX + imageWidth) * LEFT_SHIFT16 + imageY + imageHeight;
1066+
const compressedImageUL = imageX * LEFT_SHIFT16 + imageY + imageHeight;
10711067

10721068
if (billboardCollection._instanced) {
10731069
i = billboard._index;
1074-
writer(i, compressed0, compressed1, compressed2, compressedTexCoordsLL);
1070+
writer(i, compressed0, compressed1, compressed2, compressedImageLL);
10751071
} else {
10761072
i = billboard._index * 4;
10771073
writer(
10781074
i + 0,
10791075
compressed0 + LOWER_LEFT,
10801076
compressed1,
10811077
compressed2,
1082-
compressedTexCoordsLL,
1078+
compressedImageLL,
10831079
);
10841080
writer(
10851081
i + 1,
10861082
compressed0 + LOWER_RIGHT,
10871083
compressed1,
10881084
compressed2,
1089-
compressedTexCoordsLR,
1085+
compressedImageLR,
10901086
);
10911087
writer(
10921088
i + 2,
10931089
compressed0 + UPPER_RIGHT,
10941090
compressed1,
10951091
compressed2,
1096-
compressedTexCoordsUR,
1092+
compressedImageUR,
10971093
);
10981094
writer(
10991095
i + 3,
11001096
compressed0 + UPPER_LEFT,
11011097
compressed1,
11021098
compressed2,
1103-
compressedTexCoordsUL,
1099+
compressedImageUL,
11041100
);
11051101
}
11061102
}
@@ -1254,23 +1250,8 @@ function writeEyeOffset(
12541250
);
12551251

12561252
if (billboardCollection._instanced) {
1257-
scratchCartesian2.x = 0;
1258-
scratchCartesian2.y = 0;
1259-
1260-
if (billboard.ready) {
1261-
const imageRectangle = billboard.computeTextureCoordinates(
1262-
scratchBoundingRectangle,
1263-
);
1264-
1265-
scratchCartesian2.x = imageRectangle.width;
1266-
scratchCartesian2.y = imageRectangle.height;
1267-
}
1268-
1269-
const compressedTexCoordsRange =
1270-
AttributeCompression.compressTextureCoordinates(scratchCartesian2);
1271-
12721253
i = billboard._index;
1273-
writer(i, eyeOffset.x, eyeOffset.y, eyeOffsetZ, compressedTexCoordsRange);
1254+
writer(i, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);
12741255
} else {
12751256
i = billboard._index * 4;
12761257
writer(i + 0, eyeOffset.x, eyeOffset.y, eyeOffsetZ, 0.0);

packages/engine/Source/Shaders/BillboardCollectionVS.glsl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
uniform vec2 u_atlasSize;
12
uniform float u_threePointDepthTestDistance;
23
#ifdef INSTANCED
34
in vec2 direction;
@@ -47,6 +48,7 @@ const float SHIFT_LEFT3 = 8.0;
4748
const float SHIFT_LEFT2 = 4.0;
4849
const float SHIFT_LEFT1 = 2.0;
4950

51+
const float SHIFT_RIGHT16 = 1.0 / 65536.0;
5052
const float SHIFT_RIGHT12 = 1.0 / 4096.0;
5153
const float SHIFT_RIGHT8 = 1.0 / 256.0;
5254
const float SHIFT_RIGHT7 = 1.0 / 128.0;
@@ -146,16 +148,10 @@ void main()
146148
float show = floor(compressed * SHIFT_RIGHT2);
147149
compressed -= show * SHIFT_LEFT2;
148150

149-
#ifdef INSTANCED
150-
vec2 textureCoordinatesBottomLeft = czm_decompressTextureCoordinates(compressedAttribute0.w);
151-
vec2 textureCoordinatesRange = czm_decompressTextureCoordinates(eyeOffset.w);
152-
vec2 textureCoordinates = textureCoordinatesBottomLeft + direction * textureCoordinatesRange;
153-
#else
151+
#ifndef INSTANCED
154152
vec2 direction;
155153
direction.x = floor(compressed * SHIFT_RIGHT1);
156154
direction.y = compressed - direction.x * SHIFT_LEFT1;
157-
158-
vec2 textureCoordinates = czm_decompressTextureCoordinates(compressedAttribute0.w);
159155
#endif
160156

161157
float temp = compressedAttribute0.y * SHIFT_RIGHT8;
@@ -177,6 +173,15 @@ void main()
177173

178174
vec2 imageSize = vec2(floor(temp), temp2);
179175

176+
float imageOffsetX = floor(compressedAttribute0.w * SHIFT_RIGHT16);
177+
float imageOffsetY = compressedAttribute0.w - (imageOffsetX * SHIFT_LEFT16);
178+
vec2 textureCoordinates = vec2(imageOffsetX, imageOffsetY) / u_atlasSize.xy;
179+
180+
#ifdef INSTANCED
181+
vec2 textureCoordinatesRange = imageSize.xy / u_atlasSize.xy;
182+
textureCoordinates += direction * textureCoordinatesRange;
183+
#endif
184+
180185
#ifdef FS_THREE_POINT_DEPTH_CHECK
181186
float labelHorizontalOrigin = floor(compressedAttribute2.w - (temp2 * SHIFT_LEFT2));
182187
float applyTranslate = 0.0;
@@ -328,7 +333,7 @@ void main()
328333
if (lengthSq < (u_threePointDepthTestDistance * u_threePointDepthTestDistance) && (enableDepthCheck == 1.0)) {
329334
float depthsilon = 10.0;
330335
vec2 depthOrigin;
331-
// Horizontal origin for labels comes from a special attribute. If that value is 0, this is a billboard - use the regular origin.
336+
// Horizontal origin for labels comes from a special attribute. If that value is 0, this is a billboard - use the regular origin.
332337
// Otherwise, transform the label origin to -1, 0, 1 (right, center, left).
333338
depthOrigin.x = floor(compressedAttribute2.w - (temp2 * SHIFT_LEFT2));
334339
depthOrigin.x = czm_branchFreeTernary(depthOrigin.x == 0.0, origin.x, depthOrigin.x - 2.0);

0 commit comments

Comments
 (0)