零基础也能学会的数学曲线——Unity Shader基础之函数库(三)上

1,264 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

你可以简单浏览一下目录,有需要的阅读,写文章不易,阅读之前请给我点个赞吧~

本文为Mathematical Surfaces的译文,添加个人理解与创作,逐步带你实战 Unity shader。

这个部分主要讲述下面的内容:

其中上篇讲述

  • 为曲线着色
  • 让曲线动起来
  • 创建一个函数库。

中篇讲述

  • 使用委托(delegate)和枚举(enumeration)类型。

下篇讲述

  • 用网格(grid)显示 2D 函数。
  • 在 3D 空间中定义曲面。

这是关于学习使用Unity的基础知识的系列教程的第三个教程。这是构建舞动的曲线教程的延续,所以我们不会开始一个新的项目,在上一个项目上继续开发。这一次,我们将使显示多个更复杂的函数成为可能。

我们先接着上回说到的继续。

截屏2022-10-22 下午10.10.25.png

为了使光滑度这个配置选项出现在编辑器中,向上面我们必须在着色器的顶部,子着色器的上方添加一个Properties 块。在这里写入 _Smoothness,后面跟着("Smoothness", Range(0,1)) = 0.5。这赋予它Smoothness标签,将其作为一个范围为 0-1 的滑块,并将其默认值设置为0.5。

Shader "Graph/Point Surface" {

    Properties {
        _Smoothness ("Smoothness", Range(0,1)) = 0.5
    }


    SubShader {
            …
    }
}

让我们的立方体预制(prefab)资产使用这种材料,而不是默认的一个,这会使点变黑。

usem.gif

一、基于世界坐标的着色

为了调整点的颜色,我们必须修改 surface.Albedo。由于反照率(Albedo)和世界坐标都有三个组成部分,我们可以直接使用位置来表示反照率。其中 Albedo 在拉丁语中的意思是白色。这是一种测量光被表面漫反射的量的方法。如果反照率不是完全白色,那么部分光能被吸收而不是被反射。

void ConfigureSurface (Input input, inout SurfaceOutputStandard surface) {
        surface.Albedo = input.worldPos;
        surface.Smoothness = _Smoothness;
}

image.png

现在世界 X 坐标控制着点的红色成分,Y 坐标控制着绿色成分,Z 坐标控制着蓝色成分。但是我们的图的 X 域是[−1,1],负颜色分量没有意义。所以我们必须将位置减半,然后加上 1/2 ,使颜色符合定义域。我们可以同时对这三个维度做这个。

surface.Albedo = input.worldPos * 0.5 + 0.5;

我们记录一下之前的笑脸代码,因为之后要绘制其他曲线了

using UnityEngine;

public class Graph : MonoBehaviour
{
    [SerializeField]
    Transform pointPrefab;

    [SerializeField, Range(10, 100)]
    int resolution = 10;

    //笑脸
    void Awake()
    {
        var position = Vector3.zero;
        var position2 = Vector3.zero;
        var position3 = Vector3.zero;

        float step = 2f / resolution;
        var scale = Vector3.one * step;

        for (int i = 0; i < resolution; i++)
        {
            Transform point = Instantiate(pointPrefab);
            position.x = (i + 0.5f) * step - 1f;
            position.y = position.x * position.x;
            point.localPosition = position;
            point.localScale = scale;

            Transform point2 = Instantiate(pointPrefab);
            position2.x = (i + 0.5f) * step - 2f;
            position2.y = -1 * position2.x * position2.x - 2 * position2.x + 1;
            point2.localPosition = position2;
            point2.localScale = scale;

            Transform point3 = Instantiate(pointPrefab);
            position3.x = (i + 0.5f) * step;
            position3.y = -1 * position3.x * position3.x + 2 * position3.x + 1;
            point3.localPosition = position3;
            point3.localScale = scale;

            point.SetParent(transform);
            point2.SetParent(transform);
            point3.SetParent(transform);

        }
    }
}

为了更好地了解颜色是否正确,让我们更改 Graph.Awake。所以我们展示函数 f(x)=x3f(x)=x^3 这使得Y 也从 −1 到 1。

position.y = position.x * position.x * position.x;

image.png

结果是浅蓝色的,因为所有立方体面的 Z 坐标都接近零,这使得它们的蓝色分量接近 0.5。在设置反照率时,我们可以通过只包括红色和绿色通道来消除蓝色。这可以在着色器中通过只分配给 surface.Albedo.rg,并且只使用 input.worldPos.xy。这样蓝色分量就保持为零。

下面是常见的颜色对应的 RGB 值。

RGB成分
颜色Red(红色)Green(绿色)Blue(蓝色)
Black(黑)000
White(白)111
Red(红)100
Green(绿)010
Blue(蓝)001
Yellow(黄)110
surface.Albedo.rg = input.worldPos.xy * 0.5 + 0.5;

由于红色加绿色的结果是黄色,这将使点在左下方接近黑色,也可以根据上面的表格进行颜色分析。当Y最初增长快于X时变成绿色,当X赶上时变成黄色,当X增长快时变成微橙色,最后在右上方接近亮黄色结束。

image.png