Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do bilinear interpolation of normals over a quad?

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?

like image 502
Thomas Avatar asked Feb 11 '12 12:02

Thomas


People also ask

How do you calculate bilinear interpolation?

Bilinear interpolation formulaStart by performing two linear interpolations in the x-direction (horizontal): first at (x, y₁) , then at (x, y₂) . Next, perform linear interpolation in the y-direction (vertical): use the interpolated values at (x, y₁) and (x, y₂) to obtain the interpolation at the final point (x, y) .

How do you do bilinear interpolation in Excel?

Name the cell containing the x- and y-values you entered in x and y, respectively. Select the row of x-values in the data table and name those “xvalues”. Select the column of y-values in the same table and name those “yvalues”. Next, select all the air velocity numbers (cells D7-L15) and name them “zvalues”.

What is the difference between linear and bilinear interpolation?

Bilinear interpolation is performed using linear interpolation first in one direction, and then again in the other direction. Although each step is linear in the sampled values and in the position, the interpolation as a whole is not linear but rather quadratic in the sample location.

What is bilinear upsampling?

Bilinear Interpolation : is a resampling method that uses the distanceweighted average of the four nearest pixel values to estimate a new pixel value. The four cell centers from the input raster are closest to the cell center for the output processing cell will be weighted and based on distance and then averaged.


1 Answers

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.
like image 79
Bahbar Avatar answered Sep 25 '22 15:09

Bahbar