我在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;
以上实际产生的照明像perlin噪音一样四处移动!那么关于如何正确获得每顶点法线的任何建议呢?
答案 0 :(得分:8)
法线是垂直于切线的矢量(也称为斜率)。函数的斜率是它的导数;对于n维,它是n个偏导数。因此,您在中心点P和P±(δx,0)和P±(0,δy)周围采样噪声,δx,δy选择尽可能小,但大到足以保证数值稳定性。这会产生每个方向的切线。然后你取它们的叉积,使结果标准化并在P
得到正常值答案 1 :(得分:5)
你没有确切地说出你实际上是如何产生这些职位的。所以我假设您正在使用Perlin噪声在高度图中生成高度值。因此,对于hieghtmap中的任何位置X,Y,您使用2D噪声函数来生成Z值。
所以,我们假设您的位置计算如下:
vec3 CalcPosition(in vec2 loc) {
float height = MyNoiseFunc2D(loc);
return vec3(loc, height);
}
这会生成3D位置。但在 space 这个位置是什么?这是个问题。
大多数噪声函数期望loc
在某个特定浮点范围内是两个值。噪声函数的好坏将决定您可以传入值的范围。现在,如果您的模型空间2D位置不能保证在噪声函数的范围内,那么您需要将它们转换为该范围,进行计算,以及然后将它返回转换为模型空间。
这样,您现在拥有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
值是噪声纹理输入空间中的一个小偏移量。此偏移的大小取决于您的噪声函数;它需要足够大才能返回与实际当前位置返回的高度明显不同的高度。但它需要小足以让你不会从噪声分布的随机部分中拉出来。
你应该了解你的噪音功能。
现在您在模型空间中有三个位置(当前位置,x偏移和y偏移),您可以在模型空间中计算顶点法线:
vec3 modelXGrad = modelXOffset - modelPosition;
vec3 modelYGrad = modelYOffset - modelPosition;
vec3 modelNormal = normalize(cross(modelXGrad, modelYGrad));
从这里开始做常规的事情。但是从不忘记跟踪各种向量的空间。
哦,还有一件事:这应该在 vertex 着色器中完成。没有理由在几何着色器中执行此操作,因为没有计算会影响其他顶点。让GPU的并行性为您服务。