1 Star 0 Fork 2

衷于栖 / 谷歌瓦片图加载从原理到实现

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
main.js 6.78 KB
一键复制 编辑 原始数据 按行查看 历史
zhoyq 提交于 2020-01-24 01:16 . 更新工程
// 获取 canvas 对象
const canvas = document.getElementById('main-canvas');
// 获取 gl context 对象
const gl = getWebGLContext(canvas);
// 加载 gl 扩展
const requiredWebglExtensions = [
'EXT_shader_texture_lod',
'OES_standard_derivatives',
'OES_element_index_uint',
'OES_texture_float',
'OES_texture_float_linear'
];
loadWebGlExtensions(gl, requiredWebglExtensions);
// 获取 shader
const vertShaderContent = `
attribute vec3 a_Position;
attribute vec2 a_UV;
uniform mat4 u_ViewProjectionMatrix;
varying vec2 v_UV;
void main()
{
gl_Position = u_ViewProjectionMatrix * vec4(a_Position, 1.0);
v_UV = a_UV;
}
`;
const fragShaderContent = `
precision highp float;
uniform sampler2D u_Sampler;
varying vec2 v_UV;
void main()
{
vec4 textureColor = texture2D(u_Sampler, v_UV);
gl_FragColor = vec4(vec3(textureColor), 1.0);
}
`;
// 编译 shader 成 program
const vertShader = compileVertShader(gl, vertShaderContent);
const fragShader = compileFragShader(gl, fragShaderContent);
const program = linkProgram(gl, vertShader, fragShader);
// 设置相机
const camera = new Camera();
// 鼠标绑定事件对象属性
const mouseEventProp = {
canvas: canvas,
mouseDown: false,
pressedButton: undefined,
lastMouseX: 0,
lastMouseY: 0,
orignMouseX: 0,
orignMouseY: 0,
onClick: (x, y) => {},
onRotate: (x, y) => {
camera.rotate(x, y);
},
onPan: (x, y) => {
camera.pan(x, y);
},
onZoom: (delta) => {
camera.zoomIn(delta);
}
};
// 绑定鼠标
document.addEventListener('mouseup', mouseUpHandler.bind(mouseEventProp), { passive: true });
document.addEventListener('mousemove', mouseMoveHandler.bind(mouseEventProp), { passive: true });
canvas.addEventListener('mousedown', mouseDownHandler.bind(mouseEventProp), { passive: true });
canvas.addEventListener('wheel', mouseWheelHandler.bind(mouseEventProp), { passive: true });
// 屏蔽右键菜单
document.oncontextmenu = () => false;
// 准备
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.colorMask(true, true, true, true);
gl.clearDepth(1.0);
gl.cullFace(gl.FRONT);
// 地图相关函数
const quadCache = {};
function prepareQuads(gl, renderQuads, tileXY, currentLevel, globalWidth, globalHeight, range = 3) {
const xTiles = getNumberOfXTilesAtLevel(currentLevel);
const yTiles = getNumberOfYTilesAtLevel(currentLevel);
for (let i = Math.max(tileXY.x - range, 0), leni = Math.min(tileXY.x + range, xTiles - 1); i <= leni; i++) {
for (let j = Math.max(tileXY.y - range, 0), lenj = Math.min(tileXY.y + range, yTiles - 1); j <= lenj; j++) {
// 摄像机所在位置tile 的key
this.prepareQuad(gl, renderQuads, i, j, currentLevel, globalWidth, globalHeight);
}
}
}
function prepareQuad(gl, renderQuads, x, y, currentLevel, globalWidth, globalHeight) {
const cameraPosKey = `${x}-${y}-${currentLevel}`;
if (quadCache[cameraPosKey] === undefined) {
quadCache[cameraPosKey] = new Quad(gl, x, y, currentLevel, [globalWidth, globalHeight]);
}
renderQuads.push(quadCache[cameraPosKey]);
}
camera.fitViewToScene();
camera.updatePosition();
// 渲染帧
function renderFrame(ms) {
window.requestAnimationFrame(renderFrame);
// 全局宽度和高度
const globalWidth = canvas.clientWidth;
const globalHeight = canvas.clientHeight;
canvas.width = globalWidth;
canvas.height = globalHeight;
gl.viewport(0, 0, globalWidth, globalHeight);
// 计算摄像机位置
camera.aspectRatio = globalWidth / globalHeight;
camera.updatePosition();
// 计算投影矩阵视图矩阵
const projMatrix = camera.projectionMatrix();
const viewMatrix = camera.viewMatrix();
const viewProjectionMatrix = glMatrix.mat4.create();
glMatrix.mat4.multiply(viewProjectionMatrix, projMatrix, viewMatrix);
// 计算摄像机中心点于地平面的交点
const origin = camera.position;
const direction = glMatrix.vec3.create();
glMatrix.vec3.subtract(direction, camera.target, origin);
glMatrix.vec3.normalize(direction, direction);
const normal = glMatrix.vec3.clone([0.0, 1.0, 0.0]);
const distance = 0.0;
const result = rayIntersectionWidthPlane([origin, direction], [normal, distance]);
if (result === undefined) {
// 没有交点或者角度不对
return;
}
// 获取当前层级 界定最大和最小层级
const currentLevel = Math.min(
Math.max(0,
getCurrentLevel(camera, [globalWidth, globalHeight], result)),
11
);
// 计算摄像机射线于地面交点的经纬度弧度
const rayLonlatRadians = tileWorld2lonlatRadians([result[0], result[2]]);
// 计算摄像机所在位置的 tile x y
const tileXY = radiansToTileXY(rayLonlatRadians[0], rayLonlatRadians[1], currentLevel);
// 准备渲染quad列表
const renderQuads = [];
prepareQuads(gl, renderQuads, tileXY, currentLevel, globalWidth, globalHeight);
// 清理 颜色和深度缓冲
gl.clearColor(66.0 / 255.0,66.0 / 255.0, 66.0 / 255.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 使用当前shader
gl.useProgram(program);
// 绘制
for (let i = 0, len = renderQuads.length; i < len; i++) {
const quadBuf = renderQuads[i];
// 如果没有准备好 则不渲染
if (quadBuf.status !== 1) {
return ;
}
// 更新 uniform 值
const viewProjectionMatrixLoc = gl.getUniformLocation(program, 'u_ViewProjectionMatrix');
gl.uniformMatrix4fv(viewProjectionMatrixLoc, false, viewProjectionMatrix);
// 更新 attr 值
const positionLoc = setAttributeBuffer(gl, program, 'a_Position', quadBuf.quadBufferVertices, 3);
const uvLoc = setAttributeBuffer(gl, program, 'a_UV', quadBuf.quadBufferUV, 2);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadBuf.quadBufferIndices);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, quadBuf.texture);
if (!quadBuf.textureInit) {
gl.texImage2D(gl.TEXTURE_2D, 0,
gl.RGBA, gl.RGBA,
gl.UNSIGNED_BYTE, quadBuf.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); // NEAREST
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); // LINEAR
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
quadBuf.textureInit = true;
}
const loc = gl.getUniformLocation(program, 'u_Sampler');
gl.uniform1i(loc, 0);
// gl.enable(gl.CULL_FACE);
gl.disable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
gl.drawElements(4, 6, 5123, 0);
gl.disableVertexAttribArray(positionLoc);
gl.disableVertexAttribArray(uvLoc);
}
}
// 启动渲染帧
window.requestAnimationFrame(renderFrame);
// 取消遮罩
const spinner = document.getElementById('mask');
if (spinner !== undefined) {
spinner.className = 'mask-none';
setTimeout(() => {
spinner.style.display = 'none';
}, 1000);
}
JavaScript
1
https://gitee.com/zhoyq/from-principle-to-implementation-of-WebGL-loading-Google-tiles.git
git@gitee.com:zhoyq/from-principle-to-implementation-of-WebGL-loading-Google-tiles.git
zhoyq
from-principle-to-implementation-of-WebGL-loading-Google-tiles
谷歌瓦片图加载从原理到实现
master

搜索帮助