Unity Shader Learning Note 1

本文从 0 开始学习 Unity Shader

先新建一个 Unity Shader （Surface Shader）开始：

Copy
Shader "Custom/NewSurfaceShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows

// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0

sampler2D _MainTex;

struct Input
{
float2 uv_MainTex;
};

half _Glossiness;
half _Metallic;
fixed4 _Color;

// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)

void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

这段 Unity Shader 代码定义了一个自定义的 Surface Shader，它用于渲染具有基本物理属性（如颜色、光泽度、金属感）的表面。让我们详细解释一下各个部分：

Shader 声明

Shader "Custom/NewSurfaceShader"
这行声明了一个新的 Shader，并给它命名为 "Custom/NewSurfaceShader"。这个名字可以在 Unity 的材质编辑器中找到并应用于游戏对象。

Properties

这块代码定义 Shader 从外部接收的参数，以便在 Inspector 面板修改。

为了看到具体结果，首先要先创建一个新的材质，然后在材质的 Inspector 面板的 Shader 中选择刚才创建的 Shader，随后可以看到 Shader 代码中 Properties 中定义的属性：

再来分析一下 Properties 的语法：

Copy
_PropertyName ("Display Name", PropertyType) = DefaultValue

对应的可以解释 Shader 代码中的声明：

Copy
Properties {
_Color ("Color", Color) = (1,1,1,1) // 定义了一个默认为白色的颜色属性。
_MainTex ("Albedo (RGB)", 2D) = "white" {} // 定义了一个2D纹理属性，默认使用白色纹理。
_Glossiness ("Smoothness", Range(0,1)) = 0.5 // 定义了一个在0到1之间的浮点数属性，表示表面的光滑度。
_Metallic ("Metallic", Range(0,1)) = 0.0 // 定义了一个在0到1之间的浮点数属性，表示材料的金属感。
}

SubShader

SubShader块定义了实际的渲染逻辑。如果一个 Shader 包含多个 SubShader，Unity 会根据硬件和性能从中选择最合适的一个执行。

Tags { "RenderType"="Opaque" }：指定了这个 Shader 是用于不透明物体的渲染。

LOD 200：设置了 Shader 的细节等级（Level Of Detail），影响渲染性能和质量。

CGPROGREM

CGPROGRAM和ENDCG之间是实际的 Shader 代码，使用Cg/HLSL语言编写。

#pragma surface surf Standard fullforwardshadows

声明该 Shader 的光影

在 Unity 的 Shader 代码中，#pragma 指令用于向编译器提供特定的编译指令。

#pragma surfaceasdasd
这部分是 Surface Shader 特有的，它告诉 Unity 这是一个 Surface Shader，并开始定义这个 Shader 的属性和行为。

surf是该 Shader 代码后面自定义的一个函数，Unity 在渲染每个像素时，会调用这个函数来确定各种属性，例如光泽 颜色等。

standard 表明使用 Unity 中的 PBR（physically based rendering）模型，这是一种常用的渲染模型。

fullforwardshadows是一个选项，它启用了在前向渲染路径中的全部阴影投射。这意味着无论是定向光、点光源还是聚光灯，所有类型的光源都会在 Shader 中正确地计算阴影。这对于需要在游戏或场景中实现复杂光照和阴影效果的应用特别重要。不使用这个选项可能会在某些光源下得到不准确的阴影渲染，或者是阴影质量下降。

#pragma target 3.0：指定了 Shader 模型 3.0，以获取更好的光照效果。

sampler2D _MainTex; 定义了一个 2D 纹理采样器，在 Shader 中，纹理采样用于从纹理中读取像素数据，通常存储基础的漫反射贴图。

在 CG 中，贴图一般是 3 通道或者 4 通道（取决于是否有 A，每个通道 8bit）的内存，Sampler 帮助我们自动计算每个像素和坐标的对应关系以及偏移。

input 结构体定义了输入的数据结构，具体而言，input 定义了输入的数据结构 uv_MainTex (纹理坐标) 是 float2 类型。

half _Glossiness 定义了一个half变量（占用内存较少的浮点型），这个变量用于控制材质的光滑度。类似地，定义_Metallic变量控制材质的金属度。_Color是fixed4类型（低精度四元数），用来存储材质的基本颜色。

下面两行关于 GPU 实例化支持的指令，pass。

surf函数是 shader 的核心，这个函数计算材质表面的属性，即前面定义的一系列变量，还负责对材质进行着色。

surf 函数

在 Unity 的 Surface Shader 中，surf 函数是一个核心组件，用于定义材质表面的视觉属性。这个函数处理输入，并将结果输出到一个特定的数据结构中，这个结构描述了物体表面如何与光线互动。

执行过程
输入：surf 函数接收一个 Input 结构体作为参数，这个结构体包含了所有必要的输入数据，例如 UV 坐标、法线等，具体取决于 Shader 的定义。在你的代码示例中，Input 结构体包含了 uv_MainTex，即主纹理的 UV 坐标。

多次调用：在渲染过程中，Unity 对每个像素或每个顶点调用 surf 函数。对于每个像素，它使用相应的输入数据（如纹理坐标和其他可用的顶点数据）计算表面的输出。

输出：输出是通过修改 surf 函数的第二个参数实现的，这是一个可写的 SurfaceOutputStandard 结构体。这个结构体包含了决定像素最终外观的各种属性，如反照率（Albedo）、金属感（Metallic）、光滑度（Smoothness）和透明度（Alpha）。这些属性基于物理属性来模拟真实世界材质的表现。

影响材质效果：surf 函数输出的数据被 Unity 的渲染引擎使用，结合光照模型和场景的光照条件，来计算最终的像素颜色。这样，每个像素的颜色都是根据其具体的表面特性和场景中的光照条件动态计算的。

以上就是一个新建 shader 的所有代码内容。