我正在使用随机分形为视频游戏在程序上生成一些污垢。我已经使用中点位移算法生成了高度图并将其保存到纹理中。我有一些关于如何将其变成法线纹理的想法,但是一些反馈将非常受欢迎。
我的高度纹理目前是257 x 257灰度图像(高度值是为了可见度而缩放):
我的想法是图像的每个像素代表256 x 256网格中的网格坐标(因此,为什么有257 x 257高度)。这意味着坐标(i,j)处的法线由(i,j),(i,j + 1),(i + 1,j)和(i + 1,j + 1)处的高度确定。 )(分别称为A,B,C和D)。
因此,考虑到A,B,C和D的3D坐标,它是否有意义:
...还是有一种我更容易丢弃的方法?
答案 0 :(得分:41)
来自我的水面渲染着色器的示例GLSL代码:
#version 130
uniform sampler2D unit_wave
noperspective in vec2 tex_coord;
const vec2 size = vec2(2.0,0.0);
const ivec3 off = ivec3(-1,0,1);
vec4 wave = texture(unit_wave, tex_coord);
float s11 = wave.x;
float s01 = textureOffset(unit_wave, tex_coord, off.xy).x;
float s21 = textureOffset(unit_wave, tex_coord, off.zy).x;
float s10 = textureOffset(unit_wave, tex_coord, off.yx).x;
float s12 = textureOffset(unit_wave, tex_coord, off.yz).x;
vec3 va = normalize(vec3(size.xy,s21-s01));
vec3 vb = normalize(vec3(size.yx,s12-s10));
vec4 bump = vec4( cross(va,vb), s11 );
结果是凹凸矢量:xyz = normal,a = height
答案 1 :(得分:13)
我的想法是图像的每个像素代表256 x 256网格中的网格坐标(因此,为什么有257 x 257高度)。这意味着坐标(i,j)处的法线由(i,j),(i,j + 1),(i + 1,j)和(i + 1,j + 1)处的高度确定。 )(分别称为A,B,C和D)。
没有。图像的每个像素表示网格的顶点,因此直观地,从对称性来看,其法线由相邻像素(i-1,j),(i + 1,j),(i,j-1)的高度确定。 ,(i,j + 1)。
给定函数f:ℝ 2 →ℝ描述ℝ 3 中的表面,(x,y)处的单位法则由
给出v =( - ∂f/∂x,-∂f/∂y,1)和n = v / | v |。
可以证明,两个样本对∂f/∂x的最佳近似值归档为:
∂f/∂x(x,y)=(f(x +ε,y) - f(x-ε,y))/(2ε)
要获得更好的近似值,您需要使用至少四个点,因此添加第三个点(即(x,y))不会改善结果。
您的hightmap是常规网格上某些函数f的示例。取ε= 1,你得到:
2v =(f(x-1,y) - f(x + 1,y),f(x,y-1) - f(x,y + 1),2)
答案 2 :(得分:10)
一种常见的方法是使用Sobel滤镜在每个方向上进行加权/平滑导数。
首先对每个纹素周围的3x3高度区域进行采样(此处[4]
是我们想要法线的像素)。
[6][7][8]
[3][4][5]
[0][1][2]
然后,
//float s[9] contains above samples
vec3 n;
n.x = scale * -(s[2]-s[0]+2*(s[5]-s[3])+s[8]-s[6]);
n.y = scale * -(s[6]-s[0]+2*(s[7]-s[1])+s[8]-s[2]);
n.z = 1.0;
n = normalize(n);
可以调整scale
以匹配相对于其大小的高度图真实世界深度。
答案 3 :(得分:9)
如果您将每个像素视为顶点而不是面,则可以生成简单的三角形网格。
+--+--+
|\ |\ |
| \| \|
+--+--+
|\ |\ |
| \| \|
+--+--+
每个顶点的x和y坐标对应于地图中像素的x和y。 z坐标基于该位置的地图中的值。三角形可以通过它们在网格中的位置显式或隐式生成。
您需要的是每个顶点的正常 。
顶点法线可以通过对在该点遇到的每个三角形的 surface 法线的面积加权平均值进行计算。
如果你有一个顶点为v0
,v1
,v2
的三角形,那么你可以使用一个矢量叉积(两个矢量位于三角形的两边) )计算法线方向的矢量,并按比例缩放三角形区域。
Vector3 contribution = Cross(v1 - v0, v2 - v1);
不在边缘的每个顶点将由六个三角形共享。你可以循环遍历这些三角形,总结contribution
s,然后规范化矢量和。
注意:您必须以一致的方式计算交叉积,以确保法线指向同一方向。始终以相同的顺序(顺时针或逆时针)选择两侧。如果你把它们中的一些混合在一起,那些贡献将指向相反的方向。
对于边缘上的顶点,最终会出现一个较短的循环和许多特殊情况。围绕伪顶点网格创建边框可能更容易,然后计算内部顶点的法线并丢弃假边框。
for each interior vertex V {
Vector3 sum(0.0, 0.0, 0.0);
for each of the six triangles T that share V {
const Vector3 side1 = T.v1 - T.v0;
const Vector3 side2 = T.v2 - T.v1;
const Vector3 contribution = Cross(side1, side2);
sum += contribution;
}
sum.Normalize();
V.normal = sum;
}
如果需要三角形上某个特定点(不是顶点之一)的法线,可以通过点的重心坐标称量三个顶点的法线进行插值。这就是图形光栅化器如何处理着色的法线。它允许三角形网格看起来像光滑的曲面,而不是一堆相邻的扁平三角形。
提示:对于第一次测试,请使用完全平坦的网格,并确保所有计算的法线都指向正确。