有符号距离函数的正常估计是行不通的
Normal estimation of signed distance function just won't work
我正在尝试在 Unity 中创建一个执行光线行进的计算着色器。到目前为止,我有一个盒子的 SDF 和一个光线生成/行进功能,所有这些似乎都工作正常:
struct Ray {
float3 position;
float3 direction;
};
Ray CreateCameraRay(float2 uv) {
Ray ray;
ray.position = mul(CameraToWorld, float4(0, 0, 0, 1)).xyz;
ray.direction = mul(CameraInverseProjection, float4(uv, 0, 1)).xyz;
ray.direction = mul(CameraToWorld, float4(ray.direction, 0)).xyz;
ray.direction = normalize(ray.direction);
return ray;
}
float sdf_box (float3 p)
{
float3 c = float3(0.0f, 0.0f, 0.0f);
float3 s = float3(1.0f, 1.0f, 1.0f);
float x = max
( p.x - c.x - float3(s.x / 2., 0, 0),
c.x - p.x - float3(s.x / 2., 0, 0)
);
float y = max
( p.y - c.y - float3(s.y / 2., 0, 0),
c.y - p.y - float3(s.y / 2., 0, 0)
);
float z = max
( p.z - c.z - float3(s.z / 2., 0, 0),
c.z - p.z - float3(s.z / 2., 0, 0)
);
float d = x;
d = max(d,y);
d = max(d,z);
return d;
}
[numthreads(32,32,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
uint width, height;
Result.GetDimensions(width, height);
float2 uv = id.xy / float2(width, height) * 2.0 - 1.0;
Ray ray = CreateCameraRay(uv);
float4 colour = float4(0, 0, 0, 0);
bool cont = true;
int step = 0;
while (cont) {
step++;
float dist = sdf_box(ray.position);
ray.position += ray.direction * dist;
if (dist < 0.01f) {
colour = float4(ray.position, 0.0f);
}
cont = (dist > 0.01f) && (step < 500);
}
Result[id.xy] = colour;
}
以上代码生成如下图像:
下一步是添加一点灯光。为此,我需要一个表面法线函数,我将其定义为:
float EPSILON = 0.001f;
float3 normal(float3 p) {
float2 e = float2(EPSILON, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p + e.xyy),
sdf_box(p + e.yxy) - sdf_box(p + e.yxy),
sdf_box(p + e.yyx) - sdf_box(p + e.yyx))
);
}
现在,为了检查法线功能是否正常工作,我决定绘制盒子上每个接触点的法线(通过将 colour = float4(ray.position, 0.0f);
替换为 colour = float4(normal(ray.position), 0.0f)
。我期待的是立方体的 6 个面中的 3 个被着色为红色、绿色和蓝色(即顶面应该是绿色,因为它的法线为 (0, 1, 0))。然而,我得到的却是全黑立方体:
这似乎是错误的。 normalize()
应该意味着如果生成的法线不为零,则至少有一些颜色,这对我来说意味着所有点的法线只是 (0, 0, 0)。但这不可能是正确的,因为立方体内部或立方体上没有任何地方的表面法线为 0(中心点除外)。
我现在已经尝试了 4 种不同的正常评估函数,这些函数是我在网上找到的,NONE 其中有效。在这一点上,我严重质疑我的理智。
算了,我傻了:
float EPSILON = 0.001f;
float3 normal(float3 p) {
float2 e = float2(EPSILON, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p + e.xyy),
sdf_box(p + e.yxy) - sdf_box(p + e.yxy),
sdf_box(p + e.yyx) - sdf_box(p + e.yyx))
);
}
显然应该是:
float EPSILON = 0.001f;
float3 normal(float3 p) {
float2 e = float2(EPSILON, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p - e.xyy),
sdf_box(p + e.yxy) - sdf_box(p - e.yxy),
sdf_box(p + e.yyx) - sdf_box(p - e.yyx))
);
}
在我的辩护中,我在这个之前写了 3 个不同的正常函数,但也没有用。
编辑:
奇怪的是,在多搞了一点之后,当上面的代码是 运行 时,法线总是 (0, 0, 0),但当下面的代码是 运行?[=13= 时,法线总是 (0, 0, 0) ]
float3 normal(float3 p) {
float2 e = float2(0.001f, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p - e.xyy),
sdf_box(p + e.yxy) - sdf_box(p - e.yxy),
sdf_box(p + e.yyx) - sdf_box(p - e.yyx))
);
}
不知道为什么。
我正在尝试在 Unity 中创建一个执行光线行进的计算着色器。到目前为止,我有一个盒子的 SDF 和一个光线生成/行进功能,所有这些似乎都工作正常:
struct Ray {
float3 position;
float3 direction;
};
Ray CreateCameraRay(float2 uv) {
Ray ray;
ray.position = mul(CameraToWorld, float4(0, 0, 0, 1)).xyz;
ray.direction = mul(CameraInverseProjection, float4(uv, 0, 1)).xyz;
ray.direction = mul(CameraToWorld, float4(ray.direction, 0)).xyz;
ray.direction = normalize(ray.direction);
return ray;
}
float sdf_box (float3 p)
{
float3 c = float3(0.0f, 0.0f, 0.0f);
float3 s = float3(1.0f, 1.0f, 1.0f);
float x = max
( p.x - c.x - float3(s.x / 2., 0, 0),
c.x - p.x - float3(s.x / 2., 0, 0)
);
float y = max
( p.y - c.y - float3(s.y / 2., 0, 0),
c.y - p.y - float3(s.y / 2., 0, 0)
);
float z = max
( p.z - c.z - float3(s.z / 2., 0, 0),
c.z - p.z - float3(s.z / 2., 0, 0)
);
float d = x;
d = max(d,y);
d = max(d,z);
return d;
}
[numthreads(32,32,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
uint width, height;
Result.GetDimensions(width, height);
float2 uv = id.xy / float2(width, height) * 2.0 - 1.0;
Ray ray = CreateCameraRay(uv);
float4 colour = float4(0, 0, 0, 0);
bool cont = true;
int step = 0;
while (cont) {
step++;
float dist = sdf_box(ray.position);
ray.position += ray.direction * dist;
if (dist < 0.01f) {
colour = float4(ray.position, 0.0f);
}
cont = (dist > 0.01f) && (step < 500);
}
Result[id.xy] = colour;
}
以上代码生成如下图像:
下一步是添加一点灯光。为此,我需要一个表面法线函数,我将其定义为:
float EPSILON = 0.001f;
float3 normal(float3 p) {
float2 e = float2(EPSILON, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p + e.xyy),
sdf_box(p + e.yxy) - sdf_box(p + e.yxy),
sdf_box(p + e.yyx) - sdf_box(p + e.yyx))
);
}
现在,为了检查法线功能是否正常工作,我决定绘制盒子上每个接触点的法线(通过将 colour = float4(ray.position, 0.0f);
替换为 colour = float4(normal(ray.position), 0.0f)
。我期待的是立方体的 6 个面中的 3 个被着色为红色、绿色和蓝色(即顶面应该是绿色,因为它的法线为 (0, 1, 0))。然而,我得到的却是全黑立方体:
这似乎是错误的。 normalize()
应该意味着如果生成的法线不为零,则至少有一些颜色,这对我来说意味着所有点的法线只是 (0, 0, 0)。但这不可能是正确的,因为立方体内部或立方体上没有任何地方的表面法线为 0(中心点除外)。
我现在已经尝试了 4 种不同的正常评估函数,这些函数是我在网上找到的,NONE 其中有效。在这一点上,我严重质疑我的理智。
算了,我傻了:
float EPSILON = 0.001f;
float3 normal(float3 p) {
float2 e = float2(EPSILON, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p + e.xyy),
sdf_box(p + e.yxy) - sdf_box(p + e.yxy),
sdf_box(p + e.yyx) - sdf_box(p + e.yyx))
);
}
显然应该是:
float EPSILON = 0.001f;
float3 normal(float3 p) {
float2 e = float2(EPSILON, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p - e.xyy),
sdf_box(p + e.yxy) - sdf_box(p - e.yxy),
sdf_box(p + e.yyx) - sdf_box(p - e.yyx))
);
}
在我的辩护中,我在这个之前写了 3 个不同的正常函数,但也没有用。
编辑: 奇怪的是,在多搞了一点之后,当上面的代码是 运行 时,法线总是 (0, 0, 0),但当下面的代码是 运行?[=13= 时,法线总是 (0, 0, 0) ]
float3 normal(float3 p) {
float2 e = float2(0.001f, 0);
return normalize(float3(
sdf_box(p + e.xyy) - sdf_box(p - e.xyy),
sdf_box(p + e.yxy) - sdf_box(p - e.yxy),
sdf_box(p + e.yyx) - sdf_box(p - e.yyx))
);
}
不知道为什么。