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

Commit

Permalink
[core] align raster imagery rendering to pixel rid to avoid blurriness
Browse files Browse the repository at this point in the history
  • Loading branch information
kkaefer committed Jan 24, 2018
1 parent 2502a3a commit 749e477
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 11 deletions.
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

0 comments on commit 749e477

Please sign in to comment.