独辟蹊径:逆推Krpano切图算法,实现在浏览器切多层级瓦片图( 三 )


独辟蹊径:逆推Krpano切图算法,实现在浏览器切多层级瓦片图

文章插图
以下仅列举了部分配置,完整配置可以参考krpano官网文档
// 多分辨率切图配置multires=true //是否是多分辨率tilesize=512 // 瓦片图大小levels=auto // 自动层级levelstep=2 // (重点)每一层与上一层maxsize=auto // 最高层级分辨率(自动计算)maxcubesize=auto // 每一面最大的尺寸stereosupport=trueadjustlevelsizes=true // 允许调节每一层级的尺寸adjustlevelsizesformipmapping=true<!-- XML中image节点信息 --><image> <cube url="panos/IMG_1914.tiles/%s/l%l/%0v/l%l_%s_%0v_%0h.jpg" multires="512,1024,2048,3840,7680" /></image>再通过官网,查看 cube节点的multires属性,第一个值表示单张瓦片图的大小 。
独辟蹊径:逆推Krpano切图算法,实现在浏览器切多层级瓦片图

文章插图
既然单张瓦片图尺寸是512,那我就打开查看生成的图片,看看到底是不是 。结果发现:几乎所有的图片都是512x512,除了最后一张图片和最后一行 。
官网对tilesize=auto的解读:
  • Size of the multi-resolution tile images.
  • Should be between 256 and 1024.
  • When using 'auto' the tool will automatically try find a good value for 'symmetric tile splitting'.
  • The today recommendation for best rendering performance is using 512 as tilesize.
  • It's a good compromise between the GPU-texture-upload-time and the number of GPU-draw-calls required to fill the screen.
  • Note - the tilesize affects the loading and decoding time and also the rendering performance.
得知:
  • 瓦片图大小在256 - 1024之间
  • 性能最好的是512 。这也是krpano强大和严谨之处,他经过大量测试的出来的结果 。
另一个属性:levelstep=2
  • 表示每一层与相邻一层的比为 2
到此 , 我们先整理一下已知信息:
  • 瓦片图的大小为512x512,但最后一行或者每行的最后一列可能不是512
  • 最高层级分辨率 = 全景图 / 3.125
  • 每一层级的分辨率与相邻层级的比为 2
虽然官方说瓦片图尺寸为256-512,但是看官方切出来的图片 , 最后一行很多都小于256 。我通过大量样本分析 , 最小值为64 。那么我给瓦片图尺寸的定义为:大小为64-512,优先切512的图片,最后假设不足512但也不能小于64 。
每一层级的宽度 % 512 % 64 = 0
经过验证,krpano所有切图都满足这样的条件 。
如果余数不为零,那咋办?同样经过大量样本推算,如果余数小于64,则舍弃 , 即当前层级的分辨率要减去这个余数,如果余数大于64,则相加 。
这时候我简单写一条算法来计算一下我的猜想:
// 设全景图大小为10000x5000const panoSize = 10000// 系数,瓦片图最高层级的尺寸 = 图片宽度 / 系数const coefficient = 3.125// 瓦片图最大尺寸const maxTileSize = 512// 瓦片图最小尺寸const minTileSize = 64// 相邻层级的比const levelstep = 2// 调整层级的尺寸:控制 faceSize % 512 % 64 = 0function adjustLevelSize(inputLevelSize: number) {if (inputLevelSize % maxTileSize % minTileSize === 0) return inputLevelSizeconst lastTileSize = inputLevelSize % maxTileSize// 最后一行小于64则舍弃if (lastTileSize < minTileSize) {inputLevelSize -= lastTileSize} else {//最后一行瓦片的余数(对64取余)const minRemainder = lastTileSize % minTileSizeif (minRemainder !== 0) {inputLevelSize = inputLevelSize - (minTileSize - minRemainder)}}return inputLevelSize}// 最高层级(余数为0)let levelSize1 = panoSize / coefficient // levelSize1 = 3200levelSize1 = adjustLevelSize(levelSize1) // levelSize1 = 3200// 下一级(余数为0)let levelSize2 = levelSize1 / levelstep // levelSize2 = 1600levelSize2 = adjustLevelSize(levelSize2) // levelSize2 = 1600// 下一级(余数为32,800 % 512 % 64 = 32,舍弃,故levelSize3 = 800 - 32 = 768)let levelSize3 = levelSize2 / levelstep // levelSize3 = 800levelSize3 = adjustLevelSize(levelSize3) // levelSize3 = 768...// 官方1万-1.5万像素的 , 只有三个层级,故切到第三层,那我就不能再切了,我得找出最低层级的最小分辨率 。通过以上的计算,同一张全景图我的算法与krpano切图进行对比:
level我的算法krpano算法3320032002160016641768768第二层级虽然有64像素的差距,但是我遵循的是层级比为2,krpano第二层级偶尔会略大或者略?。?其实这是动态计算的 , 前面也有讲,几乎约等于2,在正常波动内,所以这没问题 。
2万px以内的全景图,每隔1000px我都测试一下,发现没有问题,完全可用 。
04.最终算法实现

推荐阅读