使用LUT给模型上色
更新时间: 2022-05-18 14:16:04
# 关于LUT
Lut属于Three.js的Math模块,主要方便对模型按照常见颜色方案进行上色,适用用于二维、三维的图表,比如下面这种颜色条。
# 引入
import { Lut } from 'three/examples/jsm/math/Lut.js';
1
# 使用
// 先初始化
let lut = new Lut();
//设置一下colorMap
let params = {
colorMap: 'rainbow',
};
lut.setColorMap( params.colorMap );
// 然后设置一下映射的最大值和最小值范围
lut.setMax( 2000 );
lut.setMin( 0 );
//然后将value转换成颜色
let colorValue = 100
let newcolor = lut.getColor(colorValue);
//一般来说是将这个颜色赋给模型顶点
let g = mesh.geometry;
let colors = g.attributes.color;
for (let i = 0; i < colors.length; i ++ ) {
colors.setXYZ(i, newcolor.r, newcolor.g, newcolor.b);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 案例学习
接下来试一试threejs官网的那个例子吧:
下面分析一下代码:
init()
function init() {
container = document.getElementById('container')
// 这个是加载模型的Scene
scene = new THREE.Scene()
scene.background = new THREE.Color(0xffffff)
// 这个是加载2d色条的scene
uiScene = new THREE.Scene()
lut = new Lut()
const width = container.clientWidth
const height = container.clientHeight
// 透视相机,针对模型的
perpCamera = new THREE.PerspectiveCamera( 60, width/height, 1, 100)
perpCamera.position.set(0,0,10)
scene.add(perpCamera)
// 正交相机,用来渲染旁边那个颜色条的
/*
正交相机无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变,这对于渲染2D场景或者UI元素是非常有用的。
构造器
OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )
left — 摄像机视锥体左侧面。
right — 摄像机视锥体右侧面。
top — 摄像机视锥体上侧面。
bottom — 摄像机视锥体下侧面。
near — 摄像机视锥体近端面。
far — 摄像机视锥体远端面。
*/
orthoCamera = new THREE.OrthographicCamera(-1,1,1,-1,1,2)
// 设置位置是为了让色条偏一点
orthoCamera.position.set(0.5, 0, 1)
// 用精灵来画色条,并使用Lut创造的canvas元素作为纹理
sprite = new THREE.Sprite(new THREE.SpriteMaterial({
map: new THREE.CanvasTexture(lut.createCanvas())
}))
// 这是里为了让色条变得细长
sprite.scale.x = 0.125
uiScene.add(sprite)
mesh = new THREE.Mesh(undefined, new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
color: 0xF5F5F5,
// 这里将mesh设置为顶点着色,这里很重要!!
vertexColors: true
}))
scene.add(mesh)
params = {
colorMap: 'rainbow'
}
loadModel()
const pointLight = new THREE.PointLight(0xffffff, 1)
/*
这里为什么将点光源添加到相机中呢,因为相机基于Object3D,将点光源添加到相机,
就能让点光源和相机坐标一致,后面相机的坐标因为Orbitcontrol发生改变光源的位置会跟着一起变
*/
perpCamera.add(pointLight)
renderer = new THREE.WebGLRenderer({antialias: true})
//这里不需要让renderer自动的clear,可以节省资源
renderer.autoClear = false
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
container.appendChild(renderer.domElement)
// 加上轨道控制器
const controls = new OrbitControls(perpCamera, renderer.domElement)
controls.addEventListener('change', render)
var gui = new dat.GUI()
gui.add(params,'colorMap', ['rainbow','cooltowarm','blackbody','grayscale'])
.onChange(function() {
updateColors()
render()
})
}
function loadModel() {
// 加载模型
const loader = new THREE.BufferGeometryLoader()
loader.load('/models/json/pressure.json', function(geometry) {
console.log(geometry)
// 根据边界将几何体居中
geometry.center()
// 通过片面法向量的平均值计算每个顶点的法向量,兰伯特材质需要光照,所以必须计算出每个顶点的法向量才能正确使用兰伯特材质
geometry.computeVertexNormals()
// 设定一个默认颜色
const colors = []
for(let i = 0,n = geometry.attributes.position.count; i<n; ++i) {
colors.push(1,1,1)
}
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))
mesh.geometry = geometry
updateColors()
render()
})
}
function updateColors() {
// 修改lut的颜色表
lut.setColorMap(params.colorMap)
// 设定最大最小值
lut.setMax(2000)
lut.setMin(0)
const geometry = mesh.geometry
// 取数据
const pressures = geometry.attributes.pressure
const colors = geometry.attributes.color
for(let i = 0;i<pressures.array.length;i++) {
const colorValue = pressures.array[i]
const color = lut.getColor(colorValue)
if(color === undefined) {
console.log('找不到对应的颜色')
}else{
//设置给定索引的矢量的第一、二、三维数据
colors.setXYZ(i, color.r, color.g, color.b)
}
}
//该标志位指明当前 attribute 已经被修改过,且需要再次送入 GPU 处理。当开发者改变了该队列的值,则标志位需要设置为 true。
//记住不要打错字了,我少打了个s结果查了半天才找到这里
colors.needsUpdate = true
const map = sprite.material.map
lut.updateCanvas(map.image)
map.needsUpdate = true
}
function render() {
renderer.clear()
renderer.render(scene,perpCamera)
renderer.render(uiScene,orthoCamera)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# 实战学习
我们公司的项目实际存在以下问题:
- 客户提供的模型格式不确定,质量也不确定
- 数据并不会每个顶点都有数据,而且给到的数据可能也和顶点对不上
所以接下来我拿客户的模型来试试看效果: