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

最终效果:

ArcGIS Maps SDK for Java 解析bundle arcgis js api 实例_3d

 2、添加矩阵

两步:

1、在shader里添加矩阵

2、引用@math.gl/core,api见下面的链接

math.gl | Overview

例子(将上面的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);
        },

最终效果:

ArcGIS Maps SDK for Java 解析bundle arcgis js api 实例_3d_02

 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);
        },

最终效果:

ArcGIS Maps SDK for Java 解析bundle arcgis js api 实例_初始化_03

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);
        },

最终效果:

ArcGIS Maps SDK for Java 解析bundle arcgis js api 实例_初始化_04

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);
        },

最终效果:

ArcGIS Maps SDK for Java 解析bundle arcgis js api 实例_javascript_05