<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add a custom style layer</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.9.2/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.9.2/mapbox-gl.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiaWxvdmVneXgxMzE0IiwiYSI6ImNpb2h6emI0ODAwNnR2ZWt4bDFncGI3ZjYifQ.Pu9l94qL1MEDdDim2-s6RA';
const map = new mapboxgl.Map({
container: 'map',
zoom: 3,
center: [7.5, 58],
style: 'mapbox://styles/mapbox/light-v10',
antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
});
// create a custom style layer to implement the WebGL content
const highlightLayer = {
id: 'highlight',
type: 'custom',
// method called when the layer is added to the map
// https://docs.mapbox.com/mapbox-gl-js/api/#styleimageinterface#onadd
onAdd: function (map, gl) {
// create GLSL source for vertex shader
const vertexSource = `
uniform mat4 u_matrix;
attribute vec3 a_pos;
attribute vec2 aTextureCoords;
varying vec2 vTextureCoords;
void main() {
gl_Position = u_matrix * vec4(a_pos, 1.0);
vTextureCoords=aTextureCoords;
}`;
// create GLSL source for fragment shader
const fragmentSource = `
precision highp float;
varying vec2 vTextureCoords;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture,vTextureCoords);
}`;
// create a vertex shader
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
console.log('vertexShader', gl.getShaderInfoLog(vertexShader));
console.log('fragShader', gl.getShaderInfoLog(vertexShader));
// create a fragment shader
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
// link the two shaders into a WebGL program
this.program = gl.createProgram();
gl.attachShader(this.program, vertexShader);
gl.attachShader(this.program, fragmentShader);
gl.linkProgram(this.program);
console.log(gl.getProgramInfoLog(this.program));
this.aPos = gl.getAttribLocation(this.program, 'a_pos');
this.aTextureCoords = gl.getAttribLocation(this.program, 'aTextureCoords');
this.u_TextureLocation = gl.getUniformLocation(this.program, 'uTexture')
// define vertices of the triangle to be rendered in the custom style layer
const helsinki = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 25.004,
lat: 60.239
}, 1000000);
const berlin = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 13.403,
lat: 52.562
}, 1000000);
const kyiv = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 30.498,
lat: 50.541
}, 1000000);
const vertices = new Float32Array([
helsinki.x, helsinki.y, 0, 0, 0,
helsinki.x, helsinki.y, 0.02, 0, 1,
berlin.x, berlin.y, 0, 1, 0,
berlin.x, berlin.y, 0.02, 1, 1,
berlin.x, berlin.y, 0, 0, 0,
berlin.x, berlin.y, 0.02, 0, 1,
kyiv.x, kyiv.y, 0, 1, 0,
kyiv.x, kyiv.y, 0.02, 1, 1,
kyiv.x, kyiv.y, 0, 0, 0,
kyiv.x, kyiv.y, 0.02, 0, 1,
helsinki.x, helsinki.y, 0, 1, 0,
helsinki.x, helsinki.y, 0.02, 1, 1
])
console.log(vertices)
// create and initialize a WebGLBuffer to store vertex and color data
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
vertices,
gl.STATIC_DRAW
);
// const indices = new Uint16Array([0, 1, 2,
// 2, 1, 3, 3, 2, 4, 4, 3, 5, 5, 4, 0, 0, 5, 1])
const indices = new Uint16Array([0, 2, 1,
1, 2, 3,
4, 5, 6, 6, 5, 7,
8, 9, 10, 10, 9, 11
])
this.indexbuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexbuffer);
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
indices,
gl.STATIC_DRAW
);
this.texture = this.createTexture(gl, './test2.png');
},
createTexture (gl, data) {
const texture = gl.createTexture();
texture.image = new Image();
var me = texture;
texture.image.onload = function () {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, me);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, me.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null);
}
texture.image.src = data
return texture;
},
// method fired on each animation frame
// https://docs.mapbox.com/mapbox-gl-js/api/#map.event:render
render: function (gl, matrix) {
gl.useProgram(this.program);
gl.uniformMatrix4fv(
gl.getUniformLocation(this.program, 'u_matrix'),
false,
matrix
);
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexbuffer);
gl.enableVertexAttribArray(this.aPos);
gl.vertexAttribPointer(this.aPos, 3, gl.FLOAT, false, (3 + 2) * 4, 0);
gl.enableVertexAttribArray(this.aTextureCoords);
gl.vertexAttribPointer(this.aTextureCoords, 2, gl.FLOAT, false, (3 + 2) * 4, 3 * 4);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
if (this.texture) {
gl.activeTexture(gl.TEXTURE0);
// gl.activeTexture(this.texture);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.uniform1i(this.u_TextureLocation, 0);
}
// gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3);
gl.drawElements(gl.TRIANGLES, 18, gl.UNSIGNED_SHORT, 0)
}
};
// add the custom style layer to the map
map.on('load', () => {
map.addLayer(highlightLayer, 'building');
});
</script>
</body>
</html>
效果
遇到的问题记录一下
1.glsl里面没有 precision highp float;这一句,好像纹理坐标就出现问题,无法加载纹理,黑乎乎一片
2.图片必须是2的幂,否则也会错误
3.就是
gl.enableVertexAttribArray(this.aPos);
gl.vertexAttribPointer(this.aPos, 3, gl.FLOAT, false, (3 + 2) * 4, 0);
gl.enableVertexAttribArray(this.aTextureCoords);
gl.vertexAttribPointer(this.aTextureCoords, 2, gl.FLOAT, false, (3 + 2) * 4, 3 * 4);
这里面的stride,offset问题;我用的是float32array,确认为是8字节
4.glsl中错误可以用 这两个函数获取
console.log(gl.getProgramInfoLog(this.program))
console.log('fragShader', gl.getShaderInfoLog(vertexShader))
5.关于一般polygon生成我已经写了一个,但是很粗,美化一下再放上来;
const polygoncoords=[....]//
//使用 mapboxgl.MercatorCoordinate.fromLngLat转换一下成NDC坐标?【存疑】;
let len = coords.length-1;//最后一点重复
const vertices=new Float32Array(len*4*3);//len个侧面*4个顶点*5个分量(顶点3+纹理2);这里考虑纹理坐标,所以有重复的顶点
const indices= new UInt16Array(len*2*3);//len个面,一个矩形面两个三角*三角三个索引
const vertexOffset=0;
for(let i = 0 ;i<len;i++){
vertices[vertexOffset]=coords[i].x;
vertices[vertexOffset+1]=coords[i].y;
vertices[vertexOffset+2]=0;
vertices[vertexOffset+3]=0;
vertices[vertexOffset+4]=0;
vertices[vertexOffset+5]=coords[i].x;
vertices[vertexOffset+6]=coords[i].y;
vertices[vertexOffset+7]=0.0003;
vertices[vertexOffset+8]=0;
vertices[vertexOffset+9]=1;
vertices[vertexOffset+10]=coords[i].x;
vertices[vertexOffset+11]=coords[i].y;
vertices[vertexOffset+12]=0.0;
vertices[vertexOffset+13]=1;
vertices[vertexOffset+14]=0;
vertices[vertexOffset+15]=coords[i].x;
vertices[vertexOffset+16]=coords[i].y;
vertices[vertexOffset+17]=0.0003;
vertices[vertexOffset+18]=1;
vertices[vertexOffset+19]=1;
vertexOffset+=20;
}
let indexOffset=0;
//[2i,2i+1,2i+2,2i+3]
for(let i= 0;i<len;i++){
indices[indexOffset]=2*(i+1);
indices[indexOffset]=2*i+1;
indices[indexOffset]=2*i;
indices[indexOffset]=2*(i+1);
indices[indexOffset]=2*i+1;
indices[indexOffset]=2*(i+1)+1;
}