所以在我们的例子中,我们声明了一个表面着色器,实际的代码在surf函数中(在下面能找到该函数),使用Lambert(也就是普通的diffuse)作为光照模型。
接下来一句sampler2D _MainTex;,sampler2D是个啥?其实在CG中,sampler2D就是和texture所绑定的一个数据容器接口。等等..这个说法还是太复杂了,简单理解的话,所谓加载以后的texture(贴图)说白了不过是一块内存存储的,使用了RGB(也许还有A)通道,且每个通道8bits的数据。而具体地想知道像素与坐标的对应关系,以及获取这些数据,我们总不能一次一次去自己计算内存地址或者偏移,因此可以通过sampler2D来对贴图进行操作。更简单地理解,sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3D,samplerCube等等式。
解释通了sampler2D是什么之后,还需要解释下为什么在这里需要一句对_MainTex的声明,之前我们不是已经在Properties里声明过它是贴图了么。答案是我们用来实例的这个shader其实是由两个相对独立的块组成的,外层的属性声明,回滚等等是Unity可以直接使用和编译的ShaderLab;而现在我们是在CGPROGRAM...ENDCG这样一个代码块中,这是一段CG程序。对于这段CG程序,要想访问在Properties中所定义的变量的话,必须使用和之前变量相同的名字进行声明。于是其实sampler2D _MainTex;做的事情就是再次声明并链接了_MainTex,使得接下来的CG程序能够使用这个变量。
终于可以继续了。接下来是一个struct结构体。相信大家对于结构体已经很熟悉了,我们先跳过之,直接看下面的的surf函数。上面的#pragma段已经指出了我们的着色器代码的方法的名字叫做surf,那没跑儿了,就是这段代码是我们的着色器的工作核心。我们已经说过不止一次,着色器就是给定了输入,然后给出输出进行着色的代码。CG规定了声明为表面着色器的方法(就是我们这里的surf)的参数类型和名字,因此我们没有权利决定surf的输入输出参数的类型,只能按照规定写。这个规定就是第一个参数是一个Input结构,第二个参数是一个inout的SuceOutput结构。
它们分别是什么呢?Input其实是需要我们去定义的结构,这给我们提供了一个机会,可以把所需要参与计算的数据都放到这个Input结构中,传入surf函数使用;SuceOutput是已经定义好了里面类型输出结构,但是一开始的时候内容暂时是空白的,我们需要向里面填写输出,这样就可以完成着色了。先仔细看看INPUT吧,现在可以跳回来看上面定义的INPUT结构体了:
作为输入的结构体必须命名为Input,这个结构体中定义了一个float2的变量…你没看错我也没打错,就是float2,表示浮点数的float后面紧跟一个数字2,这又是什么意思呢?其实没什么魔法,float和vec都可以在之后加入一个2到4的数字,来表示被打包在一起的2到4个同类型数。比如下面的这些定义:
在访问这些时,我们即可以只使用名称来获得整组,也可以使用下标的方式(比如.xyzw,.rgba或它们的部分比如.x等等)来获得某个。在这个例子里,我们声明了一个叫做uv_MainTex的包含两个浮点数的变量。
如果你对3D开发稍有耳闻的话,一定不会对uv这两个字母感到陌生。UV mapping的作用是将一个2D贴图上的点按照一定规则映射到3D模型上,是3D渲染中最常见的一种顶点处理手段。在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是_MainTex)之前加上uv两个字母,就代表提取它的uv(其实就是两个代表贴图上点的二维坐标)。我们之后就可以在surf程序中直接通过访问uv_MainTex来取得这张贴图当前需要计算的点的坐标了。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-48390-5.html
刚好送别