【问题标题】:Three.js: Handling per-instance texture offsets with instanced geometryThree.js:使用实例化几何处理每个实例的纹理偏移
【发布时间】:2019-01-05 02:12:49
【问题描述】:

我的问题很简单:是否有任何示例说明如何使用 Three.js 中的索引实例四边形渲染具有自定义(非网格状)偏移的纹理?

我的上下文问题:我正在处理一个带有许多四边形的 Three.js 场景,每个四边形都用于显示来自图像图集的图像。地图集中的每张图像每边都是 32 像素。四边形是使用索引的、实例化的几何体渲染的;所以有 4 个顶点定义了所有四边形实例的“原型”,并且所有实例特定的信息都作为属性提供给 RawShaderMaterial

挑战在于,要在给定四边形上表示的图像包含在图集中的 32 像素正方形中,但这些小图像可以有边距。因此,虽然图像在地图集中形成了各种网格,但每个图像的顶部和侧面(样本地图集区域)都有一个已知的未使用像素边距:

处理这些边距的相关属性是:

var geometry = new THREE.InstancedBufferGeometry();

// uv identifies the blueprint uv values
geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array([ 1, 1, 0, 1, 0, 0, 1, 0, ]), 2));

// texOffset = the offset of an instance's 32px cell in the atlas {x,y}
geometry.addAttribute('texOffset', new IBA(... 2, true, 1));

// uvOffset = the left/right and top/bottom margins of an instance's image in its 32px cell
geometry.addAttribute('uvOffset', new IBA(..., 2, true, 1));

然后在片段着色器中,我可以为实例渲染完整的 32px 单元格:

vec2 scaledUv = vUv + vTextureOffset;
gl_FragColor = texture2D(texIdx, scaledUv);

我的问题是:假设我可以创建一个 vec2 uvOffset,其中包含图像在其 32px 单元格容器中的左侧和顶部偏移量,我如何仅渲染包含图像的 32px 正方形的子集?其他人可以提供有关此问题的任何帮助将不胜感激。

【问题讨论】:

  • 你的意思是“我怎样才能只渲染包含图像的 32px 正方形的子集”?您是否希望这些黑色边距中的任何内容都只是透明而不是黑色?
  • 现在我的四边形在顶部/底部或左侧/右侧有黑色边距,因为它们是从对应于给定实例的完整 32 像素 x 32 像素单元格中采样的。但我希望四边形仅显示 32px 单元格的非边距部分。我不想丢弃黑色像素(这会导致 GPU 拾取问题);我只想用给定的 32px x 32px 单元格的子集绘制整个四边形。这有意义吗?

标签: three.js


【解决方案1】:

UV 的范围是 [0.0, 1.0],因此假设您希望左侧/右侧的边距为 0.1。这为您留下了[0.1, 0.9] 的可见范围。要使边距不可见,您可以使用step() function 创建一条不透明度曲线,如下所示:

低于 0.1 和高于 0.9 的不透明度将为 0。

// You first declare the range of the margin (this has x and y)
vec2 uvMargin = vec2(0.1, 0.2);

// Anything outside the margin will have 0 opacity, 
// anything inside the margin will have 1 opacity

// First, you apply a step() function to the left X value
float opacity = step(uvMargin.x, vUV.x);

// then to the right X value (1.0 - margin)
opacity *= step(vUV.x, 1.0 - uvMargin.x);

// then the top Y value
opacity *= step(uvMargin.y, vUV.y)

// then the bottom Y value
opacity *= step(vUV.y, 1.0 - uvMargin.y);

// Sample the texture
vec2 scaledUv = vUv + vTextureOffset;
gl_FragColor = texture2D(texIdx, scaledUv);

// Apply opacity to alpha
gl_FragColor.a = opacity;

这可以优化,但为了清楚起见,我逐步进行了。当您将transparent: true 设置为您的材质时,您会看到边距之外的任何内容都是不可见的。

  • 如果您发现 GPU 选择有问题,也许您可​​以在不透明度中添加一个无法区分的量,例如 gl_FragColor.a = opacity + 0.01;
  • 如果您发现不喜欢的锯齿或锯齿状边缘,您可以尝试使用smoothstep()
  • 您必须将 uvMargin 声明为 InstancedBufferAttribute,而不是像我一样在着色器中进行。

【讨论】:

  • 感谢@Marquizzo。我只担心 GPU 选择行为。如果我在不透明度中添加“无法区分的数量”,这不会影响 GPU 选择吗?如果是这样的话,你知道为什么我在使用带纹理的 GL_POINTS(完全丢弃了边距)时会遇到 GPU 选择问题吗?
  • @duhaime 我以前从未在透明对象上进行过 GPU 拾取,所以我不确定你会遇到什么样的问题,抱歉。但如果透明度是问题,我建议不要完全透明,所以不是alpha = 0,而是alpha = 0.01可能可以解决它?我不确定。
猜你喜欢
  • 2018-07-14
  • 1970-01-01
  • 2020-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-26
  • 1970-01-01
  • 2011-02-02
相关资源
最近更新 更多