arcgis js api的externalRenderers可以直接使用webgl的shader,这样就使得你可以按照自己想法做各种效果。但前提是:熟悉webgl。
1、externalRenderers简单使用
先看一下externalRenderers的介绍:
externalRenderers | API Reference | ArcGIS API for JavaScript 4.24 | ArcGIS DevelopersDocumentation site for ArcGIS API for JavaScript on ArcGIS Developers.https://developers.arcgis.com/javascript/latest/api-reference/esri-views-3d-externalRenderers.html这里的例子不是很清晰,以下是个简单的例子(在arcgis三维场景上画一个三角形):
<template>
<div id="main-view"></div>
</template>
<script>
import SceneView from '@arcgis/core/views/SceneView'
import Map from '@arcgis/core/Map'
import esriConfig from "@arcgis/core/config";
import * as externalRenderers from "@arcgis/core/views/3d/externalRenderers";
import {initWebgl2Shaders} from '../utils/initWebgl2Shaders'
import Matrix4 from '@math.gl/core/dist/es5/classes/matrix4'
let viewer = null
let map = null
export default {
mounted() {
this.initConfig()
this.init()
this.initRender()
},
methods:{
// 初始设置
initConfig(){
esriConfig.assetsPath = "./assets";
},
init(){
map = new Map({
basemap: 'hybrid',
ground: "world-elevation"
})
viewer = new SceneView({
map,
container: 'main-view'
})
viewer.ui.empty('bottom-trailing')
viewer.ui.empty('manual')
},
initRender(){
let myExternalRenderer = {
vbo: null,
shaderProgram: null,
a_Position: null,
setup: function(context) {
let gl = context.gl;
// 初始化shader
var VSHADER=`#version 300 es
layout (location=0) in vec4 a_Position;
void main(){
gl_Position=a_Position;
}
`;
var FSHADER=`#version 300 es
precision mediump float;
out vec4 fragColor;
void main(){
fragColor=vec4(1.0,0.0,0.0,1.0);
}
`;
this.shaderProgram=initWebgl2Shaders(gl, VSHADER, FSHADER)
if(!this.shaderProgram){
console.assert("初始化shader错误")
}
this.a_Position= gl.getAttribLocation(this.shaderProgram, 'a_Position')
gl.enableVertexAttribArray(this.a_Position) //连接变量a_Position和缓冲区对象
// 初始化bufferdata
this.vbo = context.gl.createBuffer();
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
let positions = new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]);
context.gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
},
render: function(context) {
let gl = context.gl
gl.enable(gl.DEPTH_TEST); // 开启深度检测
// gl.disable(gl.CULL_FACE);
// gl.disable(gl.BLEND);
gl.useProgram(this.shaderProgram);
// 创建数据
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.vertexAttribPointer(this.a_Position,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_Posiotion
context.gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(1) // 每帧检测
}
}
externalRenderers.add(viewer, myExternalRenderer);
},
}
}
</script>
<style>
#main-view{
width: 1000px;
height: 800px;
}
</style>
其中initWebgl2Shaders的方法
function checkShaderCompilation(gl, shader) {
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
// console.log("Shader compiled successfully: " + compiled);
var compilationLog = gl.getShaderInfoLog(shader);
// console.log("Shader compiler log: " + compilationLog);
}
export function initWebgl2Shaders(gl, vertCode, fragCode) {
// Create a vertex shader object
var vertShader = gl.createShader(gl.VERTEX_SHADER);
// Attach vertex shader source code
gl.shaderSource(vertShader, vertCode);
// Compile the vertex shader
gl.compileShader(vertShader);
checkShaderCompilation(gl, vertShader);
// Create fragment shader object
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
// Attach fragment shader source code
gl.shaderSource(fragShader, fragCode);
// Compile the fragment shader
gl.compileShader(fragShader);
checkShaderCompilation(gl, fragShader);
// Create a shader program object to store
// the combined shader program
var shaderProgram = gl.createProgram();
// Attach a vertex shader
gl.attachShader(shaderProgram, vertShader);
// Attach a fragment shader
gl.attachShader(shaderProgram, fragShader);
// Link both the programs
gl.linkProgram(shaderProgram);
// Use the combined shader program object
gl.useProgram(shaderProgram);
return shaderProgram;
}
最终效果:
2、添加矩阵
两步:
1、在shader里添加矩阵
2、引用@math.gl/core,api见下面的链接
例子(将上面的initshader改为initshader2):
initRender2() {
let myExternalRenderer = {
vbo: null,
shaderProgram: null,
a_Position: null,
setup: function(context) {
let gl = context.gl;
// 初始化shader
var VSHADER=`#version 300 es
layout (location=0) in vec4 a_Position;
uniform mat4 u_formMatrix;
void main(){
gl_Position= u_formMatrix*a_Position;
}
`;
var FSHADER=`#version 300 es
precision mediump float;
out vec4 fragColor;
void main(){
fragColor=vec4(1.0,0.0,0.0,1.0);
}
`;
this.shaderProgram=initWebgl2Shaders(gl, VSHADER, FSHADER)
if(!this.shaderProgram){
console.assert("初始化shader错误")
}
this.a_Position= gl.getAttribLocation(this.shaderProgram, 'a_Position')
gl.enableVertexAttribArray(this.a_Position) //连接变量a_Position和缓冲区对象
// 初始化bufferdata
this.vbo = context.gl.createBuffer();
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
let positions = new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]);
context.gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
},
render: function(context) {
let gl = context.gl
gl.enable(gl.DEPTH_TEST); // 开启深度检测
// gl.disable(gl.CULL_FACE);
// gl.disable(gl.BLEND);
gl.useProgram(this.shaderProgram);
// 创建数据
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.vertexAttribPointer(this.a_Position,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_Posiotion
//旋转矩阵
let u_formMatrix=gl.getUniformLocation(this.shaderProgram, 'u_formMatrix')
let formMatrix=new Matrix4().translate([-1.0, -1.0, 0])
gl.uniformMatrix4fv(u_formMatrix, false, formMatrix.elements)
context.gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(1) // 每帧检测
}
}
externalRenderers.add(viewer, myExternalRenderer);
},
最终效果:
3、提取出颜色
提取出颜色的步骤:
1、在shader中提取出颜色
2、给颜色赋值
例子3(将例1的initshader改为initshader3):
initRender3() {
let myExternalRenderer = {
vbo: null,
shaderProgram: null,
a_Position: null,
a_color: null,
colorBuffer: null,
setup: function(context) {
let gl = context.gl;
// 初始化shader
var VSHADER=`#version 300 es
layout (location=0) in vec4 a_Position;
layout (location=1) in vec4 a_color;
out vec4 v_color;
uniform mat4 u_formMatrix;
void main(){
gl_Position= u_formMatrix*a_Position;
v_color=a_color;
}
`;
var FSHADER=`#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 fragColor;
void main(){
fragColor=v_color;
}
`;
this.shaderProgram=initWebgl2Shaders(gl, VSHADER, FSHADER)
if(!this.shaderProgram){
console.assert("初始化shader错误")
}
this.a_Position= gl.getAttribLocation(this.shaderProgram, 'a_Position')
gl.enableVertexAttribArray(this.a_Position) //连接变量a_Position和缓冲区对象
this.a_color= gl.getAttribLocation(this.shaderProgram, 'a_color')
gl.enableVertexAttribArray(this.a_color) //连接变量a_color和缓冲区对象
// 初始化顶点的bufferdata
this.vbo = context.gl.createBuffer();
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
let positions = new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]);
context.gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
// 给顶点的各个点赋予颜色
this.colorBuffer=gl.createBuffer() //创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer) //将缓冲区对象绑定到目标
let colors=new Float32Array([1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0])
gl.bufferData(gl.ARRAY_BUFFER,colors,gl.STATIC_DRAW) //向缓冲区对象写入数据
},
render: function(context) {
let gl = context.gl
gl.enable(gl.DEPTH_TEST); // 开启深度检测
// gl.disable(gl.CULL_FACE);
// gl.disable(gl.BLEND);
gl.useProgram(this.shaderProgram);
// 创建数据
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.vertexAttribPointer(this.a_Position,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_Posiotion
gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer) //将缓冲区对象绑定到目标
gl.vertexAttribPointer(this.a_color,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_color
//旋转矩阵
let u_formMatrix=gl.getUniformLocation(this.shaderProgram, 'u_formMatrix')
let formMatrix=new Matrix4().translate([-1.0, -1.0, 0])
gl.uniformMatrix4fv(u_formMatrix, false, formMatrix.elements)
context.gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(1) // 每帧检测
}
}
externalRenderers.add(viewer, myExternalRenderer);
},
最终效果:
4、添加动态效果
添加动态效果,通常最好的办法就是修改矩阵。
在例3的基础上,修改一下矩阵,注意动态效果需要在render里添加externalRenderers.requestRender(viewer);用来场景变化时,仍然调用该render。
例子4((将例1的initshader改为initshader4)
initRender4() {
let myExternalRenderer = {
vbo: null,
shaderProgram: null,
a_Position: null,
a_color: null,
colorBuffer: null,
i: 0,
setup: function(context) {
let gl = context.gl;
// 初始化shader
var VSHADER=`#version 300 es
layout (location=0) in vec4 a_Position;
layout (location=1) in vec4 a_color;
out vec4 v_color;
uniform mat4 u_formMatrix;
void main(){
gl_Position= u_formMatrix*a_Position;
v_color=a_color;
}
`;
var FSHADER=`#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 fragColor;
void main(){
fragColor=v_color;
}
`;
this.shaderProgram=initWebgl2Shaders(gl, VSHADER, FSHADER)
if(!this.shaderProgram){
console.assert("初始化shader错误")
}
this.a_Position= gl.getAttribLocation(this.shaderProgram, 'a_Position')
gl.enableVertexAttribArray(this.a_Position) //连接变量a_Position和缓冲区对象
this.a_color= gl.getAttribLocation(this.shaderProgram, 'a_color')
gl.enableVertexAttribArray(this.a_color) //连接变量a_color和缓冲区对象
// 初始化顶点的bufferdata
this.vbo = context.gl.createBuffer();
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
let positions = new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0]);
context.gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
// 给顶点的各个点赋予颜色
this.colorBuffer=gl.createBuffer() //创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer) //将缓冲区对象绑定到目标
let colors=new Float32Array([1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0])
gl.bufferData(gl.ARRAY_BUFFER,colors,gl.STATIC_DRAW) //向缓冲区对象写入数据
},
render: function(context) {
let gl = context.gl
this.i += 1
gl.enable(gl.DEPTH_TEST); // 开启深度检测
// gl.disable(gl.CULL_FACE);
// gl.disable(gl.BLEND);
gl.useProgram(this.shaderProgram);
// 创建数据
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.vertexAttribPointer(this.a_Position,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_Posiotion
gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer) //将缓冲区对象绑定到目标
gl.vertexAttribPointer(this.a_color,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_color
//旋转矩阵
let u_formMatrix=gl.getUniformLocation(this.shaderProgram, 'u_formMatrix')
let formMatrix=new Matrix4()
formMatrix.rotateY(Math.PI/180 * this.i)
gl.uniformMatrix4fv(u_formMatrix, false, formMatrix.elements)
context.gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(1) // 每帧检测
externalRenderers.requestRender(viewer);
}
}
externalRenderers.add(viewer, myExternalRenderer);
},
最终效果:
5、关联相机
根据webgl的三维矩阵方程:
gl_position=ProjectionMatrix*ViewMatrix*ModelMatrix*position
可以在render里使用相机的ProjectionMatrix和ViewMatrix。(根据地球的大小,可以将三角形长宽设置在10000000m)
initRender5() {
let myExternalRenderer = {
vbo: null,
shaderProgram: null,
a_Position: null,
a_color: null,
colorBuffer: null,
i: 0,
setup: function(context) {
let gl = context.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;
uniform mat4 u_formMatrix;
void main(){
gl_Position= u_projectionMatrix*u_viewMatrix*u_formMatrix*vec4(a_Position,1.0);
v_color=a_color;
}
`;
var FSHADER=`#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 fragColor;
void main(){
fragColor=v_color;
}
`;
this.shaderProgram=initWebgl2Shaders(gl, VSHADER, FSHADER)
if(!this.shaderProgram){
console.assert("初始化shader错误")
}
this.a_Position= gl.getAttribLocation(this.shaderProgram, 'a_Position')
gl.enableVertexAttribArray(this.a_Position) //连接变量a_Position和缓冲区对象
this.a_color= gl.getAttribLocation(this.shaderProgram, 'a_color')
gl.enableVertexAttribArray(this.a_color) //连接变量a_color和缓冲区对象
// 初始化顶点的bufferdata
this.vbo = context.gl.createBuffer();
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
let positions = new Float32Array([0, 0, 0, 10000000, 0, 0, 0, 10000000, 0]);
context.gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
// 给顶点的各个点赋予颜色
this.colorBuffer=gl.createBuffer() //创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer) //将缓冲区对象绑定到目标
let colors=new Float32Array([1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0])
gl.bufferData(gl.ARRAY_BUFFER,colors,gl.STATIC_DRAW) //向缓冲区对象写入数据
},
render: function(context) {
let gl = context.gl
this.i += 1
gl.enable(gl.DEPTH_TEST); // 开启深度检测
// gl.disable(gl.CULL_FACE);
// gl.disable(gl.BLEND);
gl.useProgram(this.shaderProgram);
// 创建数据
context.gl.bindBuffer(gl.ARRAY_BUFFER, this.vbo);
gl.vertexAttribPointer(this.a_Position,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_Posiotion
gl.bindBuffer(gl.ARRAY_BUFFER,this.colorBuffer) //将缓冲区对象绑定到目标
gl.vertexAttribPointer(this.a_color,3,gl.FLOAT,false,0,0) //将缓冲区对象分配给a_color
// project 矩阵
let programUniformProjectionMatrix = gl.getUniformLocation(
this.shaderProgram,
"u_projectionMatrix"
);
console.log(context.camera)
gl.uniformMatrix4fv(
programUniformProjectionMatrix,
false,
context.camera.projectionMatrix
);
// view 矩阵
let programUniformViewMatrix = gl.getUniformLocation(
this.shaderProgram,
"u_viewMatrix"
);
gl.uniformMatrix4fv(
programUniformViewMatrix,
false,
context.camera.viewMatrix
);
//旋转矩阵
let u_formMatrix=gl.getUniformLocation(this.shaderProgram, 'u_formMatrix')
let formMatrix=new Matrix4()
formMatrix.rotateY(Math.PI/180*this.i)
gl.uniformMatrix4fv(u_formMatrix, false, formMatrix.elements)
context.gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(this.i) // 每帧检测
externalRenderers.requestRender(viewer);
}
}
externalRenderers.add(viewer, myExternalRenderer);
},
最终效果: