【译】Unity3D Shader 新手教程(1/6)

本文为翻译,附上原文连接html

转载请注明出处——polobymulberry-博客园编程

刚开始接触Unity3D Shader编程时,你会发现有关shader的文档至关散,这也形成初学者对Unity3D Shader编程望而却步。该系列教程的第一篇文章(译者注:即本文,后续还有5篇文章)详细介绍了Unity3D中的表面着色器(Surface Shader)的,为学习更复杂的Shader编程打下基础。数组

动机缓存

若是你是刚刚接触Shader编程的新手,你可能不知道从何开始踏出Shader编程的第一步。本教程将带你一步步完成一个表面着色器(Surface Shader)和片断着色器(Fragment Shader)。本教程也将介绍在Unity3D Shader编程中所使用的一些函数和变量,这些内容可能和你在网上看到的不同哦!less

若是你知足下面的条件,我以为你应该看看这篇文章:编辑器

  • 若是你是shader编程的新手。
  • 你想在你的游戏中使用shader作一些很炫酷的效果,可是你在网上找不到可用的Shader(译者注:o(╯□╰)o本身动手丰衣足食)
  • 因为缺少对基础知识的了解,形成不能为所欲为使用Strumpy着色器编辑器译者注:Strumpy Shader Editor,一种图形化编写shader的方式,看着很诱人!)
  • 你想在你的shader代码中手动处理纹理(Textures)

本文是该系列教程的第一篇文章,随后咱们会制做一些更复杂的shader。相比起来,第一篇文章确实很简单。ide

关于做者wordpress

我也是Shader编程的新手----因此我决定写这篇教程帮助你们入门——我当初也在入门上遇到不少苦恼。事实上我并非一个Shader编程专家。函数

当我想了解Shader编程时,我曾反复阅读官方文档,可是我最终发现官方文档讲述的顺序并不适合我学习shader。因此我以为我应该写一篇教程,并分享我所学到的知识。不过写完教程以后,我发现再次阅读官方文档时,以为明白多了。性能

尽管本教程中的全部例子都能正常运行,可是我相信确定有更好shaders实现这些例子。若是聪明的你对这些例子中的shaders有更好的建议,请在评论区留言!

我之因此学习shader编程是由于我须要在我建立的游戏世界中建立些东西,但这个游戏世界建立起来有个麻烦之处,由于它是由不一样角色组成的,而我必须建立由多个部分组成的一个统一网格(mesh)。因此我只能对每一个角色使用一次绘制调用(draw call)。(译者注:彻底不知道他在讲什么?因此我把原文放在下面给你们评评理)

My reason for getting into shader programming was to build something that I needed for a world populated with an endless array of different characters.  I needed to build a combined mesh out of multiple parts so I only have one draw call per character.

经过打开和关闭角色的穿衣效果,我使用Megafiers(一个变形插件)修改了角色的基本网格(base meshes)。其中的困难在于我只有一个纹理(texture),可是我却想给每一个角色的皮肤,服饰以及其余的特征使用不一样的颜色。我想到一个方法----对每一个角色使用不一样的3个4x4纹理,并使用一个shader来给模型上色。我将在整个教程中详细描述我作的这个shader,可是如今—我想大家已经火烧眉毛地想看我建立的角色表演一段即兴的快闪舞(flash mob dance)(译者注:网上截的图片)

flash mob dance

着色器和材质(shaders&materials)

一个shader所作的就是将一个模型的网格(mesh)渲染到屏幕上。Shader能够被定义为一系列的属性(译者注:就像一个函数里面的参数同样,你能够改变函数的不一样赋值来改变函数的输出结果),你能够经过改变这些属性来改变模型渲染到屏幕上的效果。而这些属性被存放起来,放到一个叫作材质(material)的地方。

Unity3D Shader有如下几种

  • 表面着色器(surface shader)----后台自动为你作的绝大部分的工做,减小了你工做量,而且适合绝大多数须要shader的状况。
  • 片断着色器(fragment shader)----可让你作更多的效果,可是此shader更难写。你也能够用它作一些底层的工做,好比顶点光照(Vertex lighting,即在每一个顶点存储该点的光照信息)。顶点光照对于移动设备颇有用(译者注:估计省内存吧)。该shader对于一些须要多通道(multiple passes)的高级渲染效果也颇有效。

本文中咱们将关注点放在表面着色器上。

学习Shader的资源

若是你要学习Shader编程,我向你推荐下面几个资源

  • Martin Kraus's fantastic Wiki Book GLSL Programming/Unity
  • Unity's Shader Reference
  • NVidia's tutorial on the CG programming language
  • CreativeTD's video series on writing surface shaders

Shader的流水化工做方式

译者注:Shader的工做方式也称为shader流水线(pipeline),由于shader工做方式很相似汽车流水线,将模型上一系列顶点数据和其余各类数据做为输入,用这个shader组成的流水线加工下,出来的就成了炫酷的效果了。)

你将在shader流水线中看到不明觉厉的各类术语,我将用我本身的语言尽可能下降理解的难度。

Shader的工做就是输入一些3D几何信息,通过shader处理后将其变为2D的像素呈如今屏幕上。好处是在shader处理过程当中,你只须要改变少数几个属性就能够产生不一样的效果。对于表面着色器,该工做流程看起来像下面这样:

pipeline

(译者注:简单讲解一下这个流程图,首先要渲染的物体将本身的几何信息传递到Shader中,而且系统获得了该物体的顶点信息,而后你能够选择经不通过Vertex Function来处理这些顶点信息,随后通过光栅化(将三维几何信息映射到二维屏幕上,打个不恰当的比喻,至关于把3D模型拍扁到屏幕上,而后你就能够专心处理屏幕上的像素了),每一个像素通过你的shader代码将获得最终的颜色值)

注意在表面着色器(Surface Shader)中的函数退出以前,像素的颜色尚未计算出来。这意味着你能够再次以前传入顶点的法向量来影响光照的计算。

片断着色器(Fragment Shader)有着一样的工做流程,但事实上,片断着色器中必须有Vertex Function(上图中的Vertex Function部分就是可选的(Optional)),并且须要在像素处理阶段作不少的工做才能产生最终的像素。而表面着色器隐藏了这些。(译者注:给个人感受就是片断着色器向用户提供了更多的接口进行更高级的渲染)。

下图展现了你的代码如何被调用以及代码构成

shader4

从上图咱们能够看到,当你写一个shader的时候,你可能得有一些属性值(properties),而且有一个或多个Subshaders。具体使用哪一个Subshader进行处理取决于你的运行平台。你应该还要指定一个Fallback shader,当你的subshader没有一个能运行在你的目标设备上,将使用Fallback shader(译者注:有点像备胎)

每一个Subshader都至少有一个通道(pass)做为数据的输入和输出。你可使用多个通道(passes)执行不一样的操做,好比在一个Grab Pass中,你能够获取将要呈现到屏幕上的像素值(译者注:相似于glsl中的fragment buffer)。当你想制做高级的扭曲效果,这很是有用。虽然当你开始学习shader编程时,你可能并不会使用到它。另一个使用多通道(multiple passes)的缘由是在不一样时刻,你可能须要写入或者禁止写入深度缓存的使用。

当你写表面着色器时,咱们将直接在Subshader这个层次上写代码,系统将把咱们的代码编译成若干个合适的通道(pass)。

尽管shader最终产生的是二维像素,可是其实这些像素除了保存xy坐标外,自己保存着深度值(即每一个像素点上的内容在原先3D场景中离照相机的远近),这样距离照相机近的物体就会把距离照相机远的物体遮挡住,在屏幕上显示时,就是将其像素值覆盖。

你能够控制是否在你的shader中使用深度缓存(Z-buffer)产生一些特效,或者在Pass中使用一些指令决定shader是否能够写入Z-buffer:好比使用ZWrite Off时,任何你输出的东西都不会更新Z-buffer的值,即关闭的Z-Buffer的写入功能。

你可使用Z-buffer技术在别的物体上掏出一个洞,你能够先写入须要打洞区域的深度值,但不输出打洞区域所属的像素值,而后在你模型后面的物体的深度值将没法写入(由于Z-buffer以为你的模型已经挡住了后面的物体)(译者注:这样你打洞区域显示的就是一开始使用的背景色,会形成一个洞穿过了这些物体的效果)

下面是一些shader代码:

image

但愿你能看出上面代码是由PropertiesSubShaderFallback三段代码组成的。

理解Shader代码

文章剩下的部分将讲述上面那段简单代码到底作了什么?真正的干货立刻就来了,你必须好好掌握这些内容。

当你进行shader编程时,你必须使用正确的变量名和函数名来调用它们,事实上变量的名称在某些状况下能让人一眼看出它的特定含义。

建立并使用默认Shader

(译者注:在详细介绍Shader以前,咱们先简单介绍下shader如何使用。)

1. 咱们先打开Unity(个人版本是4.6.1),建立新工程,并在Assets文件夹下建立三个目录,以下:

image

2. 咱们再建立一个cube。

image

能够在Inspector面板看到新建立的cube所使用的Material以下。

image

3. 打开Material文件夹,咱们在其中建立一个Shader和一个Material。

image

此时New Material的默认Shader为Diffuse。

image

咱们将NewShader拖到New Material上。

image

能够看到该材质所使用的Shader变成咱们新建的NewShader了。固然你也能够直接点击材质编辑器中Shader下拉框,选择相应的Shader。

4. 最后将New Material拖到cube上。能够看到cube所使用的材质和Shader都变成了咱们新建立的材质和Shader了。

image

Properties(属性值)简介

你在shader代码中的Properties{…}部分定义Shader中的属性值(属性值就是用户传入给shader的数据,好比纹理之类的,而后shader处理这些纹理,产生特效。能够理解为属性值至关于一种全局变量,而Shader就是那个主函数,Unity的优点在于给这个全局变量赋值能够在Inspector面板进行)。注意Properties(属性值)是全部Subshader代码中的共享的,意味着全部SubShader代码中均可以使用这些属性值。

属性值(property)定义的形式:

_Name(“Displayed Name”,type) = default value[{options}]

  • _Name 属性值的名称,是在shader代码内部使用的,区别于下面的Displayed Name,后者是在Inspector 面板上显示的,做为外界(用户)的输入提示。
  • Displayed Name 呈如今材质编辑器中的属性值名称,在Inspector面板上显示。

总结:打开咱们建立的NewShader。能够看到_MainTex是在代码中使用的,而Base (RGB)是在材质编辑器中使用的

image

  • type 属性值的类型,包括:
    • Color – 表示纯色,使用了RGBA表示法
    • 2D – 表明尺寸为2的幂次的纹理(如2,4,8,16…256,512)
    • Rect – 表明纹理(texture),不一样于上面的纹理,此处纹理的大小不必定是2的幂次。
    • Cube – 用于3d中的cube map,常常提到的天空盒就是使用了cube map。
    • Range(min, max) – 在min和max之间的一个值,在面板中可用滑动条改变其值大小。
    • Float – 任意一浮点数。
    • Vector – 4维向量值,本质就是4个浮点数组成的类型。

来张全家福:

image

image

  • default value 属性值的初始值,就至关于你的变量初始化的那个值。
    • Color – (red,green,blue,alpha) 使用了RGBA这种格式的颜色,alpha指的是透明度– 好比 (1,1,1,1)
    • 2D/Rect/Cube – 纹理的类型,上面已经介绍过了。初始化值可使一个空字符串,或者"white", "black", "gray", "bump"(说明此纹理是一个凹凸纹理)
    • Float/Range – 这个没啥说的,跟浮点数初始化同样同样的
    • Vector – 4维向量,其中4个数均为浮点数 (x,y,z,w)
  • { options } 这里注意了,{options} 仅仅用于纹理类型,好比上面提到的2DRectCube,对于这些类型,若是没有options可填,至少要写一个空的{},不然编译出错。可使用空格将多个options(选项)分开 ,可用的options(选项)以下:
    •  TexGen texgenmode纹理坐标自动生成的方式。能够是ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal其中之一,这些方式和OpenGL中的纹理坐标生成方式相对应,具体详见这篇博文。注意当你写Vertex Function时,纹理坐标产生方式将被忽略。

下面举几个属性值写法的例子:

// 定义了一个半透明(alpha=0.5)效果的红色做为默认颜色值

_MainColor(“Main Color”,Color)=(1,0,0,0.5)

// 定义了一个默认值为白色的纹理

_Texture(“Texture”,2D) =”white” {}

注意属性值的定义末尾处不需添加分号。

标签(Tags)

你的表面着色器能够用一个或多个标签(tags)进行修饰。这些标签的做用是告诉硬件什么时候去调用你的shader代码。

在咱们的例子中,咱们使用:Tags {“RenderType” = “Opaque”},这意味着当程序去渲染不透明的几何体时,将调用咱们的shader,Unity定义了一系列这样的渲染过程。另外一个很容易理解的标签就是Tags {“RenderType” = “Transparent”},意味着咱们的shader只会输出半透明或透明的像素值。

其它一些有用的标签,好比“IgnoreProjector”=“True”,意味着你渲染的物体不会受到projectors(投影仪)的影响。

“Queue”=“xxxx”(给shader所属的对象贴上渲染队列的标签)。当渲染的对象类型是透明物体时,Queue标签能产生一些很是有趣的效果。该标签决定了物体渲染的顺序(译者注:我猜想它的工做方式是这样的,一个场景中有不少个物体,当这些物体被渲染时,必须有一个渲染的顺序,好比背景应该比其余物体先渲染出来,不然背景会将以前渲染的物体遮挡住,具体方法是将背景使用的shader中贴上一个“Queue”=“Backfround”标签,这样使用该shader的物体将被贴上Background的标签。总之当渲染整个场景时,unity会根据这些渲染队列的标签决定按什么顺序去渲染对应标签所属的物体)。

  • Background – 在全部其余物体渲染以前渲染,被用于天空盒或相似的背景效果。
  • Geometry(默认tags为geometry) – 适用于大多数物体。非透明物体使用这种渲染顺序。
  • AlphaTest – 进行alpha测试的像素(alpha-test是指当前像素的alpha小于必定的值就舍弃该像素)应该使用该渲染顺序。单独设置该渲染顺序是由于当在渲染完全部实体事后,渲染alpha测试的物体将更有效率。
  • Transparent – 该渲染标签所属的物体将在标签为Geometry和AlphaTest以后的物体渲染,而且这些贴着Transparent的全部物体自己是从后往前依次渲染的。任何通过alpha-blended的物体都应该使用该标签(译者注:alpha-blended是指使用当前像素的alpha做为混合因子,来混合以前写入到缓存中像素值,注意此时shader是不能写入深度缓存的,由于关闭了写入深度缓存的功能,若是不关闭写入深度缓存,那么在进行深度检测的时候,它背后的物体原本咱们是能够透过它被咱们看到的,但因为深度检测时,小于它的深度就被剔除了,从而咱们就看不到它后面的物体了),玻璃和粒子效果比较适合该渲染标签。
  • Overlay – 该渲染标签适合覆盖效果,任何最后渲染的效果均可以使用该标签,好比透镜光晕。

有趣的是你能够给这些基本的渲染标签进行加加减减。这些预约义的值本质上是一组定义整数,Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000,最后Overlay = 4000(译者注:今后处咱们也能够一窥究竟,貌似数值大的后渲染。)这些预设值这对透明物体有很大影响,好比一个湖水的平面覆盖了你用广告牌制做的树,你能够对你的树使用“Queue”=”Transparent-102”,这样你的树就会绘制在湖水前面了。

Shader的总体结构

让咱们回顾下shader代码的结构。

image

#pragma surface surf Lambert 这段代码表示其中surface表示这是一个表面着色器,进行结果输出的函数名称为surf,其使用的光照模型为Lambert光照模型。

咱们的CG程序使用了一种通过修饰的类C语言 —— CG语言(是Nvidia和微软共同出品的一种shader语言)。详见Nvidia的文档 —— 我在文中也会介绍一些基本的Cg使用方法。

浮点数类型(float)和向量值类型(vec)通常都会在末尾加上2,3,4这些数字(float2,float4,vec3…)表示该类型具体有几个元素组成。这种定义方式使数值操做变得更方便,你能够将其当作一个总体使用,或者单独使用其份量。

//定义一个浮点类型的二维坐标

vec2 coordinate;

//定义一个颜色变量(4个浮点值份量的颜色值)

float4 color;

//经过点乘获得3个浮点值份量的颜色值

float3 multipliedColor = color.rgb * coordinate.x;

你可使用.xyzw或.rgba来代表你使用的变量类型具体的含义,好比.xyzw可能表示的是旋转四元数,而.xyz表示位置或法向量,.rgba表示颜色。固然,你能够仅仅使用float做为单个浮点值类型。其实对.rgba等份量访问符的使用也称做swizzle,尤为是对颜色的处理,好比颜色空间的转换可能会用到它,好比color=color.abgr;

你将会遇到half(半精度)和double(双精度)类型,half(通常16bit)即正常float(通常32bit)的一半精度,double(通常64bit)是正常float的两倍精度(此处的倍数衡量的方式不是指表示的范围,而是表示可使用的bit位数)。使用half常常是出于性能考虑的缘由。还有一种区别于浮点数的定点数fixed,精度更低。

当你想将颜色值规范到0~1之间时,你可能会想到使用saturate函数(saturate(x)的做用是若是x取值小于0,则返回值为0。若是x取值大于1,则返回值为1。若x在0到1之间,则直接返回x的值.),固然saturate也可使用变量的swizzled版本,好比saturate(somecolor.rgb);

你可使用length函数获得一个向量的长度,好比float size = length(someVec4.xz);

如何从表面着色器输出信息

咱们的surface function(表面函数)每一个像素调用一次,系统已经事先计算出当前处理的像素的输入值(准确来讲应该是输入结构体,即Input IN中的Input类型)。 它是根据每一个网格上的面片,并进行插值获得的结果。

来看看咱们的surf函数

void surf (Input IN, inout SurfaceOutput o) {

    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;

}

很明显咱们能够看出,咱们返回了o.Albeodo值 – 该值是Unity为咱们定义的SurfaceOutput结构体中的某个成员。接下来让咱们看看SurfaceOutput具体定义了哪些成员。该Albedo表示像素的颜色。

struct SurfaceOutput {

    half3 Albedo; //该像素的颜色值

    half3 Normal; //该像素的法向量

    half3 Emission; //该像素的辐射光,辐射光是最简单的一种光,它直接从物体发出而且不受任何光源影响

    half Specular; //该像素的镜面高光

    half Gloss; //该像素的发光强度

    half Alpha; //该像素的透明度

};

你只要将该结构体中值交给Unity,Unity会自动根据这些值产生最终效果,而不须要你关心其中的细节。

我答应大家的干货就在下面

首先看看做为咱们surf函数的输入是啥?

咱们定义了一个输入结构体以下:

struct Input {

    float2 uv_MainTex;

};

经过简单地建立结构体,咱们告诉系统当咱们每次调用surf函数时,获取MainTex在该像素的纹理坐标。若是咱们有第二个纹理叫作—_OtherTexture,咱们能够经过在输入结构体中添加下面代码获得它的纹理坐标

struct Input {

    float2 uv_MainTex;

    float2 uv_OtherTexture;

};

若是一个模型还有第二套纹理坐标,咱们能够这样作:

struct Input {

    float2 uv_MainTex;

    float2 uv2_OtherTexture;

};

此时对于咱们所使用的全部纹理,咱们的输入结构体包含一套uv坐标或者一套uv2坐标。

若是咱们的shader很复杂而且须要知道像素的其余相关信息,咱们就能够将如下变量包含在输入结构体中,以此来查询其余的相关变量。

  • float3 viewDir视图方向( view direction)值。为了计算视差效果(Parallax effects),边缘光照(rim lighting)等,须要包含视图方向(view direction)值。
  • float4  with COLOR semantic(好比float4 currentColor,即用户自定义和颜色相关的变量名称) – 每一个顶点(per-vertex)颜色的插值。
  • float4 screenPos – 为了反射效果,须要包含屏幕坐标系中的位置信息时包含此参数。
  • float3 worldPos – 世界坐标系中的位置。
  • float3 worldRefl – 表示世界坐标系中的反射向量(reflect vector)。若是表面着色器(surface shader) 不为SurfaceOutput结构中的Normal赋值,也就是说Normal不会发生变化,也就不须要从新求取worldRefl值了,那么就能够直接经过Input结构体传递该参数。
  • float3 worldNormal – 表示世界坐标系中的法线向量(normal vector)。若是表面着色器(surface shader) 不为SurfaceOutput结构中的Normal赋值,也就是说Normal不会发生变化,也就不须要从新求取worldNormal值了,那么就能够直接经过Input结构体传递该参数。
  • 下面这个参数看不明白不要紧,请移步《关于INTERNAL_DATA的详细剖析》小节。
  • INTERNAL_DATA – 相对于上面的float3 worldRefl和float3 worldNormal,若是表面着色器为SurfaceOutput结构中的Normal赋值了,好比在surf函数中使用了o.Normal = …,此时表面着色器的法向值发生了改变,所以咱们就只能借助赋值后的o.Normal来对世界坐标系下的反射向量进行修改。借助Input结构体咱们传递worldRefl参数,而且咱们用内置的INTERNAL_DATA访问局部坐标系转化到世界坐标系的变化。最后经过(WorldReflectionVector (IN, o.Normal))计算世界坐标系下的反射向量,其中o.Normal表示的是切空间的法向量,而非世界坐标系下的法向量。

关于INTERNAL_DATA的详细剖析

为了更清楚的弄懂INTERNAL_DATA的含义,咱们首先在shader中添加#pragma debug。

image

而后点击Show generated code。

image

咱们查找INTERNAL_DATA,获得以下代码。

#define INTERNAL_DATA half3 TtoW0; half3 TtoW1; half3 TtoW2;
#define WorldReflectionVector(data,normal) reflect (data.worldRefl, half3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal)))
#define WorldNormalVector(data,normal) fixed3(dot(data.TtoW0,normal), dot(data.TtoW1,normal), dot(data.TtoW2,normal))

咱们发现INTERNAL_DATA其实定义了3个half TtoWi(i=0,1,2)的变量,这三个变量合并在一块儿是一个3x3的矩阵,表示局部坐标系到世界坐标系的转换(Translate To World)。因此咱们看到若是要使用o.Normal从新计算worldRefl和worldNormal,就得使用到INTERNAL_DATA这个内置变量表示的坐标系变化矩阵!

  • 你可能会问上面的COLOR semantic是什么意思?当你写一个正常的片断着色器时,你得告诉别人你的输入结构体每一个变量表明什么意思?若是你够疯狂,你能够试试下面这样定义:float2 MyUncleFred : TEXCOORD0; 并告诉别人MyUncleFred表示该模型的uv坐标。(画外音就是这种变量命名方式很使人费解)在表面着色器中你惟一担忧的就是对COLOR类型的定义。float4 currentColor : COLOR;能够看作目前已经通过插值后的像素颜色。固然你也能够不用关心这些,不过建议你命名上最好规范些,方便本身也方便别人。

该shader实际作了哪些事?

如今咱们还有两行代码没有详细讨论:

Sampler2D _MainTex;

对每个属性值,咱们定义了属性值区域(Properties Section),该区域用来定义CG程序中使用的变量。在使用中,咱们必须保证属性名称一致。

image

注意输入结构体中的uv_MainTex是uv+对应属性值(文中为_MainTex,注意前面带下划线是CG官方推荐的写法),若是你使用uv2,那将写做uv2_MainTex。注意Sampler2D _MainTex中的_MainTex变量是一个Sampler2D(这个Sampler2D,能够理解为引用一个2D Texture),它引用了Properties中的_MainTex(译者注:注意二者同名。解释通了sampler2D是什么以后,还须要解释下为何在这里须要一句对_MainTex的声明,以前咱们不是已经在Properties里声明过它是贴图了么。答案是咱们用来实例的这个shader实际上是由两个相对独立的块组成的,外层的属性声明,回滚等等是Unity能够直接使用和编译的ShaderLab;而如今咱们是在CGPROGRAM...ENDCG这样一个代码块中,这是一段CG程序。对于这段CG程序,要想访问在Properties中所定义的变量的话,必须使用和以前变量相同的名字进行声明。因而其实sampler2D _MainTex;作的事情就是再次声明并连接了_MainTex,使得接下来的CG程序可以使用这个变量。),他能够根据指定的uv坐标来提供对应纹理上的像素值,而此处uv_MainTex的做用就是提供纹理_MainTex的uv坐标值。

若是咱们定义了一个_Color变量,咱们能够定义它的属性为

float4 _Color;

咱们surf函数中惟一一行代码

o.Albedo = tex2d( _MainTex, IN.uv_MainTex).rgb;

tex2d的做用是利用IN.uv_MainTex所表明的uv坐标(注意咱们上面指定了uv坐标产生的方式,因此此处的IN.uv_MainTex是自动生成的)对纹理_MainTex进行采样。此处,对于o.Albedo咱们只取颜色份量中的rgb三份量,其中alpha值(透明度)目前不须要,至少对于非透明物体alpha值得做用不大。

若是你要设置alpha值的话,能够像下面这样赋值

float4 texColor = tex2d( _MainTex, IN.uv_MainTex );

o.Albedo = texColor.rgb;

o.Alpha = texColor.a;

总结

你已经了解了不少的术语,可是目前咱们所写的shader还至关有限,可是当学习完第二部分教程后,咱们就能够作一些很酷炫的shader了,由于第二部分咱们将开始使用多重纹理,法向量等等酷炫技术。

  • 在第二部分中,咱们建立了一个实现积雪效果的shader,根据积雪的程度来修改模型,以呈现不一样效果。
  • 在第三部分咱们改进了咱们的shader来混合岩石边缘的积雪。
  • 在第四部分,咱们使用黑色边缘和渐变纹理来建立了具备卡通效果的shader。
  • 在第五部分,咱们建立了一个顶点/片断多通道凹凸纹理着色器(vertex/fragment multipass bumped shader) – 其复杂程度远远超越表面着色器
  • 在第六部分,咱们建立了一个顶点/片断着色器(vertex/fragment shader)来制做相比于咱们第四部分使用表面着色器制做的卡通效果shader更好的shader。
相关文章
相关标签/搜索