目录
原文链接:
Shader Tutorials by Ronja
表面着色器基础。
1 Summary
概要。
除了几乎从头开始编写着色器之外,unity 还允许我们定义一些参数并让 unity 生成执行复杂光计算的代码。 这些着色器称为“表面着色器”。
要了解表面着色器,最好先了解基本的无光照着色器,我在这里有关于它们的教程。
2 Conversion to simple Surface Shader
转换为简单的表面着色器。
使用表面着色器时,我们不必做其他一些必须做的事情,因为 unity 会为我们生成它们。 为了转换为表面着色器,我们完全删除我们的顶点着色器。 删除顶点和片段函数的编译指示定义(如:#pragma vertex vert)。删除顶点着色器到片元着色的输入结构体,删除用于纹理缩放的 MainTex_ST 变量,删除包含 UnityCG 包含文件的内容。 同时删除Pass的开始结束括号,因为Unity 会为我们自动生成Pass。删除完成之后我们的着色器应该是这样的:
Shader "Tutorial/005_surface" {
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {
}
}
SubShader {
Tags{
"RenderType"="Opaque" "Queue"="Geometry"}
CGPROGRAM
sampler2D _MainTex;
fixed4 _Color;
fixed4 frag (v2f i) : SV_TARGET {
fixed4 col = tex2D(_MainTex, i.uv);
col *= _Color;
return col;
}
ENDCG
}
FallBack "Standard"
}
我们的着色器被我们破坏了,但是我们可以添加一些东西让它作为表面着色器重新工作。
首先,我们添加一个新结构并将其命名为 Input,这将包含我们设置表面颜色所需的所有信息。 对于这个简单的着色器,只需要 UV 坐标。UV坐标的数据类型之前的着色器一样是一个二维浮点数(float2)。需要注意UV坐标的命名很重要,我们将其命名为 uv_MainTex,这样这个UV坐标(uv_MainTex)就已经拥有 MainTex 纹理的平铺和偏移。 如果纹理有不同的名称,我们必须使用 uvTextureName 来获取适合该纹理的坐标。
struct Input {
float2 uv_MainTex;
};
接下来,我们将片元函数更改为表面函数。为了使更改显而易见,我们将其重命名为 surf。同时将返回类型(函数名前面的数据类型)替换为void,这样函数就什么都不返回了。
接下来我们将surf函数的输入参数扩展为采用 2 个。第一个参数为我们刚刚定义的输入结构,这样我们可以访问基于每个顶点定义的信息。第二个参数是一个名为 SurfaceOutputStandard 的结构体。顾名思义,我们将使用它来将信息返回到着色器的生成部分。为了让“返回”起作用,我们必须在它前面写上 inout 关键字。第二个结构体是 unity 将用于其照明计算的所有数据。光照计算是基于物理的(我将在本文后面解释参数)。
接下来,我们将从方法中删除 sv_target 属性,因为和其余的一样,它是由unity在其他地方完成的。
为了使表面方法起作用,我们必须进行的最后一次更改是删除 return 语句(这就是我们将返回类型更改为 void 的原因)。相反,我们将输出结构的albedo(反照率)部分设置为我们的颜色值。
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
}
使着色器再次工作并使其正确处理光线的最后一步是添加 pragma 语句,声明着色器的类型和使用的方法。 (类似于我们在基本着色器中声明顶点和片段方法的方式)。
语句以#pragma 开头,然后是我们声明的着色器类型(surface),然后是表面方法的名称(surf),最后是我们希望它使用的光照模型(Standard)。
有了所有这些,我们的着色器应该可以再次工作并显示正确的照明。
Shader "Tutorial/005_surface" {
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {
}
}
SubShader {
Tags{
"RenderType"="Opaque" "Queue"="Geometry"}
CGPROGRAM
#pragma surface surf Standard
sampler2D _MainTex;
fixed4 _Color;
struct Input {
float2 uv_MainTex;
};
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
}
ENDCG
}
}
3 Standard Lighting Properties
为了扩展着色器,我们现在可以更多地使用材质属性。 输出结构(SurfaceOutputStandard)中的不同值分别如下:
- Albedo - (漫反射率)Albedo 是材质的基色。 它将被照着它的灯光的颜色着色,并且在阴影中变暗(正如我们所期望的)。 反照率颜色不会影响镜面光,因此您可以制作仍然有明显光泽的黑色材料。 它存储为 3 维颜色向量(fixed3)。
- Normal-(法线)这是材质(Material)的法线。 法线在“切线空间”中,这意味着返回它们后,它们将变为相对于世界的法线。 将法线置于切线空间意味着如果我们将 (0,1,0) 写入该变量,则法线实际上不会指向上方,而是远离表面(这是将法线编码为法线贴图的方式,因此我们可以直接从法线贴图Normal Map复制法线信息到这个变量)。 法线存储为 3 维方向向量(vector3)。
- Emission- (自发光)有了这个,你可以让你的材料发光。 如果你只使用它,你的着色器看起来像我们之前制作的无光着色器一样,但更性能消耗更高。自发光的颜色不受光的影响,因此你可以制作始终明亮的斑点。 如果您使用 HDR 颜色进行渲染(您可以在相机设置中设置),您可以将值大于 1 的值写入发射通道,在使用bloom屏幕后处理效果时,物体看起来会非常亮,同时向外扩散更多。自发光颜色也存储为 3维颜色向量(vector3)。
- Metallic-(金属度)金属材料与非金属材料看起来是非常不同的。 要使材质看起来更像金属,您可以调高此值。 它将使对象以不同的方式反射,并且Albedo(反照率)值将为反射着色,而不是非金属时的漫反射。 Metallic(金属度值)存储为标量(一维)值,其中 0 表示非金属材料,1 表示完全金属材料。
- Smoothness-(平滑度)使用此值,我们可以指定材质的平滑程度。 平滑度为 0 的材质看起来很粗糙,光线会反射到各个方向,我们看不到镜面高光或环境反射。 具有 1 平滑度的材料看起来非常抛光。 当你正确设置你的环境时,你可以看到它反映在你的材料上。 它也非常抛光,你也看不到镜面高光,因为镜面高光变得无限小。 当您将平滑度设置为略低于 1 的值时,您开始看到周围灯光的镜面高光。 当您降低平滑度时,高光会变大并变得不那么强烈。 平滑度也存储为标量值。
- Occlusion-(遮罩)遮罩将从您的材质中去除光线。 有了它,您可以假装光线不会进入模型的裂缝,但您可能几乎不会使用它,除非您追求超写实风格。 遮挡也存储为标量值,但违反直觉的是 1 表示像素具有完全亮度,而 0 表示它处于黑暗中。
- Alpha-(透明度)Alpha 是材质的透明度。 我们当前的材质是“不透明的”,这意味着不能有任何透明像素,并且 alpha 值不会做任何事情。 在制作透明着色器时,alpha 将定义我们可以在该像素处看到多少材质,1 是完全可见的,而 0 是完全透明的。 Alpha 也存储为标量值。
4 Implement a few Lighting Properties
实现一些灯光属性。
我们现在可以将其中一些功能添加到我们的着色器中。 我现在将使用发射、金属和平滑度值,但您显然也可以实现其他值。
首先,我们添加 2 个标量值,平滑度和金属度。 我们首先将值作为half类型(即表面输出结构中使用的数据类型)添加到我们的全局范围中(在函数或结构之外)。
half _Smoothness;
half _Metallic;
然后我们还将这些值添加到我们的Properties中,以便能够在Inspector(检视面板)中更改它们。 Properties没有half 类型,所以我们告诉它们变量是 float 类型。 这已经可以让变量显示在Inspector中,但我们还没有使用它们。
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {
}
_Smoothness ("Smoothness", float) = 0
_Metallic ("Metalness", float) = 0
}
类似于我们如何将颜色变量分配给材质的反照率,我们现在可以将平滑度分配给输出结构的平滑度,将金属度分配给输出结构的金属度。
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
}
这很好用,但是很容易将大于 1 或小于 0 的值分配给这些值并得到非常错误的结果,并且很难看出一个值有多高。 为了解决这个问题,我们可以将值分配为Range属性而不是浮点属性。Range属性允许我们定义最小值和最大值,并且 unity 将在它们之间显示一个滑块。
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {
}
_Smoothness ("Smoothness", Range(0, 1)) = 0
_Metallic ("Metalness", Range(0, 1)) = 0
}
接下来我们添加自发光颜色。 首先添加 hlsl 代码中的变量,然后添加Properties。 Properties类型我们像 tint 一样使用 color 类型。 变量我们使用 half3类型 ,因为它是没有 alpha 的 RGB 颜色,并且它的值可以大于 1(输出结构也使用 half3)。 然后我们也像其他属性一样将变量指指定给表面输出中的值。
// ...
_Emission ("Emission", Color) = (0,0,0,1)
// ...
half3 _Emission;
// ...
o.Emission = _Emission;
除了到处发光的物体看起来有点奇怪之外,我们还只能为我们的材质指定正常颜色,而不是值超过 1 的 HDR 颜色。为了解决这个问题,我们在发射属性前面添加了 hdr 标签。 通过这些更改,我们现在可以将亮度设置为更高的值。 为了更好地利用发射,您可能应该使用纹理,您可以像实现我们用于反照率值的主纹理一样实现其他纹理。
[HDR] _Emission ("Emission", Color) = (0,0,0,1)
5 Minor Improvements
小改进。
最后,我将向您展示两个让您的着色器看起来更好的小东西。 首先,您可以在子着色器下添加回退着色器。 这允许 unity 使用其他着色器的功能,而我们不必自己实现它们。 为此,我们将标准着色器设置为备用着色器,Unity 将从中借用“阴影通道”,使我们的材质在其他对象上投射阴影。 接下来我们可以扩展我们的 pragma 指令。 我们将 fullforwardshadows 参数添加到表面着色器指令中,这样我们就可以获得更好的阴影。 我们还添加了一个指令,将构建目标设置为 3.0,这意味着 unity 将使用更高的精度值,这会有更漂亮的光照。
Shader "Tutorial/005_surface" {
Properties {
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {
}
_Smoothness ("Smoothness", Range(0, 1)) = 0
_Metallic ("Metalness", Range(0, 1)) = 0
[HDR] _Emission ("Emission", color) = (0,0,0)
}
SubShader {
Tags{
"RenderType"="Opaque" "Queue"="Geometry"}
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
fixed4 _Color;
half _Smoothness;
half _Metallic;
half3 _Emission;
struct Input {
float2 uv_MainTex;
};
void surf (Input i, inout SurfaceOutputStandard o) {
fixed4 col = tex2D(_MainTex, i.uv_MainTex);
col *= _Color;
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Smoothness;
o.Emission = _Emission;
}
ENDCG
}
FallBack "Standard"
}
文章评论