返回介绍

编写表面着色器

发布于 2021-06-20 00:15:04 字数 5775 浏览 989 评论 0 收藏 0

编写与光照交互的着色器是十分复杂的事情。有不同的光照类型、不同的阴影选项和不同的渲染路径(正向和延时渲染),着色器应以某种方法处理此种程度的复杂性。

Unity 的表面着色器 (Surface Shader) 是一种代码生成方法,用它来编写光照着色器比用低级的顶点/像素着色器程序容易得多。请注意,表面着色器 (Surface Shader) 中不包含自定义语言、魔法或忍者;它只会生成本应手写的所有重复代码。您仍要用 Cg/HLSL 语言来编写着色器代码。

相关的一些示例,请参阅表面着色器示例表面着色器自定义光照示例

工作原理

定义一个“表面函数”,此函数将您需要的任意 UV 或数据作为输入信息并填入输出结构 SurfaceOutput。SurfaceOutput 大体上描述了表面的属性(它的反射率颜色、法线、发射量、高光等)。您要用 Cg/HLSL 语言来编写此代码。

然后,表面着色器 (Surface Shader) 编译器会计算出需要什么输入信息以及填入了什么输出信息等,并生成实际的顶点和像素着色器以及渲染通道 (rendering pass) 来处理正向和延时渲染。

标准的表面着色器输出结构如下:

struct SurfaceOutput {
    half3 Albedo;
    half3 Normal;
    half3 Emission;
    half Specular;
    half Gloss;
    half Alpha;
};

样例

请参阅表面着色器示例表面着色器自定义光照示例表面着色器密铺页面。

表面着色器编译指令

表面着色器与其他任何着色器一样放置于 CGPROGRAM..ENDCG 块中。区别是:

  • 必须将其放置于子着色器块中,而不能放在通道中。表面着色器自身会编译为多个通道。
  • 它使用 #pragma surface ...指令来表明它是个表面着色器。

#pragma surface 指令是:

    #pragma surface surfaceFunction lightModel [optionalparams]

所需参数:

  • surfaceFunction - 表明哪个 Cg 函数有表面着色器代码。该函数格式应为 void surf (Input IN, inout SurfaceOutput o),其中“Input”是您定义的结构。Input 结构应包含表面函数所需的任何纹理坐标和额外自动变量。
  • lightModel - 要使用的光照模型。内置的光照模型是 Lambert(漫反射)和 BlinnPhong(高光)。有关如何编写自己的光照模型,请参阅自定义光照模型页面。

Optional parameters:

  • alpha - Alpha 混合模式。将该参数用于半透明着色器。
  • alphatest:VariableName - Alpha 测试模式。将该参数用于透明镂空着色器。镂空值在有变量名 (VariableName) 的浮点型变量中。
  • vertex:VertexFunction - 自定义顶点修改函数。具体示例可见树皮着色器 (Tree Bark Shader)。
  • finalcolor:ColorFunction - 自定义最终颜色修改函数。请参阅表面着色器示例
  • exclude_path:prepassexclude_path:forward - 请勿为给定的渲染路径生成通道。
  • addshadow - 添加阴影投射器 (shadow caster) 和集合通道 (collector pass)。通常与自定义顶点修改函数一起使用,使得阴影投射也可获得所有程序性顶点动画效果。
  • dualforward - 将双光照贴图用于正向渲染路径中。
  • fullforwardshadows - 支持正向渲染路径中的所有阴影类型。
  • decal:add - 附加的印花着色器(例如,地形 AddPass)。
  • decal:blend - 半透明印花着色器。
  • softvegetation - 使表面着色器仅在“软植被”(Soft Vegetation) 开启时被渲染。
  • noambient - 请勿应用任何环境光照或球面调和光照 (spherical harmonics light)。
  • novertexlights - 请勿在正向渲染 (Forward Rendering) 中应用任何球面调和光照 (spherical harmonic light) 或逐顶点光照 (per-vertex light)。
  • nolightmap - 在该着色器中禁用光照贴图支持(使着色器变小)。
  • nodirlightmap - 在该着色器中禁用方向性光照贴图支持(使着色器变小)。
  • noforwardadd - 禁用正向渲染附加通道。这会使着色器支持一个全方向灯,而其他所有光照则逐顶点/SH 被计算。也会使着色器变小。
  • approxview - 对于有需要的着色器,逐顶点而不是逐像素计算规范化视线方向。这种方法更快速,但当相机靠近表面时,视线方向不会完全正确。
  • halfasview - 将半方向向量(而非视线方向向量)传递到光照函数中。半方向向量将会被逐顶点计算和规范化。这种方法更快速,但不会完全正确。
  • tessellate:TessFunction - 使用 DX11 GPU 密铺 (tessellation);函数计算密铺 (tessellation) 系数。有关详细信息,请参阅表面着色器密铺

此外,您还可以在 CGPROGRAM 块中编写 #pragma debug,然后表面编译器 (surface compiler) 将产生大量生成代码的注释。您可以在着色器检视器中使用开放的编译着色器 (Open Compiled Shader) 进行查看。

表面着色器输入结构

输入结构 Input 通常具有着色器所需的所有纹理坐标。纹理坐标必须被命名为纹理名称前加“uv”(或以“uv2”开头以使用第二纹理坐标集)。

其他能放入 Input 结构中的值:

  • float3 viewDir - 将包含视线方向,用于计算视差 (Parallax) 效果、边缘光照等。
  • 颜色 (COLOR) 语义的 float4 - 将包含插值逐顶点颜色。
  • float4 screenPos - 将包含反射效果的屏幕空间位置。例如,在 Dark Unity 中由 WetStreet 着色器 (WetStreet Shader) 使用。
  • float3 worldPos - 将包含世界坐标空间位置。
  • float3 worldRefl - 将包含世界坐标反射向量,条件是表面着色器不写入 o.Normal。具体示例可见反射 - 漫反射着色器 (Reflect-Diffuse Shader)。
  • float3 worldNormal - 将包含世界坐标法线向量,条件是表面着色器不写入 o.Normal
  • float3 worldRefl; INTERNAL_DATA - 将包含世界坐标反射向量,条件是表面着色器写入 o.Normal。要基于逐像素法线贴图获得反射向量,请使用 WorldReflectionVector (IN, o.Normal)。具体示例可见反射 - 凹凸着色器 (Reflect-Bumped Shader)。
  • float3 worldNormal; INTERNAL_DATA - 将包含世界坐标法线向量,条件是表面着色器写入 o.Normal。要基于逐像素法线贴图获得法线向量,请使用 WorldNormalVector (IN, o.Normal)

表面着色器和 DirectX 11

目前表面着色器编译管线 (compilation pipeline) 的一些部分不理解 DirectX 11 HLSL 语法,因此如果是诸如 StructuredBuffers、 RWTextures 和其他非 DX9 语法等的 HLSL 功能,则您必须将它纳入仅特定于 DX11 的预处理器宏:有关详细信息,请参阅特定于平台的差异页面。

更多文档

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文