来自perlin噪声的每个顶点法线? [英] Per-Vertex Normals from perlin noise?

查看:103
本文介绍了来自perlin噪声的每个顶点法线?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Opengl几何着色器生成地形,无法计算照明的法线.我正在使用在几何着色器中实现的perlin噪声函数动态生成每帧的地形.因此,我需要一种基于噪声函数(没有纹理或任何东西)来计算每个顶点的法线的有效方法.我可以取2边的叉积来获得面法线,但是它们是随几何图形动态生成的,因此我无法再回去平滑顶点法线的面法线.如何仅使用生成y平面中地形高度(因此,高度在1到-1之间)的噪波函数即时获得顶点法线.我相信我必须为每个顶点对噪声函数进行4次采样,但是我尝试了如下操作,但是它没有用...

I'm generating terrain in Opengl geometry shader and am having trouble calculating normals for lighting. I'm generating the terrain dynamically each frame with a perlin noise function implemented in the geometry shader. Because of this, I need an efficient way to calculate normals per-vertex based on the noise function (no texture or anything). I am able to take cross product of 2 side to get face normals, but they are generated dynamically with the geometry so I cannot then go back and smooth the face normals for vertex normals. How can I get vertex normals on the fly just using the noise function that generates the height of my terrain in the y plane (therefore height being between 1 and -1). I believe I have to sample the noise function 4 times for each vertex, but I tried something like the following and it didn't work...

vec3 xP1 = vertex + vec3(1.0, 0.0, 0.0);
vec3 xN1 = vertex + vec3(-1.0, 0.0, 0.0);
vec3 zP1 = vertex + vec3(0.0, 0.0, 1.0);
vec3 zN1 = vertex + vec3(0.0, 0.0, -1.0);

float sx = snoise(xP1) - snoise(xN1);
float sz = snoise(zP1) - snoise(zN1);

vec3 n = vec3(-sx, 1.0, sz);
normalize(n);

return n;

以上实际生成的照明像Perlin噪声一样四处移动!那么关于如何正确获取每个顶点法线的任何建议?

The above actually generated lighting that moved around like perlin noise! So any advice for how I can get the per-vertex normals correctly?

推荐答案

您并未确切说明实际产生职位的方式.因此,我假设您正在使用Perlin噪波在高度图中生成高度值.因此,对于hieghtmap中的任何位置X,Y,您都可以使用2D噪声函数生成Z值.

You didn't say exactly how you were actually generating the positions. So I'm going to assume that you're using the Perlin noise to generate height values in a height map. So, for any position X, Y in the hieghtmap, you use a 2D noise function to generate the Z value.

因此,我们假设您的排名计算如下:

So, let's assume that your position is computed as follows:

vec3 CalcPosition(in vec2 loc) {
    float height = MyNoiseFunc2D(loc);
    return vec3(loc, height);
}

这将生成3D位置.但是这个位置在什么空间中?这就是问题.

This generates a 3D position. But in what space is this position in? That's the question.

大多数噪声函数期望loc在某些特定的浮点范围内为两个值.噪声函数的好坏将决定您可以在其中传递值的范围.现在,如果不能保证模型空间2D位置在噪声函数的范围内,则需要将其转换为该范围,进行计算,然后然后将其 back 转换为模型空间.

Most noise functions expect loc to be two values on some particular floating-point range. How good your noise function is will determine what range you can pass values in. Now, if your model space 2D positions are not guaranteed to be within the noise function's range, then you need to transform them to that range, do the computations, and then transform it back to model space.

这样做,您现在拥有3D位置. X和Y值的变换很简单(与噪声函数的空间相反),但是Z呢?在这里,您必须对高度应用某种比例.噪声函数将返回范围为[0,1)的数字,因此您需要将此范围缩放到X和Y值将要使用的相同模型空间.通常通过选择最大高度并适当缩放位置来完成此操作.因此,我们修改后的calc位置看起来像这样:

In so doing, you now have a 3D position. The transform for the X and Y values is simple (the reverse of the transform to the noise function's space), but what of the Z? Here, you have to apply some kind of scale to the height. The noise function will return a number on the range [0, 1), so you need to scale this range to the same model space that your X and Y values are going to. This is typically done by picking a maximum height and scaling the position appropriately. Therefore, our revised calc position looks something like this:

vec3 CalcPosition(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel)
{
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0);
    float height = MyNoiseFunc2D(loc);
    vec4 modelPos = noiseToModel * vec4(loc, height, 1.0);
    return modelPos.xyz;
}

这两个矩阵转换为噪声函数的空间,然后转换回.根据您的用例,您的实际代码可以使用不太复杂的结构,但是完整的仿射转换很容易描述.

The two matrices transform to the noise function's space, and then transform back. Your actual code could use less complicated structures, depending on your use case, but a full affine transformation is simple to describe.

好,现在我们已经确定,您需要牢记的是:除非您知道其所在的空间,否则没有任何意义.在确定其所在的空间之前,您的正常位置都不重要.

OK, now that we have established that, what you need to keep in mind is this: nothing makes sense unless you know what space it is in. Your normal, your positions, nothing matters until you establish what space it is in.

此函数返回模型空间中的位置.我们需要在模型空间中计算法线.为此,我们需要3个位置:顶点的当前位置和两个与当前位置稍有偏移的位置.我们得到的位置必须在模型空间中,否则我们的法线就不会在模型空间中.

This function returns positions in model space. We need to calculate normals in model space. To do that, we need 3 positions: the current position of the vertex, and two positions that are slightly offset from the current position. The positions we get must be in model space, or our normal will not be.

因此,我们需要具有以下功能:

Therefore, we need to have the following function:

void CalcDeltas(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel, out vec3 modelXOffset, out vec3 modelYOffset)
{
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0);
    vec2 xOffsetLoc = loc + vec2(delta, 0.0);
    vec2 yOffsetLoc = loc + vec2(0.0, delta);
    float xOffsetHeight = MyNoiseFunc2D(xOffsetLoc);
    float yOffsetHeight = MyNoiseFunc2D(yOffsetLoc);
    modelXOffset = (noiseToModel * vec4(xOffsetLoc, xOffsetHeight, 1.0)).xyz;
    modelYOffset = (noiseToModel * vec4(yOffsetLoc, yOffsetHeight, 1.0)).xyz;
}

很显然,您可以将这两个功能合并为一个.

Obviously, you can merge these two functions into one.

delta值是噪声纹理输入空间中的一个小偏移量.该偏移量的大小取决于您的噪波功能.它必须足够大才能返回与实际当前位置返回的高度明显不同的高度.但是它必须足够小,以至于您不会从噪声分布的随机部分中撤出.

The delta value is a small offset in the space of the noise texture's input. The size of this offset depends on your noise function; it needs to be big enough to return a height that is significantly different from the one returned by the actual current position. But it needs to be small enough that you aren't pulling from random parts of the noise distribution.

您应该了解噪声功能.

现在在模型空间中具有三个位置(当前位置,x偏移和y偏移),您可以计算模型空间中的顶点法线:

Now that you have the three positions (the current position, the x-offset, and the y-offset) in model space, you can compute the vertex normal in model space:

vec3 modelXGrad = modelXOffset - modelPosition;
vec3 modelYGrad = modelYOffset - modelPosition;

vec3 modelNormal = normalize(cross(modelXGrad, modelYGrad));

从这里开始执行平常的事情.但是从不忘记跟踪各种矢量的空间.

From here, do the usual things. But never forget to keep track of the spaces of your various vectors.

哦,还有另一件事:应该在 vertex 着色器中完成.没有理由在几何着色器中执行此操作,因为没有任何计算会影响其他顶点.让GPU的并行性为您服务.

Oh, and one more thing: this should be done in the vertex shader. There's no reason to do this in a geometry shader, since none of the computations affect other vertices. Let the GPU's parallelism work for you.

这篇关于来自perlin噪声的每个顶点法线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆