【问题标题】:Three.js canvas texture power of 2 but croppedThree.js 画布纹理幂为 2 但被裁剪
【发布时间】:2020-05-30 19:23:56
【问题描述】:

在 Three.js 中,最好将纹理保持为 2 的幂,这实际上是一些旧版 Chrome 浏览器的要求,例如 32x32、64x64 等

因此我需要创建一个二维幂的画布纹理,但我需要让 Sprite 可点击,这意味着它需要以某种方式裁剪。

考虑这个例子:

  • 黑色 bg 是 Three.js 渲染器
  • 红色 bg 是画布所需的大小(2 次方)
  • 白色 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?

【问题讨论】:

  • 仅供参考:在旧浏览器上没有也从来没有任何不同。另外,请使用snippet 并为three.js 使用版本化CDN,否则您的示例将在几周内中断并且无用。
  • 已更新为使用 cdn 和 sn-p。有不同/旧版本的 Chrome 不支持它 '251[.WebGL-0x86d0ad00]RENDER WARNING:绑定到纹理单元 0 的纹理不可渲染。它可能不是 2 的幂并且具有不兼容的纹理过滤。请参阅以下主题:github.com/mrdoob/three.js/issues/17879
  • 我在该线程中没有看到任何内容表明我写的内容不正确。 WebGL 一直对过滤有 2 的幂的要求,今天仍然有。自 9 年前发货以来,这一点一直没有改变。
  • 抱歉...我的意思是 Android 7.1 Chrome 会引发 JavaScript 错误,阻止画布渲染,而在更现代的浏览器上只显示纹理调整大小警告。了解这一直是最佳做法!

标签: canvas three.js html5-canvas sprite


【解决方案1】:

您可以设置纹理的重复,以便只有使用的画布部分覆盖精灵

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>

【讨论】:

  • 也可能需要设置纹理偏移后更改repeat texture.offset = new THREE.Vector2(0, 1 - (textHeight / canvas.height));
  • 你可以使用texture.offset.set(x,y)
猜你喜欢
  • 2013-01-13
  • 2018-11-08
  • 1970-01-01
  • 2014-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
相关资源
最近更新 更多