Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/opengl/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Opengl perlin噪波的逐顶点法线?_Opengl_Shader_Noise_Normals - Fatal编程技术网

Opengl perlin噪波的逐顶点法线?

Opengl perlin噪波的逐顶点法线?,opengl,shader,noise,normals,Opengl,Shader,Noise,Normals,我在Opengl几何体着色器中生成地形,在计算照明法线时遇到问题。我使用几何体着色器中实现的perlin噪波函数动态生成每一帧的地形。因此,我需要一种基于噪波函数(无纹理或任何东西)计算每个顶点法线的有效方法。我可以使用2边的叉积来获得面法线,但它们是随几何体动态生成的,因此我无法返回并平滑顶点法线的面法线。如何在运行中使用噪波函数来生成y平面中地形的高度(因此高度介于1和-1之间),从而获得顶点法线。我相信我必须为每个顶点对噪波函数进行4次采样,但我尝试了以下方法,但没有成功 vec3 xP1

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

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;

上面的实际生成的照明像柏林噪音一样四处移动!有没有关于如何正确获取逐顶点法线的建议?

法线是垂直于切线的向量(也称为斜率)。函数的斜率是它的导数;对于n维,它的n个偏导数。因此,对中心点P周围以及P±(δx,0)和P±(0,δy)处的噪声进行采样,选择δx,δy尽可能小,但足够大,以保证数值稳定性。这将生成每个方向上的切线。然后取它们的叉积,归一化结果,得到P处的法线。

你没有确切说明你是如何生成位置的。所以我假设你使用柏林噪声在高度贴图中生成高度值。因此,对于HightMap中的任何位置X,Y,使用2D噪波函数生成Z值

因此,假设您的位置计算如下:

vec3 CalcPosition(in vec2 loc) {
    float height = MyNoiseFunc2D(loc);
    return vec3(loc, height);
}
这将生成一个三维位置。但是这个位置在什么地方呢?这就是问题所在

大多数噪波函数期望
loc
在某些特定浮点范围内为两个值。噪波函数的好坏将决定可以传递值的范围。现在,如果模型空间二维位置不能保证在噪波函数的范围内,则需要将其变换到该范围,进行计算,然后将其变换回模型空间

这样,您现在有了一个3D位置。X和Y值的变换很简单(与噪波函数空间的变换相反),但Z值又如何呢?在这里,你必须对高度应用某种比例。噪波函数将返回范围[0,1]上的一个数字,因此您需要将此范围缩放到X和Y值将要缩放的同一模型空间。这通常是通过选择最大高度并适当缩放位置来完成的。因此,我们修改的计算位置如下所示:

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;
}
这两个矩阵变换到噪波函数的空间,然后再变换回来。根据您的用例,您的实际代码可以使用不太复杂的结构,但完全仿射变换很容易描述

好的,现在我们已经确定了,你需要记住的是:除非你知道它在什么空间里,否则没有什么是有意义的。你的正常,你的位置,在你确定它在什么空间里之前,没有什么是重要的

此函数返回模型空间中的位置。我们需要计算模型空间中的法线。为此,我们需要3个位置:顶点的当前位置,以及与当前位置略微偏移的两个位置。我们得到的位置必须在模型空间中,否则法线将不在模型空间中

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

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;
}
显然,您可以将这两个函数合并为一个

delta
值是噪波纹理输入空间中的一个小偏移量。该偏移量的大小取决于噪波函数;它需要足够大,以返回与实际当前位置返回的高度显著不同的高度。但它需要足够小,以使您不会从随机p中提取噪声分布的艺术

你应该了解你的噪声函数

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

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

vec3 modelNormal = normalize(cross(modelXGrad, modelYGrad));
从这里开始,做一些平常的事情。但永远不要忘记跟踪各种向量的空间


哦,还有一件事:这应该在顶点着色器中完成。没有理由在几何体着色器中这样做,因为任何计算都不会影响其他顶点。让GPU的并行性为您工作。

+1:这是正确的方法,尽管我不知道在着色器中这样做有多实际。这个答案是一样的g作为另一个较长的答案(基本上),对吗?不管怎样,我明天会试试看它是否有效。@Nitrex8:@Nicol Bolas的答案和我的答案一样,但形式更为详细。他当然是对的,你必须跟踪你所处的空间。但是,如果我们从函数的角度来看,它可以归结为偏导数,我认为e.梯度。你“摇摆”噪声采样坐标,以及由此产生的噪声值会相应地摆动。然后只是将输入摆动空间向量与输出摆动空间相关联的问题。我考虑过这样的方法,尝试过,但失败了。在阅读了你的答案后,我可能没有将所有内容都放在正确的空间中,这会导致错误。我正在工作使用顶点、几何体和碎片着色器进行渲染,因此我意识到我必须格外小心保存东西的空间。明天我将使用此方法尝试一种清晰的方法,看看会发生什么。我使用此方法获得了效果。然而,要使其发挥作用,我需要制作4个偏移向量,并使用偏移之间的梯度(不是梯度o