shader
《U3D_shader编程》
##《U3D_Shader编程》发布说明:
++++Shader一个高大上的领域,不管怎么样,我来了。
++++立钻哥哥从2018年开始正式对Shader进行战略布局。
++++《U3D_Shader编程》将从零开始,循序渐进探索,还是先探索一段时间吧,后期根据需要再推出一个精品博文,这篇就算一个杂谈吧。
##《U3D_Shader编程》目录:
#第一篇:快速入门篇
#第二篇:基础夯实篇
#第三篇:基础进阶篇
#第四篇:中级挑战篇
#第五篇:高级成魔篇
#第一篇:快速入门篇
#第一篇:快速入门篇
++++第一章:Shader着色器
++++第二章:Surface/Vertex Shader
++++第三章:Shader案例
##第一章:Shader着色器
++第一章:Shader着色器
++++1.1、什么是Shader
++++1.2、什么是渲染管线
++++1.3、Shader和材质,贴图的关系
++++1.4、三大主流的高级编程语言
++++1.5、Unity Shader的组织形式
++++1.6、固定管线Shader
++1.1、什么是Shader
++++Shader,中文翻译即着色器,是一种较为短小的程序片段,用于告诉图形硬件如何计算和输出图像。(过去由汇编语言编写,现在可以使用高级语言来编写。)
++++Shader就是可编程图形管线的算法片段。
++++Shader主要分为两类:Vertex Shader(顶点着色)和Fragment Shader(片段着色)。
++1.2、什么是渲染管线
++++渲染管线也称为渲染流水线,是显示芯片内部处理图形信号相互独立的并行处理单元。(一个流水线是一系列可以并行和按照固定顺序进行的阶段。)(每个阶段都从它的前一阶段接收输入,然后把输出发给随后的阶段。)
++++渲染管线包括:应用程序阶段(APPlication State)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)、后期缓冲(Back Buffer)等阶段。
++++应用程序阶段(Application State):
--准备场景数据:将数据(摄像机位置、视锥体、场景中的模型、光源等)加载到显存中;
--粗粒度剔除(culling);
--设置模型的渲染状态:指定材质(漫反射颜色、高光颜色)、指定纹理、指定Shader(指定顶点着色器(Vertex Shader)、指定片元着色器(Fragment Shader))、指定光源属性。
--调用DrawCall:发起方cpu、接收方GPU、命令缓冲区(command Buffer,可放入改变渲染状态、DrawCall在内的多种命令。)
=>>>>输出渲染图元(rendering primitives):进入下一阶段(几何阶段(Geometry Stage))
++++几何阶段(Geometry Stage):
--顶点着色器(Vertex Shader):完全可编程,通常用于实现顶点的空间变换、顶点着色等功能。
----坐标变换:对顶点坐标进行某种变换(可模拟水面、布料等);把顶点坐标从模型空间转换到齐次裁剪空间,接着通常再由硬件做透视除法后,最终得到归一化的设备坐标(NDC:Normalized Device Coordinates)。
----逐顶点光照。
----输出后续阶段所需的数,如纹理坐标。
--曲面细分着色器(tessellation Shader):用于细分图元。
--几何着色器(geometry Shader):执行逐图元(perPrimitive)的着色操作或产生更多图元。
--裁剪(Clipping):将摄像机视野外的顶点裁剪掉,并剔除某些三角图元的面片。
--屏幕映射(Screen Mapping):把每个图元的坐标转换到屏幕坐标中。(只将x和y坐标缩放到屏幕坐标系(Screen Coordinates),z值保留,屏幕坐标系和z坐标一起构成了窗口坐标系(Window Coordinates))
=>>>>输出屏幕空间的顶点信息:进入下一阶段(光栅化阶段(Rasterizer Stage))
++++光栅化阶段(Rasterizer Stage):
--三角形设置(Triangle Setup):计算光栅化一个三角网格所需的信息。
--三角形遍历(Triangle Traversal):检查每个像素是否被一个三角网格覆盖,如果被覆盖的话,就生成一个片元(fragment)。(一个片元并不是像素,而是包含了很多状态的集合。)
--片元着色器(Fragment Shader):用于实现逐片元(Per-Fragment)的着色操作。(纹理采样)
--逐片元操作(Per-Fragment Operations,OpenGL的说法)或称输出合并阶段(Output-Merger,DirectX的说法)
----模板测试(Stencil Test):该片元的模板值(使用读取掩码在模板缓冲区(Stencil Buffer)中读取)、参考值(reference value:使用读取掩码)。(使用开发者指定的比较函数决定是否舍弃该片元:不管通过与否都能修改模板缓冲区的值)
----深度测试(depth Test):该片元深度值、深度缓冲区的深度值。(使用开发者指定的比较函数决定是否通过:通过才有资格修改深度缓冲区的值。(通过开启/关闭深度写入实现))
----混合(Blend):源颜色(片元着色器得到的颜色)、模板颜色(颜色缓冲区中的颜色)。(使用混合函数进行混合操作)
=>>>>输出图形:进入下一阶段(后置缓冲(Back Buffer))
++++后置缓冲(Back Buffer):使用双重缓冲(Double Buffering)策略避免看到正在进行光栅化的图元。(渲染完成后通过一个swap交换操作将前置缓冲(Front Buffer)和后置缓冲中的内容交换。)
++1.3、Shader和材质,贴图的关系
++++Shader是图形可编程方案的程序片段。Shader实际上就是一小段程序,它负责将输入的顶点数据以指定的方式和输入的贴图或者颜色等组合起来,然后输出。(绘图单元可以根据这个输出来将图像绘制到屏幕上。)
++++渲染管线是一种计算机从数据到最终图形成像的描述。输入的贴图或颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容打包存储到一起得到的就是一个Material(材质)。(之后我们便可以将材质赋予三维物体来进行渲染(输出)了。)
++++材质是商品,Shader是方法,贴图是材料。(材质好比引擎最终使用的商品,Shader好比是生产这种商品的加工方法,而贴图就是原材料。)
++1.4、三大主流的高级编程语言(GLSL、HLSL、Cg)
++++目前Shader Language有三种语言:GLSL(基于OpenGL的OpenGL Shading Language)、HLSL(基于DirectX的High Level Shading Language)、Cg语言(NVIDIA公司的C for Graphic)。
++++OpenGL(Open Graphics Library):定义了一个跨编程语言、跨平台的编程接口规范的专业的图形程序接口。(它用于三维图像(二维亦可),是一个功能强大,调用方便的底层图形库。)(顶点数组的特性:提高顶点位置、法线、颜色、色彩指数、纹理坐标、多形边缘标识的传输速度。)(OpenGLShading Language:用于着色对象、顶点着色以及片段着色技术的扩展功能。)
++++DirectX(Direct eXtension,简称DX):是由微软公司创建的多媒体编程接口。(由C++编程语言实现,遵循COM。)
++++Cg语言(C for Graphics):是为GPU编程设计的高级着色器语言。(由NVIDIA公司开发。)(Cg极力保留C语言的大部分语义,并让开发者从硬件细节中解脱出来,Cg同时也有一个高级语言的其他好处,如代码的易重用性,可读性得到提高,编译器代码优化等。)
++1.5、Unity Shader的组织形式(ShaderLab基本结构)
++++Unity中的Shader称之为ShaderLab,语法结构:
Shader “name”{
[Properties] //可选(在Inspector面板上显示)
SubShaders //必选(Shader的核心代码)
[Fallback] //可选(默认Shader)
}
++++【Properties】:属性定义,用来定义着色器中使用的贴图资源或者数值参数等,这些属性会在Inspector视图的材质界面中显示,可以方便的进行设置和修改。
++++【SubShader】:子着色器,一个着色器中包含有一个或者多个子着色器,Unity会从上到下遍历子着色器,找到第一个能被用户设备支持的子着色器,并使用该着色器渲染,如果都不能使用,则使用备用着色器。
++++【Fallback】:备用着色器,是对硬件要求最低的着色器。
++++ShaderLab基本结构:
Shader “MyShader”{
Properties{
_MyTexture(“My Texture”, 2D) = “white”{ };
//这里还可以写其他属性,如颜色等。
}
SubShader{
//立钻哥哥:这里才是真正的Shader代码
//--表面着色器(SurfaceShader)
//--顶点片段着色器(Vertex and Fragment Shader)
//--固定管线着色器(Fixed Function Shader)
}
SubShader{
//立钻哥哥:这里可以写简化版的Shader
//当由于设备性能不够,上面的SubShader无法运行时,就会运行这个Shader
}
}
++++内建Shader:
--Unlit:不发光(这只是一个纹理,不被任何光照影响。)
--VertexLit:顶点光照。
--Diffuse:漫反射。
--Normal mapped:法线贴图。(比漫反射更昂贵:增加了一个或多个纹理(法线贴图)和几个着色器结构。)
--Specular:高光。(这增加了特殊的高光计算。)
--Normal Mapped Specular:高光法线贴图。(这比高光更昂贵一点。)
--Parallax Normal mapped:视差法线贴图。(这增加了视差法线贴图计算。)
--Parallax Normal Mapped Specular:视差高光法线贴图。(这增加了视差法线贴图和镜面高光计算。)
++1.6、固定管线Shader
++++固定管线Shader:propeties、material、lighting、set texture、alpha通道。
++++properties:
Properties{
//立钻哥哥:定义属性(将显示在inspector中)
_color(“Main Color”, color) = (1,1,1,1) //颜色
_Ambient(“Ambient”, color)=(0.3,0.3,0.3,0.3) //环境光
_Specular(“Specular”, color)=(1,1,1,1) //高光
_Shininess(“Shininess”, range(0,8))=4 //默认为4,高光部分的范围大小
_Emission(“Emission”, color)=(1,1,1,1) //自发光
_Maintex(“MainTex”,2d)=”” //第一张纹理图
_Secondtex(“Secondtex”,2d)=”” //第二张纹理图
}
++++material:
material{
//使用属性
diffuse[_Color]
ambient[_Ambient]
specular[_Specular]
shininess[_Shininess]
emission[_Emission]
}
++++lighting:
--lighting on
--separatespecular on
++++set texture:
//设置纹理图片
settexture[_Maintex]{
//合并贴图与之前设置好的颜色/高光等信息
//double表示高亮度提高2倍
//primary表示material里面的信息
combine texture * primary double
}
++++Alpha通道:
_constant(“ConstantColor”, color)=(1,1,1,0.3)
SubShader{
//渲染队列
Pass{
//Alpha通道处理
Blend SrcAlpha OneMinusSrcAlpha
//previous之前的
settexture[_Secondtex]{
constantColor[_Constant]
//合并第二张贴图与之前设置的所有信息
combine texture * previous double, texture
//,后面加texture表示使用贴图本身的alpha信息
}
}
}
++Shader简单示例:
Shader “YlzShaderDemo/shader1”{
Properties{
//立钻哥哥:定义属性,将显示在inspector中
_Color(“Main Color”, color)=(1,1,1,1) //颜色
_Ambient(“Ambient”, color)=(0.3,0.3,0.3,0.3) //环境光
_Specular(“Specular”, color)=(1,1,1,1) //高光
_Shininess(“Shininess”, range(0,8))=4 //默认为4(高光部分的范围大小)
_Emission(“Emission”, color)=(1,1,1,1) //自发光
_Constant(“ConstantColor”, color)=(1,1,1,0.3)
_Maintex(“MainTex”,2d)=”” //第一张纹理图
_Secondtex(“Secondtex”,2d)=”” //第二张纹理图
}
SubShader{
Tags{“Queue” = “Transparent”} //渲染队列
Pass{
Blend SrcAlpha OneMinusSrcAlpha //Alpha通道处理
material{
//使用属性
diffuse[_Color]
ambient[_Ambient]
specular[_Specular]
shininess[_Shininess]
emission[_Emission]
}
lighting on
spearatespecular on
//设置纹理图片
settexture[_Maintex]{
//合并贴图与之前设置好的颜色/高光等信息
//double表示亮度提高2倍
//primary表示material里面的信息
combine texture * primary double
}
//previous之前的
settexture[_Secondtex]{
constantColor[_Constant]
//合并第二张贴图与之前设置的所有信息
combine texture * previous double, texture
//,后面加texture表示使用贴图本身的alpha信息
}
}
}
}
##第二章:Surface/Vertex Shader
++第二章:Surface/Vertex Shader
++++2.1、Surface Shader表面着色器
++++2.2、Vertex&Fragment Shader顶点&片段着色器
++2.1、Surface Shader表面着色器
++++在使用Surface Shader时,它可以自动生成一些代码,比直接去使用低阶的顶点和像素着色器来说要容易很多。(但是Surface Shader并不是一种定制的语言,它只不过自动生成了以前必须去手写的代码。)
++++Surface Shader还是使用Cg或HLSL语言编写。(Surface Shader实际上就是对顶点和像素着色器的一种包装,它让我们不用去关注更多的顶点和片段程序的细节,能够快速地得到想要的着色器。)
++++创建一个Surface Shader:【Create】=>【Shader】=>【Standard Surface Shader】
++++\assets\Shader\NewYanlzSurfaceShader.shader
//立钻哥哥:新建表面着色器代码参考
Shader “Custom/NewYanlzSurfaceShader”{
Properties{
_Color(“Color”, Color) = (1,1,1,1) //颜色值
_MainTex(“Albedo(RGB)”, 2D) = “white”{} //主纹理
_Glossiness(“Smoothness”, Range(0,1)) = 0.5 //浮点值,用于计算高光的光泽度
_Metallic(“Metallic”, Range(0,1)) = 0.0 //浮点值,用于计算表现金属的光泽度
}
SubShader{
Tags{ “RenderType”=”Opaque”} //渲染类型(Opaque:不透明物体)
LOD 200 //层次细节
CGPROGRAM //代码块开始
//pragma指令格式:#pragam surface surfaceFunction lightModel [optionalparams]
#pragma surface surf Standard fullforwardshadows //编译指令
#pragma target 3.0 //使用硬件的能力
sampler2D _MainTex;
struct Input{
float2 uv_MainTex;
}
half _Glossiness;
half _Metallic;
fixed4 _Color;
void surf(Input IN, inout SurfaceOutputStandard o){
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) *_Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG //代码块结束
}
FallBack “Diffuse”
}
++++Surface Shader的属性:
--_Color:颜色值。
--_MainTex:主纹理。
--_Glossiness:一个浮点值,用于计算高光的光泽度。
--_Metallic:一个浮点值,用于计算表现金属的光泽度。
++++SubShader:该shader有一个SubShader,但是,这个SubShader中并没有Pass通道。(在Surface Shader当中不需要去编写Pass通道,是因为Surface Shader就是对Vertex & Fragment Shader的一种包装,它能够自动生成着色器代码,生成的过程不要我们干预,Pass通道也能自动生成。)(添加了Pass通道,就会出现编译错误。)
++++作为ShaderLab的基本结构,该默认的Surface Shader拥有一个FallBack,如果shader中的某一种特性不能够使用,那么会回滚到Diffuse(漫反射)。
++++【Tags{“RenderType”=”Opaque”}】:描述的是渲染类型。(Opaque:表示不透明的物体。)
++++【LOD 200】:是指层次细节。
++++【CGPROGRAM 到 ENDCG】:是一个代码块,表示其中使用Cg语法,要真正地学会Surface Shader编程,有必须先学会Cg语言。
++++【#pragma surface surf Standard fullforwardshadows】:pragma是一个编译指令。
++++pragma指令格式:#pragam surface surfaceFunction lightModel [optionalparams]
--以“#”开头,后接pragam:表示一个编译指令;
--“surface”关键字:表示该shader以Surface Shader格式来编写。(Unity引擎在处理这个shader结构的时候,就会自动地进行编译低阶的代码。)
--“surfaceFunction”:是指surface的函数,在一定条件下可以随便取名,在默认创建的Surface Shader中的“surf”就是函数名。(下面也有对应的函数体)
--“lightModel”:光照模型,在默认创建的Surface Shader中使用的是“Standard”。(这个光照模型实际上也是一个函数。)(在UnityPBSLighting.cginc文件中的函数“LightingStandard”,其中“Lighting”后的名称就是我们真正使用的函数名。)
--“[optionalparams]”:表示其他的一些选项,默认Surface Shader使用的是“fullforwardshadows”,从表面上理解是关于阴影的一些功能。(fullforwardshadows:表示它能够在Forward渲染路径下支持所有的阴影类型,默认的shader仅仅支持一个方向光的阴影,如要在Forward渲染路径下使用点光源或聚光灯产生的阴影就需要使用该指令。)
--补充:“addshadow”指令:它主要功能就是生成一个阴影投射器。
++++【#pragma target 3.0】:表示我们将要对这个着色器使用硬件的“shader model 3.0”的能力。(硬件的“shader model”是硬件一个用于着色处理的基础的指令范围和能力,值越高表示能使用越高级的功能,默认使用的是2.0)
++++【sampler2D】:是一个二维纹理,这里表示该shader的主纹理参数。
++++【struct Input{ float2 uv_MainTex; };】:Input结构体,用于描述纹理uv坐标,必须以“uv”或“uv2”开头。
--使用“uv”表示用第一套uv坐标集合,用“uv2”表示第二套uv坐标集合。
--如果在Properties中使用类似“_MainTex”纹理属性,就需要在SubShader中的一个输入结构体中必须以“uv”开头的变量,否则就得不到该纹理采样值。
++++除了Input结构体之外,在CGPROGRAM要对Properties中的属性做对应的声明:
--“2D”:对应“sampler2D”;
--“Range”:对应“half”;
--“Color”:对应“fixed4”;
++++Surface函数:surface函数总是无返回值的,但是Unity当中要渲染物体,一个函数总是要输出最后的结果,这个“surf”函数没有返回结果,实际上它是利用参数进行传递结果的:“Input IN”和“inout SurfaceOutputStandard o”。
--void surf(Input IN, inout SurfaceOutPutStandard o){ }
--【Input IN】:Input结构体。(在这个结构体中当前只有主纹理的uv坐标。)(“Surface Shader input structure”中描述了除了使用“uv”作为开头的成员外,还有其他的成员,这些主要用于光照的计算,关于这些内容的使用,只有在系统地学习Cg语法、Vertex&Fragment Shader程序设计和计算机图形学的一些经典光照算法后,才能灵活地使用这些功能。)
--【inout SurfaceOutPutStandard o】:“inout”,它描述这个参数既是输入的也是输出的。(“out”修饰,它的值在最后当做输出,并且在外部可以被直接使用。)(这个”surf”函数虽然没有返回值,但是它的第二个参数是具有输出功能的。)
----“SurfaceOutputStandard”:有一个结构体为“SurfaceOutput”,具有不同的形态,它演变成了两种结构体,分别是:“SurfaceOutputStandard”和“SurfaceOutputStandardSpecular”。
++++SurfaceOutputStandard结构体:
struct SurfaceOutputStandard{
fixed3 Albedo; //base(diffuse or specular) color
fixed3 Normal; //tangent space normal, if written
half3 Emission;
half Metallic; //0=non-metal, 1=metal
half Smoothness; //0=rough, 1=smooth
half Occlusion; //occlusion(default 1)
fixed Alpha; //alpha for transparencies
};
++2.2、Vertex&Fragment Shader顶点&片段着色器
++++Vertex and Fragment Shader是最强大的Shader类型,属于可编程渲染管线。(使用的是CG/HLSL语法。)(分为2个部分:vertex顶点部分和Fragment像素部分。)
++++vertex program收到系统传递给它的模型数据,然后把这些处理成我们后续需要的数据(至少要包含这些顶点的位置信息)进行输出。(其他输出数据比如有纹理的UV坐标,以及其他需要传递给fragment program的数据。)(然后系统对vertex program输出的顶点数据进行插值,并将插值结果传递给fragment program。)(最后fragment program根据这些插值结果计算最后屏幕上的像素颜色。)
++++Vertex&Fragment Shader简单示例:
Shader “Custom/YanlzShaderDemo_vertextMat”{
Properties{
_MainTex(“Texture”, 2D) = “white”{ }
}
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include “UnityCG.cginc”
sampler2D _MainTex;
float4 _MainTex_ST;
struct v2f{
float4 pos : SV_POSITION;
float4 nv : TEXC00RD0;
};
v2f vert(appdata_base v){
v2f o;
o.pos = mul(UNITY_MATRI_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
float4 frag(v2f i) : COLOR{
float4 texCol = tex2D(_MainTex, i.uv);
float4 outp = texCol;
return outp;
}
ENDCG
}
}
}
++++裁剪空间的范围是[-1,1],也就是在经过MVP矩阵后,o.pos.x/p.pos.w以及o.pos.y/o.pos.w的范围都是[-1,1],故可以将裁剪空间坐标转换为相对屏幕位置的uv坐标,如下:
--o.uv = float2((o.pos.x/o.pos.w+1)*0.5, (o.pos.y/o.pos.w+1)*0.5)
Shader “Custom/YanlzShaderDemo_vertexMat2”{
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//输入位置和切线数据
struct appdata{
float4 vertex : POSITION;
float4 tangent : TANGENT;
};
//定义顶点数据结构体
struct v2f{
float4 pos : SV_POSITION;
fixed4 color : COLOR;
}
//顶点数据函数
v2f vert(appdata v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //计算顶点位置
o.color = v.tangent * 0.5 + 0.5; //计算顶点颜色
return o;
}
//片段程序,直接返回顶点颜色
float4 frag(v2f i) : COLOR{
return i.color;
}
ENDCG
}
}
}
##第三章:Shader案例
++第三章:Shader案例
++++3.1、基础Shader示例1
++++3.2、基础Shader示例2
++++3.3、SurfaceShader示例1
++++3.4、SurfaceShader示例2
++++3.5、Vertex&Fragment Shader示例1
++++3.6、Vertex&Fragment Shader示例2
++++3.7、综合Shader案例1(转动的地球)
++++3.8、综合Shader案例2(LOGO闪光效果)
++++3.9、综合Shader案例3(摇摆的树)
###3.1、基础Shader示例1
++3.1、基础Shader示例1
++++此示例主要是让我们熟悉一下ShaderLab基本结构。
++立钻哥哥推荐的基础Shader示例1:
Shader “Custom/YanlzShaderDemo_Shader1”{
Properties{
//定义属性(将显示在inspector中)
_Color(“Main Color”, color) = (1,1,1,1) //颜色
_Ambient(“Ambient”, color) = (0.3,0.3,0.3,0.3) //环境光
_Specular(“Specular”, color) = (1,1,1,1) //高光
_Shininess(“Shininess”, range(0,8)) = 4 //高光部分的范围大小(默认为4)
_Emission(“Emission”, color) = (1,1,1,1) //自发光
_Constant(“ConstantColor”, color) = (1,1,1,0.3)
_Maintex(“MainTex”, 2d) = “” //第一张纹理图
_Secondtex(“SecondTex”, 2d) = “” //第二张纹理图
}
SubShader{
Tags{ “Queue” = “Transparent” } //渲染队列
Pass{
Blend SrcAlpha OneMinusSrcAlpha //Alpha通道处理
material{
//使用属性
diffuse[_Color]
ambient[_Ambient]
specular[_Specular]
shininess[_Shininess]
emission[_Emission]
}
lighting on
separatespecular on
//设置纹理图片
settexture[_Maintex]{
//立钻哥哥:合并贴图与之前设置好的颜色/高光等信息
//double表示亮度提高两倍
//primary表示material里面的信息
combine texture * primary double
}
//previous之前的
settexture[_SecondTex]{
constantColor[_Constant]
//合并第二张贴图与之前设置的所有信息
combine texture * previous double, texture
//,后面加texture表示使用贴图本身的alpha信息
}
}
}
}
###3.2、基础Shader示例2
++3.2、基础Shader示例2
++++此示例主要是让我们熟悉一下ShaderLab基本结构。
++立钻哥哥推荐的基础Shader示例2:
Shader “Custom/YanlzShaderDemo_shader2”{
//属性
Properties{
_Color(“主颜色”, Color) = (1,1,1,0)
_SpecColor(“高光颜色”, Color) = (1,1,1,1)
_Emission(“光泽颜色”, Color) = (0,0,0,0)
_Shininess(“光泽度”, Range(0.01, 1)) = 0.7
_MainTex(“基础纹理(RGB)-透明度(A)”, 2D) = “white”{ }
}
//子着色器
SubShader{
//定义材质
Material{
Diffuse[_Color]
Ambient[_Color]
Shininess[_Shininess]
Specular[_SpecColor]
Emission[_Emission]
}
Lighting On //开启光照
SeparateSpecular On //开启独立镜面反射
Blend SrcAlpha OneMinusSrcAlpha //开启透明度混合(alpha blending)
//通道一:渲染对象的背面部分
Pass{
//如果对象是凸型,那么总是离镜头离得比前面更远
Cull Front //不绘制面向观察者的几何面
SetTexture[_MainTex]{
Combine Primary * Texture
}
}
//通道二:渲染对象背对我们的部分
Pass{
//如果对象是凸型,那么总是离镜头离得比背面更远
Cull Back //不绘制背离观察者的几何面
SetTexture[_MainTex]{
Combine Primary * Texture
}
}
}
}
###3.3、SurfaceShader示例1
++3.3、SurfaceShader示例1
++++此示例让我们熟悉一下Surface Shader表面着色器的基本结构。
++立钻哥哥推荐的SurfaceShader示例1:
Shader “Custom/YanlzShaderDemo_SurfaceShader01”{
Properties{
_Color(“Color”, Color) = (1,1,1,1)
_MainTex(“Albedo(RGB)”, 2D) = “white”{ }
_Glossiness(“Smoothness”, Range(0, 1)) = 0.5 //平滑度,高光的光泽强度
_Metallic(“Metallic”, Range(0, 1)) = 0.0 //金属质感
}
SubShader{
Tags{ “RenderType” = “Opaque” } //渲染类型
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
struct Input{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
void surf(Input IN, inout SurfaceOutputStandard o){
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic =_Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack “Diffuse”
}
###3.4、SurfaceShader示例2
++3.4、SurfaceShader示例2
++++此示例让我们熟悉一下Surface Shader表面着色器的基本结构。
++立钻哥哥推荐的SurfaceShader示例1:
Shader “Custom/YanlzShaderDemo_SurfaceShader02”{
Properties{
_MainTex(“Albedo(RGB)”, 2D) = “white”{ }
}
SubShader{
Tags{ “RenderType” = “Opaque” “queue”=”transparent”}
LOD 200
CGPROGRAM
#pragma surface surf lambert fullforwardshadows alpha //添加alpha(变透明)
#pragma target 3.0
sampler2D _MainTex;
struct Input{
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o){
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack “Diffuse”
}
###3.5、Vertex&Fragment Shader示例1
++3.5、Vertex&Fragment Shader示例1
++++此示例让我们熟悉Vertex&Fragment Shader顶点&片段着色器的基本结构。
++立钻哥哥推荐的Vertex&Fragment Shader示例1:
Shader “Custom/YanlzShaderDemo_vertexMat01”{
Properties{
_MainTex(“Texture”, 2D) = “white”{ }
}
SubShader{
pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include “UnityCG.cginc”
sampler2D _MainTex;
float4 _MainTex_ST;
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata_base v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
float4 frag(v2f i) : COLOR{
float4 texCol = tex2D(_MainTex, i.uv);
float4 outp = texCol;
return outp;
}
ENDCG
}
}
}
###3.6、Vertex&Fragment Shader示例2
++3.6、Vertex&Fragment Shader示例2
++++此示例让我们熟悉Vertex&Fragment Shader顶点&片段着色器的基本结构。
++立钻哥哥推荐的Vertex&Fragment Shader示例2:
Shader “Custom/YanlzShaderDemo_vertexMat2”{
SubShader{
pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//输入位置和切线数据
struct appdata{
float4 vertex : POSITION;
float4 tangent : TANGENT;
};
//定义顶点数据结构体
struct v2f{
float pos : SV_POSITION;
fixed4 color : COLOR;
};
//顶点数据函数
v2f vert(appdata v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //计算顶点位置
o.color = v.tangent * 0.5 + 0.5; //计算顶点颜色
return o;
}
//片段程序,直接返回顶点颜色
float4 frag(v2f i) : COLOR{
return i.color;
}
ENDCG
}
}
}
###3.7、综合Shader案例1(转动的地球)
++3.7、综合Shader案例1(转动的地球)
++++通过一个简单的案例来巩固Shader基本结构。
++++案例原理:通过2张贴图,一张地球图,一张云层分布图。(通过uv.x(纹理坐标)的横轴与时间相乘,产生球在移动的效果。)(通过地球和云的颜色值叠加形成层叠的效果。)
++++注意1:贴图的“Wrap Mode”属性一定要设置成Repeat。
++++当Wrap Mode为Clamp:意思是纹理xy轴的值即xy轴的(即uv)被限制在[0,1]的范围内。
++++当Wrap Mode为Repeat:好处是本来在clamp模式中,贴图的两侧(即u为0和u为1的地方)是连不上的。通过Repeat,左右两侧连上了。(这个技术主要运用是当纹理为砖式纹理(Tiled Texture),即左右和上下的纹路都是相接的时候,很小的一块纹理贴图能渲染出一大片区域。例如一大片草地等等。)
++立钻哥哥推荐的Shader案例1:转动的地球:
Shader “Custom/YanlzShaderDemo_Shader1”{
Properties{
_MainTex(“Texture”, 2D) = “white”{ }
_Cloud(“_Cloud”, 2D) = “white”{ }
}
SubShader{
Tags{ “Queue” = “Transparent” “RenderType”=”Transparent”}
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include “UnityCG.cginc”
float4 _Color;
sampler2D _MainTex;
sampler2D _Cloud;
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
float4 _MainTex_ST;
v2f vert(appdata_base v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
half4 frag(v2f i) : COLOR{
//地球的贴图uv,x即横向在动
float u_x = i.uv.x + -0.1*_Time;
float2 uv_earth = float2(u_x, i.uv.y);
half4 texcolor_earth = tex2D(_MainTex, uv_earth);
//云层的贴图uv的x也在动,但是动的更快一些
float2 uv_cloud;
u_x = i.uv.x + -0.2*_Time;
uv_cloud = float2(u_x, i.uv.y);
half4 tex_cloudDepth = tex2D(_Cloud, uv_cloud);
//纯白 x深度值 = 该点的云颜色
half4 texcolor_cloud = float4(1,1,1,0) * (tex_cloudDepth.x);
//地球云彩颜色混合
return Lerp(texcolor_earth, texcolor_cloud, 0.5f);
}
ENDCG
}
}
}
###3.8、综合Shader案例2(LOGO闪光效果)
++3.8、综合Shader案例2(LOGO闪光效果)
++++通过一个简单的案例来巩固Shader基本结构。
++++这种流光效果在很多LOGO及广告宣传中都会用到。(本案例不用图而完全通过计算得出闪光区域,主要是借此加深uv计算实现特殊效果的实现,以及计算uv的方法。)(商业开发的做法应该是哪一张闪光的图,对其做uv移动,然后和原图两张图混合,这样运算会小很多,需要存储的变量也会小很多。)
++立钻哥哥推荐的Shader案例2:LOGO流光:
Shader “Custom/YanlzShaderDemo_Shader2”{
Properties{
_MainTex(“Texture”, 2D) = “white”{ }
}
SubShader{
Tags{ “Queue”=”Transparent” “ignoreProjector”=”True” “RenderType”=”Transparent” }
Blend SrcAlpha OneMinusSrcAlpha
AlphaTest Greater 0.1
pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include “UnityCG.cginc”
sampler2D _MainTex;
float4 _MainTex_ST;
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
//顶点函数没有什么特别的,和常规一样
v2f vert(appdata_base v){
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
//必须放在使用 frag函数之前,否则无法识别
//参数:角度,uv,光带的x长度,间隔,开始时间,偏移,单次循环时间
float inFlash(float angle, float2 uv, float xLength, int Interval, int beginTime, float offX, float loopTime){
float brightness = 0; //亮度值
float angleInRad = 0.0174444 * angle; //倾斜角
float currentTime = _Time.y; //当前时间
int currentTimeInt = _Time.y/interval; //获取本次光照的起始时间
currentTimeInt *= interval;
//获取本次光照的流逝时间 = 当前时间 - 起始时间
float currentTimePassed = currentTime - currentTimeInt;
if(currentTimePassed > beginTime){
float xBottomLeftBound; //底部左边界和右边界
float xBottomRightBound;
float xPointLeftBound; //此点边界
float xPointRightBound;
float x0 = currentTimePassed - beginTime;
x0 /= loopTime;
xBottomRightBound = x0; //设置右边界
xBottomLeftBound = x0 - xLength; //设置左边界
//投影至x的长度 = y/tan(angle)
float xProjL;
xProjL = (uv.y)/tan(angleInRad);
//此点的左边界 = 底部左边界 - 投影至x的长度
xPointLeftBound = xBottomLeftBound - xProjL;
//此点的右边界 = 底部右边界 - 投影至x的长度
xPointRightBound = xBottomRightBound - xProjL;
xPointLeftBound += offX; //边界加上一个偏移
xPointRightBound += offX;
//如果该点在区域内
if(uv.x > xPointLeftBound && uv.x < xPointRightBound){
//得到发光区域的中心点
float midness = (xPointLeftBound + xPointRightBound)/2;
//趋近中心点的程度,0表示位于边缘,1表示位于中心点
float rate = (xLength - 2*abs(uv.x - midness))/(xLength);
brightness = rate;
}
}
brightness = max(brightness, 0);
float4 col = float4(1,1,1,1) * brightness; //返回颜色 = 纯白色 * 亮度
return brightness;
}
float4 frag(v2f i) : COLOR{
float4 outp;
float4 texCol = tex2D(_MainTex, i.uv); //根据uv取得纹理颜色
//传进i.uv等参数,得到高亮值
float tmpBrightness;
tmpBrightness = inFlash(75, i.uv, 0.25f, 5, 2, 0.15, 0.7f);
//图像区域,判断设置为 颜色的A>0.5,输出为材质颜色+光亮值
if(texCol.w > 0.5){
outp = texCol + float4(1,1,1,1) * tmpBrightness;
}else{
//空白区域,判定设置为 颜色的A <=0.5,输出空白
outp = float4(0,0,0,0);
}
return outp;
}
ENGCG
}
}
}
###3.9、综合Shader案例3(摇摆的树)
++3.9、综合Shader案例3(摇摆的树)
++++通过一个简单的案例来巩固Shader基本结构。
++++实现一片森林的树木随微风轻轻摇摆的效果:
--方案A、给每棵树加上动画,让动画来控制摆动。(CPU和DrawCall会爆表。)
--方案B、用代码控制树木位移来实现。(很难达到理想的效果)
--方案C、使用Unity自带的Tree和WindZone。(性价比不高。)
--方案D、通过Shader的帧动画变换UV采样贴图来实现效果。(一个不错的选择。)
++++通过Shader的帧动画变换UV采样贴图来实现“摇摆的树”的效果:一个贴图具体如何被映射到模型的表面是根据模型定点的UV值来决定的。(假如不修改的话,一个平面的左下角的UV是(0,0),右上角是(1,1)。然后,根据时间计算出当前帧贴图上相应的UV位置,按照这个位置取贴图中对应颜色。)(其实模型顶点本身的UV信息没变,只是把它传递给我们的UV信息进行加工,获取我们想要的位置上的像素。)
++立钻哥哥推荐的Shader案例3:摇摆的树:
Shader “Custom/YanlzShadeDemo_Shader3”{
Properties{
_MainTex(“Base(RGB)”, 2D) = “white”{ }
_Pos(“Position”, vector) = (0,0,0,0)
_Direction(“Direction”, Vector) = (0,0,0,0)
_Timescale(“TimeScale”, float) = 1
_TimeDelay(“TimeDelay”, float) = 1
}
SubShader{
LOD 100
Tags{
“RenderType”=”Opaque”
“Queue”=”Transparent”
}
CGPROGRAM
#pragma surface surf Lambert vertex:vert alpha
sampler2D _MainTex;
fixed4 _Pos;
fixed4 _Direction;
half _TimeScale;
half _TimeDelay;
struct Input{
half2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o){
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
void vert(inout appdata_full v){
half dis = distance(v.vertex,_Pos);
half time = (_Time.y + _TimeDelay) * _TimeScale;
//动态顶点变换(核心)
v.vertex.xyz += dis * (sin(time) * cos(time*2/3) + 1) * _Direction.xyz;
}
ENDCG
}
FallBack “Transparent/Cutout/VertexLit”
}
#立钻哥哥:由于篇幅太多,我们将第一篇【快速入门篇】独立发布了,后续篇幅也将独立发布。
#立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++立钻哥哥推荐的拓展学习链接(Link_Url):
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
++++U3D_Shader编程(第一篇:快速入门篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372071
++++U3D_Shader编程(第二篇:基础夯实篇):https://blog.csdn.net/vrunsoftyanlz/article/details/80372628
++++设计模式简单整理:https://blog.csdn.net/vrunsoftyanlz/article/details/79839641
++++U3D小项目参考:https://blog.csdn.net/vrunsoftyanlz/article/details/80141811
++++UML类图:https://blog.csdn.net/vrunsoftyanlz/article/details/80289461
++++Unity知识点0001:https://blog.csdn.net/vrunsoftyanlz/article/details/80302012
++++Unity引擎基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78881685
++++Unity面向组件开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78881752
++++Unity物理系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78881879
++++Unity2D平台开发:https://blog.csdn.net/vrunsoftyanlz/article/details/78882034
++++UGUI基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78884693
++++UGUI进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78884882
++++UGUI综合:https://blog.csdn.net/vrunsoftyanlz/article/details/78885013
++++Unity动画系统基础:https://blog.csdn.net/vrunsoftyanlz/article/details/78886068
++++Unity动画系统进阶:https://blog.csdn.net/vrunsoftyanlz/article/details/78886198
++++navigation导航系统:https://blog.csdn.net/vrunsoftyanlz/article/details/78886281
++++Unity特效渲染:https://blog.csdn.net/vrunsoftyanlz/article/details/78886403
++++Unity数据存储:https://blog.csdn.net/vrunsoftyanlz/article/details/79251273
++++Unity中sqlite数据库:https://blog.csdn.net/vrunsoftyanlz/article/details/79254162
++++WWW类和协程:https://blog.csdn.net/vrunsoftyanlz/article/details/79254559
++++Unity网络:https://blog.csdn.net/vrunsoftyanlz/article/details/79254902
++++C#事件:https://blog.csdn.net/vrunsoftyanlz/article/details/78631267
++++C#委托:https://blog.csdn.net/vrunsoftyanlz/article/details/78631183
++++C#集合:https://blog.csdn.net/vrunsoftyanlz/article/details/78631175
++++C#泛型:https://blog.csdn.net/vrunsoftyanlz/article/details/78631141
++++C#接口:https://blog.csdn.net/vrunsoftyanlz/article/details/78631122
++++C#静态类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630979
++++C#中System.String类:https://blog.csdn.net/vrunsoftyanlz/article/details/78630945
++++C#数据类型:https://blog.csdn.net/vrunsoftyanlz/article/details/78630913
++++Unity3D默认的快捷键:https://blog.csdn.net/vrunsoftyanlz/article/details/78630838
++++游戏相关缩写:https://blog.csdn.net/vrunsoftyanlz/article/details/78630687
++++立钻哥哥Unity 学习空间: http://blog.csdn.net/VRunSoftYanlz/
--_--VRunSoft : lovezuanzuan--_--
相关阅读
主流脚本编程语言大比拼 优缺点一目了然 2015-07-03 09:41 路饭网 字号:T | T从EJB的崩溃,Spring的兴起到Java遭受质疑,Rails取得
前言 本篇文章摘抄于网络,放在自己的博客只是为了方便查看和记忆。现在CSDN已经有手
微信公众号【Java技术江湖】一位阿里 Java 工程师的技术小站。作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中
关于匿名内部类和lambda的基础可以看我博客文章:内部类 , Lambda 看完上面链接的两篇文章你就能看懂下面代码了: 1. 匿名内部
少儿学习编程课程是否真的适合七八岁的低龄儿童[图]:天下熙熙皆为利来,天下攘攘皆为利往。这几年来,乐高教育机构在国内如同雨后春笋