最近遇见一个模型uv做不了贴图,但是领导要让做热力图那种,然后我就想到了遍历顶点。然后设置不一样的颜色,但是老是做不成功,不知道什么原因,希望能帮忙看看,有偿
具体需求如图:
详细描述一下问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="x-shader/x-vertex" id="vertexshader">
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main() {
// uv 和 顶点变换
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
uniform sampler2D alphaScaleMap;
uniform sampler2D paletteMap;
void main() {
// 温度转为权重alpha 并且 createRadialGradient 渐变的图
vec4 alphaColor = texture2D(alphaScaleMap, vUv);
// 根据温度转换为的权重alpha,确定改点的颜色 ,paletteMap 指定颜色条形图
vec4 color = texture2D(paletteMap, vec2(alphaColor.a, 0.0));
gl_FragColor = vec4(color.r, color.g, color.b, 1.0);
}
</script>
<script type="importmap">
{
"imports": {
"three": "../../../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from '../../../build/three.module.js';
import { OrbitControls } from '../../jsm/controls/OrbitControls.js';
let renderer, scene, camera;
const segments = 45;
const w = 256;
const h = 256;
// 随机给出温度值 储存在2维数组
const getTemperature = () => {
const temperatureArray = new Array();
for ( let i = 0; i < segments; i ++ ) {
temperatureArray[ i ] = parseInt( Math.random() * 25 + 10 ); // 颜色的变化区间 10 - 35
}
return temperatureArray;
};
// 绘制辐射圆
const drawCircular = ( context, opts ) => {
var { x, y, radius, weight } = opts;
radius = parseInt( radius * weight );
// 创建圆设置填充色
const rGradient = context.createRadialGradient( x, y, 0, x, y, radius );
rGradient.addColorStop( 0, 'rgba(255, 0, 0, 1)' );
rGradient.addColorStop( 1, 'rgba(0, 255, 0, 0)' );
context.fillStyle = rGradient;
// 设置globalAlpha
context.globalAlpha = weight;
context.beginPath();
context.arc( x, y, radius, 0, 2 * Math.PI );
context.closePath();
context.fill();
};
// 获得渐变颜色条图
const getPaletteMap = () => {
//颜色条的颜色分布
const colorStops = {
1.0: '#f00',
0.8: '#e2fa00',
0.6: '#33f900',
0.3: '#0349df',
0.0: '#0f00ff'
};
//颜色条的大小
const width = 256, height = 10;
// 创建canvas
const paletteCanvas = document.createElement( 'canvas' );
paletteCanvas.width = width;
paletteCanvas.height = height;
paletteCanvas.style.position = 'absolute';
paletteCanvas.style.top = '20px';
paletteCanvas.style.right = '10px';
const ctx = paletteCanvas.getContext( '2d' );
// 创建线性渐变色
const linearGradient = ctx.createLinearGradient( 0, 0, width, 0 );
for ( const key in colorStops ) {
linearGradient.addColorStop( key, colorStops[ key ] );
}
// 绘制渐变色条
ctx.fillStyle = linearGradient;
ctx.fillRect( 0, 0, width, height );
document.body.appendChild( paletteCanvas );
const paletteTexture = new THREE.Texture( paletteCanvas );
paletteTexture.minFilter = THREE.NearestFilter;
paletteTexture.needsUpdate = true;
return paletteTexture;
};
// 获取透明度阶梯图
const getAlphaScaleMap = ( width, height ) => {
const canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
const context = canvas.getContext( '2d' );
// 随机生成温度
const tenperature = getTemperature();
// 绘制透明度阶梯图
for ( let i = 0; i < segments; i ++ ) {
// 计算出当前温度占标准温度的权值
const weight = tenperature[ i ] / 25; // 25 是之前颜色的变化区间 10 - 35
drawCircular( context, {
x: Math.random() * w,
y: Math.random() * h,
radius: 50,
weight: weight
} );
}
// 创建 Three 中的图片
const tex = new THREE.Texture( canvas );
tex.minFilter = THREE.NearestFilter;
tex.needsUpdate = true;
return tex;
};
init();
animate();
// 初始化
function init() {
// 渲染器
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( '#ccc' );
document.body.appendChild( renderer.domElement );
// 场景创建
scene = new THREE.Scene();
// camera 创建设置
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 0, 0, 3000 );
scene.add( new THREE.AmbientLight( 0xeef0ff ) );
// 创建热力图渲染的平面几何体
const heatMapGeo = new THREE.PlaneBufferGeometry( 500, 500 );
// 创建热力图渲染的材质
const heatMapMaterial = new THREE.ShaderMaterial( {
transparent: true,
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
uniforms: {
'alphaScaleMap': {
type: 't',
value: getAlphaScaleMap( w, h )
},
'paletteMap': {
type: 't',
value: getPaletteMap()
},
}
} );
// 创建热力图Mesh,并显示在 Plane 上
const heatMapPlane = new THREE.Mesh( heatMapGeo, heatMapMaterial );
scene.add( heatMapPlane );
const contorl = new OrbitControls( camera, renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
// 窗口变化的时候,进行 camera 视口的更新
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
// 动画 update
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>07SimpleHeatMapInThreejsCSDN</title>
<style>
</style>
</head>
<body>
<div id="container" >
</div>
<div>
<canvas style="height: 900px; width: 100%;" id="heatmap"></canvas>
</div>
</body>
<script src="heatmap.js"></script>
<script type="importmap">
{
"imports": {
"three": "../../../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from '../../jsm/controls/OrbitControls.js';
let renderer, scene, camera;
let heatmapInstance;
let texture;
let points;
let mesh;
const TemperatureColorStops = {
1.0: '#f00',
0.9: '#e2fa00',
0.6: '#33f900',
0.3: '#0349df',
0.0: '#0f00ff'
};
init();
animate();
function init() {
initRender();
initCameraAndScene();
initLight();
addPluginHeatmap();
// control 鼠标控制旋转移动
const contorl = new OrbitControls( camera, renderer.domElement );
// window
window.addEventListener( 'resize', onWindowResize, false );
}
// 初始化 Renderer
function initRender() {
const container = document.getElementById( 'container' );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( container.innerWidth, container.innerHeight );
renderer.setClearColor( '#ccc' );
container.appendChild( renderer.domElement );
}
// 初始化场景和相机
function initCameraAndScene() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 0, 50 );
scene.add( camera );
}
// 光照
function initLight() {
// 基本的半球光,类似环境光
const hemiLight = new THREE.HemisphereLight( '#ffffff',
'#444444', 0.5 );
hemiLight.position.set( 0, 400, 0 );
scene.add( hemiLight );
// 添加平行光
const dirLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
dirLight.position.set( 0, 200, 100 );
dirLight.castShadow = true;
dirLight.shadow.camera.top = 180;
dirLight.shadow.camera.bottom = - 100;
dirLight.shadow.camera.left = - 120;
dirLight.shadow.camera.right = 120;
scene.add( dirLight );
}
// 窗口更新
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
// 创建热力图
function addPluginHeatmap() {
// 创建一个heatmap实例对象
// “h337” 是heatmap.js全局对象的名称.可以使用它来创建热点图实例
// 这里直接指定热点图渲染的div了.heatmap支持自定义的样式方案,网页外包接活具体可看官网api
heatmapInstance = h337.create( {
container: document.getElementById( 'heatmap' ),
//backgroundColor:'red', // '#121212' 'rgba(0,102,256,0.2)'
gradient: TemperatureColorStops,
radius: 50, // [0,+∞)
opacity: .5,
blur: '.8',
} );
setHeatMapData();
// 获取 heatmap
texture = new THREE.Texture( heatmapInstance._renderer.canvas );
const material = new THREE.MeshLambertMaterial( {
map: texture,
transparent: true,
opacity: 1
} );
mesh = new THREE.Mesh( new THREE.PlaneGeometry( 10, 10, 10 ), material );
scene.add( mesh );
// 更新图片
if ( texture ) {
texture.needsUpdate = true;
}
}
// 设置热力图位置温度数据
function setHeatMapData() {
//构建一些随机数据点,网页切图价格这里替换成你的业务数据
points = [];
let max = 0;
const width = document.body.clientWidth;
const height = document.body.clientHeight;
let len = 500;
// 随机位置点设置温度值
while ( len -- ) {
var val = Math.floor( Math.random() * 25 + 10 );
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 的数据
const data = {
max: max,
data: points
};
//因为data是一组数据,web切图报价所以直接setData
heatmapInstance.setData( data ); //数据绑定还可以使用
}
</script>
</html>
let 的「创建」过程被提升了,但是初始化没有提升。
var 的「创建」和「初始化」都被提升了。
function 的「创建」「初始化」和「赋值」都被提升了。
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
使用遍历顶点的方式给模型设置热力图需要用到以下步骤:
首先需要定义一个热力图的颜色值数组,例如:
var colors = [
0x0000FF, // 最低温度,蓝色
0x00FFFF,
0x00FF00,
0xFFFF00,
0xFF0000 // 最高温度,红色
];
这里将热力图颜色值从蓝色到红色依次排列。
使用 THREE.Geometry
中的 vertices
属性获取模型的顶点数组,并使用 for
循环遍历所有顶点。
例如:
mesh.geometry.vertices.forEach(function(vertex) {
// 遍历顶点的代码
});
根据模型的实际需求,可以通过函数计算每个顶点的温度值,例如:
function getTemperature(vertex) {
// 返回该顶点的温度值,范围在 0 ~ 4 之间
return Math.floor(Math.random() * 5);
}
在真实应用中,可能需要根据传感器等实际设备来获取每个顶点的温度值。
根据每个顶点的温度值,计算需要显示的颜色,例如
var temperature = getTemperature(vertex); // 获取当前顶点的温度值
var colorIndex = Math.floor(temperature / 1.25); // 计算需要使用的颜色索引,值范围在 0 ~ 4 之间
var color = new THREE.Color(colors[colorIndex]); // 获取对应颜色值,并生成 THREE.Color 对象
这里将温度值分为 5 个等级(0-1.25、1.25-2.5、2.5-3.75、3.75-5),并将每个等级对应一个颜色值,根据当前顶点的温度值计算需要使用的颜色索引并获取对应色值。
使用 THREE.Geometry
中的 colors
属性设置获取到的颜色值。
mesh.geometry.colors.push(color);
将获取到的颜色值push到颜色数组中。
定义一个新的材质并将颜色数组赋值给材质的 vertexColors
属性,例如:
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors
});
将定义好的材质应用到模型上,然后进行绘制即可:
mesh.geometry.colorsNeedUpdate = true; // 标记颜色变化需要更新
mesh.material = material; // 应用新的材质
mesh.geometry.computeVertexNormals(); // 计算面法线向量
完整代码示例:
var colors = [
0x0000FF, // 最低温度,蓝色
0x00FFFF,
0x00FF00,
0xFFFF00,
0xFF0000 // 最高温度,红色
];
function getTemperature(vertex) {
// 返回该顶点的温度值,范围在 0 ~ 4 之间
return Math.floor(Math.random() * 5);
}
function setHeatMap(mesh) {
// 遍历所有顶点,并根据温度值设置颜色
mesh.geometry.vertices.forEach(function(vertex) {
var temperature = getTemperature(vertex);
var colorIndex = Math.floor(temperature / 1.25);
var color = new THREE.Color(colors[colorIndex]);
mesh.geometry.colors.push(color);
});
// 定义新的材质,并将颜色数组赋值给 vertexColors 属性
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors
});
// 将材质应用到模型,并进行绘制
mesh.geometry.colorsNeedUpdate = true;
mesh.material = material;
mesh.geometry.computeVertexNormals();
}
以上就是使用遍历顶点的方式给模型设置热力图的示例,你可以根据自己的需求进行修改和扩展。
如果我的回答解决了您的问题,请采纳!
以下内容引用CHATGPT、有用望采纳:
可以通过遍历模型的顶点,根据顶点的位置信息设置不同的颜色,来实现热力图的效果。具体思路如下:
遍历模型的顶点,获取每个顶点的位置信息。
根据顶点的位置信息计算出对应的热力值(可以根据不同的需求自定义计算公式)。
根据热力值设置顶点的颜色。
创建材质对象,将顶点颜色作为材质的vertexColors属性传入,将材质应用到模型上。
下面是一个简单的示例代码:
// 遍历模型顶点,设置热力图
function setHeatMap(model) {
// 创建顶点颜色数组
const colors = [];
const vertices = model.geometry.vertices;
// 遍历顶点,计算热力值并设置颜色
for (let i = 0; i < vertices.length; i++) {
const vertex = vertices[i];
// 计算热力值,这里简单假设热力值为顶点z坐标的值
const heatValue = vertex.z;
// 根据热力值设置颜色,这里简单假设颜色为红色到蓝色的渐变
const color = new THREE.Color(0x0000ff);
color.lerp(new THREE.Color(0xff0000), heatValue);
colors.push(color);
}
// 创建材质对象,将顶点颜色作为材质的vertexColors属性传入
const material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors
});
// 创建新的几何体,将顶点颜色赋值给新的几何体
const geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.colors = colors;
// 创建新的网格对象,将新的几何体和材质应用到网格对象上
const mesh = new THREE.Mesh(geometry, material);
// 移除原有的模型,将新的网格对象添加到场景中
scene.remove(model);
scene.add(mesh);
}
需要注意的是,这里示例代码使用的是MeshBasicMaterial,如果需要更高级的材质效果,可以使用其他类型的材质对象。另外,由于这里使用了遍历顶点的方式,对于顶点数较多的模型可能会有一定的性能问题,需要根据具体情况进行优化。
什么模型做不了uv?跟热力图有什么关系,问题描述不清楚。不知道如何帮你。
该回答引用ChatGPTjavascript
// 创建一个初始的几何体
var geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建一个自定义的着色器
var vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
var fragmentShader = `
uniform vec3 colorA;
uniform vec3 colorB;
varying vec2 vUv;
void main() {
gl_FragColor = vec4( mix( colorA, colorB, vUv.y ), 1.0 );
}
`;
var uniforms = {
colorA: { value: new THREE.Color(0xff0000) },
colorB: { value: new THREE.Color(0x0000ff) }
};
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader
});
// 创建一个网格对象
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
要为 Three.js 中的模型设置热力图,可以按照以下步骤进行:
遍历模型的顶点,并计算每个顶点的热力值。
根据每个顶点的热力值,为该顶点设置对应的颜色值。
创建一个 THREE.BufferGeometry
对象,并将每个顶点的位置和颜色值存储到该对象中。
创建一个 THREE.PointsMaterial
材质,并将其设置为 vertexColors
模式。
创建一个 THREE.Points
对象,并将上述 BufferGeometry
对象和 PointsMaterial
材质分别设置给该对象。
最后将该 Points
对象加入场景中即可。
下面是一份完整的代码示例:
// 创建场景和相机
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 加载模型并遍历顶点
var loader = new THREE.OBJLoader();
loader.load('your-model.obj', function (object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
var geometry = child.geometry;
var vertices = geometry.vertices;
var colors = [];
for (var i = 0; i < vertices.length; i++) {
// 计算热力值,这里可以根据具体需求自行实现
var heatValue = calculateHeatValue(vertices[i]);
// 根据热力值设置对应颜色
var color = new THREE.Color();
color.setHSL(0.7 * (1 - heatValue), 1, 0.5);
// 将颜色值存储到数组中
colors.push(color.r, color.g, color.b);
}
// 创建 BufferGeometry 对象并将顶点位置和颜色值存储到该对象中
var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(geometry.vertices.length * 3), 3));
bufferGeometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
for (var i = 0; i < geometry.vertices.length; i++) {
bufferGeometry.attributes.position.setXYZ(i, vertices[i].x, vertices[i].y, vertices[i].z);
}
// 创建 PointsMaterial 材质并设置为 vertexColors 模式
var pointsMaterial = new THREE.PointsMaterial({ size: 0.05, vertexColors: THREE.VertexColors });
// 创建 Points 对象并将上述 BufferGeometry 对象和 PointsMaterial 材质分别设置给该对象
var points = new THREE.Points(bufferGeometry, pointsMaterial);
// 将 Points 对象加入场景中
scene.add(points);
}
});
});
// 渲染循环
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
在上述代码中,我们使用了 Three.js 的 OBJLoader
加载模型,并在遍历模型的顶点时,根据具体需求计算了热力值,并为每个顶点设置了对应的颜色值。我们使用了 THREE.BufferGeometry
对象来存储顶点位置和颜色值,并创建了 THREE.PointsMaterial
材质并将其设置为 vertexColors
模式。最后创建了一个 THREE.Points
对象,并将上述 BufferGeometry
对象和 PointsMaterial
材质分别设置给该对象,然后将该 Points
对象加入场景中。在渲染循环中,我们使用了 Three.js 的 requestAnimationFrame
函数来循环渲染场景和相机,以实现动画效果。
需要注意的是,在计算热力值时,您需要根据具体需求选择合适的算法,并根据算法实现代码。在本示例中,我们使用了简单的线性函数来计算热力值,如下所示:
function calculateHeatValue(vertex) {
return (vertex.y + 1) / 2; // 将顶点 y 坐标映射到 [0, 1] 范围内
}
您可以根据具体需求修改该函数,以实现更好的效果。
另外,需要注意的是,由于遍历顶点并为每个顶点设置颜色的操作可能比较耗时,因此在处理较大模型时,可能会导致性能问题。为了避免这种情况,您可以考虑使用一些优化策略,例如:只处理模型的部分顶点,或者使用多线程等技术。
如果以上回答对您有所帮助,点击一下采纳该答案~谢谢