0

我正在尝试在 Unity 中创建一个执行 raymarching 的计算着色器。到目前为止,我有一个盒子的 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 种不同的正常评估函数,我在网上找到了这些函数,但没有一个有效。在这一点上,我严重质疑我的理智。

4

1 回答 1

0

没关系,我很傻:

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),但运行下面的代码时不是?

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))
    );
}

不知道为什么。

于 2021-09-23T18:47:19.020 回答