找回密码
 立即注册
查看: 180|回复: 0

Unity面试题学习笔记(12)——Shader入门表面着色器篇

[复制链接]
发表于 2022-11-21 13:29 | 显示全部楼层 |阅读模式
参考文献:
1.表面着色器:

概念:

表面着色器是Unity对顶点和片元着色器的一个封装。
Unity会通过程序员编写的Surf函数来自动生成顶点和片元着色器(本质上还是顶点和片元着色器)。
原理:

可以定义一个“表面函数”,它将您需要的所有 UV 或数据作为输入,并填充输出结构SurfaceOutput。SurfaceOutput 基本上描述了_表面的属性_(反照率颜色、法线、发光、镜面反射等)。需要使用 HLSL 编写此代码。
        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;
        }
输出结构(inout):

下面是表面着色器的标准输出结构:
struct SurfaceOutput
{
    fixed3 Albedo;  // 漫射颜色
    fixed3 Normal;  // 切线空间法线(如果已写入)
    fixed3 Emission; // 自发光
    half Specular;  // 0..1 范围内的镜面反射能力
    fixed Gloss;    // 镜面反射强度
    fixed Alpha;    // 透明度 Alpha
};
表面着色器还可以使用基于物理的光照模型。内置标准光照模型和标准镜面反射光照模型(见下文)分别使用以下输出结构:
struct SurfaceOutputStandard
{
    fixed3 Albedo;      // 基础(漫射或镜面反射)颜色
    fixed3 Normal;      // 切线空间法线(如果已写入)
    half3 Emission;
    half Metallic;      // 0=非金属,1=金属
    half Smoothness;    // 0=粗糙,1=平滑
    half Occlusion;     // 遮挡(默认为 1)
    fixed Alpha;        // 透明度 Alpha
};
struct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;      // 漫射颜色
    fixed3 Specular;    // 镜面反射颜色
    fixed3 Normal;      // 切线空间法线(如果已写入)
    half3 Emission;
    half Smoothness;    // 0=粗糙,1=平滑
    half Occlusion;     // 遮挡(默认为 1)
    fixed Alpha;        // 透明度 Alpha
};
输入结构(Input):

输入结构 Input 通常具有着色器所需的所有纹理坐标。纹理坐标必须命名为“uv”后跟纹理名称的形式(如果要使用第二个纹理坐标集,则以“uv2”开头)。
可以放入输入结构的其他值:

  • float3 viewDir - 包含视图方向,用于计算视差效果、边缘光照等等。
  • 具有 COLOR 语义的 float4 - 包含插值的每顶点颜色。
  • float4 screenPos - 包含反射或屏幕空间效果的屏幕空间位置。请注意,这不适合 GrabPass;您需要使用 ComputeGrabScreenPos 函数自己计算自定义 UV。
  • float3 worldPos - 包含世界空间位置。
  • float3 worldRefl - 在_表面着色器不写入 o.Normal_ 的情况下,包含世界反射矢量。有关示例,请参阅反光漫射 (Reflect-Diffuse) 着色器。
  • float3 worldNormal - 在_表面着色器不写入 o.Normal_ 的情况下,包含世界法线矢量。
  • float3 worldRefl; INTERNAL_DATA - 在_表面着色器写入 o.Normal_ 的情况下,包含世界反射矢量。要获得基于每像素法线贴图的反射矢量,请使用 WorldReflectionVector (IN, o.Normal)。有关示例,请参阅反光凹凸 (Reflect-Bumped) 着色器。
  • float3 worldNormal; INTERNAL_DATA - 在_表面着色器写入 o.Normal_ 的情况下,包含世界法线矢量。要获得基于每像素法线贴图的法线矢量,请使用 WorldNormalVector (IN, o.Normal)。
编译指令:

就像任何其他着色器一样,表面着色器放置在 CGPROGRAM..ENDCG 代码块内。不同之处在于:

  • 它必须放在 SubShader 代码块内,不能在 Pass 内。表面着色器本身将编译为多个通道。
  • 它使用 #pragma surface ... 指令来指示自己是表面着色器。
#pragma surface 指令为:
# pragma surface surfaceFunction lightModel [optionalparams]

  • surfaceFunction - 具有表面着色器代码的 Cg 函数。该函数的格式应为 void surf (Input IN, inout SurfaceOutput o),其中 Input 是您定义的结构。Input 应包含表面函数所需的任何纹理坐标和额外自动变量。
  • lightModel - 要使用的光照模型。内置光照模型是基于物理的 Standard 和 StandardSpecular,以及简单的非基于物理的 Lambert(漫射)和 BlinnPhong(镜面反射)。请参阅自定义光照模型页面以了解如何编写自己的光照模型。

    • Standard 光照模型使用 SurfaceOutputStandard 输出结构,并与 Unity 中的标准(金属性工作流)着色器匹配。
    • StandardSpecular 光照模型使用 SurfaceOutputStandardSpecular 输出结构,并与 Unity 中的标准(镜面反射设置)着色器匹配。
    • Lambert 和 BlinnPhong 光照模型不是基于物理的(来自 Unity 4.x),但使用这两个光照模型的着色器在低端硬件上可以提高渲染速度。

  • 可选参数(optionalparams)详情见参考文献。
2.编写表面着色器:


  • 首先,表面着色器不需要定义通道(Pass),所有内容全部写在SubShader中。
  • CGPROGRAM ENDCG语句块依然适用。
  • #pragma 这次用表着色器特殊的格式书写,详情请见上述编译指令模块。
  • 只有一个主函数,通过获取输入结构中的信息,在函数中对这些信息进行处理,并将它们赋值给输出结构对应的参数,最终返回输出结构即可。
  • 输入输出结构详情见上节。
3.法线贴图:

概念:

使表面看起来更粗糙,且不会生成额外的平面,降低渲染成本。
语法:


  • 在properties中定义一个2D类型的变量,默认值为“Bump”(凹凸贴图)。
_NormalMap("NormalMap",2D) = "bump" {}

  • 在SubShader中申明一个sampler2D类型的采样器,在表面着色器的surf函数中用该采样器对法线贴图进行采样。
  • 使用UnpackNormal()函数将tex2D()函数采集到的数据封装成法线类型的变量,并将该变量赋值给输出结构中对应的Normal变量。
sampler2D _NormalMap;
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;
            // 用uv坐标对法线贴图进行采样并赋值给输出结构中的Normal变量
            o.Normal = UnpackNormal(tex2D(_NormalMap,IN.uv_MainTex));
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-15 16:30 , Processed in 0.132501 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表