Browse Source

muli resolution equirectangular type (multiresrec)

shaders,drawing procedure, and visible tile determination
pull/880/head
NiHoel 6 years ago
parent
commit
1080e672fe
2 changed files with 521 additions and 27 deletions
  1. +519
    -25
      src/js/libpannellum.js
  2. +2
    -2
      src/js/pannellum.js

+ 519
- 25
src/js/libpannellum.js View File

@@ -42,7 +42,10 @@ function Renderer(container) {
var pose;
var image, imageType, dynamic;
var texCoordBuffer, cubeVertBuf, cubeVertTexCoordBuf, cubeVertIndBuf;
var imageVertices;
var texelWidth, texelHeight;
var globalParams;
var epsilon = 1e-15;

/**
* Initialize renderer.
@@ -68,7 +71,7 @@ function Renderer(container) {
_imageType = 'equirectangular';

if (_imageType != 'equirectangular' && _imageType != 'cubemap' &&
_imageType != 'multires') {
_imageType != 'multires' && _imageType != 'multiresrec') {
console.log('Error: invalid image type specified!');
throw {type: 'config error'};
}
@@ -297,7 +300,7 @@ function Renderer(container) {
var maxWidth = 0;
if (imageType == 'equirectangular') {
maxWidth = gl.getParameter(gl.MAX_TEXTURE_SIZE);
if (Math.max(image.width / 2, image.height) > maxWidth) {
if (Math.max(image.width / 2, image.height) > maxWidth && !config.multiRes) {
console.log('Error: The image is too big; it\'s ' + image.width + 'px wide, '+
'but this device\'s maximum supported size is ' + (maxWidth * 2) + 'px.');
throw {type: 'webgl size error', width: image.width, maxWidth: maxWidth * 2};
@@ -338,6 +341,8 @@ function Renderer(container) {
fragmentSrc = fragCube;
} else if (imageType == 'multires') {
fragmentSrc = fragMulti;
} else if (imageType == 'multiresrec') {
fragmentSrc = fragMultiresrec;
}
gl.shaderSource(fs, fragmentSrc);
gl.compileShader(fs);
@@ -370,12 +375,12 @@ function Renderer(container) {
program.texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
gl.enableVertexAttribArray(program.texCoordLocation);

if (imageType != 'multires') {
if (imageType == 'equirectangular' || imageType == 'cubemap') {
// Provide texture coordinates for rectangle
if (!texCoordBuffer)
texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,1,1,1,1,-1,-1,1,1,-1,-1,-1]), gl.STATIC_DRAW);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW);
gl.vertexAttribPointer(program.texCoordLocation, 2, gl.FLOAT, false, 0, 0);

// Pass aspect ratio
@@ -462,7 +467,7 @@ function Renderer(container) {
gl.texParameteri(glBindType, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(glBindType, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

} else {
} else if (imageType == 'multires') {
// Look up vertex coordinates location
program.vertPosLocation = gl.getAttribLocation(program, 'a_vertCoord');
gl.enableVertexAttribArray(program.vertPosLocation);
@@ -477,11 +482,11 @@ function Renderer(container) {

// Bind texture coordinate buffer and pass coordinates to WebGL
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertTexCoordBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0,1,0,1,1,0,1]), gl.STATIC_DRAW);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), gl.STATIC_DRAW);

// Bind square index buffer and pass indicies to WebGL
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertIndBuf);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0,1,2,0,2,3]), gl.STATIC_DRAW);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);

// Find uniforms
program.perspUniform = gl.getUniformLocation(program, 'u_perspMatrix');
@@ -493,6 +498,75 @@ function Renderer(container) {
program.currentNodes = [];
program.nodeCache = [];
program.nodeCacheTimestamp = 0;
} else if (imageType == 'multiresrec') {
//TODO
let right = haov / (Math.PI * 2.0);
let left = -right;
let up = (vaov / 2.0 + voffset) / Math.PI * 2.0;
let down = (-vaov / 2.0 + voffset) / Math.PI * 2.0;
imageVertices = [
[left, up],
[right, up],
[right, down],
[left, down]
];

if (image.originalWidth >= image.originalHeight) {
image.relativeTileSize = (right - left) / (image.originalWidth / image.tileResolution);
} else {
image.relativeTileSize = (up - down) / (image.originalHeight / image.tileResolution);
}

texelWidth = (right - left) / image.originalWidth;
texelHeight = (up - down) / image.originalHeight;

// Provide texture coordinates for rectangle
if (!texCoordBuffer)
texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW);
gl.vertexAttribPointer(program.texCoordLocation, 2, gl.FLOAT, false, 0, 0);

// Pass aspect ratio
program.aspectRatio = gl.getUniformLocation(program, 'u_aspectRatio');
gl.uniform1f(program.aspectRatio, gl.drawingBufferWidth / gl.drawingBufferHeight);

// Locate psi, theta, focal length, horizontal extent, vertical extent, and vertical offset
program.psi = gl.getUniformLocation(program, 'u_psi');
program.theta = gl.getUniformLocation(program, 'u_theta');
program.f = gl.getUniformLocation(program, 'u_f');
program.h = gl.getUniformLocation(program, 'u_h');
program.v = gl.getUniformLocation(program, 'u_v');
program.vo = gl.getUniformLocation(program, 'u_vo');
program.rot = gl.getUniformLocation(program, 'u_rot');

// Pass horizontal extent, vertical extent, and vertical offset
gl.uniform1f(program.h, haov / (Math.PI * 2.0));
gl.uniform1f(program.v, vaov / Math.PI);
gl.uniform1f(program.vo, voffset / Math.PI * 2);

// Locate parameters for tile boundaries
program.bt = gl.getUniformLocation(program, 'u_bt');
program.br = gl.getUniformLocation(program, 'u_br');
program.bb = gl.getUniformLocation(program, 'u_bb');
program.bl = gl.getUniformLocation(program, 'u_bl');

//The extent of a pixel from the original image in the [-1,1]² space
program.texelWidth = gl.getUniformLocation(program, 'u_texelWidth');
program.texelHeight = gl.getUniformLocation(program, 'u_texelHeight');

program.backgroundColor = gl.getUniformLocation(program, 'u_backgroundColor');
gl.uniform4fv(program.backgroundColor, color.concat([1]));

//linearly blend fragments on the border of tiles
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

program.level = -1;

program.currentNodes = [];
program.nodeCache = [];
program.nodeCacheTimestamp = 0;
}

// Check if there was an error
@@ -639,7 +713,7 @@ function Renderer(container) {
return;
}
if (imageType != 'multires') {
if (imageType == 'equirectangular' || imageType == 'cubemap') {
// Calculate focal length from vertical field of view
var vfov = 2 * Math.atan(Math.tan(hfov * 0.5) / (gl.drawingBufferWidth / gl.drawingBufferHeight));
focal = 1 / Math.tan(vfov * 0.5);
@@ -661,7 +735,7 @@ function Renderer(container) {
// Draw using current buffer
gl.drawArrays(gl.TRIANGLES, 0, 6);
} else {
} else if(imageType == 'multires'){
// Create perspective matrix
var perspMatrix = makePersp(hfov, gl.drawingBufferWidth / gl.drawingBufferHeight, 0.1, 100.0);
@@ -692,13 +766,13 @@ function Renderer(container) {
}
}
program.currentNodes = [];
var sides = ['f', 'b', 'u', 'd', 'l', 'r'];
for (s = 0; s < 6; s++) {
var ntmp = new MultiresNode(vtmps[s], sides[s], 1, 0, 0, image.fullpath);
testMultiresNode(rotPersp, ntmp, pitch, yaw, hfov);
}
program.currentNodes.sort(multiresNodeRenderSort);
// Unqueue any pending requests for nodes that are no longer visible
@@ -726,6 +800,66 @@ function Renderer(container) {
// Draw tiles
multiresDraw();
} else if (imageType == 'multiresrec') {
//TODO
// Find correct zoom level
checkZoom(hfov);

// Calculate focal length from vertical field of view
var vfov = 2 * Math.atan(Math.tan(hfov * 0.5) / (gl.drawingBufferWidth / gl.drawingBufferHeight));
focal = 1 / Math.tan(vfov * 0.5);

// Pass psi, theta, roll, and focal length
gl.uniform1f(program.psi, yaw);
gl.uniform1f(program.theta, pitch);
gl.uniform1f(program.rot, roll);
gl.uniform1f(program.f, focal);

// Find current nodes
program.nodeCache.sort(multiresNodeSort);
if (program.nodeCache.length > 200 &&
program.nodeCache.length > program.currentNodes.length + 50) {
// Remove older nodes from cache
var removed = program.nodeCache.splice(200, program.nodeCache.length - 200);
for (var i = 0; i < removed.length; i++) {
// Explicitly delete textures
gl.deleteTexture(removed[i].texture);
}
}
program.currentNodes = [];


var ntmp = new MultiresNode(imageVertices, "equirectangular", 1, 0, 0, image.fullpath);
testMultiresrecNode(ntmp, yaw, pitch, roll, hfov);

program.currentNodes.sort(multiresNodeRenderSort);

// Unqueue any pending requests for nodes that are no longer visible
for (i = pendingTextureRequests.length - 1; i >= 0; i--) {
if (program.currentNodes.indexOf(pendingTextureRequests[i].node) === -1) {
pendingTextureRequests[i].node.textureLoad = false;
pendingTextureRequests.splice(i, 1);
}
}

// Allow one request to be pending, so that we can create a texture buffer for that in advance of loading actually beginning
if (pendingTextureRequests.length === 0) {
for (i = 0; i < program.currentNodes.length; i++) {
var node = program.currentNodes[i];
if (!node.texture && !node.textureLoad) {
node.textureLoad = true;

setTimeout(processNextTile, 0, node);

// Only process one tile per frame to improve responsiveness
break;
}
}
}

// Draw tiles
multiresrecDraw();
}
if (params.returnImage !== undefined) {
@@ -740,7 +874,7 @@ function Renderer(container) {
* @returns {boolean} Whether or not images are loading.
*/
this.isLoading = function() {
if (gl && imageType == 'multires') {
if (gl && (imageType == 'multires' || imageType == 'multiresrec')) {
for ( var i = 0; i < program.currentNodes.length; i++ ) {
if (!program.currentNodes[i].textureLoaded) {
return true;
@@ -829,6 +963,41 @@ function Renderer(container) {
}

/**
* Draws multires nodes of equrectangular image.
* @private
*/
function multiresrecDraw() {
if (!program.drawInProgress) {
program.drawInProgress = true;
gl.clear(gl.COLOR_BUFFER_BIT);
for (var i = 0; i < program.currentNodes.length; i++) {
if (program.currentNodes[i].textureLoaded > 1) {
//transform input parameters from [-1,1]² to [0,1]² in screen coordinates (i.e. y-axis pointing downwards)
let v = program.currentNodes[i].vertices;
let factor = Math.pow(2, image.maxLevel - program.currentNodes[i].level - 1);

// Upload extents of tile relative to full panorama
gl.uniform1f(program.bb, (-v[0][1] + texelHeight/4 + 1) / 2);
gl.uniform1f(program.br, (v[1][0] - texelWidth/4 + 1) / 2);
gl.uniform1f(program.bt, (-v[2][1] - texelHeight/4 + 1) / 2);
gl.uniform1f(program.bl, (v[3][0] + texelWidth/4 + 1) / 2);
gl.uniform1f(program.texelWidth, factor*texelWidth);
gl.uniform1f(program.texelHeight, factor*texelHeight);

// Prep for texture
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(program.texCoordLocation, 2, gl.FLOAT, false, 0, 0);

// Bind texture and draw tile
gl.bindTexture(gl.TEXTURE_2D, program.currentNodes[i].texture); // Bind program.currentNodes[i].texture to TEXTURE0
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
}
program.drawInProgress = false;
}
}

/**
* Creates new multires node.
* @constructor
* @private
@@ -987,7 +1156,7 @@ function Renderer(container) {
v[9]*f1+v[6]*i1, v[10]*f+v[7]*i, v[11]*f3+v[8]*i3,
v[9], v[10], v[11]
];
ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x*2, node.y*2+1, image.fullpath);
ntmp = new MultiresNode(vtmp, node.side, node.level + 1, node.x * 2, node.y * 2 + 1, image.fullpath);
children.push(ntmp);
}
for (var j = 0; j < children.length; j++) {
@@ -996,7 +1165,96 @@ function Renderer(container) {
}
}
}

function testMultiresrecNode(node, yaw, pitch, roll, hfov) {
let v = node.vertices;
var children = [];

// Create rotation matrix
var matrix = identityMatrix3();
matrix = rotateMatrix(matrix, -yaw, 'y');
matrix = rotateMatrix(matrix, pitch, 'x');
matrix = rotateMatrix(matrix, roll, 'z');

//compute vertices of viewport and tile on sphere
let vecViewCenter = applyMatrix(matrix, [0, 0, -1]);
let vecNodeCenter = applyMatrix(matrix, imageToSphereCoordinates([(v[0][0] + v[1][0]) / 2, (v[1][1] + v[2][1]) / 2]));
var sum = 0;
for (var i = 0; i < 3; ++i) {
let diff = vecViewCenter[i] - vecNodeCenter[i];
sum += diff * diff;
}
node.diff = Math.sqrt(sum);

let frustumVertices = frustumVerticesOnImage(hfov)
.map(v => imageToSphereCoordinates(v))
.map(v => applyMatrix(matrix, v));

let nodeVertices = node.vertices
.map(v => imageToSphereCoordinates(v));

var inView = false;
if (node.level <= 2)
inView = true;
else
inView = intersect(frustumVertices, nodeVertices);

if (inView) {
// Add node to current nodes and load texture if needed
var inCurrent = false;
for (var k = 0; k < program.nodeCache.length; k++) {
if (program.nodeCache[k].path == node.path) {
inCurrent = true;
program.nodeCache[k].timestamp = program.nodeCacheTimestamp++;
program.nodeCache[k].diff = node.diff;
program.currentNodes.push(program.nodeCache[k]);
break;
}
}
if (!inCurrent) {
node.timestamp = program.nodeCacheTimestamp++;
program.currentNodes.push(node);
program.nodeCache.push(node);
}

// Create child nodes
if (node.level < program.level) {
let dimX = v[1][0] - v[0][0];
let dimY = v[1][1] - v[2][1];
let x0 = v[0][0];
let y0 = v[0][1];
let dimXC = Math.min(dimX, texelWidth * Math.pow(2, image.maxLevel - node.level - 1) * image.tileResolution);
let dimYC = Math.min(dimY, texelHeight * Math.pow(2, image.maxLevel - node.level - 1) * image.tileResolution);

var children = [];
var childrenIndices = [[0, 0]];
if (dimX > dimXC) {
childrenIndices.push([1, 0]);
if (dimY > dimYC)
childrenIndices.push([1, 1]);
}
if (dimY > dimYC)
childrenIndices.push([0, 1]);

for (var child of childrenIndices) {
let vtmp = [
[x0 + dimXC * child[0], y0 - dimYC * child[1]],
[x0 + (child[0] ? dimX : dimXC), y0 - dimYC * child[1]],
[x0 + (child[0] ? dimX : dimXC), y0 - (child[1] ? dimY : dimYC)],
[x0 + dimXC * child[0], y0 - (child[1] ? dimY : dimYC)]
]
var ntmp = new MultiresNode(vtmp, "equirectangular", node.level + 1, node.x * 2 + child[0], node.y * 2 + child[1], image.fullpath);
children.push(ntmp);
}

for (var j = 0; j < children.length; j++) {
testMultiresrecNode(children[j], yaw, pitch, roll, hfov);
}
}
}
}

/**
* Creates cube vertex array.
* @private
@@ -1108,7 +1366,159 @@ function Renderer(container) {
0, 0, -1, 0
];
}

/**
* @private
* @param {number} hfov - Desired horizontal field of view.
* @returns {number[][]} Array of 2-vectors
*/
function frustumVerticesOnImage(hfov) {
let fovy = 2 * Math.atan(Math.tan(hfov / 2) * gl.drawingBufferHeight / gl.drawingBufferWidth);
let x = hfov / Math.PI / 2;
let y = fovy / Math.PI;
return [
[-x, y],
[x, y],
[x, -y],
[-x, -y]
];
}

/**
* @private
* @param {number[]} v - Vector from [-1,1]²
* @returns {number[]} 3-vector on the unit sphere which is obtained
* when projecting an equirectangular image (normalized to [-1,1]²) back onto the sphere
*/
function imageToSphereCoordinates(v) {
let z = -Math.cos(v[1] * Math.PI / 2.0) * Math.cos(v[0] * Math.PI);
let x = Math.cos(v[1] * Math.PI / 2.0) * Math.sin(v[0] * Math.PI);
let y = Math.sin(v[1] * Math.PI / 2.0);
return [x, y, z];
}

/**
* @private
* @param {number[]} v1 - 3-vector on sphere
* @param {number[]} v2 - 3-vector on sphere
* @returns {number[]} normal of plane based on v1, v2 and (0,0,0)
*/
function normal(v1, v2) {
return [
(v1[1] * v2[2]) - (v1[2] * v2[1]),
(v1[2] * v2[0]) - (v1[0] * v2[2]),
(v1[0] * v2[1]) - (v1[1] * v2[0])
];
}

/**
* @private
* @param {number[]} normal - normal vector of a plane
* @param {number[]} v - vector to test for side
* @returns {number} -1, 0, 1
*/
function orientation(normal, v) {
let scalProd = normal[0] * v[0] + normal[1] * v[1] + normal[2] * v[2];
return Math.sign(scalProd);
}

/**
* @private
* @param {number[][]} spolygon - convex spherical polygon defined by its vertices
* @param {number[]} v - spherical vector
* @returns {boolean} is v contained in spolygon?
*/
function containmentOnSphere(spolygon, v) {
for (let i = 0; i < spolygon.length; ++i) {
//normals point to the inside of the polygon
let n = normal(spolygon[i], spolygon[(i + 1) % spolygon.length]);
let o = orientation(n, v);
if (o < 0)
return false;
}
return true;
}

/**
* @private
* @param {number[][]} sTile - vertices of a (equirectangularly projected) tile on the sphere
* @param {number[]} v - spherical vector
* @returns {number[]} weight vector
*/
function linearCombinationOfBoundVectors(sTile, v) {
//test whether x and z component of v and be expressed
// as a positive linear combination of the vectors
// pointing towords the left and right border of the tile
let a = [(sTile[3][0] + sTile[0][0]) / 2, (sTile[3][2] + sTile[0][2]) / 2];
let b = [(sTile[1][0] + sTile[2][0]) / 2, (sTile[1][2] + sTile[2][2]) / 2];
let c = [v[0], v[2]];
//solve (a b) * x = c
let x2 = (a[0] * c[1] - c[0] * a[1]) / (a[0] * b[1] - b[0] * a[1]);
let x1 = (c[0] * b[1] - b[0] * c[1]) / (a[0] * b[1] - b[0] * a[1]);
return [x1, x2];
}


/**
* compute modified Cohen-Sutherland out codes (with left out = right out)
* @private
* @param {number[][]} sTile - vertices of a (equirectangularly projected) tile on the sphere
* @param {number[]} v - spherical vector
* @returns {number} out code
*/
function outCode(sTile, v) {
let ymax = sTile.map(ver => ver[1]).reduce((acc, val) => Math.max(acc, val), -1);
let ymin = sTile.map(ver => ver[1]).reduce((acc, val) => Math.min(acc, val), 1);
if (Math.abs(ymax - 1.0) <= epsilon && Math.abs(ymin + 1.0) <= epsilon)
return 0; //no information where the tile ends in x/z-direction
var oCode = 0;
if (v[1] > ymax)
oCode |= 8;
if (v[1] < ymin)
oCode |= 4;

let x = linearCombinationOfBoundVectors(sTile, v);
if (x[0] < 0 || x[1] < 0)
oCode |= 1;
return oCode;
}

/**
* @private
* @param {number[][]} sTile - vertices of a (equirectangularly projected) tile on the sphere
* @param {number[]} viewpolygon - spherical convex (i.e. not more than 180°) polygon
* @returns {boolean} sTile and viewpolygon intersect
*/
function intersect(viewpolygon, sTile) {
var prevOutCode = outCode(sTile, viewpolygon[3]);
for (var v of viewpolygon) {
var oCode = outCode(sTile, v);
if (!oCode)
return true;
if ((prevOutCode & oCode) == 0)
return true;
prevOutCode = oCode;
}
if (outCode(sTile, viewpolygon[3]) == 1 && outCode(sTile, viewpolygon[2]) == 1) {
let left = linearCombinationOfBoundVectors(sTile, viewpolygon[3]);
let right = linearCombinationOfBoundVectors(sTile, viewpolygon[2]);
if (left[0] > 0 && left[1] < 0 && right[0] < 0 && right[1] > 0)
return true;
}
if (outCode(sTile, viewpolygon[0]) == 1 && outCode(sTile, viewpolygon[1]) == 1) {
let left = linearCombinationOfBoundVectors(sTile, viewpolygon[0]);
let right = linearCombinationOfBoundVectors(sTile, viewpolygon[1]);
if (left[0] > 0 && left[1] < 0 && right[0] < 0 && right[1] > 0)
return true;
}
for (let v of sTile) {
if (containmentOnSphere(viewpolygon, v))
return true;
}
return false;
}


/**
* Processes a loaded texture image into a WebGL texture.
* @private
@@ -1132,7 +1542,7 @@ function Renderer(container) {
var cacheTop = 4; // Maximum number of concurrents loads
var textureImageCache = {};
var crossOrigin;
function TextureImageLoader() {
var self = this;
this.texture = this.callback = null;
@@ -1215,16 +1625,21 @@ function Renderer(container) {
* @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++;
if (image.type == 'multires') {
// 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;
} else {
let f = Math.log2(hfov / gl.drawingBufferWidth / Math.PI / texelWidth);
program.level = image.maxLevel - Math.max(0, Math.round(f));
}
// Apply change
program.level = newLevel;
}
/**
@@ -1259,6 +1674,35 @@ function Renderer(container) {
1/(m[12]*v[0] + m[13]*v[1] + m[14]*v[2])
];
}

/**
* Applies matrix to a 3-vector
* @private
* @param {number[]} m - 3x3-Matrix.
* @param {number[]} v - Input 3-vector.
* @returns {number[]} Resulting 3-vector.
*/
function applyMatrix(m, v) {
return [
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
];
}

/**
* @private
* @param {number[]} v - Input 4-vector.
* @returns {number[]} Normalized 4-vector.
*/
function normalize(v) {
return [
v[0] / v[3],
v[1] / v[3],
v[2] / v[3],
1
];
}
/**
* Checks if a vertex is visible.
@@ -1358,6 +1802,7 @@ var vMulti = [
'}'
].join('');


// Fragment shader
var fragEquiCubeBase = [
'precision mediump float;',
@@ -1371,6 +1816,14 @@ var fragEquiCubeBase = [
'uniform float u_vo;',
'uniform float u_rot;',

//boundaries of a tile relative to a full panorama
'uniform float u_bt;',
'uniform float u_br;',
'uniform float u_bb;',
'uniform float u_bl;',
'uniform float u_texelWidth;',
'uniform float u_texelHeight;',

'const float PI = 3.14159265358979323846264;',

// Texture
@@ -1435,6 +1888,47 @@ var fragEquirectangular = fragEquiCubeBase + [
'}'
].join('\n');

var fragMultiresrec = fragEquiCubeBase + [
// Wrap image
'lambda = mod(lambda + PI, PI * 2.0) - PI;',

// Map texture to sphere
'vec2 coord = vec2(lambda / PI, phi / (PI / 2.0));',
'vec2 fragCoord = vec2((coord.x + 1.0) / 2.0, (-coord.y + 1.0) / 2.0);',

// Look up color from texture
// Map from [-1,1] to [0,1] and flip y-axis
// 'if(fragCoord.x < u_bl - u_texelWidth || fragCoord.x > u_br + u_texelWidth || fragCoord.y < u_bb - u_texelHeight || fragCoord.y > u_bb + u_texelHeight){',
//'if (fragCoord.x < u_bl - u_texelWidth || fragCoord.x > u_br + u_texelWidth) {',
// 'gl_FragColor = u_backgroundColor;',
// 'gl_FragColor.w = 0.0;',
//'} else {',
'x = (fragCoord.x - u_bl)/(u_br - u_bl);',
'float overflowX = 0.0;',
'if(x < 0.0){',
'overflowX = -x/u_texelWidth*(u_br - u_bl);',
'x = 0.0;',
'} else if (x > 1.0){',
'overflowX = (x-1.0)/u_texelWidth*(u_br - u_bl);',
'x = 1.0;',
'}',

'y = (fragCoord.y - u_bb)/(u_bt-u_bb);',
'float overflowY = 0.0;',
'if(y < 0.0){',
'overflowY = -y/u_texelHeight*(u_bt-u_bb);',
'y = 0.0;',
'} else if (y > 1.0){',
'overflowY = (y-1.0)/u_texelHeight*(u_bt-u_bb);',
'y = 1.0;',
'}',

'gl_FragColor = texture2D(u_image0, vec2(x,y));',
'gl_FragColor.w = 1.0 - overflowX - overflowY;',
// '}',
'}'
].join('\n');

var fragEquirectangularMulti = function (rows, cols) {
return [
'precision mediump float;',


+ 2
- 2
src/js/pannellum.js View File

@@ -320,7 +320,7 @@ function init() {
}
infoDisplay.load.lbox.style.display = 'block';
infoDisplay.load.lbar.style.display = 'none';
} else if (config.type == 'multires') {
} else if (config.type == 'multires' || config.type == 'multiresrec') {
var c = JSON.parse(JSON.stringify(config.multiRes)); // Deep copy
c.loader = config.multiRes.loader;
// Avoid "undefined" in path, check (optional) multiRes.basePath, too
@@ -379,7 +379,7 @@ function init() {
panoImage[i].src = sanitizeURL(p);
}
}
} else if (config.type == 'multires') {
} else if (config.type == 'multires' || config.type == 'multiresrec') {
onImageLoad();
} else {
p = '';


Loading…
Cancel
Save