openlayers结合原生webgl 图像的动态绘制tin数据保持清晰度

前言:此文章的内容涉及两个部分内容,主体为webgl内容,主要提供一个思路、解决方案。相关的需求是这么回事: 地图放大缩小时,图像不能失真。即:
openlayers结合原生webgl 图像的动态绘制tin数据保持清晰度_第1张图片
缩放有图像颜色的那一部分时,不可以失真。也就是说不能贴一张静态的图片,而需要监听地图的缩放、拖拽,动态的绘制当前视口内的图像。下图为实际效果。
openlayers结合原生webgl 图像的动态绘制tin数据保持清晰度_第2张图片
转载请著名出处。

原生webgl绘制tin数据

  • 一个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 的正交右手坐标系。
openlayers结合原生webgl 图像的动态绘制tin数据保持清晰度_第3张图片
顺带一提, 为什么是 180.0 而不是180,原因是 当你确定是浮点结果时,显示的声明180.0是一个浮点数 省去计算过程中 javascript引擎的处理,在大量数据、运算中能稍稍提高性能。你可以自己测试。

与openlayers 联动

监听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)
计算当前地图状态 所需渲染的webgl 图像

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(直接拷贝到文件中即可,已经含导出)

// 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;

一些你可能想知道的事

更新矩阵中的原理是什么呢,请看下图,可能不太准确,但能基本说明。
openlayers结合原生webgl 图像的动态绘制tin数据保持清晰度_第4张图片

我们初始时绘制的图像是基于 - 180 到180的。 而初始时 经度范围 不同,所以 需要将初始的物体矩阵先乘以一个固定的比例 也就是校准。 作为地图加载初始的正确图像。 这也就是为什么 changeMatrix 里 有extent[2] - extent[0] / 360.0 的理由。而y轴的放大 则是当前地图视口范围的分辨率与初始地图的 分辨率 之比。 平移只需要处理中心点的偏移就可以。不多做解释。

后记

最近连着搞了 3个框架(openlayers,cesium,three.js)的tin数据加载,人都麻了,还好最后还是顺利完成了,大概顺利?接着就只剩一个极坐标系的处理了,如果要出博客的话大概只会在此文的基础上做。

你可能感兴趣的:(openlayers,其他,javascript,css3)