Три. js текстура холста, сила 2, но обрезается - PullRequest
0 голосов
/ 15 февраля 2020

за три. js Лучше всего, чтобы текстуры были степенью 2, а на самом деле это требование для некоторых старых Chrome браузеров, например, 32x32, 64x64 и c

Поэтому мне нужно чтобы создать холст текстуру с силой в 2 измерения, но мне нужно, чтобы спрайт был кликабельным, что означает, что его нужно как-то обрезать.

Рассмотрим этот пример:

enter image description here

  • Черный bg - это тройка. js рендер
  • Красный bg - требуемый размер холста (до степени 2)
  • White bg - это размер текста

Я хотел бы сохранить текстуру в степени 2, но обрезать ее, чтобы показать только белую область. Это так, например, пользователь может навести курсор на ярлык. Я не хочу, чтобы они зависали на красной территории!

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 400;

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var config = {
  fontface: 'Arial',
  fontsize: 32,
  fontweight: 500,
  lineheight: 1,
};
var text = 'Hello world!';

function nextPowerOf2(n) {
  return Math.pow(2, Math.ceil(Math.log(n) / Math.log(2)));
}

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
const textMetrics = ctx.measureText(text);
var textWidth = textMetrics.width;
var textHeight = config.fontsize * config.lineheight;
canvas.width = nextPowerOf2(textWidth);
canvas.height = nextPowerOf2(textHeight);
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, textWidth, textHeight);
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
ctx.fillText(text, 0, 0);

console.log('canvas', canvas.width, canvas.height);
console.log('text', textWidth, textHeight);

var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({
  map: texture
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(spriteMaterial.map.image.width, spriteMaterial.map.image.height, 1);
scene.add(sprite);


var animate = function() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
};

animate();
body { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>

Как это возможно? Каким-то образом обрезать SpriteMaterial?

1 Ответ

1 голос
/ 15 февраля 2020

Вы можете установить повтор текстуры, чтобы спрайт покрывал только часть используемого холста

texture.repeat.set(textWidth / canvas.width, textHeight / canvas.height);

Тогда вы можете установить размер спрайта только на эту часть

sprite.scale.set(textWidth, textHeight, 1);

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 400;

var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var config = {
  fontface: 'Arial',
  fontsize: 32,
  fontweight: 500,
  lineheight: 1,
};
var text = 'Hello world!';

function nextPowerOf2(n) {
  return Math.pow(2, Math.ceil(Math.log(n) / Math.log(2)));
}

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
const textMetrics = ctx.measureText(text);
var textWidth = textMetrics.width;
var textHeight = config.fontsize * config.lineheight;
canvas.width = nextPowerOf2(textWidth);
canvas.height = nextPowerOf2(textHeight);
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, textWidth, textHeight);
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
ctx.fillText(text, 0, 0);

console.log('canvas', canvas.width, canvas.height);
console.log('text', textWidth, textHeight);

var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
texture.repeat.set(textWidth / canvas.width, textHeight / canvas.height);
var spriteMaterial = new THREE.SpriteMaterial({
  map: texture
});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(textWidth, textHeight, 1);
scene.add(sprite);


var animate = function() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
};

animate();
body { margin: 0; }
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.min.js"></script>
...