如何对四边形上的法线进行双线性插值?

发布于 2025-01-04 14:22:22 字数 965 浏览 3 评论 0原文

我正在开发一个类似 Minecraft 的引擎,作为一个业余爱好项目,看看体素地形的概念在现代硬件和 OpenGL >= 3 上可以推到多远。所以,我所有的几何体都由四边形组成,或者准确地说是正方形。

我构建了一个光线投射器来估计环境光遮挡,并使用“弯曲法线”技术来进行照明。所以我的法线不垂直于四边形,也没有单位长度;相反,它们大致指向遮挡最少的空间,并且当四边形接收到的光线较少时,它们会更短。这种技术的优点是它只需要一次性计算遮挡,并且在渲染时基本上是免费的。

然而,当我尝试将不同的法线分配给同一四边形的不同顶点以获得平滑的照明时,我遇到了麻烦。由于四边形被分成三角形,并且每个三角形上都发生线性插值,因此插值的结果清楚地显示了三角形作为丑陋的对角线伪像的存在:

渲染结果中可见的对角线

问题是 OpenGL 在每个三角形上使用重心插值,这是 4 个角中的 3 个角的加权和。理想情况下,我想使用双线性插值,其中所有 4 个角都用于计算结果。

我可以想到一些解决方法:

  1. 将法线填充到 2x2 RGB 纹理中,并让纹理处理器进行双线性插值。这是以片段着色器中的纹理查找为代价的。为了提高效率,我还需要将所有这些迷你纹理打包成更大的纹理。

  2. 使用顶点属性将所有 4 条法线附加到每个顶点。还将一些 [0..1] 系数附加到每个顶点,就像纹理坐标一样,并在片段着色器中进行双线性插值。这是以将 4 条法线(而不是 1 条)传递给着色器为代价的。

我认为这两种技术都可以发挥作用,但在我看来,它们是一些本应简单得多的东西的拼凑。也许我可以以某种方式变换法线,这样 OpenGL 的插值就会给出不依赖于所使用的特定三角测量的结果。

(请注意,该问题并不特定于法线;它同样适用于颜色或任何其他需要在四边形上平滑插值的值。)

还有什么想法可以解决这个问题?如果不是,以上两种技术中哪一种最好?

I'm working on a Minecraft-like engine as a hobby project to see how far the concept of voxel terrains can be pushed on modern hardware and OpenGL >= 3. So, all my geometry consists of quads, or squares to be precise.

I've built a raycaster to estimate ambient occlusion, and use the technique of "bent normals" to do the lighting. So my normals aren't perpendicular to the quad, nor do they have unit length; rather, they point roughly towards the space where least occlusion is happening, and are shorter when the quad receives less light. The advantage of this technique is that it just requires a one-time calculation of the occlusion, and is essentially free at render time.

However, I run into trouble when I try to assign different normals to different vertices of the same quad in order to get smooth lighting. Because the quad is split up into triangles, and linear interpolation happens over each triangle, the result of the interpolation clearly shows the presence of the triangles as ugly diagonal artifacts:

Diagonal lines visible in the rendered result

The problem is that OpenGL uses barycentric interpolation over each triangle, which is a weighted sum over 3 out of the 4 corners. Ideally, I'd like to use bilinear interpolation, where all 4 corners are being used in computing the result.

I can think of some workarounds:

  1. Stuff the normals into a 2x2 RGB texture, and let the texture processor do the bilinear interpolation. This happens at the cost of a texture lookup in the fragment shader. I'd also need to pack all these mini-textures into larger ones for efficiency.

  2. Use vertex attributes to attach all 4 normals to each vertex. Also attach some [0..1] coefficients to each vertex, much like texture coordinates, and do the bilinear interpolation in the fragment shader. This happens at the cost of passing 4 normals to the shader instead of just 1.

I think both these techniques can be made to work, but they strike me as kludges for something that should be much simpler. Maybe I could transform the normals somehow, so that OpenGL's interpolation would give a result that does not depend on the particular triangulation used.

(Note that the problem is not specific to normals; it is equally applicable to colours or any other value that needs to be smoothly interpolated across a quad.)

Any ideas how else to approach this problem? If not, which of the two techniques above would be best?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

千年*琉璃梦 2025-01-11 14:22:22

正如您清楚地了解的那样,GL 将执行的三角形插值不是您想要的。
所以普通数据不能直接来自顶点数据。

恐怕您所设想的解决方案是您能实现的最佳解决方案。无论您选择什么,都需要将 [0..1] 系数从顶点传递到着色器(包括 2x2 纹理。您需要它们作为纹理坐标)。

不过,您可以采取一些技巧来稍微简化该过程。

  • 使用顶点 ID 可以帮助您找到从顶点传递到片段着色器的顶点“角”(我们的 [0..1] 值)。对最低 2 位进行简单的位测试可以让您知道要传递哪个角,而无需输入实际的顶点数据。如果打包纹理数据,您仍然需要在纹理内传递标识符,因此这可能没有实际意义。
  • 如果您使用 2x2 纹理来允许插值,则会出现(是?)一些问题。如果源一开始的精度较低,则某些纹理插值器不一定能提供高精度插值。这可能需要您将纹理数据类型更改为更高精度,以避免出现条带伪影。

As you clearly understands, the triangle interpolation that GL will do is not what you want.
So the normal data can't be coming directly from the vertex data.

I'm afraid the solutions you're envisioning are about the best you can achieve. And no matter what you pick, you'll need to pass down [0..1] coefficients from the vertex to the shader (including 2x2 textures. You need them for texture coordinates).

There are some tricks you can do to somewhat simplify the process, though.

  • Using the vertex ID can help you out with finding which vertex "corner" to pass from vertex to fragment shader (our [0..1] values). A simple bit test on the lowest 2 bits can let you know which corner to pass down, without actual vertex data input. If packing texture data, you still need to pass an identifier inside the texture, so this may be moot.
  • if you use 2x2 textures to allow the interpolation, there are (were?) some gotchas. Some texture interpolators don't necessarily give a high precision interpolation if the source is in a low precision to begin with. This may require you to change the texture data type to something of higher precision to avoid banding artifacts.
星軌x 2025-01-11 14:22:22

嗯...当您使用弯曲法线技术时,增加结果的最佳方法是预先细分网格并使用具有更高细分的网格重新计算。

另一种方法是像素着色器中的一些技巧...一种可能的方法 - 您实际上可以在像素着色器中自己插值纹理(而不是使用内置插值器),这可以为您提供很多帮助。而且你不仅限于双线性插值,你可以做得更好,Fe 双三次插值;)

Well... as you're using Bent normals technique, the best way to increase result is to pre-tessellate mesh and re-compute with mesh with higher tessellation.

Another way would be some tricks within pixel shader... one possible way - you can actually interpolate texture on your own (and not use built-in interpolator) in pixel shader, which could help you a lot. And you're not limited just to bilinear interpolation, you could do better, F.e. bicubic interpolation ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文