如何为缓冲区几何中的每个顶点分配不同的颜色? Three.js
How to assign different color for each vertex in a buffer geometry? Three.js
我是 Three.js 的新手并且
我找到了关于该主题的一些答案,但 none 使用了缓冲区几何。我正在 React 上制作欧洲的 3D 地形项目,我希望能够根据某些值更改水位。为了做到这一点,我的着色必须逐个顶点完成,而不是从图像添加纹理,如果我更改顶点的值,着色不会改变。因此,根据顶点的 "height",我想分配不同的颜色。
这是我的 componentDidMount 函数:
const SIZE_AMPLIFIER = 15;
var container = document.getElementById("main_map");
var scene, camera, renderer, controls;
var data, plane;
//load map data from bin file
function loadTerrain(file) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', file, true);
xhr.onload = function (evt) {
if (xhr.response) {
data = new Uint16Array(xhr.response)
init()
}
};
xhr.send(null);
}
loadTerrain('stats.bin');
function init() {
// initialize camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100000);
camera.position.set(10000, 4000, 0);
// initialize scene
scene = new THREE.Scene();
// initialize directional light (sun)
var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 300);
sun.distance = 1000;
scene.add(sun);
var frame = new THREE.SpotLightHelper(sun);
scene.add(frame);
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.append(renderer.domElement);
//initialize controls
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 1;
controls.rotateSpeed = .8;
controls.maxPolarAngle = Math.PI / 2 - .3;
// initialize plane
plane = new THREE.PlaneBufferGeometry(1500 * SIZE_AMPLIFIER, 1500 * SIZE_AMPLIFIER, 999 , 999);
plane.castShadow = true;
plane.receiveShadow = true;
var vertices = plane.attributes.position.array;
console.log(data)
// apply height map to vertices of plane
// assign different color depending on the value??
for (let i = 0, j = 2; i < data.length; i += 1, j += 3) {
if(data[i] == 0){
vertices[j] = 0
}else {
vertices[j] = data[i] / 65535 * 325 + 10
}
}
// Add texture image
var material = new THREE.MeshLambertMaterial({
map:THREE.ImageUtils.loadTexture('stats_color.png')
})
var mesh = new THREE.Mesh(plane, material);
mesh.rotation.x = - Math.PI / 2;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
plane.computeFaceNormals();
plane.computeVertexNormals();
scene.add(mesh);
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
//controls.update();
}
这之后的结果(对我来说很满意,但不幸的是对应用程序的目的无效):
Here is an article on how to use BufferGeometry
要添加颜色,您可以制作一个 Float32Array 颜色并将其添加为名为 'color' 的属性。
geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(colors), uvNumComponents));
然后将 material.vertexColors
设置为 true;
const material = new THREE.MeshPhongMaterial({
vertexColors: true,
});
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 3;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
// NOT A GOOD EXAMPLE OF HOW TO MAKE A CUBE!
// Only trying to make it clear most vertices are unique
const vertices = [
// front
{ pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
// right
{ pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
{ pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
// back
{ pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
// left
{ pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 1], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 0], },
// top
{ pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 0], },
// bottom
{ pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 1], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 1], },
{ pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 0], },
];
const positions = [];
const normals = [];
const uvs = [];
const colors = [];
for (const vertex of vertices) {
positions.push(...vertex.pos);
normals.push(...vertex.norm);
uvs.push(...vertex.uv);
colors.push(Math.random(), Math.random(), Math.random());
}
const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
const normalNumComponents = 3;
const uvNumComponents = 2;
geometry.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
geometry.setAttribute(
'uv',
new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(colors), uvNumComponents));
const material = new THREE.MeshPhongMaterial({
vertexColors: true,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
我是 Three.js 的新手并且 我找到了关于该主题的一些答案,但 none 使用了缓冲区几何。我正在 React 上制作欧洲的 3D 地形项目,我希望能够根据某些值更改水位。为了做到这一点,我的着色必须逐个顶点完成,而不是从图像添加纹理,如果我更改顶点的值,着色不会改变。因此,根据顶点的 "height",我想分配不同的颜色。
这是我的 componentDidMount 函数:
const SIZE_AMPLIFIER = 15;
var container = document.getElementById("main_map");
var scene, camera, renderer, controls;
var data, plane;
//load map data from bin file
function loadTerrain(file) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', file, true);
xhr.onload = function (evt) {
if (xhr.response) {
data = new Uint16Array(xhr.response)
init()
}
};
xhr.send(null);
}
loadTerrain('stats.bin');
function init() {
// initialize camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100000);
camera.position.set(10000, 4000, 0);
// initialize scene
scene = new THREE.Scene();
// initialize directional light (sun)
var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 300);
sun.distance = 1000;
scene.add(sun);
var frame = new THREE.SpotLightHelper(sun);
scene.add(frame);
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.append(renderer.domElement);
//initialize controls
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 1;
controls.rotateSpeed = .8;
controls.maxPolarAngle = Math.PI / 2 - .3;
// initialize plane
plane = new THREE.PlaneBufferGeometry(1500 * SIZE_AMPLIFIER, 1500 * SIZE_AMPLIFIER, 999 , 999);
plane.castShadow = true;
plane.receiveShadow = true;
var vertices = plane.attributes.position.array;
console.log(data)
// apply height map to vertices of plane
// assign different color depending on the value??
for (let i = 0, j = 2; i < data.length; i += 1, j += 3) {
if(data[i] == 0){
vertices[j] = 0
}else {
vertices[j] = data[i] / 65535 * 325 + 10
}
}
// Add texture image
var material = new THREE.MeshLambertMaterial({
map:THREE.ImageUtils.loadTexture('stats_color.png')
})
var mesh = new THREE.Mesh(plane, material);
mesh.rotation.x = - Math.PI / 2;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
plane.computeFaceNormals();
plane.computeVertexNormals();
scene.add(mesh);
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
//controls.update();
}
这之后的结果(对我来说很满意,但不幸的是对应用程序的目的无效):
Here is an article on how to use BufferGeometry
要添加颜色,您可以制作一个 Float32Array 颜色并将其添加为名为 'color' 的属性。
geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(colors), uvNumComponents));
然后将 material.vertexColors
设置为 true;
const material = new THREE.MeshPhongMaterial({
vertexColors: true,
});
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="c"></canvas>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r115/build/three.module.js';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 3;
const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
// NOT A GOOD EXAMPLE OF HOW TO MAKE A CUBE!
// Only trying to make it clear most vertices are unique
const vertices = [
// front
{ pos: [-1, -1, 1], norm: [ 0, 0, 1], uv: [0, 1], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [-1, 1, 1], norm: [ 0, 0, 1], uv: [0, 0], },
{ pos: [ 1, -1, 1], norm: [ 0, 0, 1], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 0, 1], uv: [1, 0], },
// right
{ pos: [ 1, -1, 1], norm: [ 1, 0, 0], uv: [0, 1], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, 1, 1], norm: [ 1, 0, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 1, 0, 0], uv: [1, 1], },
{ pos: [ 1, 1, -1], norm: [ 1, 0, 0], uv: [1, 0], },
// back
{ pos: [ 1, -1, -1], norm: [ 0, 0, -1], uv: [0, 1], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [ 1, 1, -1], norm: [ 0, 0, -1], uv: [0, 0], },
{ pos: [-1, -1, -1], norm: [ 0, 0, -1], uv: [1, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 0, -1], uv: [1, 0], },
// left
{ pos: [-1, -1, -1], norm: [-1, 0, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 1], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [-1, 0, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [-1, 0, 0], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [-1, 0, 0], uv: [1, 0], },
// top
{ pos: [ 1, 1, -1], norm: [ 0, 1, 0], uv: [0, 1], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 1], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [ 1, 1, 1], norm: [ 0, 1, 0], uv: [0, 0], },
{ pos: [-1, 1, -1], norm: [ 0, 1, 0], uv: [1, 1], },
{ pos: [-1, 1, 1], norm: [ 0, 1, 0], uv: [1, 0], },
// bottom
{ pos: [ 1, -1, 1], norm: [ 0, -1, 0], uv: [0, 1], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 1], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [ 1, -1, -1], norm: [ 0, -1, 0], uv: [0, 0], },
{ pos: [-1, -1, 1], norm: [ 0, -1, 0], uv: [1, 1], },
{ pos: [-1, -1, -1], norm: [ 0, -1, 0], uv: [1, 0], },
];
const positions = [];
const normals = [];
const uvs = [];
const colors = [];
for (const vertex of vertices) {
positions.push(...vertex.pos);
normals.push(...vertex.norm);
uvs.push(...vertex.uv);
colors.push(Math.random(), Math.random(), Math.random());
}
const geometry = new THREE.BufferGeometry();
const positionNumComponents = 3;
const normalNumComponents = 3;
const uvNumComponents = 2;
geometry.setAttribute(
'position',
new THREE.BufferAttribute(new Float32Array(positions), positionNumComponents));
geometry.setAttribute(
'normal',
new THREE.BufferAttribute(new Float32Array(normals), normalNumComponents));
geometry.setAttribute(
'uv',
new THREE.BufferAttribute(new Float32Array(uvs), uvNumComponents));
geometry.setAttribute(
'color',
new THREE.BufferAttribute(new Float32Array(colors), uvNumComponents));
const material = new THREE.MeshPhongMaterial({
vertexColors: true,
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cube.rotation.x = time;
cube.rotation.y = time;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>