ArcGIS Maps SDK for JavaScript 从 4.29
开始增加 RenderNode
类,可以添加数据以及操作 FBO(ManagedFBO)
;
通过操作 FBO,可以通过后处理实现很多效果,官方提供了几个示例,感兴趣可以看看。
本文介绍一下通过 FBO,添加自定义数据。
之前的示例,实际效果不太好,而且存在一些问题,这里进行一下优化。
最主要的调整包括 背面不显示、动态颜色变化以及解决场景漂移问题。
本文包括核心代码、完整代码以及在线示例。
核心代码
原理就是将坐标数据转为 WebGL 内部数据,即世界坐标;
在通过相机对象属性,传入投影矩阵与视图矩阵;
通过相机矩阵转换世界坐标,显示在地图上。
传入时间参数,根据时间变化修改颜色值,实现动态颜色效果。
// 初始化shader
var VSHADER = `#version 300 es
layout (location=0) in vec3 a_Position;
layout (location=1) in vec4 a_color;
out vec4 v_color;
uniform mat4 u_projectionMatrix;
uniform mat4 u_viewMatrix;
void main(){
gl_Position =
u_projectionMatrix*u_viewMatrix*vec4(a_Position,1.0);
v_color=a_color;
}
`;
var FSHADER = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 fragColor;
uniform float u_time;
vec3 timeBasedColor() {
float t = u_time / 500.0; // 根据实际情况调整时间缩放比例
return vec3(sin(t), cos(t), sin(t) + cos(t));
}
void main() {
if (gl_FrontFacing == true){
fragColor = vec4(v_color.rgb * timeBasedColor(), 0.5);
}
}
`;
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/>
<title>自定义三角形 - 优化版 | Sample | ArcGIS Maps SDK for JavaScript 4.29</title>
<link rel="stylesheet" href="https://openlayers.vip/arcgis_api/4.29/esri/themes/light/main.css"/>
<script src="https://openlayers.vip/arcgis_api/4.29/init.js"></script>
<script src="https://openlayers.vip/examples/resources/renderCommon.js"></script>
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?f80a36f14f8a73bb0f82e0fdbcee3058";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<!-- Our application -->
<script>
require([
"esri/Map",
"esri/views/SceneView",
"esri/widgets/Home",
"esri/core/promiseUtils",
"esri/views/3d/webgl/RenderNode",
"esri/views/3d/webgl",
"esri/geometry/SpatialReference",
], (Map,
SceneView,
Home,
promiseUtils,
RenderNode,
webgl,
SpatialReference,
) => {
// 定位中心点
const pointCenter = {
type: "point", // autocasts as new Point()
x: 116.21483020568614,
y: 39.86940145817053,
z: 1,
viewH: 30000,
};
const {map, view} = initMap({Map, SceneView, Home}, undefined, pointCenter);
// 创建三角形数据
// 经纬度
let projectionCoordinates = [
// lon lat elevation
// 注意,这里的高度不能设置为 0,否则图形会漂移
116.22792493013921, 39.926236653038366, 0.1,
116.14369699912316, 39.84098386073661, 0.1,
116.272868687796, 39.83965178585689, 0.1,
];
// 创建三角形颜色
let colors = new Float32Array(
[
1.0, 0.0, 0.5,
1.0, 0.5, 0.0,
0.0, 1.0, 0.5
]
)
// 初始化渲染器
function initRender() {
// Derive a new subclass from RenderNode called LuminanceRenderNode
const TriAngleRenderNode = RenderNode.createSubclass({
constructor: function () {
// consumes and produces define the location of the the render node in the render pipeline
this.consumes = {required: ["composite-color"]};
this.produces = "composite-color";
this._time = (new Date()).getTime();
},
// Ensure resources are cleaned up when render node is removed
destroy() {
this.shaderProgram && this.gl?.deleteProgram(this.shaderProgram);
this.vbo && this.gl?.deleteBuffer(this.vbo);
this.colorBuffer && this.gl?.deleteBuffer(this.colorBuffer);
},
properties: {},
initialize: function () {
let gl = this.gl;
// 初始化shader
var VSHADER = `#version 300 es
layout (location=0) in vec3 a_Position;
layout (location=1) in vec4 a_color;
out vec4 v_color;
// 投影矩阵
uniform mat4 u_projectionMatrix;
// 视图矩阵
uniform mat4 u_viewMatrix;
void main(){
gl_Position =
u_projectionMatrix*u_viewMatrix*vec4(a_Position,1.0);
// 使用自定义颜色
v_color=a_color;
}
`;
var FSHADER = `#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 fragColor;
// 时间变量
uniform float u_time;
// 计算随机颜色
vec3 timeBasedColor() {
float t = u_time / 500.0; // 根据实际情况调整时间缩放比例
return vec3(sin(t), cos(t), sin(t) + cos(t));
}
void main() {
// 背面不显示
if (gl_FrontFacing == true){
fragColor = vec4(v_color.rgb * timeBasedColor(), 0.5);
}
}
`;
// 初始化 shader 程序
this.shaderProgram = initWebgl2Shaders(gl, VSHADER, FSHADER);
if (!this.shaderProgram) {
console.assert("初始化shader错误")
}
// 获取顶点位置
this.a_Position = gl.getAttribLocation(this.shaderProgram, 'a_Position')
// 获取顶点颜色
this.a_color = gl.getAttribLocation(this.shaderProgram, 'a_color')
// 时间
this.timeUniform = gl.getUniformLocation(this.shaderProgram, "u_time");
// 初始化顶点的 bufferdata
this.vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
// 转换坐标为渲染坐标
let renderCoordinates = webgl.toRenderCoordinates(
view,
// 经纬度坐标
projectionCoordinates,
// 开始索引
0,
// 坐标系
SpatialReference.WGS84,
// 顶点容器
new Float32Array(projectionCoordinates.length),
// 目标开始索引
0,
// 顶点数量
projectionCoordinates.length / 3
);
gl.bufferData(gl.ARRAY_BUFFER, renderCoordinates, gl.STATIC_DRAW);
// 给顶点的各个点赋予颜色
this.colorBuffer = gl.createBuffer() //创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer) //将缓冲区对象绑定到目标
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW) //向缓冲区对象写入数据
},
// 渲染
render: function (inputs) {
// The field input contains all available framebuffer objects
// We need color texture from the composite render target
this.resetWebGLState();
const output = this.bindRenderTarget();
// const output = this.acquireOutputFramebuffer();
let gl = this.gl;
// 开启透明
activeOpacity(gl);
gl.useProgram(this.shaderProgram); // 使用 shader 程序
gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.vertexAttribPointer(this.a_Position, 3, gl.FLOAT, false, 0, 0) //将缓冲区对象分配给a_Posiotion
gl.enableVertexAttribArray(this.a_Position); // 启用 a_Position
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer) //将缓冲区对象绑定到目标
gl.vertexAttribPointer(this.a_color, 3, gl.FLOAT, false, 0, 0) //将缓冲区对象分配给a_color
gl.enableVertexAttribArray(this.a_color);
gl.uniform1f(this.timeUniform, (new Date()).getTime() - this._time);
// 开启相机投影
activeMatrix(this)
gl.drawArrays(gl.TRIANGLES, 0, 3); // 绘制图形
this.requestRender(); // 请求渲染
return output;
},
a_Position: undefined, // 顶点位置
a_color: undefined, // 顶点颜色
vbo: undefined, // 顶点缓冲区对象
colorBuffer: undefined, // 颜色缓冲区对象
shaderProgram: null, // shader 程序
});
promiseUtils
.eachAlways([view.when()])
.then((results) => {
// Initializes the new custom render node and connects to SceneView
const triAngleRenderNode = new TriAngleRenderNode({view});
})
}
initRender()
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
在线示例
ArcGIS Maps SDK for JavaScript 在线示例:添加自定义数据(优化版)