《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.

目录索引

译文

在使用球体跟踪时,通常会使用运算符来生成详细的对象,例如,如果我们想在着色器中创建十字架,我们可以使用六个立方体,将它们连接起来并模拟形状(如图11.0.3a所示)。这种技术被称为构造实体几何体(CSG),它表示我们可以由基本形状(比如立方体,圆柱体,球体等)来组成复杂的形状。

图片[1]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3a. Union of six boxes)

该技术中最常用的运算符之一对应于两个曲面之间的并集。为了计算它,我们可以简单地使用第4.1.9节中详细介绍的“min”函数。然而,鉴于其性质,可以产生类似上图的效果,就是把几个面连了起来。

图片[2]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3b. Union between two circles)

如果我们想混合两个曲面,我们可以使用Íñigo Quilez的解决方案,他提出了一个称为“多项式平滑最小值”的函数,该函数使用线性插值来近似“a”和“b”之间的最小值,其中每个曲面都指代特定的形状。

其语法如下:

float smin (float a, float b, float k)
{
    float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
    return lerp(b, a, k) - k * h * (1.0 - h);
}

为了理解它的实现,我们将从创建一个类型为“Unlit shader”的新着色器开始,我们将其称为USB_function_SMIN。我们将在两个圆之间进行平滑的并集,并将其投影到场景中的四边形上。

我们将从定义一些属性开始。这些我们稍后将在效果的开发中使用。

Shader "USB/USB_function_SMIN"
{
    Properties
    {
        _Position ("Circle Position", Range(0, 1)) = 0.5
        _Smooth ("Circle Smooth", Range(0.0, 0.1)) = 0.01
        _k ("K", Range(0.0, 0.5)) = 0.1
    }
    SubShader
    {
        Pass
        {
           ...
            float _Position;
            float _Smooth;
            float _K;
           ...
        }
    }
}

如上所述,我们将创建两个圆(a和b)。其中一个将保持静止,而第二个将左右移动,以欣赏“smin”功能操作。“_Position”属性与圆的移动直接相关。

另一方面,“_Smooth”将用于平滑集合本身的边,而“_K”将用于计算两个圆之间的插值。

我们声明了一个函数,该函数允许我们生成一个圆。

float circle (float2 p, float r)
{
    float d = length(p) - r;
    return d;
}

正如我们所看到的,前面的函数具有与我们之前看到的相同的结构。使用“smin”函数,我们可以在片段着色器阶段计算两个圆之间的最小平滑度。

float circle (float2 p, float r) { ... }

float smin (float a, float b, float k) { ... }

fixed4 frag (v2d i) : SV_Target
{
    float a = circle(i.uv, 0.5);
    float b = circle(i.uv - _Position, 0.2);

    float s = smin(a, b, _K);
    return float4(s.xxx, 1);
}

在上一个例子中,我们创建了两个称为“a”和“b”的标量变量。这些变量用于函数“smin”,该函数返回两个形状之间的平滑并集。

图片[3]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3c. Smooth union according to “k”)

通过将“smoothstep”函数合并到运算中,我们可以为整个合成生成平滑的边。

fixed4 frag (v2d i) : SV_Target
{
    float a = circle(i.uv, 0.5);
    float b = circle(i.uv - _Position, 0.2);

    float s = smin(a, b, _K);
    float render = smoothstep(s - _Smooth, s + _Smooth, 0.0);
    return float4(render.xxx, 1);
}
图片[4]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3d)

原文对照

When working with Sphere Tracing, it is common to use operators for the elaborated objects’ generation, e.g., if we want to create a cross in our shader, we could use six cubes, join them and simulate the shape (as shown in the figure 11.0.3a). This technique is called constructive solid geometry (CSG) and consists of creating complex bodies from primitive structures, i.e., cubes, cylinders, spheres, and more.

图片[1]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3a. Union of six boxes)


One of the most used operators in this technique corresponds to the union between two surfaces. To calculate it, we can simply use the “min” function detailed in section 4.1.9. However, given its nature, the result will generate sharp lines between both surfaces of implicit distance, maintaining the general shape being developed.

图片[2]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3b. Union between two circles)


If we want to mix both surfaces, we can use the solution of Íñigo Quilez, who proposes a function called “polynomial smooth minimum,” which uses linear interpolation to approximate the minimum between “a” and “b,” where each one refers to a specific shape.


Its syntax is as follows:

float smin (float a, float b, float k)
{
    float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
    return lerp(b, a, k) - k * h * (1.0 - h);
}


To understand its implementation, we will start by creating a new shader of type “Unlit Shader,” which we will call USB_function_SMIN. We will make a smoothed union between two circles, which we will project onto a Quad in our scene.


We will start by defining some properties. These we will use later in the effect’s development.

Shader "USB/USB_function_SMIN"
{
    Properties
    {
        _Position ("Circle Position", Range(0, 1)) = 0.5
        _Smooth ("Circle Smooth", Range(0.0, 0.1)) = 0.01
        _k ("K", Range(0.0, 0.5)) = 0.1
    }
    SubShader
    {
        Pass
        {
           ...
            float _Position;
            float _Smooth;
            float _K;
           ...
        }
    }
}


We will create two circles (and b), as mentioned above. One of them will remain static, while the second one will move from side to side to appreciate the “smin” function operation. The “_Position” property is directly related to the circle’s movement.


On the other hand, “_Smooth” will be used to smooth the edges of the set itself, while “_K” will be used to calculate the interpolation between the two circles.


We declare a function that allows us to generate a circle for the exercise.

float circle (float2 p, float r)
{
    float d = length(p) - r;
    return d;
}

As we can see, the previous function has the same structure that we have seen previously. Using the “smin” function, we can calculate the minimum smoothing between two circles in the fragment shader stage.

float circle (float2 p, float r) { ... }

float smin (float a, float b, float k) { ... }

fixed4 frag (v2d i) : SV_Target
{
    float a = circle(i.uv, 0.5);
    float b = circle(i.uv - _Position, 0.2);

    float s = smin(a, b, _K);
    return float4(s.xxx, 1);
}


In the previous exercise, we created two scalar variables called “a” and “b.” These we use in the function “smin,” which returns the smoothed union between the two shapes.

图片[3]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3c. Smooth union according to “k”)


By incorporating the “smoothstep” function into the operation, we can generate smoothed edges for the overall composition.

fixed4 frag (v2d i) : SV_Target
{
    float a = circle(i.uv, 0.5);
    float b = circle(i.uv - _Position, 0.2);

    float s = smin(a, b, _K);
    float render = smoothstep(s - _Smooth, s + _Smooth, 0.0);
    return float4(render.xxx, 1);
}
图片[4]-《Unity着色器圣经》11.0.3 | Smooth minimum between two surfaces.-软件开发学习笔记
(Fig. 11.0.3d)
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容