使用表面着色器的属性

现在我们已经为着色器创建了一些属性,这里我们将要正式的把这些属性跟着色器关联起来,这些属性就像着色器的调节器一样,可以让材质拥有更好的交互性。

我们可以在材质的 检查器面板(Inspector tab) 使用着色器属性的值,因为我们为这个属性添加了一个变量名,但是如果你在着色器代码中想要通过这个变量名来获得这个值,我们仍然还有很多事情要做。


  • 操作步骤

    下面的步骤展示了如何在表面着色器中使用属性:

    1. 开始之前,我们先删除下面的行的代码,就好像我们在章节 创建一个基本的标准着色器 中删除属性的操作步骤一样,删除 _MainTex 属性:
    _MainTex ("Albedo (RGB)", 2D) = "white" {}
    
    sampler2D _MainTex;
    
    fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    
    1. 下一步,添加下面这些行的代码到着色器代码中,添加到 CGPROGRAM 下面, add the following lines of code to the shader, below the CGPROGRAM line:
    float4 _AmbientColor;
    float _MySliderValue;
    
    1. 当第二部完成之后,我们就可以在着色器中使用属性的值了。我们把 _Color 属性的值与 _AmbientColor 值相加,并且把两者的结果赋值给 o.Albedo 。为了达成目的,我们需要在着色器代码中的 surf() 方法中添加如下代码:
    void surf (Input IN, inout SurfaceOutputStandard o) 
    {
          fixed4 c        = pow((_Color + _AmbientColor), _MySliderValue);
          o.Albedo        = c.rgb;
          o.Metallic      = _Metallic;
          o.Smoothness    = _Glossiness;
          o.Alpha         = c.a;
    }
    
    1. 最终你的代码将会是如下所示。如果你在你的VSCode中保存好然后返回Unity编辑器,你的着色器将会重新编译。 如果没有什么错误,那么现在你可以修改材质的环境光和自发光的颜色,当然也可以通过滑动条增加最终颜色的饱和度。听巧妙的噢。
    Shader "CookbookShaders/StandardDiffuse3" 
    {
          // We define Properties in the properties block
          Properties 
          {
              _Color ("Color", Color) = (1,1,1,1)
              _AmbientColor("Ambient Color", Color) = (1,1,1,1)
              _MySliderValue("This is a Slider", Range(0,10)) = 2.5
          }
          SubShader 
          {
              Tags { "RenderType"="Opaque" }
              LOD 200
              // We need to declare the properties variable type inside of the 
              //CGPROGRAM so we can access its value from the properties block.
              CGPROGRAM
              #pragma surface surf Standard fullforwardshadows
              #pragma target 3.0
              struct Input { float2 uv_MainTex;};
              fixed4 _Color;
              float4 _AmbientColor;
              float  _MySliderValue;
              void surf (Input IN, inout SurfaceOutputStandard o) 
              {
                  // We can then use the properties values in our shader
                  fixed4 c = pow((_Color + _AmbientColor), _MySliderValue);
                  o.Albedo = c.rgb;
                  o.Alpha = c.a;
              }
              ENDCG
          }
          FallBack "Diffuse"
    }  
    
  • 提示
    下载示例代码
    你可以在 Packt 网站登陆后,下载所有已购买书籍的所有示例代码文件,下载地址:http://www.packtpub.com。如果你是在别处购买的书,那么请访问: http://www.packtpub.com/support然后注册,以便通过邮件直接获取相关的代码文件。

  • 注意
    pow(arg1, arg2) 这个方法是内建的,他的功能跟数学的幂函数 power 是等价的。第一个参数是底数,第二参数是指数 。想了解更深入的了解 pow() 方法,请去看Cg语言的教程。下面这个网址提供了非常棒的资源,能让你学习更多有关着色器的知识,并且里面有Cg着色器语言的所有函数的表:
    http://http.developer.nvidia.com/CgTutorial/cg_tutorial_appendix_e.html 下面的屏幕截图展示了通过材质的 检查器面板(Inspector tab) 来控制材质的属性,从而控制材质的颜色和饱和度:

    diagram


  • 原理介绍

    当你在 属性(Properties ) 代码块中声明一个新的属性时,等于是在材质的 检查器面板( Inspector tab) 给着色器添加了可以访问调整值[tweaked value]的方式。这个值存储在这个属性的变量名中。在这个例子中,_AmbientColor_Color_MySliderValue 这三个变量就是我们用来保存调整值的。为了让你能够在 SubShader{} 这个代码块中使用这些值,你需要在这个代码块中对应创建三个变量,而且这三个变量的名字要跟属性代码块中的变量名保持一致。这样的话Unity会自动把 SubShader{} 代码块和属性代码块中的变量关联起来,然后让着色器知道这些变量使用的是相同的数据。另外,这个属性声明同时也告诉我们在 SubShader{} 代码块中对应的变量的数据类型,在后续有关着色器的优化相关的章节中,会用到这些。

    当你再 subshader 代码块中创建好变量后,接下来你就可以再 surf() 方法中使用它们的值。在这个例子中,_Color_AmbientColor_MySliderValue 这三个变量获得了来自材质的 检查器面板(Inspector tab) 对应的值。变量 _Color 跟变量 _AmbientColor 这两个值相加,并且把这个值当作底数,_MySliderValue 的值当作指数,进行幂运算。

    大多数着色器都是从一个标准着色器开始,然后一步一步修改它直到它们符合设计的样子。我们现如的这个例子为以后需要散射组件的表面着色器打好了基础。


    注意
    材质时一种 资源(assets) 。这意味着,当你的游戏在编辑器中运行时,你对它的任何改变都将会是永久的[这里解释一下,一般在Unity编辑器运行时,你对游戏做的修改,在停止运行后,又会恢复到运行之前的状态,这里材质不一样,修改之后,即使停止运行,它也不会恢复]。如果你不小心错误修改了属性的值,你可以通过快捷键 Ctrl + Z 取消这个修改。


  • 相关补充
    跟其他一些编程语言一样,Cg也是不允许有错误。如果你的着色器代码中有错误的话,着色器将不会起作用。当着色器不起作用时,你的材质会因为没有着色器而以品红的方式渲染: diagram 当一个脚本没有被编译,Unity引擎会禁止你的游戏导出或者运行。 然而,着色器中有错误,Unity并不会阻止你运行你的游戏。如果你的着色器呈现出品红色,那么就应该检查一下到底哪里出现了问题。当你在Unity的编辑器中选中这个报错的着色器,那么你可以在 检查器面板(Inspectortab) 中看到一大串错误: diagram 尽管错误提示展示了错误所在的行,但是通常不一定是引起错误的正真原因。上一张示例图所展示的错误是因为删除了了 SubShader{} 代码块中的 sampler2D _MainTex 变量引起的。 然而报错的地方是试图去访问这个未定义的变量所在的代码行。找到错误并且修复的过程就是我们常说的 debug 。你最常用的一些错误检查如下:

    • 忘记括号匹配。 如果忘记用花括号对代码块进行配对匹配,那么编译器就会在代码文档的最后,开始或者新的代码块中提示错误。

    • 忘记写分号结束语句。这是最常见的错误,同时也是最容易定位和修复的错误。通常会在下一行开始产生一个错误。

    • 属性(Properties) 代码块中定义了一个属性,但是没有在 SubShader{} 代码块中定义对应的变量。【这两者是需要成对出现的,也就是说在 属性(Properties) 块里面定义了一个新的属性,那么在 SubShader{} 代码块中就需要声明一个与之对应的变量】

    • 更Unity中的C#脚本不一样,Cg语言中的浮点值不需要在后面加 f 来表示浮点类型:浮点值1.0就写作 1.0 ,而不是 1.0f

    着色器中提示的错误很具有误导性,特别是因为它们那严格的语法约束。如果看不懂错误什么意思,那就直接百度或者谷歌。 或者去Unity的论坛搜索一下看看,里面可能就有跟你遇到相同问题(修复了相同问题)的开发者。


    • 额外内容

      在第二章,表面着色器和纹理纹理 ,我们会了解如何去掌握 表面着色器(Surface Shaders) 跟它们的 属性(properties) 。如果使用着色器的所有潜能和特性,到底能做些什么?如果你对这个问题感兴趣,你应该去看一看第十章,更高级的着色器技术 。里面有本书的一些最高级的着色器技术。