前言:此文章的内容涉及两个部分内容,主体为webgl内容,主要提供一个思路、解决方案。相关的需求是这么回事: 地图放大缩小时,图像不能失真。即:
缩放有图像颜色的那一部分时,不可以失真。也就是说不能贴一张静态的图片,而需要监听地图的缩放、拖拽,动态的绘制当前视口内的图像。下图为实际效果。
转载请著名出处。
以下代码做了:
1、将着色器的代码赋给shader。2、编译着色器。
顶点着色器:
1、v_Color 顶点颜色 传入到片元着色器 着色。
2、 渲染位置为模型矩阵 乘 初始顶点位置
// 创建 着色器
createShader: function (gl) {
var vertexShaderSource =
'attribute vec4 position;\n' +
'attribute vec4 color;\n' +
'uniform mat4 u_modelMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
'v_Color = color;\n' +
'gl_Position = u_modelMatrix * position;\n' +
'}\n'
var fragmentShaderSource =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
' gl_FragColor = v_Color;\n' +
'}\n'
//从上面例子使用 createShader 函数。
var vertexShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertexShader, vertexShaderSource)
gl.compileShader(vertexShader)
//从上面例子使用 createShader 函数。
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, fragmentShaderSource)
// 编译此 着色器
gl.compileShader(fragmentShader)
return { vertexShader, fragmentShader }
},
initBufferData: function (gl, shaderProgram) {
let dataLen = tindata.data.length
let data = tindata.data
let vertexPositions = []
for (let i = 0; i < dataLen; i++) {
vertexPositions.push(data[i][0] / 180.0, data[i][1] / 90.0)
}
let indices = tindata.indices
var vertices = new Float32Array(vertexPositions)
var n = indices.length //点的个数
//创建缓冲区对象
var vertexBuffer = gl.createBuffer()
if (!vertexBuffer) {
console.log('Failed to create the buffer object')
return -1
}
//将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
//向缓冲区写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
//获取坐标点在缓冲区存放位置
var a_Position = gl.getAttribLocation(shaderProgram, 'position')
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
//连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position)
// ------------------------------------
var colorBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW)
//获取顶点颜色苏哦因
var __color = gl.getAttribLocation(shaderProgram, 'color')
console.log(__color)
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(__color, 4, gl.FLOAT, false, 0, 0)
//连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(__color)
// -------------------------------------
var indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
new Uint32Array(indices),
gl.STATIC_DRAW
)
return n
},
特别说明一下,this.colors 的生成也可以放在上方函数里执行。 ColorMaker此 取色器 前往我的其他博客查找。
initVertexAttribute: function () {
let dataLen = tindata.data.length
let data = tindata.data
let colors = []
let colormaker = new ColorMaker({
avgDivide: true,
maxData: tindata.header.maxData,
minData: tindata.header.minData,
colors: getTestColors(),
})
for (let i = 0; i < dataLen; i++) {
colors.push(
…colormaker.makeColor(data[i][2]).map((e) => e / 255.0),
1.0
)
}
this.colors = new Float32Array(colors)
},
值得说明 gl.vertexAttribPointer(__color, 4, gl.FLOAT, false, 0, 0) 与 this.colors = new Float32Array(colors) 为什么这么写? ,理由很简单,由于我们没有设置normalize 映射,例如 从 255 =>1.0 , 我直接先处理了 返回来的 rgb值 。 所以 这里接收的是浮点。
webglRenderTraingles: function () {
console.time('all')
console.log(tindata)
let father = document.getElementById('map1')
let canvas = document.createElement('canvas', {
preserveDrawingBuffer: true, //prevent canvas being cleared as the render framerate is not in sync with dom events
})
canvas.width = father.clientWidth
canvas.height = father.clientHeight
// father.appendChild(canvas)
let gl = canvas.getContext('webgl')
this.gl = gl
console.log(gl)
var program = gl.createProgram()
this.program = program
let info = this.createShader(gl)
// 添加预先存在的着色器
gl.attachShader(program, info.vertexShader)
gl.attachShader(program, info.fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
//获取坐标点
var a_Position = gl.getAttribLocation(program, 'position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position')
return
}
var n = this.initBufferData(gl, program)
if (n < 0) {
console.log('Failed to set the positions')
return
}
var modelMatrix = new Matrix4() // 模型矩阵
modelMatrix.setScale(1, 1, 1)
// modelMatrix.translate(-0.25,-0.25,0)
// var viewMatrix = new Matrix4() // 视点矩阵
// var projMatrix = new Matrix4() // 投影矩阵
// var mvpMatrix = new Matrix4() // 用于相乘用
var u_modelMatrix = gl.getUniformLocation(program, 'u_modelMatrix')
// 旋转位移 等于绕原点Y旋转
// viewMatrix.setLookAt(0, -1, 0, 0, 0, 0, 0, 1, 1);
// (视点,观察目标点,上方向)
// projMatrix.setOrtho(
// // 投影矩阵(fov可视空间底面和顶面夹角<大于0>,近裁截面宽高比,近裁截面位置<大于0>,远裁截面位置<大于0> )
// -1 , 1,-1,1 ,0,5
// )
// 矩阵相乘
// mvpMatrix.set(projMatrix).multiply(viewMatrix)
// console.log(mvpMatrix)
// 赋值
gl.uniformMatrix4fv(u_modelMatrix, false, modelMatrix.elements)
// console.log(u_MvpMatrix)
// 清除指定<画布>的颜色
gl.clearColor(0.0, 0.0, 0.0, 0.0)
//清屏|清深度缓冲
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// 启用多边形偏移,避免深度冲突
// gl.enable(gl.POLYGON_OFFSET_FILL)
var ext = gl.getExtension('OES_element_index_uint')
console.log(n)
this.count = n
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_INT, 0)
// gl.drawArrays(gl.TRIANGLES, 0, n);
console.timeEnd('all')
return canvas.toDataURL()
},
不少注释,不要心烦,说不准哪一天你就用上了呢?比如甲方突然提起想看高度上存在的变化。。。然后这个的关键在于,顶点数组传进去缓冲区,使用drawElementsAPI绘制的时候,如果是 int类型的,需要引入拓展 var ext = gl.getExtension(‘OES_element_index_uint’),然后 顶点类型中 标明 gl.UNSIGNED_INT。踩坑了很久,没有文章提到此事,郑重告知。
如果顺利的话,此时把该canvas插入到页面里面 应该能出来一张 图片。具体的原理为,这也就是代码里为什么要除以 180.0 与 90.0 ,原因是需要把经纬度转换成webgl 的正交右手坐标系。
顺带一提, 为什么是 180.0 而不是180,原因是 当你确定是浮点结果时,显示的声明180.0是一个浮点数 省去计算过程中 javascript引擎的处理,在大量数据、运算中能稍稍提高性能。你可以自己测试。
监听moveend 事件,拖拽完成后、缩放完成后,将计算后的结果图像 贴到当前的 可视extent中。通俗点说就是,计算当前屏幕表示的经纬度范围的正确图像。
map.on('moveend', (evt) => {
console.log(evt);
this.changeMatrix(this.gl,this.program,evt.frameState)
let source1 = new ImageStatic({
imageExtent: evt.frameState.extent,
url: this.gl.canvas.toDataURL(),
projection: 'EPSG:4326',
})
layer.setSource(source1)
// this.generateGridData(data.data, evt.frameState.extent)
})
let layer = new ImageLayer({
zIndex: 11,
})
map.addLayer(layer)
clear当前缓冲区的图像,重新绘制。 计算当前webgl 图像模型的矩阵(缩放与平移)。对x轴进行拉伸。y轴放大或者缩小,ratio 则是初始的分辨率 比 当前 视口状态的分辨率。
平移的位置则是 - 中心点的经度 / 180.0 || - 中心点的维度 / 90.0 ,这个得你自己画图去琢磨了。
changeMatrix: function (gl,program,frameState) {
var calcMatrix = new Matrix4()
let ratio = (this.__origin__resolution / frameState.viewState.resolution).toFixed(2)
let center = frameState.viewState.center
let extent = frameState.extent
let originDiff = (extent[2] - extent[0]) / 360.0
calcMatrix.scale(1 / originDiff , ratio, 1)
calcMatrix.translate(-(center[0] / 180.0 ),-(center[1] / 90.0 ),0)
// calcMatrix.translate(-0.5 * 10,-0.5 * 10,0)
var u_modelMatrix = gl.getUniformLocation(program, 'u_modelMatrix')
gl.uniformMatrix4fv(u_modelMatrix, false, calcMatrix.elements)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.drawElements(gl.TRIANGLES, this.count, gl.UNSIGNED_INT, 0)
},
/**
* 一个用于 webgl 加载tin数据的 处理类
* @wrriten {liuqingQAQ} on 2022/3/25
*/
import Matrix4 from './martrix.js'
// 调试工具
// var SPECTOR = require("spectorjs");
// var spector = new SPECTOR.Spector();
// spector.displayUI();
class WebGLRenderer {
constructor(options) {
this.init(options)
}
/**
* 初始化内容
*/
init(options) {
// 生成 可以被 截图的 canvas
let mapDom = document.getElementById(options.mapID)
let canvas = document.createElement('canvas', {
preserveDrawingBuffer: true,
})
canvas.width = mapDom.clientWidth
canvas.height = mapDom.clientHeight
this.gl = canvas.getContext('webgl')
this.canvas = canvas
this.tindata = options.tindata
this.program = this.gl.createProgram()
this.__origin__resolution = options.originResolution
this.colormaker = options.colormaker
this.initShader(this.gl, this.program)
this.linkShaderToProgram(this.gl, this.program)
let count = this.initBufferData(this.gl, this.program)
this.count = count
this.initModelMatrix(this.gl, this.program)
this.renderElement(this.gl, this.count)
console.log(this.canvas.toDataURL());
}
/**
* 绘制图像
*/
renderElement(gl, count) {
gl.clearColor(0.0, 0.0, 0.0, 0.0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
var ext = gl.getExtension('OES_element_index_uint')
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_INT, 0)
}
changeMatrix(frameState) {
let { gl, program } = this
var calcMatrix = new Matrix4()
let ratio = (this.__origin__resolution / frameState.viewState.resolution).toFixed(2)
let center = frameState.viewState.center
let extent = frameState.extent
let originDiff = (extent[2] - extent[0]) / 360.0
calcMatrix.scale(1 / originDiff, ratio, 1)
calcMatrix.translate(-(center[0] / 180.0), -(center[1] / 90.0), 0)
// calcMatrix.translate(-0.5 * 10,-0.5 * 10,0)
var u_modelMatrix = gl.getUniformLocation(program, 'u_modelMatrix')
gl.uniformMatrix4fv(u_modelMatrix, false, calcMatrix.elements)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.drawElements(gl.TRIANGLES, this.count, gl.UNSIGNED_INT, 0)
}
/**
* 将shader 链接到 program
*/
linkShaderToProgram(gl, program) {
gl.linkProgram(program)
gl.useProgram(program)
}
/**
* 初始化 模型矩阵
*/
initModelMatrix(gl, program) {
let modelMatrix = new Matrix4()
modelMatrix.scale(1, 1, 1)
// 缓冲区重 存储 modelMatrix的索引位置
let modelMatrixIndex = gl.getUniformLocation(program, 'u_modelMatrix')
// 往着色器中赋值
gl.uniformMatrix4fv(modelMatrixIndex, false, modelMatrix.elements)
}
/**
* 装载着色器
*/
initShader(gl, program) {
var vertexShaderSource =
'attribute vec4 position;\n' +
'attribute vec4 color;\n' +
'uniform mat4 u_modelMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
'v_Color = color;\n' +
'gl_Position = u_modelMatrix * position;\n' +
'}\n'
var fragmentShaderSource =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main(){\n' +
' gl_FragColor = v_Color;\n' +
'}\n'
//从上面例子使用 createShader 函数。
var vertexShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertexShader, vertexShaderSource)
gl.compileShader(vertexShader)
//从上面例子使用 createShader 函数。
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, fragmentShaderSource)
// 编译此 着色器
gl.compileShader(fragmentShader)
// 绑定预先存在的着色器 至program
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
// 调试代码,alert的内容为ERROR: 0:2:" :No precision specified for (float)
if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(fragmentShader));
}
// 调试代码,alert的内容为ERROR: 0:2:" :No precision specified for (float)
if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(vertexShader));
}
}
/**
* 初始化 缓冲数据
* @param {*} gl
* @param {*} program
* @returns n 绘制的顶点个数
*/
initBufferData(gl, program) {
let dataLen = this.tindata.data.length
let data = this.tindata.data
let vertexPositions = []
let colors = []
for (let i = 0; i < dataLen; i++) {
vertexPositions.push(data[i][0] / 180.0, data[i][1] / 90.0)
colors.push(
...this.colormaker.makeColor(data[i][2]).map((e) => e / 255.0),
1.0
)
}
let indices = this.tindata.indices
var vertices = new Float32Array(vertexPositions)
var n = indices.length //点的个数
//创建缓冲区对象
var vertexBuffer = gl.createBuffer()
if (!vertexBuffer) {
console.log('Failed to create the buffer object')
return -1
}
//将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
//向缓冲区写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
//获取坐标点
var a_Position = gl.getAttribLocation(program, 'position')
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
//连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position)
// ------------------------------------
var colorBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW)
//获取顶点颜色苏哦因
var __color = gl.getAttribLocation(program, 'color')
console.log(__color)
//将缓冲区对象分配给a_Position变量
gl.vertexAttribPointer(__color, 4, gl.FLOAT, false, 0, 0)
//连接a_Position变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(__color)
// -------------------------------------
var indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
new Uint32Array(indices),
gl.STATIC_DRAW
)
return n
}
}
export default WebGLRenderer;
let webglRenderer = new WebGLRenderer({
originResolution: map.getView().getResolution(),
mapID: 'map1',
tindata: tindata,
colormaker: new ColorMaker({
avgDivide: true,
maxData: tindata.header.maxData,
minData: tindata.header.minData,
colors: getTestColors(),
}),
})
// cuon-matrix.js (c) 2012 kanda and matsuda
/**
* This is a class treating 4x4 matrix.
* This class contains the function that is equivalent to OpenGL matrix stack.
* The matrix after conversion is calculated by multiplying a conversion matrix from the right.
* The matrix is replaced by the calculated result.
*/
/**
* Constructor of Matrix4
* If opt_src is specified, new matrix is initialized by opt_src.
* Otherwise, new matrix is initialized by identity matrix.
* @param opt_src source matrix(option)
*/
var Matrix4 = function (opt_src) {
var i, s, d;
if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) {
s = opt_src.elements;
d = new Float32Array(16);
for (i = 0; i < 16; ++i) {
d[i] = s[i];
}
this.elements = d;
} else {
this.elements = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
}
};
/**
* Set the identity matrix.
* @return this
*/
Matrix4.prototype.setIdentity = function () {
var e = this.elements;
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = 0;
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = 0;
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = 0;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
return this;
};
/**
* Copy matrix.
* @param src source matrix
* @return this
*/
Matrix4.prototype.set = function (src) {
var i, s, d;
s = src.elements;
d = this.elements;
if (s === d) {
return;
}
for (i = 0; i < 16; ++i) {
d[i] = s[i];
}
return this;
};
/**
* Multiply the matrix from the right.
* @param other The multiply matrix
* @return this
*/
Matrix4.prototype.concat = function (other) {
var i, e, a, b, ai0, ai1, ai2, ai3;
// Calculate e = a * b
e = this.elements;
a = this.elements;
b = other.elements;
// If e equals b, copy b to temporary matrix.
if (e === b) {
b = new Float32Array(16);
for (i = 0; i < 16; ++i) {
b[i] = e[i];
}
}
for (i = 0; i < 4; i++) {
ai0 = a[i]; ai1 = a[i + 4]; ai2 = a[i + 8]; ai3 = a[i + 12];
e[i] = ai0 * b[0] + ai1 * b[1] + ai2 * b[2] + ai3 * b[3];
e[i + 4] = ai0 * b[4] + ai1 * b[5] + ai2 * b[6] + ai3 * b[7];
e[i + 8] = ai0 * b[8] + ai1 * b[9] + ai2 * b[10] + ai3 * b[11];
e[i + 12] = ai0 * b[12] + ai1 * b[13] + ai2 * b[14] + ai3 * b[15];
}
return this;
};
Matrix4.prototype.multiply = Matrix4.prototype.concat;
/**
* Multiply the three-dimensional vector.
* @param pos The multiply vector
* @return The result of multiplication(Float32Array)
*/
Matrix4.prototype.multiplyVector3 = function (pos) {
var e = this.elements;
var p = pos.elements;
var v = new Vector3();
var result = v.elements;
result[0] = p[0] * e[0] + p[1] * e[4] + p[2] * e[8] + e[11];
result[1] = p[0] * e[1] + p[1] * e[5] + p[2] * e[9] + e[12];
result[2] = p[0] * e[2] + p[1] * e[6] + p[2] * e[10] + e[13];
return v;
};
/**
* Multiply the four-dimensional vector.
* @param pos The multiply vector
* @return The result of multiplication(Float32Array)
*/
Matrix4.prototype.multiplyVector4 = function (pos) {
var e = this.elements;
var p = pos.elements;
var v = new Vector4();
var result = v.elements;
result[0] = p[0] * e[0] + p[1] * e[4] + p[2] * e[8] + p[3] * e[12];
result[1] = p[0] * e[1] + p[1] * e[5] + p[2] * e[9] + p[3] * e[13];
result[2] = p[0] * e[2] + p[1] * e[6] + p[2] * e[10] + p[3] * e[14];
result[3] = p[0] * e[3] + p[1] * e[7] + p[2] * e[11] + p[3] * e[15];
return v;
};
/**
* Transpose the matrix.
* @return this
*/
Matrix4.prototype.transpose = function () {
var e, t;
e = this.elements;
t = e[1]; e[1] = e[4]; e[4] = t;
t = e[2]; e[2] = e[8]; e[8] = t;
t = e[3]; e[3] = e[12]; e[12] = t;
t = e[6]; e[6] = e[9]; e[9] = t;
t = e[7]; e[7] = e[13]; e[13] = t;
t = e[11]; e[11] = e[14]; e[14] = t;
return this;
};
/**
* Calculate the inverse matrix of specified matrix, and set to this.
* @param other The source matrix
* @return this
*/
Matrix4.prototype.setInverseOf = function (other) {
var i, s, d, inv, det;
s = other.elements;
d = this.elements;
inv = new Float32Array(16);
inv[0] = s[5] * s[10] * s[15] - s[5] * s[11] * s[14] - s[9] * s[6] * s[15]
+ s[9] * s[7] * s[14] + s[13] * s[6] * s[11] - s[13] * s[7] * s[10];
inv[4] = - s[4] * s[10] * s[15] + s[4] * s[11] * s[14] + s[8] * s[6] * s[15]
- s[8] * s[7] * s[14] - s[12] * s[6] * s[11] + s[12] * s[7] * s[10];
inv[8] = s[4] * s[9] * s[15] - s[4] * s[11] * s[13] - s[8] * s[5] * s[15]
+ s[8] * s[7] * s[13] + s[12] * s[5] * s[11] - s[12] * s[7] * s[9];
inv[12] = - s[4] * s[9] * s[14] + s[4] * s[10] * s[13] + s[8] * s[5] * s[14]
- s[8] * s[6] * s[13] - s[12] * s[5] * s[10] + s[12] * s[6] * s[9];
inv[1] = - s[1] * s[10] * s[15] + s[1] * s[11] * s[14] + s[9] * s[2] * s[15]
- s[9] * s[3] * s[14] - s[13] * s[2] * s[11] + s[13] * s[3] * s[10];
inv[5] = s[0] * s[10] * s[15] - s[0] * s[11] * s[14] - s[8] * s[2] * s[15]
+ s[8] * s[3] * s[14] + s[12] * s[2] * s[11] - s[12] * s[3] * s[10];
inv[9] = - s[0] * s[9] * s[15] + s[0] * s[11] * s[13] + s[8] * s[1] * s[15]
- s[8] * s[3] * s[13] - s[12] * s[1] * s[11] + s[12] * s[3] * s[9];
inv[13] = s[0] * s[9] * s[14] - s[0] * s[10] * s[13] - s[8] * s[1] * s[14]
+ s[8] * s[2] * s[13] + s[12] * s[1] * s[10] - s[12] * s[2] * s[9];
inv[2] = s[1] * s[6] * s[15] - s[1] * s[7] * s[14] - s[5] * s[2] * s[15]
+ s[5] * s[3] * s[14] + s[13] * s[2] * s[7] - s[13] * s[3] * s[6];
inv[6] = - s[0] * s[6] * s[15] + s[0] * s[7] * s[14] + s[4] * s[2] * s[15]
- s[4] * s[3] * s[14] - s[12] * s[2] * s[7] + s[12] * s[3] * s[6];
inv[10] = s[0] * s[5] * s[15] - s[0] * s[7] * s[13] - s[4] * s[1] * s[15]
+ s[4] * s[3] * s[13] + s[12] * s[1] * s[7] - s[12] * s[3] * s[5];
inv[14] = - s[0] * s[5] * s[14] + s[0] * s[6] * s[13] + s[4] * s[1] * s[14]
- s[4] * s[2] * s[13] - s[12] * s[1] * s[6] + s[12] * s[2] * s[5];
inv[3] = - s[1] * s[6] * s[11] + s[1] * s[7] * s[10] + s[5] * s[2] * s[11]
- s[5] * s[3] * s[10] - s[9] * s[2] * s[7] + s[9] * s[3] * s[6];
inv[7] = s[0] * s[6] * s[11] - s[0] * s[7] * s[10] - s[4] * s[2] * s[11]
+ s[4] * s[3] * s[10] + s[8] * s[2] * s[7] - s[8] * s[3] * s[6];
inv[11] = - s[0] * s[5] * s[11] + s[0] * s[7] * s[9] + s[4] * s[1] * s[11]
- s[4] * s[3] * s[9] - s[8] * s[1] * s[7] + s[8] * s[3] * s[5];
inv[15] = s[0] * s[5] * s[10] - s[0] * s[6] * s[9] - s[4] * s[1] * s[10]
+ s[4] * s[2] * s[9] + s[8] * s[1] * s[6] - s[8] * s[2] * s[5];
det = s[0] * inv[0] + s[1] * inv[4] + s[2] * inv[8] + s[3] * inv[12];
if (det === 0) {
return this;
}
det = 1 / det;
for (i = 0; i < 16; i++) {
d[i] = inv[i] * det;
}
return this;
};
/**
* Calculate the inverse matrix of this, and set to this.
* @return this
*/
Matrix4.prototype.invert = function () {
return this.setInverseOf(this);
};
/**
* Set the orthographic projection matrix.
* @param left The coordinate of the left of clipping plane.
* @param right The coordinate of the right of clipping plane.
* @param bottom The coordinate of the bottom of clipping plane.
* @param top The coordinate of the top top clipping plane.
* @param near The distances to the nearer depth clipping plane. This value is minus if the plane is to be behind the viewer.
* @param far The distances to the farther depth clipping plane. This value is minus if the plane is to be behind the viewer.
* @return this
*/
Matrix4.prototype.setOrtho = function (left, right, bottom, top, near, far) {
var e, rw, rh, rd;
if (left === right || bottom === top || near === far) {
throw 'null frustum';
}
rw = 1 / (right - left);
rh = 1 / (top - bottom);
rd = 1 / (far - near);
e = this.elements;
e[0] = 2 * rw;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 0;
e[5] = 2 * rh;
e[6] = 0;
e[7] = 0;
e[8] = 0;
e[9] = 0;
e[10] = -2 * rd;
e[11] = 0;
e[12] = -(right + left) * rw;
e[13] = -(top + bottom) * rh;
e[14] = -(far + near) * rd;
e[15] = 1;
return this;
};
/**
* Multiply the orthographic projection matrix from the right.
* @param left The coordinate of the left of clipping plane.
* @param right The coordinate of the right of clipping plane.
* @param bottom The coordinate of the bottom of clipping plane.
* @param top The coordinate of the top top clipping plane.
* @param near The distances to the nearer depth clipping plane. This value is minus if the plane is to be behind the viewer.
* @param far The distances to the farther depth clipping plane. This value is minus if the plane is to be behind the viewer.
* @return this
*/
Matrix4.prototype.ortho = function (left, right, bottom, top, near, far) {
return this.concat(new Matrix4().setOrtho(left, right, bottom, top, near, far));
};
/**
* Set the perspective projection matrix.
* @param left The coordinate of the left of clipping plane.
* @param right The coordinate of the right of clipping plane.
* @param bottom The coordinate of the bottom of clipping plane.
* @param top The coordinate of the top top clipping plane.
* @param near The distances to the nearer depth clipping plane. This value must be plus value.
* @param far The distances to the farther depth clipping plane. This value must be plus value.
* @return this
*/
Matrix4.prototype.setFrustum = function (left, right, bottom, top, near, far) {
var e, rw, rh, rd;
if (left === right || top === bottom || near === far) {
throw 'null frustum';
}
if (near <= 0) {
throw 'near <= 0';
}
if (far <= 0) {
throw 'far <= 0';
}
rw = 1 / (right - left);
rh = 1 / (top - bottom);
rd = 1 / (far - near);
e = this.elements;
e[0] = 2 * near * rw;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 0;
e[5] = 2 * near * rh;
e[6] = 0;
e[7] = 0;
e[8] = (right + left) * rw;
e[9] = (top + bottom) * rh;
e[10] = -(far + near) * rd;
e[11] = -1;
e[12] = 0;
e[13] = 0;
e[14] = -2 * near * far * rd;
e[15] = 0;
return this;
};
/**
* Multiply the perspective projection matrix from the right.
* @param left The coordinate of the left of clipping plane.
* @param right The coordinate of the right of clipping plane.
* @param bottom The coordinate of the bottom of clipping plane.
* @param top The coordinate of the top top clipping plane.
* @param near The distances to the nearer depth clipping plane. This value must be plus value.
* @param far The distances to the farther depth clipping plane. This value must be plus value.
* @return this
*/
Matrix4.prototype.frustum = function (left, right, bottom, top, near, far) {
return this.concat(new Matrix4().setFrustum(left, right, bottom, top, near, far));
};
/**
* Set the perspective projection matrix by fovy and aspect.
* @param fovy The angle between the upper and lower sides of the frustum.
* @param aspect The aspect ratio of the frustum. (width/height)
* @param near The distances to the nearer depth clipping plane. This value must be plus value.
* @param far The distances to the farther depth clipping plane. This value must be plus value.
* @return this
*/
Matrix4.prototype.setPerspective = function (fovy, aspect, near, far) {
var e, rd, s, ct;
if (near === far || aspect === 0) {
throw 'null frustum';
}
if (near <= 0) {
throw 'near <= 0';
}
if (far <= 0) {
throw 'far <= 0';
}
fovy = Math.PI * fovy / 180 / 2;
s = Math.sin(fovy);
if (s === 0) {
throw 'null frustum';
}
rd = 1 / (far - near);
ct = Math.cos(fovy) / s;
e = this.elements;
e[0] = ct / aspect;
e[1] = 0;
e[2] = 0;
e[3] = 0;
e[4] = 0;
e[5] = ct;
e[6] = 0;
e[7] = 0;
e[8] = 0;
e[9] = 0;
e[10] = -(far + near) * rd;
e[11] = -1;
e[12] = 0;
e[13] = 0;
e[14] = -2 * near * far * rd;
e[15] = 0;
return this;
};
/**
* Multiply the perspective projection matrix from the right.
* @param fovy The angle between the upper and lower sides of the frustum.
* @param aspect The aspect ratio of the frustum. (width/height)
* @param near The distances to the nearer depth clipping plane. This value must be plus value.
* @param far The distances to the farther depth clipping plane. This value must be plus value.
* @return this
*/
Matrix4.prototype.perspective = function (fovy, aspect, near, far) {
return this.concat(new Matrix4().setPerspective(fovy, aspect, near, far));
};
/**
* Set the matrix for scaling.
* @param x The scale factor along the X axis
* @param y The scale factor along the Y axis
* @param z The scale factor along the Z axis
* @return this
*/
Matrix4.prototype.setScale = function (x, y, z) {
var e = this.elements;
e[0] = x; e[4] = 0; e[8] = 0; e[12] = 0;
e[1] = 0; e[5] = y; e[9] = 0; e[13] = 0;
e[2] = 0; e[6] = 0; e[10] = z; e[14] = 0;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
return this;
};
/**
* Multiply the matrix for scaling from the right.
* @param x The scale factor along the X axis
* @param y The scale factor along the Y axis
* @param z The scale factor along the Z axis
* @return this
*/
Matrix4.prototype.scale = function (x, y, z) {
var e = this.elements;
e[0] *= x; e[4] *= y; e[8] *= z;
e[1] *= x; e[5] *= y; e[9] *= z;
e[2] *= x; e[6] *= y; e[10] *= z;
e[3] *= x; e[7] *= y; e[11] *= z;
return this;
};
/**
* Set the matrix for translation.
* @param x The X value of a translation.
* @param y The Y value of a translation.
* @param z The Z value of a translation.
* @return this
*/
Matrix4.prototype.setTranslate = function (x, y, z) {
var e = this.elements;
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x;
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y;
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
return this;
};
/**
* Multiply the matrix for translation from the right.
* @param x The X value of a translation.
* @param y The Y value of a translation.
* @param z The Z value of a translation.
* @return this
*/
Matrix4.prototype.translate = function (x, y, z) {
var e = this.elements;
e[12] += e[0] * x + e[4] * y + e[8] * z;
e[13] += e[1] * x + e[5] * y + e[9] * z;
e[14] += e[2] * x + e[6] * y + e[10] * z;
e[15] += e[3] * x + e[7] * y + e[11] * z;
return this;
};
/**
* Set the matrix for rotation.
* The vector of rotation axis may not be normalized.
* @param angle The angle of rotation (degrees)
* @param x The X coordinate of vector of rotation axis.
* @param y The Y coordinate of vector of rotation axis.
* @param z The Z coordinate of vector of rotation axis.
* @return this
*/
Matrix4.prototype.setRotate = function (angle, x, y, z) {
var e, s, c, len, rlen, nc, xy, yz, zx, xs, ys, zs;
angle = Math.PI * angle / 180;
e = this.elements;
s = Math.sin(angle);
c = Math.cos(angle);
if (0 !== x && 0 === y && 0 === z) {
// Rotation around X axis
if (x < 0) {
s = -s;
}
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = 0;
e[1] = 0; e[5] = c; e[9] = -s; e[13] = 0;
e[2] = 0; e[6] = s; e[10] = c; e[14] = 0;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
} else if (0 === x && 0 !== y && 0 === z) {
// Rotation around Y axis
if (y < 0) {
s = -s;
}
e[0] = c; e[4] = 0; e[8] = s; e[12] = 0;
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = 0;
e[2] = -s; e[6] = 0; e[10] = c; e[14] = 0;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
} else if (0 === x && 0 === y && 0 !== z) {
// Rotation around Z axis
if (z < 0) {
s = -s;
}
e[0] = c; e[4] = -s; e[8] = 0; e[12] = 0;
e[1] = s; e[5] = c; e[9] = 0; e[13] = 0;
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = 0;
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
} else {
// Rotation around another axis
len = Math.sqrt(x * x + y * y + z * z);
if (len !== 1) {
rlen = 1 / len;
x *= rlen;
y *= rlen;
z *= rlen;
}
nc = 1 - c;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * s;
ys = y * s;
zs = z * s;
e[0] = x * x * nc + c;
e[1] = xy * nc + zs;
e[2] = zx * nc - ys;
e[3] = 0;
e[4] = xy * nc - zs;
e[5] = y * y * nc + c;
e[6] = yz * nc + xs;
e[7] = 0;
e[8] = zx * nc + ys;
e[9] = yz * nc - xs;
e[10] = z * z * nc + c;
e[11] = 0;
e[12] = 0;
e[13] = 0;
e[14] = 0;
e[15] = 1;
}
return this;
};
/**
* Multiply the matrix for rotation from the right.
* The vector of rotation axis may not be normalized.
* @param angle The angle of rotation (degrees)
* @param x The X coordinate of vector of rotation axis.
* @param y The Y coordinate of vector of rotation axis.
* @param z The Z coordinate of vector of rotation axis.
* @return this
*/
Matrix4.prototype.rotate = function (angle, x, y, z) {
return this.concat(new Matrix4().setRotate(angle, x, y, z));
};
/**
* Set the viewing matrix.
* @param eyeX, eyeY, eyeZ The position of the eye point.
* @param centerX, centerY, centerZ The position of the reference point.
* @param upX, upY, upZ The direction of the up vector.
* @return this
*/
Matrix4.prototype.setLookAt = function (eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
fx = centerX - eyeX;
fy = centerY - eyeY;
fz = centerZ - eyeZ;
// Normalize f.
rlf = 1 / Math.sqrt(fx * fx + fy * fy + fz * fz);
fx *= rlf;
fy *= rlf;
fz *= rlf;
// Calculate cross product of f and up.
sx = fy * upZ - fz * upY;
sy = fz * upX - fx * upZ;
sz = fx * upY - fy * upX;
// Normalize s.
rls = 1 / Math.sqrt(sx * sx + sy * sy + sz * sz);
sx *= rls;
sy *= rls;
sz *= rls;
// Calculate cross product of s and f.
ux = sy * fz - sz * fy;
uy = sz * fx - sx * fz;
uz = sx * fy - sy * fx;
// Set to this.
e = this.elements;
e[0] = sx;
e[1] = ux;
e[2] = -fx;
e[3] = 0;
e[4] = sy;
e[5] = uy;
e[6] = -fy;
e[7] = 0;
e[8] = sz;
e[9] = uz;
e[10] = -fz;
e[11] = 0;
e[12] = 0;
e[13] = 0;
e[14] = 0;
e[15] = 1;
// Translate.
return this.translate(-eyeX, -eyeY, -eyeZ);
};
/**
* Multiply the viewing matrix from the right.
* @param eyeX, eyeY, eyeZ The position of the eye point.
* @param centerX, centerY, centerZ The position of the reference point.
* @param upX, upY, upZ The direction of the up vector.
* @return this
*/
Matrix4.prototype.lookAt = function (eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
return this.concat(new Matrix4().setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ));
};
/**
* Multiply the matrix for project vertex to plane from the right.
* @param plane The array[A, B, C, D] of the equation of plane "Ax + By + Cz + D = 0".
* @param light The array which stored coordinates of the light. if light[3]=0, treated as parallel light.
* @return this
*/
Matrix4.prototype.dropShadow = function (plane, light) {
var mat = new Matrix4();
var e = mat.elements;
var dot = plane[0] * light[0] + plane[1] * light[1] + plane[2] * light[2] + plane[3] * light[3];
e[0] = dot - light[0] * plane[0];
e[1] = - light[1] * plane[0];
e[2] = - light[2] * plane[0];
e[3] = - light[3] * plane[0];
e[4] = - light[0] * plane[1];
e[5] = dot - light[1] * plane[1];
e[6] = - light[2] * plane[1];
e[7] = - light[3] * plane[1];
e[8] = - light[0] * plane[2];
e[9] = - light[1] * plane[2];
e[10] = dot - light[2] * plane[2];
e[11] = - light[3] * plane[2];
e[12] = - light[0] * plane[3];
e[13] = - light[1] * plane[3];
e[14] = - light[2] * plane[3];
e[15] = dot - light[3] * plane[3];
return this.concat(mat);
}
/**
* Multiply the matrix for project vertex to plane from the right.(Projected by parallel light.)
* @param normX, normY, normZ The normal vector of the plane.(Not necessary to be normalized.)
* @param planeX, planeY, planeZ The coordinate of arbitrary points on a plane.
* @param lightX, lightY, lightZ The vector of the direction of light.(Not necessary to be normalized.)
* @return this
*/
Matrix4.prototype.dropShadowDirectionally = function (normX, normY, normZ, planeX, planeY, planeZ, lightX, lightY, lightZ) {
var a = planeX * normX + planeY * normY + planeZ * normZ;
return this.dropShadow([normX, normY, normZ, -a], [lightX, lightY, lightZ, 0]);
};
/**
* Constructor of Vector3
* If opt_src is specified, new vector is initialized by opt_src.
* @param opt_src source vector(option)
*/
var Vector3 = function (opt_src) {
var v = new Float32Array(3);
if (opt_src && typeof opt_src === 'object') {
v[0] = opt_src[0]; v[1] = opt_src[1]; v[2] = opt_src[2];
}
this.elements = v;
}
/**
* Normalize.
* @return this
*/
Vector3.prototype.normalize = function () {
var v = this.elements;
var c = v[0], d = v[1], e = v[2], g = Math.sqrt(c * c + d * d + e * e);
if (g) {
if (g == 1)
return this;
} else {
v[0] = 0; v[1] = 0; v[2] = 0;
return this;
}
g = 1 / g;
v[0] = c * g; v[1] = d * g; v[2] = e * g;
return this;
};
/**
* Constructor of Vector4
* If opt_src is specified, new vector is initialized by opt_src.
* @param opt_src source vector(option)
*/
var Vector4 = function (opt_src) {
var v = new Float32Array(4);
if (opt_src && typeof opt_src === 'object') {
v[0] = opt_src[0]; v[1] = opt_src[1]; v[2] = opt_src[2]; v[3] = opt_src[3];
}
this.elements = v;
}
export default Matrix4;
更新矩阵中的原理是什么呢,请看下图,可能不太准确,但能基本说明。
我们初始时绘制的图像是基于 - 180 到180的。 而初始时 经度范围 不同,所以 需要将初始的物体矩阵先乘以一个固定的比例 也就是校准。 作为地图加载初始的正确图像。 这也就是为什么 changeMatrix 里 有extent[2] - extent[0] / 360.0 的理由。而y轴的放大 则是当前地图视口范围的分辨率与初始地图的 分辨率 之比。 平移只需要处理中心点的偏移就可以。不多做解释。
最近连着搞了 3个框架(openlayers,cesium,three.js)的tin数据加载,人都麻了,还好最后还是顺利完成了,大概顺利?接着就只剩一个极坐标系的处理了,如果要出博客的话大概只会在此文的基础上做。