Browse Source

Optimize multires tile display.

Previously, a single multires tile level was used for the entire viewport,
which resulted in too much resolution at the center and too little at the
edges. This was particularly noticable when zoomed out all the way.

The new approach selectively loads tiles based on their individual projected
resolution, which is more efficient and should resolve long-standing
edge-softness issues.
pull/1121/head
Matthew Petroff 2 years ago
parent
commit
05dae19b28
1 changed files with 59 additions and 34 deletions
  1. +59
    -34
      src/js/libpannellum.js

+ 59
- 34
src/js/libpannellum.js View File

@@ -538,8 +538,6 @@ function Renderer(container, context) {
program.cubeUniform = gl.getUniformLocation(program, 'u_cubeMatrix');
//program.colorUniform = gl.getUniformLocation(program, 'u_color');

program.level = -1;

program.currentNodes = [];
program.nodeCache = [];
program.nodeCacheTimestamp = 0;
@@ -882,9 +880,7 @@ function Renderer(container, context) {

// Create perspective matrix
var perspMatrix = makePersp(hfov, gl.drawingBufferWidth / gl.drawingBufferHeight, 0.1, 100.0);
// Find correct zoom level
checkZoom(hfov);
var perspMatrixNoClip = makePersp(hfov, gl.drawingBufferWidth / gl.drawingBufferHeight, -100.0, 100.0);
// Create rotation matrix
var matrix = identityMatrix3();
@@ -899,6 +895,7 @@ function Renderer(container, context) {
// Find current nodes
var rotPersp = rotatePersp(perspMatrix, matrix);
var rotPerspNoClip = rotatePersp(perspMatrixNoClip, matrix);
program.nodeCache.sort(multiresNodeSort);
if (program.nodeCache.length > 200 &&
program.nodeCache.length > program.currentNodes.length + 50) {
@@ -913,7 +910,7 @@ function Renderer(container, context) {
for (s = 0; s < 6; s++) {
var ntmp = new MultiresNode(vtmps[s], sides[s], 1, 0, 0, image.fullpath, null);
testMultiresNode(rotPersp, ntmp, pitch, yaw, hfov);
testMultiresNode(rotPersp, rotPerspNoClip, ntmp, pitch, yaw, hfov);
}
program.currentNodes.sort(multiresNodeRenderSort);
@@ -1058,12 +1055,18 @@ function Renderer(container, context) {
gl.clear(gl.COLOR_BUFFER_BIT);
// Determine tiles that need to be drawn
var node_paths = {};
for (var i = 0; i < program.currentNodes.length; i++)
node_paths[program.currentNodes[i].parentPath] |= !(program.currentNodes[i].textureLoaded > 1); // !(undefined > 1) != (undefined <= 1)
for (var i = 0; i < program.currentNodes.length; i++) {
if (node_paths[program.currentNodes[i].parentPath] === undefined)
node_paths[program.currentNodes[i].parentPath] = 0
node_paths[program.currentNodes[i].parentPath] += !(program.currentNodes[i].textureLoaded > 1); // !(undefined > 1) != (undefined <= 1)
}
// Draw tiles
for (var i = 0; i < program.currentNodes.length; i++) {
// This optimization that doesn't draw a node if all its children
// will be drawn ignores the fact that some nodes don't have
// four children; these tiles are always drawn.
if (program.currentNodes[i].textureLoaded > 1 &&
node_paths[program.currentNodes[i].path] != 0) { // 1 or undefined
node_paths[program.currentNodes[i].path] != 4) {
//var color = program.currentNodes[i].color;
//gl.uniform4f(program.colorUniform, color[0], color[1], color[2], 1.0);
@@ -1106,13 +1109,55 @@ function Renderer(container, context) {
* load its texture, and load appropriate child nodes.
* @private
* @param {number[]} rotPersp - Rotated perspective matrix.
* @param {number[]} rotPersp - Rotated perspective matrix without clipping behind camera.
* @param {MultiresNode} node - Multires node to check.
* @param {number} pitch - Pitch to check at.
* @param {number} yaw - Yaw to check at.
* @param {number} hfov - Horizontal field of view to check at.
*/
function testMultiresNode(rotPersp, node, pitch, yaw, hfov) {
function testMultiresNode(rotPersp, rotPerspNoClip, node, pitch, yaw, hfov) {
if (checkSquareInView(rotPersp, node.vertices)) {
// In order to determine if this tile resolution needs to be loaded
// for this node, start by calculating positions of node corners
// and checking if they're in view
var cornersWinX = [],
cornersWinY = [],
minCornersWinZ = 2,
cornersInView = [],
numCornersInView = 0;
for (var i = 0; i < 4; i++) {
var corner = applyRotPerspToVec(rotPerspNoClip, node.vertices.slice(i * 3, (i + 1) * 3));
cornersWinX.push(corner[0] * corner[3]);
cornersWinY.push(corner[1] * corner[3]);
var cornerWinZ = corner[2] * corner[3];
minCornersWinZ = Math.min(minCornersWinZ, cornerWinZ);
cornersInView.push(Math.abs(cornersWinX[i]) <= 1 && Math.abs(cornersWinY[i]) <= 1 && cornerWinZ > 0);
numCornersInView += cornersInView[i];
}

// Tiles should always be loaded if they're base tiles, in the
// extremely-wide FOV edge case when a node corner is behind the
// camera, and when no node corners are in the viewport. In other
// cases, additional checks are required.
if (node.level > 1 && minCornersWinZ > 0 && numCornersInView > 0) {
// Calculate length of node sides that are at least partly in view
var maxSide = 0;
for (var i = 0; i < 4; i++) {
var j = (i + 1) % 4;
if (cornersInView[i] || cornersInView[j]) {
var diffX = (cornersWinX[j] - cornersWinX[i]) * gl.drawingBufferWidth / 2,
diffY = (cornersWinY[j] - cornersWinY[i]) * gl.drawingBufferHeight / 2;
// TODO: should non-square tiles have special handling?
maxSide = Math.max(maxSide, Math.sqrt(diffX * diffX + diffY * diffY));
}
}
// Don't load tile if the largest node side is smaller than
// half the tile resolution, since the parent node is smaller
// than the parent tile in this case
if (maxSide <= image.tileResolution / 2)
return;
}

// Calculate central angle between center of view and center of tile
var v = node.vertices;
var x = v[0] + v[3] + v[6] + v[ 9];
@@ -1143,10 +1188,9 @@ function Renderer(container, context) {
program.currentNodes.push(node);
program.nodeCache.push(node);
}
// TODO: Test error

// Create child nodes
if (node.level < program.level) {
if (node.level < image.maxLevel) {
var cubeSize = image.cubeResolution * Math.pow(2, node.level - image.maxLevel);
var numTiles = Math.ceil(cubeSize * image.invTileResolution) - 1;
var doubleTileSize = cubeSize % image.tileResolution * 2;
@@ -1243,7 +1287,7 @@ function Renderer(container, context) {
children.push(ntmp);
}
for (var j = 0; j < children.length; j++) {
testMultiresNode(rotPersp, children[j], pitch, yaw, hfov);
testMultiresNode(rotPersp, rotPerspNoClip, children[j], pitch, yaw, hfov);
}
}
}
@@ -1507,24 +1551,6 @@ function Renderer(container, context) {
} else {
processNextTile = processNextTileFallback;
}

/**
* Finds and applies optimal multires zoom level.
* @private
* @param {number} hfov - Horizontal field of view to check at.
*/
function checkZoom(hfov) {
// Find optimal level
var newLevel = 1;
while ( newLevel < image.maxLevel &&
gl.drawingBufferWidth > image.tileResolution *
Math.pow(2, newLevel - 1) * Math.tan(hfov / 2) * 0.707 ) {
newLevel++;
}
// Apply change
program.level = newLevel;
}
/**
* Rotates perspective matrix.
@@ -1607,8 +1633,6 @@ function Renderer(container, context) {
return false;
var testZ = check1[2] + check2[2] + check3[2] + check4[2];
return testZ != 4;

}

/**
@@ -1890,6 +1914,7 @@ var fragMulti = [
'void main(void) {',
// Look up color from texture
'gl_FragColor = texture2D(u_sampler, v_texCoord);',
// 'if(v_texCoord.x > 0.99 || v_texCoord.y > 0.99 || v_texCoord.x < 0.01 || v_texCoord.y < 0.01) {gl_FragColor = vec4(0.0,0.0,0.0,1.0);}', // Draw tile edges
// 'gl_FragColor = u_color;',
'}'
].join('');


Loading…
Cancel
Save