Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Align projection matrix to pixel grids to draw crisp raster tiles #10984

Merged
merged 1 commit into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion mapbox-gl-js
Submodule mapbox-gl-js updated 81 files
+48 −0 debug/raster-streets.html
+36 −40 dist/mapbox-gl.css
+27 −7 src/geo/transform.js
+1 −1 src/render/draw_raster.js
+1 −1 src/style-spec/expression/is_constant.js
+1 −0 src/style/style.js
+12 −0 src/symbol/cross_tile_symbol_index.js
+6 −1 src/ui/control/attribution_control.js
+1 −1 src/ui/map.js
+ test/integration/render-tests/background-opacity/image/expected.png
+ test/integration/render-tests/combinations/background-opaque--raster-translucent/expected.png
+ test/integration/render-tests/combinations/background-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/circle-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/fill-extrusion-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/fill-opaque--raster-translucent/expected.png
+ test/integration/render-tests/combinations/fill-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/heatmap-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/hillshade-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/line-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--background-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--circle-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--fill-extrusion-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--fill-opaque/expected.png
+ test/integration/render-tests/combinations/raster-translucent--fill-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--heatmap-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--hillshade-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--line-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--raster-translucent/expected.png
+ test/integration/render-tests/combinations/raster-translucent--symbol-translucent/expected.png
+ test/integration/render-tests/combinations/symbol-translucent--raster-translucent/expected.png
+ test/integration/render-tests/image/default/expected.png
+ test/integration/render-tests/image/pitched/expected.png
+ test/integration/render-tests/image/raster-brightness/expected.png
+ test/integration/render-tests/image/raster-contrast/expected.png
+ test/integration/render-tests/image/raster-hue-rotate/expected.png
+ test/integration/render-tests/image/raster-opacity/expected.png
+ test/integration/render-tests/image/raster-saturation/expected.png
+ test/integration/render-tests/image/raster-visibility/expected.png
+ test/integration/render-tests/raster-alpha/default/expected.png
+ test/integration/render-tests/raster-brightness/default/expected.png
+ test/integration/render-tests/raster-brightness/function/expected.png
+ test/integration/render-tests/raster-brightness/literal/expected.png
+ test/integration/render-tests/raster-contrast/default/expected.png
+ test/integration/render-tests/raster-contrast/function/expected.png
+ test/integration/render-tests/raster-contrast/literal/expected.png
+ test/integration/render-tests/raster-hue-rotate/default/expected.png
+ test/integration/render-tests/raster-hue-rotate/function/expected.png
+ test/integration/render-tests/raster-hue-rotate/literal/expected.png
+ test/integration/render-tests/raster-loading/missing/expected.png
+ test/integration/render-tests/raster-masking/overlapping-zoom/expected.png
+ test/integration/render-tests/raster-masking/overlapping/expected.png
+ test/integration/render-tests/raster-opacity/default/expected.png
+ test/integration/render-tests/raster-opacity/function/expected.png
+ test/integration/render-tests/raster-opacity/literal/expected.png
+ test/integration/render-tests/raster-rotation/0/expected.png
+34 −0 test/integration/render-tests/raster-rotation/0/style.json
+ test/integration/render-tests/raster-rotation/180/expected.png
+34 −0 test/integration/render-tests/raster-rotation/180/style.json
+ test/integration/render-tests/raster-rotation/270/expected.png
+34 −0 test/integration/render-tests/raster-rotation/270/style.json
+ test/integration/render-tests/raster-rotation/45/expected.png
+34 −0 test/integration/render-tests/raster-rotation/45/style.json
+ test/integration/render-tests/raster-rotation/90/expected.png
+34 −0 test/integration/render-tests/raster-rotation/90/style.json
+ test/integration/render-tests/raster-saturation/default/expected.png
+ test/integration/render-tests/raster-saturation/function/expected.png
+ test/integration/render-tests/raster-saturation/literal/expected.png
+ test/integration/render-tests/raster-visibility/visible/expected.png
+ test/integration/render-tests/retina-raster/default/expected.png
+ test/integration/render-tests/runtime-styling/layer-add-raster/expected.png
+ test/integration/render-tests/runtime-styling/set-style-layer-add-raster/expected.png
+ test/integration/render-tests/runtime-styling/set-style-source-add-raster-inline/expected.png
+ test/integration/render-tests/runtime-styling/set-style-source-add-raster-url/expected.png
+ test/integration/render-tests/runtime-styling/source-add-raster-inline/expected.png
+ test/integration/render-tests/runtime-styling/source-add-raster-url/expected.png
+ test/integration/render-tests/video/default/expected.png
+ test/integration/render-tests/zoomed-raster/fractional/expected.png
+ test/integration/render-tests/zoomed-raster/overzoom/expected.png
+ test/integration/render-tests/zoomed-raster/underzoom/expected.png
+23 −0 test/unit/symbol/cross_tile_symbol_index.js
+36 −0 test/unit/ui/control/attribution.test.js
21 changes: 18 additions & 3 deletions src/mbgl/map/transform_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void TransformState::matrixFor(mat4& matrix, const UnwrappedTileID& tileID) cons
matrix::scale(matrix, matrix, s / util::EXTENT, s / util::EXTENT, 1);
}

void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {
void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ, bool aligned) const {
if (size.isEmpty()) {
return;
}
Expand Down Expand Up @@ -63,8 +63,8 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {

matrix::rotate_z(projMatrix, projMatrix, getAngle() + getNorthOrientationAngle());

matrix::translate(projMatrix, projMatrix, pixel_x() - size.width / 2.0f,
pixel_y() - size.height / 2.0f, 0);
const double dx = pixel_x() - size.width / 2.0f, dy = pixel_y() - size.height / 2.0f;
matrix::translate(projMatrix, projMatrix, dx, dy, 0);

if (axonometric) {
// mat[11] controls perspective
Expand All @@ -77,6 +77,21 @@ void TransformState::getProjMatrix(mat4& projMatrix, uint16_t nearZ) const {

matrix::scale(projMatrix, projMatrix, 1, 1,
1.0 / Projection::getMetersPerPixelAtLatitude(getLatLng(LatLng::Unwrapped).latitude(), getZoom()));

// Make a second projection matrix that is aligned to a pixel grid for rendering raster tiles.
// We're rounding the (floating point) x/y values to achieve to avoid rendering raster images to fractional
// coordinates. Additionally, we adjust by half a pixel in either direction in case that viewport dimension
// is an odd integer to preserve rendering to the pixel grid. We're rotating this shift based on the angle
// of the transformation so that 0°, 90°, 180°, and 270° rasters are crisp, and adjust the shift so that
// it is always <= 0.5 pixels.
if (aligned) {
const float xShift = float(size.width % 2) / 2, yShift = float(size.height % 2) / 2;
const double angleCos = std::cos(angle), angleSin = std::sin(angle);
double devNull;
const float dxa = -std::modf(dx, &devNull) + angleCos * xShift + angleSin * yShift;
const float dya = -std::modf(dy, &devNull) + angleCos * yShift + angleSin * xShift;
matrix::translate(projMatrix, projMatrix, dxa > 0.5 ? dxa - 1 : dxa, dya > 0.5 ? dya - 1 : dya, 0);
}
}

#pragma mark - Dimensions
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/map/transform_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class TransformState {

// Matrix
void matrixFor(mat4&, const UnwrappedTileID&) const;
void getProjMatrix(mat4& matrix, uint16_t nearZ = 1) const;
void getProjMatrix(mat4& matrix, uint16_t nearZ = 1, bool aligned = false) const;

// Dimensions
Size getSize() const;
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/renderer/layers/render_raster_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source

if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) {
// Draw only the parts of the tile that aren't drawn by another tile in the layer.
draw(tile.matrix,
draw(parameters.matrixForTile(tile.id, true),
*bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.segments);
} else {
// Draw the full tile.
draw(tile.matrix,
draw(parameters.matrixForTile(tile.id, true),
parameters.staticData.rasterVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.rasterSegments);
Expand Down
8 changes: 6 additions & 2 deletions src/mbgl/renderer/paint_parameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ PaintParameters::PaintParameters(gl::Context& context_,
// Update the default matrices to the current viewport dimensions.
state.getProjMatrix(projMatrix);

// Also compute a projection matrix that aligns with the current pixel grid, taking into account
// odd viewport sizes.
state.getProjMatrix(alignedProjMatrix, 1, true);

// Calculate a second projection matrix with the near plane clipped to 100 so as
// not to waste lots of depth buffer precision on very close empty space, for layer
// types (fill-extrusion) that use the depth buffer to emulate real-world space.
Expand All @@ -47,10 +51,10 @@ PaintParameters::PaintParameters(gl::Context& context_,
}
}

mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID) {
mat4 PaintParameters::matrixForTile(const UnwrappedTileID& tileID, bool aligned) const {
mat4 matrix;
state.matrixFor(matrix, tileID);
matrix::multiply(matrix, projMatrix, matrix);
matrix::multiply(matrix, aligned ? alignedProjMatrix : projMatrix, matrix);
return matrix;
}

Expand Down
3 changes: 2 additions & 1 deletion src/mbgl/renderer/paint_parameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ class PaintParameters {
gl::StencilMode stencilModeForClipping(const ClipID&) const;
gl::ColorMode colorModeForRenderPass() const;

mat4 matrixForTile(const UnwrappedTileID&);
mat4 matrixForTile(const UnwrappedTileID&, bool aligned = false) const;

mat4 projMatrix;
mat4 alignedProjMatrix;
mat4 nearClippedProjMatrix;

int numSublayers = 3;
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/renderer/sources/render_image_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void RenderImageSource::startRender(PaintParameters& parameters) {
mat4 matrix;
matrix::identity(matrix);
parameters.state.matrixFor(matrix, tileIds[i]);
matrix::multiply(matrix, parameters.projMatrix, matrix);
matrix::multiply(matrix, parameters.alignedProjMatrix, matrix);
matrices.push_back(matrix);
}

Expand Down