threejs高度热力图(超详细)

本文详细介绍了如何利用three.js和heatmap.js库创建3D热力图,包括编写顶点着色器和片元着色器代码,以及使用灰度图和热力图数据生成3D视觉效果的过程。通过实例展示了如何自定义材质并实现动态渲染。
摘要由CSDN通过智能技术生成

最近遇到做threejs实现3D效果的热力图的需求然后也在网上搜了一下但是非常少,然后根据根据找到的加根据源码的参考最终实现了下面效果,废话少说直接上代码:

准备工作

需要先引入three.js,OrbitControls.js,heatmap.js

编写shader相关代码

<script type="x-shader/x-vertex" id="vertexshader">

    varying vec2 vUv;
    uniform float Zscale;
    uniform sampler2D greyMap;
    void main() {
     vUv = uv;
    vec4 frgColor = texture2D(greyMap, uv);//获取灰度图点位信息
    float height = Zscale * frgColor.a;//通过灰度图的rgb*需要设置的高度计算出热力图每个点位最终在z轴高度
    vec3 transformed = vec3( position.x, position.y, height);//重新组装点坐标
    gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);//渲染点位

    }

  </script>

<script type="x-shader/x-fragment" id="fragmentshader">
    varying vec2 vUv;
    uniform sampler2D heatMap;//热力图
    uniform vec3 u_color;//基础颜色
    uniform float u_opacity; // 透明度
    void main() {
      //vec4 alphaColor = texture2D(heatMap, vUv);
      // gl_FragColor = alphaColor;
       gl_FragColor = vec4(u_color, u_opacity) * texture2D(heatMap, vUv);//把热力图颜色和透明度进行渲染
    }

  </script>
  • 什么是shader?
    shader中文叫做着色器,它实际上是给用户一种方式来介入GPU渲染流程,定制gpu如何组织数据和绘制数据到屏幕上

  • vertexshader(顶点着色器):
    含义:顶点着色器主要负责处理顶点数据,其实顶点着色器能做的事情并不多,大部分就是在处理顶点的矩阵变换,将顶点的位置通过MVP矩阵乘法最终变换到裁剪空间。
    代码解释:这里 varying,uniform表示声明变量 ,uniform:可以在顶点或者片元着色器中使用。但是uniform的值是只读的,不可以修改它的值,一般用来传递一些全局参数,比如mvp的矩阵等。varying: 的作用是将顶点着色器中的数据传递给片元着色器。这里的数据一般是一些顶点相关的属性,比如每个顶点的颜色。注意varying在传值的时候,会被gpu插值,所以到片元着色器的时候,值与原先的值不一定完全一致。 varying vec2 vUv;定义二维变量用来存放uv坐标, uniform float Zscale;定义热力图的拉伸高度量的变量,uniform sampler2D greyMap;用于存放纹理图片的像素数据。最后一句, gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);这句的作用是,通过矩阵运算,计算当前顶点在裁剪空间坐标点。

  • fragmentshader(片元着色器)
    含义:片元着色器在整个渲染中起到了非常大的作用,一般颜色,贴图采样,光照,阴影等计算都会在片元着色器中计算。
    代码解释:varying表示输入变量,接收从顶点着色器传入的数据,varying vec2 vUv;定义变量用来接收顶点着色器传入的uv,最后使用 gl_FragColor = vec4(u_color, u_opacity) * texture2D(heatMap, vUv);给当前顶点着色输出。

heatmap.js生成热力图和灰度图

var heatmap = h337.create({
        container: document.getElementById('heatmap')
    });
    let len = 100;
    let width = 500;
    let height = 500;
    let points = [];
    let max = 0;
    while (len--) {
        var val = Math.floor(Math.random() * 100);
        max = Math.max(max, val);
        var point = {
            x: Math.floor(Math.random() * width),
            y: Math.floor(Math.random() * height),
            value: val
        };
        points.push(point);
    }
    heatmap.setData({
        max: max,
        data: points
    });
    // 灰度图
    var greymap = h337.create({
        container: document.getElementById('greymap'),
        gradient: {
            '0': 'black',
            '1.0': 'white'
        }
    });

    greymap.setData({
        max: max,
        data: points
    });

自定义材质

        let heatMapMaterial = new THREE.ShaderMaterial({
            transparent: true,
            vertexShader: document.getElementById( 'vertexshader' ).textContent,
            fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
            uniforms: {
                'heatMap' : {
                    value: {value: undefined}
                },
                'greyMap' : {
                    value: {value: undefined}
                },
                Zscale: {value: 100.0},
                u_color:{value: new THREE.Color('rgb(255, 255, 255)')
                },
                u_opacity:{
                    value:1.0
                }
            }
        });
		let texture = new THREE.Texture(heatmap._config.container.children[0]);
        texture.needsUpdate = true;
        let texture2 = new THREE.Texture(greymap._config.container.children[0]);
        texture2.needsUpdate = true;
        heatMapMaterial.uniforms.heatMap.value = texture;
        heatMapMaterial.side = THREE.DoubleSide; // 双面渲染
        heatMapMaterial.uniforms.greyMap.value = texture2,

最终运行效果

在这里插入图片描述

最后附上完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <title>热力图</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <script src="../node_modules/three/build/three.js"></script>
    <script src="../node_modules/three/examples/js/libs/stats.min.js"></script>

    <script src="../OrbitControls.js"></script>
    <script src="../node_modules/three/examples/js/loaders/OBJLoader.js"></script>
    <script src="../node_modules/heatmap.js/build/heatmap.js"></script>
    <script src="../node_modules/three/examples/js/controls/OrbitControls.js"></script>
    <script src="../node_modules/heatmap.js/build/heatmap.js"></script>

</head>

<body>
<div id="heatmap"  style="width:800px; height: 800px;"></div>
<div id="greymap" style="width:800px; height: 800px;"></div>
<script type="x-shader/x-vertex" id="vertexshader">

    varying vec2 vUv;
    uniform float Zscale;
    uniform sampler2D greyMap;
    void main() {
     vUv = uv;
    vec4 frgColor = texture2D(greyMap, uv);
    float height = Zscale * frgColor.a;
    vec3 transformed = vec3( position.x, position.y, height);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);

    }

  </script>

<script type="x-shader/x-fragment" id="fragmentshader">

    #ifdef GL_ES
    precision highp float;
    #endif
    varying vec2 vUv;
    uniform sampler2D heatMap;
    uniform vec3 u_color;//基础颜色
    uniform float u_opacity; // 透明度
    void main() {
      //vec4 alphaColor = texture2D(heatMap, vUv);
      // gl_FragColor = alphaColor;
       gl_FragColor = vec4(u_color, u_opacity) * texture2D(heatMap, vUv);
    }

  </script>

<script type="module">
    var heatmap = h337.create({
        container: document.getElementById('heatmap')
    });
    let len = 100;
    let width = 500;
    let height = 500;
    let points = [];
    let max = 0;
    while (len--) {
        var val = Math.floor(Math.random() * 100);
        max = Math.max(max, val);
        var point = {
            x: Math.floor(Math.random() * width),
            y: Math.floor(Math.random() * height),
            value: val
        };
        points.push(point);
    }
    heatmap.setData({
        max: max,
        data: points
    });
    // 灰度图
    var greymap = h337.create({
        container: document.getElementById('greymap'),
        gradient: {
            '0': 'black',
            '1.0': 'white'
        }
    });

    greymap.setData({
        max: max,
        data: points
    });



    let renderer,scene,camera

    init()
    animate()

    function init() {
        renderer = new THREE.WebGLRenderer({ antialias:true })
        renderer.setPixelRatio( window.devicePixelRatio )
        renderer.setSize(window.innerWidth, window.innerHeight)
        document.body.appendChild( renderer.domElement )

        scene = new THREE.Scene()

        camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 )
        camera.position.set(0, 0, 3000)

        scene.add( new THREE.AmbientLight( 0xeef0ff ) )

        let heatMapGeo = new THREE.PlaneBufferGeometry(800, 800,300,300)


        let heatMapMaterial = new THREE.ShaderMaterial({
            transparent: true,
            vertexShader: document.getElementById( 'vertexshader' ).textContent,
            fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
            uniforms: {
                'heatMap' : {
                    value: {value: undefined}
                },
                'greyMap' : {
                    value: {value: undefined}
                },
                Zscale: {value: 100.0},
                u_color:{value: new THREE.Color('rgb(255, 255, 255)')
                },
                u_opacity:{
                    value:1.0
                }
            }
        });

        let texture = new THREE.Texture(heatmap._config.container.children[0]);
        texture.needsUpdate = true;
        let texture2 = new THREE.Texture(greymap._config.container.children[0]);
        texture2.needsUpdate = true;
        heatMapMaterial.uniforms.heatMap.value = texture;
        heatMapMaterial.side = THREE.DoubleSide; // 双面渲染
        heatMapMaterial.uniforms.greyMap.value = texture2;
        // heatMapGeo.geometry.verticesNeedUpdate = true
        // let position = heatMapGeo.attributes.position;
        // position.dynamic = true;//设置planeGeometry为动态的,这样才允许改变其中的顶点
        // position.needsUpdate = true;//更新位置
        let heatMapPlane = new THREE.Mesh(heatMapGeo, heatMapMaterial)
        scene.add(heatMapPlane)

        let contorl = new THREE.OrbitControls(camera, renderer.domElement)

        window.addEventListener( 'resize', onWindowResize, false )
    }

    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight
        camera.updateProjectionMatrix()

        renderer.setSize( window.innerWidth, window.innerHeight )

    }

    function animate() {
        requestAnimationFrame(animate)
        renderer.render(scene, camera)

    }

</script>
</body>
</html>

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值