Unity ShaderShader前述.docx

上传人:b****5 文档编号:8221011 上传时间:2023-01-29 格式:DOCX 页数:9 大小:20.32KB
下载 相关 举报
Unity ShaderShader前述.docx_第1页
第1页 / 共9页
Unity ShaderShader前述.docx_第2页
第2页 / 共9页
Unity ShaderShader前述.docx_第3页
第3页 / 共9页
Unity ShaderShader前述.docx_第4页
第4页 / 共9页
Unity ShaderShader前述.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

Unity ShaderShader前述.docx

《Unity ShaderShader前述.docx》由会员分享,可在线阅读,更多相关《Unity ShaderShader前述.docx(9页珍藏版)》请在冰豆网上搜索。

Unity ShaderShader前述.docx

UnityShaderShader前述

1.什么是Shader

 

Shader,也就是着色器,它的工作就是读取你的网格并渲染在屏幕上。

Shader可以定义一些属性,你会用它来影响渲染模型时所显示的效果。

当存储了这些属性的设置时,就是一个Material,材质。

--来自狗刨学习网

 

Shader有以下几个种类:

∙SurfaceShaders——也称为表面着色器。

这大概是Unity的骄傲。

它去除了大部分“麻烦的工作”,可以适用于很多情况下 

∙FragmentShaders——片段着色器。

它允许你做更多的工作,但也更难写,而且它还让我们可以做低层的一些东西,像顶点光照,这对于移动设备和多个通道(passes)所必需的更高级的效果会非常有用。

SurfaceShaders的代码需要看起来像这样:

 

 

总结一下就是:

首先需要定义一些Properties,然后将有一个或多个Subshaders。

使用的subshader将取决于所运行的平台。

除此之外,还应该指定一个FallbackShader,如果你的subshaders都不可以在目标设备上运行,那么就会调用这个备用Shader。

 

每个subshader都至少具有一个Pass,它读入数据处理后再输出数据。

你可以使用这些passes执行不同的操作。

例如,在GrabPass中你可以捕捉到显示屏上已经呈现的像素。

通过这种方法可以得到一些高级的屏幕效果。

另外需要有多个passes的情况是,你需要处理不同的事情,像在创建效果的不同阶段写入或者忽略缓冲区。

 

 

当你编写一个SurfaceShader时,是直接写在Subshader里面的。

系统将会把你的代码编译成多个passes。

 

 

虽然着色器是写入到一个2D屏幕上,但是它也保持了每个像素距离摄像机的远近——以此来避免后续渲染的对象在空间上位于已渲染对象的后面,但却覆盖了之前像素的情况出现。

你可以使用Pass中的语句来控制​​这个Z方向的缓存是否对你的着色器代码有影响,或者你的Shader是否写入该缓冲区内,例如:

ZwriteOff表明不会更新你的任何输出像素的Z方向的缓存区。

 

您可以使用这种技术来在其他对象上打孔——通过写入Z缓冲区,但不输出任何实际的像素颜色,那么使用这个Shader的模型背后的对象将不能被写入(因为Z缓冲区中有距离更近的像素了),这样看起来这个对象就像是被打了洞一样。

下面展示了一个最简单的SurfaceShader:

 

 

 

下一次,将对上面代码中的几个部分分别做出说明:

Properties,Tags,SubShader的结构,Input结构体,surf函数等。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

Shader "Custom/Hello" {

    Properties{

        _MainTex("Base(RGB)",2D)= "white" {}

    }

    SubShader{

        Tags{ "RenderType"="Opaque" }

        LOD200

         

        CGPROGRAM

        #pragmasurfacesurfLambert

 

        sampler2D_MainTex;

 

        struct Input{

            float2uv_MainTex;

        };

 

        void surf(InputIN,inoutSurfaceOutputo){

            half4c=tex2D(_MainTex,IN.uv_MainTex);

            o.Albedo=c.rgb;

            o.Alpha=c.a;

        }

        ENDCG

    }

    FallBack "Diffuse"

}

 

2.理解Shader代码

 

接下来,我们将概要地解释Shader中各个部分的作用,进一步理解Shader。

 

 

Properties

 

由上面的代码可以看出,Shader的第一行指明了Shader的名字:

“Example/DiffuseTexture”。

紧跟着就是Shader的第一个重要组成部分,Properties{}。

我们可以在Properties里面定义一系列属性,这些属性被下面的SubShader所共享,它们可以让你在Unity的面板里直观地控制Shader变量,影响渲染结果。

 

 

这些属性的定义格式如下:

 

1

_Name( "DisplayedName",type)= default value{options}

∙_Name:

程序中引用的名字,和我们一般理解的变量名称是一样的。

∙DisplayedName:

这个字符串将会出现在Unity材质的编辑面板上。

∙type:

该属性的类型。

Unity支持以下几种属性类型:

∙Color:

表示一个单一的RGBA颜色值;

∙2D:

表示一张大小为2的次方的纹理贴图,可以使用基于模型UV坐标来进行采样;

∙Rect:

表示一张纹理不是2的次方的纹理贴图;

∙Cube:

表示一个可用于反射的3D立方体映射贴图,可以进行采样;

∙Range(min,max):

一个取值范围在min到max之间的浮点值;

∙Float:

一个可以为任意值的浮点值;

∙Vector:

一个4维度的向量。

∙defaultvalue:

该属性的默认值。

∙Color:

使用浮点值表示的(r,g,b,a),例如(1,1,1,1);

∙2D/Rect/Cube:

对于贴图类型的属性,默认值可以是一个空字符串,或者"white","black","gray","bump"这样的字符串;

∙Float/Range:

在此范围内的值即可;

∙Vector:

以(x,y,z,w)形式表示的4D向量;

∙{options}:

只和纹理类型的2D、Rect和Cube相关,它必须至少被指定为{}。

你可以使用空格分隔多个选项,有如下选择:

∙TexGen贴图生成模式:

该纹理的自动纹理坐标生成模式。

可以为ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal。

这些直接对应了OpenGL中的texgenmodes。

注意,如果你编写了一个顶点函数,那么可以忽略TexGen。

 

下面的实例展示了Properties的写法:

 

1

2

3

4

//Defineacolorwithadefaultvalueofsemi-transparentred 

_MainColor("MainColor",Color)=(1,0,0,0.5) 

//Defineatexturewithadefaultofwhite

_Texture("Texture",2D)= "white" {}

 

Tags

 

我们的SurfaceShader可以使用Tags进行装饰。

这些Tags可以告诉硬件应该什么时候调用你的Shader。

 

在上述例子里,有:

Tags{"RenderType"="Opaque"},该指令告诉系统在渲染Opaque几何图形时调用我们的Shader。

Unity定义了一系列这样的队列。

另一个常用的是"RenderType"="Transparent",这表明你的Shader可能会输出一些半透明或全透明的像素。

 

其他的tags也同样非常有用,例如"IgnoreProjector"="True",这表明你的对象不会受投影和"Queue"="xxxx"的影响。

 

Queuetag可以指定何时渲染你的Shader,详情可参见这篇文章。

 

Shader结构

 

我们来看Shader下面的部分。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

Shader "Custom/Hello" {

    Properties{

        _MainTex("Base(RGB)",2D)= "white" {}

    }

    SubShader{

        Tags{ "RenderType"="Opaque" }

        LOD200

         

        CGPROGRAM

                //Lambert定义了光照模型

        #pragmasurfacesurfLambert

 

        sampler2D_MainTex;

 

        struct Input{

            float2uv_MainTex;

        };

 

        void surf(InputIN,inoutSurfaceOutputo){

            half4c=tex2D(_MainTex,IN.uv_MainTex);

            o.Albedo=c.rgb;

            o.Alpha=c.a;

        }

        ENDCG

    }

    FallBack "Diffuse"

}

 

可以看出,这部分代码使用CG语言写的,它有点像C语言。

你可以去查看Nvidia的文档去了解更多内容。

这里将会进行简单的介绍。

 

其中很重要的变量为float类型和向量(vec)类型,这两种类型可以紧跟一个2、3或者4进行定义。

这种类型在图形处理里面非常常见。

 

//Defineafloatvariablevec2coordinate;//Defineacolorvariablefloat4color;//Multiplyoutacolorfloat3multipliedColor=color.rgb*coordinate.x;

 

 我们可以使用.xyzw和.rgba符号访问存储在这些变量中的值(颜色、位置、法线等)。

 我们还会遇到half和double类型。

和字面表示的一样,这两种类型分别是普通float类型大小的一半和两倍。

half通常被用于性能限制的情况下。

还有一种类型是fixed,它表示固定小数的实数。

 

 

从SurfaceShader输出信息

 

因此,每个像素都会调用我们的SurfaceShader。

系统在模型的每个面片上进行插值,为我们的输入结构设置了当前值。

 

surf函函数和下面类似:

 

1

2

3

void surf(InputIN,inoutSurfaceOutputo){ 

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

}

显然,我们是为SurfaceOutput中的Albedo属性进行了设置。

SurfaceOutput是Unity为我们定义的一种结构体,它的结构定义如下:

 

1

2

3

4

5

6

7

8

struct SurfaceOutput{ 

    half3Albedo;      //颜色纹理 

    half3Normal;     //法线 

    half3Emission;   //自发光,不受照明的影响 

    halfSpecular;     //高光指数 

    halfGloss;         //光泽度 

    halfAlpha;         //Alpha通道 

}; 

  

 

 

下面我们来解释Shader代码中最关键的部分。

 

首先,是surf函数的输入参数Input结构:

 

只需要通过Input结构体,我们就可以告诉系统,为当前正在处理的像素得到在MainTex中对应的纹理坐标。

如果不止一张纹理,例如还有一张_OtherTexture,我们只需要添加如下代码:

 

structInput{float2uv_MainTex;float2uv_OtherTexture;}; 

 

structInput{float2uv_MainTex;float2uv2_OtherTexture;};

 

Input结构体

 

Input结构体通常包含了所有贴图的uv或uv2坐标。

但是,如果我们的Shader比较复杂,并且需要了解像素渲染的其他信息,我也就可以通过将其包括在Input结构体中来要求得到这些变量。

 

∙float3viewDir:

视角方向,用于计算视差效果和边缘照明等;

∙使用COLOR语义的float4:

包含了插值后的逐顶点颜色;

∙float4screenPos:

屏幕空间中的位置;

∙float3worldPos:

世界空间中的位置;

∙float3worldRefl:

如果SurfaceShader没有改写o.Normal,将包含了环境反射向量;

∙float3worldNormal:

如果SurfaceShader没有改写o.Normal,将包含了环境法线向量;

∙INTERNAL_DATA:

当我们需要改写o.Normal时,一些函数,如WorldNormalVector等,需要该变量进行计算;

 

 

对于Properties中定义的每一个属性,你都需要在CG程序中声明一个变量来访问它。

而且该变量和属性的名称必须完全一样。

Shader"Custom/Hello"{Properties{

     //1、_MainTex变量名 _MainTex("Base(RGB)",2D)="white"{}}SubShader{Tags{"RenderType"="Opaque"}LOD200CGPROGRAM#pragmasurfacesurfLambert     //2、_MainTex变量名sampler2D_MainTex;structInput{

      //3、uv_MainTex变量名float2uv_MainTex;};voidsurf(InputIN,inoutSurfaceOutputo){half4c=tex2D(_MainTex,IN.uv_MainTex);o.Albedo=c.rgb;o.Alpha=c.a;}ENDCG}FallBack"Diffuse"}

当该属性是一张文字,而你需要在Input结构中得到对应的uv坐标时,uv或者uv2后面的名称也必须和属性名相同。

 在上图中,_MainTex对应了一张纹理贴图,它对应的变量类型就是sampler2D类型,只要得到了一个uv坐标,我们就可以在贴图上进行采样得到颜色值。

surf函数中仅用了一个函数:

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

它使用Input中得到的该像素对应的_MainTex中的uv坐标,在_MainTex进行采样,得到一个float4类型的颜色值(包括了透明通道)。

如果我们需要得到透明通道的值,可以这样做:

 

float4texColor=tex2d(_MainTex,IN.uv_MainTex);o.Albedo=texColor.rgb;o.Alpha=texColor.a;

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 自我管理与提升

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1