#javascript #three.js #texture-mapping
#javascript #three.js #текстурное отображение
Вопрос:
Я создал BufferGeometry, которая состоит из 5 плоскостей (100x25)
с двумя треугольниками в каждой.
function createGeometry() {
var geometry = new THREE.PlaneGeometry(100, 25, 1);
return geometry;
}
function createScene() {
var bufferGeometry = new THREE.BufferGeometry();
var radius = 125;
var count = 5;
var positions = [];
var normals = [];
var colors = [];
var vector = new THREE.Vector3();
var color = new THREE.Color( 0xffffff );
var heartGeometry = createGeometry();
var geometry = new THREE.Geometry();
var step = 0;
for ( var i = 1, l = count; i <= l; i ) {
geometry.copy( heartGeometry );
const y = i * 30
geometry.translate(-100, y, 0);
// color.setHSL( ( i / l ), 1.0, 0.7 );
geometry.faces.forEach( function ( face ) {
positions.push( geometry.vertices[ face.a ].x );
positions.push( geometry.vertices[ face.a ].y );
positions.push( geometry.vertices[ face.a ].z );
positions.push( geometry.vertices[ face.b ].x );
positions.push( geometry.vertices[ face.b ].y );
positions.push( geometry.vertices[ face.b ].z );
positions.push( geometry.vertices[ face.c ].x );
positions.push( geometry.vertices[ face.c ].y );
positions.push( geometry.vertices[ face.c ].z );
normals.push( face.normal.x );
normals.push( face.normal.y );
normals.push( face.normal.z );
normals.push( face.normal.x );
normals.push( face.normal.y );
normals.push( face.normal.z );
normals.push( face.normal.x );
normals.push( face.normal.y );
normals.push( face.normal.z );
colors.push( color.r );
colors.push( color.g );
colors.push( color.b );
colors.push( color.r );
colors.push( color.g );
colors.push( color.b );
colors.push( color.r );
colors.push( color.g );
colors.push( color.b );
});
}
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors,
side: THREE.FrontSide
});
var mesh = new THREE.Mesh( bufferGeometry, material );
scene.add( mesh );
}
Теперь вместо того, чтобы раскрашивать каждую плоскость как я могу добавить текст к каждой плоскости. Скажем, я просто хочу отобразить 1,2,3,4,5 в центре каждой плоскости.
Я знаю, что для добавления текстуры необходимо выполнить следующее.
- Сгенерировать текстуру из холста
- Измените материал на материал шейдера с помощью
map:texture
- Добавьте
uv
s в BufferGeometry.
Но какова связь между uv
s и текстурой.
У меня есть функция создания текстуры
//not sure for each character or one texture as a single texture
function createTexture(ch){
var fontSize = 20;
var c = document.createElement('canvas');
c.width = 100;
c.height = 25;
var ctx = c.getContext('2d');
ctx.font = fontSize 'px Monospace';
ctx.fillText(ch, c.width/2, c.height/2);
var texture = new THREE.Texture(c);
texture.flipY = false;
texture.needsUpdate = true;
return texture;
}
Полный демонстрационный код
Редактировать
Я рассматриваю производительность как высокий приоритет для этого эксперимента. Мы можем добавить каждую сетку для каждого текста, но это увеличит количество сеток на экране и снизит производительность. Я ищу любую идею с одной сеткой, как в моем примере.
Самое близкое, что я нашел, — это это, но я не понял, какую именно технику они используют.
Комментарии:
1.Вам не нужно менять свой
MeshBasicMaterial
наShaderMaterial
, поскольку он уже обрабатываетmap
свойство. Все, что вам нужно сделать сейчас, это добавить атрибут uv к вашей геометрии в диапазоне[0, 1]
, чтобы она следовала этой схеме[0, 0]
в одном углу и[1, 1]
в противоположном.
Ответ №1:
Вы должны скопировать первый uv-канал .faceVertexUvs
свойства из THREE.Geometry
формы, аналогично тому, как вы делаете это с координатами вершин и векторами нормалей:
for ( var i = 1, l = count; i <= l; i ) {
geometry.copy( heartGeometry );
const y = i * 30
geometry.translate(-100, y, 0);
geometry.faces.forEach( function ( face ) {
let f = [face.a, face.b, face.c];
for (let i=0; i < 3; i) {
positions.push( ...geometry.vertices[f[i]].toArray() );
normals.push( ...face.normal.toArray() );
colors.push( ...color.toArray() );
}
} );
geometry.faceVertexUvs[0].forEach( function ( faceUvs ) {
for (let i=0; i < 3; i) {
uv.push( ...faceUvs[i].toArray() );
}
} );
}
Если вы хотите определить несколько текстур для одной геометрии, то вам придется использовать несколько THREE.Material
s для одной THREE.Mesh
.
Определите группы (см. .addGroup
) для THREE.BufferGeometry
. Каждая группа связывает диапазон вершин с материалом.
var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
bufferGeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv, 2 ) );
let materials = []
let v_per_group= positions.length / 3 / count;
for ( var i = 0; i < count; i ) {
bufferGeometry.addGroup(i * v_per_group, v_per_group, i);
let material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors,
side: THREE.FrontSide,
map : createTexture("button" (i 1))
});
materials.push(material);
}
var mesh = new THREE.Mesh( bufferGeometry, materials );
scene.add( mesh );
var camera, scene, renderer, controls, stats;
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( 45.0, window.innerWidth / window.innerHeight, 100, 1500.0 );
camera.position.z = 480.0;
scene.add( camera );
controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.minDistance = 100.0;
controls.maxDistance = 800.0;
controls.dynamicDampingFactor = 0.1;
scene.add( new THREE.AmbientLight( 0xffffff, 1 ) );
createScene();
//stats = new Stats();
//document.body.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
}
function createGeometry() {
var geometry = new THREE.PlaneGeometry(100, 25, 1);
return geometry;
}
function createTexture(ch){
var fontSize = 20;
var c = document.createElement('canvas');
c.width = 128;
c.height = 32;
var ctx = c.getContext('2d');
ctx.beginPath();
ctx.rect(0, 0, 128, 32);
ctx.fillStyle = "white";
ctx.fill();
ctx.fillStyle = "black";
ctx.font = fontSize 'px Monospace';
ctx.fillText(ch, 20, 24);
var texture = new THREE.Texture(c);
texture.flipY = true;
texture.needsUpdate = true;
return texture;
}
function createScene() {
var radius = 125;
var count = 5;
var vector = new THREE.Vector3();
var color = new THREE.Color( 0xffffff );
var heartGeometry = createGeometry();
var geometry = new THREE.Geometry();
var positions = [];
var normals = [];
var colors = [];
var uv = [];
for ( var i = 1, l = count; i <= l; i ) {
geometry.copy( heartGeometry );
const y = i * 30
geometry.translate(-100, y, 0);
geometry.faces.forEach( function ( face ) {
let f = [face.a, face.b, face.c];
for (let i=0; i < 3; i) {
positions.push( ...geometry.vertices[f[i]].toArray() );
normals.push( ...face.normal.toArray() );
colors.push( ...color.toArray() );
}
} );
geometry.faceVertexUvs[0].forEach( function ( faceUvs ) {
for (let i=0; i < 3; i) {
uv.push( ...faceUvs[i].toArray() );
}
} );
}
var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
bufferGeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv, 2 ) );
let materials = []
let v_per_group = positions.length / 3 / count;
for ( var i = 0; i < count; i ) {
bufferGeometry.addGroup(i * v_per_group, v_per_group, i);
let material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors,
side: THREE.FrontSide,
map : createTexture("button" (i 1))
});
materials.push(material);
}
var mesh = new THREE.Mesh( bufferGeometry, materials );
scene.add( mesh );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
//stats.update();
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
Комментарии:
1. да, отдельная сетка для каждого текста будет работать, но это не будет масштабируемым решением. Это увеличит количество ячеек в сцене и, следовательно, fps снизится, если мы выйдем за пределы, скажем, 10K.
2. Итак, вы хотите 1 сетку с 5 текстурами (материалами)?
3. Да .. точно .. как и в моем коде, я использовал только одну сетку.
4. @Sarath Вы должны определить группы для геометрии буфера. Я изменил ответ.
5. @Sarath «атлас текстур» означает, что у вас есть одна текстура (например, все символы шрифта подряд). Координаты текстуры «выделяют» прямоугольную область из текстуры. Координаты каждого глифа хранятся в «атласе». Для каждого глифа должен быть отрисован отдельный квадрат, но все квадраты могут быть сохранены в одном буфере и могут быть нарисованы с помощью одной инструкции рисования одновременно.