three.js 通过着色器实现热力图效果

three.js 通过着色器实现热力图效果

在线预览 https://threehub.cn/#/codeMirror?navigation=ThreeJS&classify=shader&id=heatmapShader

https://threehub.cn 中还有很多案例

csharp 复制代码
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="importmap">
{
    "imports": {
        "three": "https://threejs.org/build/three.module.min.js",
        "three/addons/": "https://threejs.org/examples/jsm/",
        "three/examples/jsm/": "https://threejs.org/examples/jsm/",
        "gsap": "https://file.threehub.cn/js/gsap/index.js",
        "postprocessing": "https://threehub.cn/js/postprocessing.js",
        "cannon-es": "https://threehub.cn/js/cannon-es.js",
        "dat.gui": "https://threehub.cn/js/dat.gui.module.js",
        "@tweenjs/tween.js": "https://threehub.cn/js/tween.esm.js"
    }
}
</script>
<style>
    body {
        margin: 0;
        padding: 1px;
        box-sizing: border-box;
        background-color: #1f1f1f;
        display: flex;
        flex-direction: column;
        width: 100vw;
        height: 100vh;
        overflow: hidden;
    }
    #box {
        width: 100%;
        height: 100%;
    }
</style>
</head>
<body>
<div id="box"></div>
<script type="module">
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GUI } from "three/addons/libs/lil-gui.module.min.js";

const box = document.getElementById('box')

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(75, box.clientWidth / box.clientHeight, 0.1, 1000)

camera.position.set(0, 10, 10)

const renderer = new THREE.WebGLRenderer()

renderer.setSize(box.clientWidth, box.clientHeight)

box.appendChild(renderer.domElement)

new OrbitControls(camera, renderer.domElement)

animate()

function animate() {

    requestAnimationFrame(animate)

    renderer.render(scene, camera)

}

window.onresize = () => {

    renderer.setSize(box.clientWidth, box.clientHeight)

    camera.aspect = box.clientWidth / box.clientHeight

    camera.updateProjectionMatrix()

}

scene.add(new THREE.AmbientLight(0xffffff, 2), new THREE.AxesHelper(1000))

const arr = [[0., 0., 10.], [.2, .6, 5.], [.25, .7, 8.], [.33, .9, 5.], [.35, .8, 6.], [0.017, 5.311, 6.000], [-.45, .8, 4.], [-.2, -.6, 5.], [-.25, -.7, 8.], [-.33, -.9, 8.], [.35, -.45, 10.], [-.1, -.8, 10.], [.33, -.3, 5.], [-.35, .75, 6.], [.6, .4, 10.], [-.4, -.8, 4.], [.7, -.3, 6.], [.3, -.8, 8.]].map(i => new THREE.Vector3(...i))

const uniforms1 = {

    HEAT_MAX: { value: 10, type: 'number', unit: 'float' },

    PointRadius: { value: 0.42, type: 'number', unit: 'float' },

    PointsCount: { value: arr.length, type: 'number-array', unit: 'int' }, // 数量

    c1: { value: new THREE.Color(0x000000), type: 'color', unit: 'vec3' },

    c2: { value: new THREE.Color(0x000000), type: 'color', unit: 'vec3' },

    uvY: { value: 1, type: 'number', unit: 'float' },

    uvX: { value: 1, type: 'number', unit: 'float' },

    opacity: { value: 1, type: 'number', unit: 'float' }

}

const gui = new GUI()

gui.add(uniforms1.HEAT_MAX, 'value', 0, 10).name('HEAT_MAX')

gui.add(uniforms1.PointRadius, 'value', 0, 1).name('PointRadius')

gui.add(uniforms1.uvY, 'value', 0, 1).name('uvY')

gui.add(uniforms1.uvX, 'value', 0, 1).name('uvX')

gui.add(uniforms1.opacity, 'value', 0, 1).name('opacity')

gui.addColor(uniforms1.c1, 'value').name('c1')

gui.addColor(uniforms1.c2, 'value').name('c2')

const uniforms2 = {

    Points: { value: arr, type: 'vec3-array', unit: 'vec3' }

}

const uniforms = {

    ...uniforms1,

    ...uniforms2

}

const vertexShader = `
varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`

const fragmentShader = 'precision highp float;\n' + 'varying vec2 vUv; \n' +

    Object.keys(uniforms1).map(i => 'uniform ' + uniforms1[i].unit + ' ' + i + ';')
        .join('\n')

    + '\nuniform vec3 Points['
    + uniforms1.PointsCount.value + '];'
    +
    `
vec3 gradient(float w, vec2 uv) {
    w = pow(clamp(w, 0., 1.) * 3.14159 * .5, .9);
    return vec3(sin(w), sin(w * 2.), cos(w))* 1.1 + mix(c1, c2, w) * 1.1; 
}
void main()
{
    vec2 uv = vUv;
    uv.xy *= vec2(uvX, uvY);
    float d = 0.;
    for (int i = 0; i < PointsCount; i++) {
        vec3 v = Points[i];
        float intensity = v.z / HEAT_MAX;
        float pd = (1. - length(uv - v.xy) / PointRadius) * intensity;
        d += pow(max(0., pd), 2.);
    }
    gl_FragColor = vec4(gradient(d, uv), opacity);
} `


const shaderMaterial = new THREE.ShaderMaterial({

    uniforms,

    vertexShader,

    fragmentShader,

    transparent: true


})

const plane = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), shaderMaterial)

scene.add(plane)

console.log(shaderMaterial)
/**
 * 名称: 热力图
 * 来源:优雅永不过时
 * https://github.com/z2586300277
 */

</script>
</body>
</html>
相关推荐
优雅永不过时·16 分钟前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three
爱编程的鱼1 小时前
javascript用来干嘛的?赋予网站灵魂的语言
开发语言·javascript·ecmascript
神夜大侠3 小时前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱3 小时前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号3 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72933 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲3 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
究极无敌暴龙战神X4 小时前
前端学习之ES6+
开发语言·javascript·ecmascript
王解4 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里4 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6