我使用单纯形噪声创建了随机生成的地形。我的代码如下所示:
float [ ] vertices = new float [ SIZE * SIZE * 3 ];
short [ ] indices = new short [ ( SIZE - 1 ) * ( SIZE - 1 ) * 2 * 3 ];
for ( int x = 0 , index = 0 ; x < SIZE ; x ++ )
{
for ( int z = 0 ; z < SIZE ; z ++ )
{
vertices [ index ++ ] = x;
vertices [ index ++ ] = ( float ) SimplexNoise.noise ( x , z );
vertices [ index ++ ] = z;
}
}
for ( int i = 0 , index = 0 ; i < ( SIZE - 1 ) ; i ++ )
{
int offset = i * SIZE;
for ( int j = 0 ; j < ( SIZE - 1 ) ; j ++ )
{
indices [ index ++ ] = ( short ) ( j + offset );
indices [ index ++ ] = ( short ) ( j + offset + 1 );
indices [ index ++ ] = ( short ) ( j + offset + 1 + SIZE );
indices [ index ++ ] = ( short ) ( j + offset + 1 + SIZE );
indices [ index ++ ] = ( short ) ( j + offset + SIZE );
indices [ index ++ ] = ( short ) ( j + offset );
}
}
这给了我索引和顶点,但除非我使用GL_LINES
,否则它看起来就像一群颜色。在我真正知道我在看什么之前,我无法继续在地形上取得进展,但我知道如何使用法线的唯一方法是将它们加载到模型中并将它们发送到着色器。我不知道如何实际生成它们。我四处搜索并阅读了很多有关获取周围面的法线并标准化它们和其他一些内容的内容,但我不理解其中的大部分内容,并且据我所知,您一次只能访问顶点着色器中的一个顶点,所以我不确定您将如何处理整个三角形。如您所见,我们将不胜感激。
编辑:
我做了一些研究,现在已经计算了所有三角形的法线:
float [ ] triNormals = new float [ indices.length ];
for ( int i = 0 , index = 0 ; i < indices.length ; )
{
float x , y , z;
x = vertices [ ( 3 * indices [ i ] ) ];
y = vertices [ ( 3 * indices [ i ] ) + 1 ];
z = vertices [ ( 3 * indices [ i ++ ] ) + 2 ];
Vector3f p1 = new Vector3f ( x , y , z );
x = vertices [ ( 3 * indices [ i ] ) ];
y = vertices [ ( 3 * indices [ i ] ) + 1 ];
z = vertices [ ( 3 * indices [ i ++ ] ) + 2 ];
Vector3f p2 = new Vector3f ( x , y , z );
x = vertices [ ( 3 * indices [ i ] ) ];
y = vertices [ ( 3 * indices [ i ] ) + 1 ];
z = vertices [ ( 3 * indices [ i ++ ] ) + 2 ];
Vector3f p3 = new Vector3f ( x , y , z );
Vector3f u = Vector3f.subtract ( p2 , p1 );
Vector3f v = Vector3f.subtract ( p3 , p1 );
Vector3f normal = Vector3f.crossProduct ( u , v );
triNormals [ index ++ ] = normal.x;
triNormals [ index ++ ] = normal.y;
triNormals [ index ++ ] = normal.z;
}
现在我只需要知道如何使用周围三角形的法线来计算顶点的法线。
最佳答案
通过标准化两条边的叉积来生成看起来平坦且具有锐利边缘的每个三角形或面法线。正如 @neuo 所说,这可以在几何着色器中完成,但是听起来您追求的是“平滑”法线。
平滑法线不一定是从顶点几何中隐含的,但适当的猜测是基于相邻三角形(的平均值或加权平均值)。除非您有邻接信息(如曲面分割着色器(?)),否则在渲染时这在 GLSL 中是不可能的。它需要在单独的过程中完成(可能使用变换反馈),并且可能很容易在 CPU 上处理。
在 GL 中,每个顶点都有一个法线*。因此,从一个等于您的顶点的新的归零数组开始(或者如果您交错,则在顶点之间留有空间)。对于由索引数组 (a, b, c) 形成的每个三角形,使用叉积 (b - a) × (c - a) 找到三角形法线并将其标准化。然后将三角形法线添加到每个三角形顶点(即 a、b 和 c)的每个顶点法线。最后跑通每一个常态并归一化。完成。
根据网格的不同,这个简单的方法可以很好地工作。在三角形非常小或很薄的情况下,相同的权重可能会导致奇怪的结果:
解决此问题的一种方法是按每个三角形的面积缩放其法线贡献。为此,只需在将叉积结果添加到每个法线之前不要对其进行标准化即可。
for (int t = 0; t < numIndices / 3; ++t)
{
unsigned int t1 = dataIndices[t*3+0];
unsigned int t2 = dataIndices[t*3+1];
unsigned int t3 = dataIndices[t*3+2];
const vec3f& a = verts[t1];
const vec3f& b = verts[t2];
const vec3f& c = verts[t3];
vec3f u = b - a;
vec3f v = c - a;
vec3f n = u.cross(v);
//if (n.size() > 0.0) n.normalize(); //removes weighting based on triangle area
norms[t1] += n;
norms[t2] += n;
norms[t3] += n;
}
for (int v = 0; v < numVertices; ++v)
norms[v].normalize();
* 要获得面法线,您必须复制顶点,这就是为什么为每个顶点位置使用两个红色法线绘制图像的原因。您还可以在 in/out
变量上使用关键字 flat
,但这样一切都会平坦。
关于java - 如何在 GLSL 中生成法线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33447291/