Unity Shader 学习之旅

Unity Shader 学习之旅

纸上学来终觉浅,绝知此事要躬行

美丽的梦和美丽的诗一样 都是可遇而不可求的——席慕蓉

一、渲染流水线

 

示例图
示例图

enter description here

 

Tips:什么是 GPU 加速计算?

1.1Draw Call

CPU过Draw Call来g告诉GPU开始一个渲染过程。一个Draw Call会指向本次调用需要渲染的图元列表。
通俗的讲我们可以把CPU理解成一群专家,他们有着超强和快速的计算能力,能解决各种各样的问题。GPU则是许许多多个流水线上的工人,尽管它们只能做简易单一的任务,但是成千上万个工人一起开动可以迅速处理大量的工作。但是专家和这些工人协同工作的过程中需要交接任务,此时交接的过程就是Draw Call调用的过程。专家告诉工人们这些东西你们需要把这些零件加工成什么样,交接沟通是需要代价,这也就是为什么Draw Call过多会影响帧率。琐碎的零部件一点点的交接给工人是很浪费资源的,工人成千上万个对于100个零部件加工和一万个零部件加工耗费的代价是一样的,这样专家也忙不过来,这时专家忙的焦头烂额,工人还在等待下一批零部件,零部件组装成成品的过程就出现了卡顿。所以Draw Call优化的过程也就是尽可能一次由专家给工人一大批需要统一操作组装的零件(合并网格),和减少这些零件的类型(避免过多材质,尽可能多的公用材质)。

二、最简单的顶点片元着色器

2.1.顶点片元着色器基本结构

Unity Shader基本结构:Shader ,Properties,SubShder,Fallback等。

2.1.1结构

  1.  
  2. Shader "ShaderName"{ 
  3. Properties{ 
  4. //属性,暴露在inspector面板上 
  5.  
  6. SubShader{ 
  7. //针对显卡A的SubShader 
  8. Pass{ 
  9. //设置渲染状态和标签 
  10.  
  11. CGPROGRAM 
  12. //该代码的编译指令,如: 
  13. #pragma vertex vert 
  14. #pragma fragment frag 
  15.  
  16. //CG代码 
  17.  
  18.  
  19. ENDCG 
  20. //其他设置 
  21.  
  22. SubShader{ 
  23. //针对显卡B的SubShader 
  24.  
  25. //上述SubShader都失败后调用回调的Unity Shader 
  26. Fallback "VertexLit" 

2.1.2一个简单的示例

  1. Shader "UnityShaderBoook/SimplerShader"{ 
  2.  
  3. //Properties并非必须,此shader中不声明任何材质属性 
  4. SubShader{ 
  5.  
  6. Pass{  
  7.  
  8. CGPROGRAM 
  9. //编译命令,指定顶点/片元着色器处理函数 
  10. #pragma vertex vert 
  11. #pragma fragment frag 
  12.  
  13. //顶点着色器代码,逐个顶点执行 
  14. //使用POSITION语义指定函数的输入参数v为顶点的位置信息 
  15. //SV_POSITION语义指明函数的返回值为顶点着色器输出的是裁剪空间中的位置 
  16. //UnityObjectToClipPos则是Unity内置的模型-观察-投影矩阵,帮我们完成操作(mul(UNITY_MATRIX_MVP,*)' 现已更新为'UnityObjectToClipPos(*)') 
  17. float4 vert(float4 v:POSITION):SV_POSITION{ 
  18. return UnityObjectToClipPos(v); 
  19.  
  20. //片元着色器,逐个面片执行 
  21. //此片元着色器没有任何输入参数 
  22. //SV_TARGET语义指定函数输出颜色存储一个渲染目标上 
  23. //这里仅仅简单的返回表示白色的fixed4的变量 
  24. fixed4 frag():SV_TARGET{ 
  25. return fixed4(1.0,1.0,1.0,1.0); 
  26.  
  27. ENDCG 
  28.  
  29.  

当我们把使用该shader的材质赋给场景物体时,物体仅仅表现出单纯的白色。我们在shader中没有赋予它更丰富的内容,如法线,纹理等,所以他不会表现出任何深度,阴影等,当然也不会受到光照的影响。

注意:

  1. POSITIONSV_POSITION都是CG/HLSL中的语义,他们是不可省略的,这些语义用来告诉系统输入值和输出值的含义。POSTION告诉Unity这里将模型的顶点数据传入到v参数,而SV_POSITION则告诉Unity顶点着色器输出的是裁剪空间中的顶点坐标。
  2. UnityObjectToClipPos(v) 是mul(UNITY_MATRIX_MVP,v)更新后的函数,他是Unity内置函数,用来帮我们进行顶点坐标从模型空间转换到裁剪空间的转换。
  3. SV_TARGETHLSL中的系统语义,它输出的是一个float4类型的变量,它等同于告诉渲染器,把用户的输出颜色存储到一个渲染目标中,有时也使用COLOR代替,考虑到平台的通用性最好使用SV_TARGET
  4. 模型空间:模型空间我们可以理解为模型自身的局部坐标,一个模型上的顶点位置信息,都是依托自身的局部坐标的,但当我们把它放到场景中的时候,就需要我们转换这些信息来使用。
  5. 裁剪空间:可以简单的理解为Unity中摄像机视椎体包围的空间,不在其空间内的信息将会被剔除,保留的信息后续会被投影到屏幕上被看到。

2.2.顶点着色器与片元着色器之间通信

片元着色器是无法直接获取陈模型的顶点信息的,这时就需要我们通过使用结构体作为媒介,有顶点着色器向片元着色器传递一些数据,如:模型法线,纹理坐标等。

2.2.1示例

  1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' 
  2.  
  3. Shader "UnityShaderBoook/vert2frag"{ 
  4. Properties { 
  5. _Color ("Color Tint", Color) = (1, 1, 1, 1
  6. //Properties并非必须,此shader中不声明任何材质属性 
  7. SubShader{ 
  8.  
  9.  
  10. Pass{  
  11.  
  12. CGPROGRAM 
  13. //编译命令,指定顶点/片元着色器处理函数 
  14. #pragma vertex vert 
  15. #pragma fragment frag 
  16.  
  17. uniform fixed4 _Color; 
  18. //结构体用于指定向顶点着色器传递的信息 
  19. struct a2v{ 
  20. float4 vertex:POSITION;//模型的顶点坐标 
  21. float4 normal:NORMAL;//模型的法线信息 
  22. float4 texcoord:TEXCOORD0;//模型的第一套纹理 
  23. }; 
  24.  
  25. //结构体指定了顶点着色器向片元着色器传出的信息 
  26. struct v2f{ 
  27. float4 pos:SV_POSITION;//裁剪空间中的坐标信息 
  28. fixed3 color:COLOR0;//COLOR0语义可用来存储颜色信息 
  29. }; 
  30.  
  31. v2f vert(a2v v) { 
  32. v2f o; 
  33. o.pos = UnityObjectToClipPos(v.vertex); 
  34. o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); 
  35. return o; 
  36.  
  37.  
  38. //接收传入的信息 
  39. fixed4 frag(v2f i):SV_TARGET{ 
  40. fixed3 c = i.color; 
  41. c *= _Color.rgb; 
  42. return fixed4(c, 1.0); 
  43.  
  44. ENDCG 
  45.  
  46.  

注意: 顶点着色器是逐个顶点调用的,片元着色器是逐片元调用的。片元着色器中的输入实际是吧顶点着色器的输出插值后得到的结果。

2.3.Unity内置文件及变量

2.3.1 名词解释

ShaderLab是unity自己封装调用CG/HLSL/GLSL的接口。可以理解为C#与C,一个方便我们使用,一个更加接近底层。
语义是CG/HLSL这些底层提供的用于限定参数含义的字符串。而unity为了方便的对模型的数据进行传输,对一些语义进行了特别规定。如:顶点着色器中用TEXCOORD0来描述texcoord,unity会自动识别出TEXCOORD0的语义,并把模型的第一套纹理坐标信息填充到texcoord中。

2.3.2 Unity支持的常用语义

从应用阶段传递模型数据到顶点着色器

语义描述
POSITION 模型空间中的顶点位置,通常是float4类型
NORMAL 顶点法线,通常是float3类型
TANGENT 顶点切线,通常是float4类型
TEXCOORDn 如TEXCOORD0、TEXCOORD1该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标,依此类推,通常是float2或float4类型
COLOR 顶点颜色,通常是fixed4或float4类型

注意:
其中TEXCOORDn中n的数目是和Shader Model有关的,例如一般在Shader Model 2(即Unity默认编译到的Shader Model版本)和Shader Model 3中,n等于8,而在Shader Model 4和Shader Mode 5中,n等于16。通常情况下,一个模型的纹理坐标组数一般不超过2,即我们往往只使用TEXCOORD0和TEXCOORD1。在Unity内置的数据结构体appdata_full中,它最多使用了6个坐标纹理组。
从顶点着色器传递数据给片元着色器

语义 描述
SV_POSITION 裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量。等同于DirectX9中的POSITION,但最好使用SV_POSITION
COLOR0 通常用于输出第一组顶点颜色,但不是必须的
COLOR1 通常用于输出第二组顶点颜色,但不是必须的
TEXCOORD0~TEXCOORD7 通常用于输出纹理坐标,但不是必须的

注意
上面的语义中,除了SV_POSITION是有特别含义外,其他语义对变量的含义没有明确要求,也就是说,我们可以存储任意值到这些语义描述变量中。通常,如果我们需要把一些自定义的数据从顶点着色器传递给片元着色器,一般选用TEXCOORD0等。
从片元着色器输出时Unity支持的语义

语义 描述
SV_Target 输出值将会存储到渲染目标(render target)中。等同于DirectX9中的COLOR语义,但最好使用SV_Target

注意
一个语义可以使用的寄存器只能处理4个浮点值(float)。因此我们想要定义矩阵类型,如float3×4,float4×4等变量是就需要使用更多的空间。一种方法是,把这些变量拆分成多个变量,例如对于float4×4的矩阵类型,我们可以拆分成四个float类型的变量,每个变量存储矩阵中的一行数据。

三、Unity中三种shader类型

3.1固定管线着色器

3.1.1简介

固定功能着色器为固定功能渲染管线的具体表现。实现较为简单,但是功能单一,效果较差。Unity5.2及以后备抛弃,所有的固定管线着色器都会别Unity编译成对应的顶点片元着色器。

3.1.2效果

 

固定功能
固定功能

 

3.1.3示例代码

  1. Shader "ShaderCookbook/固定功能着色器" 
  2. //-------------------[属性]-------------------------- 
  3. Properties 
  4. _Color("主颜色",Color)=(1,1,1,0
  5. _SpecColor("高光颜色",Color)=(1,1,1,1
  6. _Emission("自发光颜色",Color)=(0,0,0,0
  7. _Shininess("光泽度",Range(0.01,1))=0.7 
  8. _MainTex("基本纹理",2D)="white"{} 
  9. _SecTex("副纹理",2D)="white"{} 
  10. //------------------[子着色器]----------------------- 
  11. SubShader 
  12. Tags{"Queue"="Transparent"} 
  13. //-------------------[通道]-------------------------- 
  14. Pass 
  15. Blend SrcAlpha OneMinusSrcAlpha 
  16. //-------------------[材质]-------------------------- 
  17. Material 
  18. //漫反射光 
  19. Diffuse[_Color] 
  20. //环境光 
  21. Ambient[_Color] 
  22. //光泽度 
  23. Shininess[_Shininess] 
  24. //高光颜色 
  25. Specular[_SpecColor] 
  26. //自发光暗色 
  27. Emission[_Emission] 
  28.  
  29. //开启光照 
  30. Lighting On 
  31. //开启独立镜面反射 
  32. SeparateSpecular On 
  33. //设置纹理并进行纹理混合 
  34. SetTexture[_MainTex] 
  35. Combine texture* primary DOUBLE,texture * primary 
  36.  
  37. SetTexture[_SecTex]{ 
  38. Combine texture * previous double,texture * previous 

3.2表面着色器

表面着色器介绍

3.2.1简介

Unity包装过一层的着色器类型,需要较少的代码量就能达到很好的效果,但由于Unity背后会做很多工作,渲染的代价比较大。

3.2.2效果

 

表面着色器
表面着色器

 

3.2.1示例代码

  1. Shader "ShaderCookbook/简单表面着色器" 
  2. //-------------------------------【属性】-----------------------------------------  
  3. Properties 
  4. {  
  5. _MainTex ("【纹理】Texture", 2D) = "white" {}  
  6. _BumpMap ("【凹凸纹理】Bumpmap", 2D) = "bump" {}  
  7. _RimColor ("【边缘颜色】Rim Color", Color) = (0.17,0.36,0.81,0.0)  
  8. _RimPower ("【边缘颜色强度】Rim Power", Range(0.6,9.0)) = 1.0 
  9. }  
  10. //----------------------------【开始一个子着色器】---------------------------  
  11. SubShader 
  12. {  
  13. //渲染类型为Opaque,不透明  
  14. Tags { "RenderType" = "Opaque"}  
  15. Blend SrcAlpha OneMinusSrcAlpha 
  16. //-------------------开始CG着色器编程语言段-----------------  
  17. CGPROGRAM 
  18. //使用兰伯特光照模式  
  19. #pragma surface surf Lambert  
  20. //输入结构  
  21. struct Input 
  22. {  
  23. float2 uv_MainTex;//纹理贴图  
  24. float2 uv_BumpMap;//法线贴图  
  25. float3 viewDir;//观察方向  
  26. };  
  27. //变量声明  
  28. sampler2D _MainTex;//主纹理  
  29. sampler2D _BumpMap;//凹凸纹理  
  30. float4 _RimColor;//边缘颜色  
  31. float _RimPower;//边缘颜色强度  
  32. //表面着色函数的编写  
  33. void surf (Input IN, inout SurfaceOutput o)  
  34. {  
  35. //表面反射颜色为纹理颜色  
  36. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;  
  37. //表面法线为凹凸纹理的颜色  
  38. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));  
  39. //边缘颜色  
  40. half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));  
  41. //边缘颜色强度  
  42. o.Emission = _RimColor.rgb * pow (rim, _RimPower);  
  43. }  
  44. //-------------------结束CG着色器编程语言段------------------  
  45. ENDCG 
  46. }  
  47. //普通漫反射  
  48. Fallback "Diffuse" 
  49.  

3.3顶点片元着色器

3.3.1 简介

更为复杂,但也更为灵活,可以完成很多复杂的效果,但是需要我们控制渲染的实现细节。

3.3.2效果

 

顶点片元着色器
顶点片元着色器

 

3.3.3示例代码:

  1. shader "ShaderCookbook/简单顶点片元着色器"{ 
  2.  
  3. //-------------------------------【属性】-------------------------------------- 
  4. Properties 
  5. _Color ("Color", Color) = (1.0,1.0,1.0,1.0
  6. _MainTex("MainTex",2D)="white"{} 
  7. _SpecColor ("Specular Color", Color) = (1.0,1.0,1.0,1.0
  8. _Shininess ("Shininess", Float) = 10 
  9. //--------------------------------【子着色器】-------------------------------- 
  10. SubShader 
  11. //-----------子着色器标签---------- 
  12. Tags { "LightMode" = "ForwardBase" } 
  13. //----------------通道--------------- 
  14. Pass 
  15. //-------------------开始CG着色器编程语言段-----------------  
  16. CGPROGRAM 
  17. #pragma vertex vert 
  18. #pragma fragment frag 
  19. #include "UnityCG.cginc" 
  20. //---------------声明变量-------------- 
  21. float4 _Color; 
  22. sampler2D _MainTex; 
  23. float4 _MainTex_ST; 
  24. float4 _SpecColor; 
  25. float _Shininess; 
  26. //--------------定义变量-------------- 
  27. float4 _LightColor0; 
  28. //--------------顶点输入结构体------------- 
  29. struct vertexInput  
  30. float4 vertex : POSITION; 
  31. float3 normal : NORMAL; 
  32. float4 texcoord : TEXCOORD0; 
  33. }; 
  34. //--------------顶点输出结构体------------- 
  35. struct vertexOutput  
  36. float4 pos : SV_POSITION; 
  37. float4 col : COLOR; 
  38. float2 uv :TEXCOORD0; 
  39. }; 
  40. //--------------顶点函数-------------- 
  41. vertexOutput vert(vertexInput v) 
  42. vertexOutput o; 
  43. //获取纹理信息 
  44. o.uv= v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;; 
  45.  
  46. //一些方向 
  47. float3 normalDirection = normalize( mul( float4(v.normal, 0.0), unity_WorldToObject ).xyz ); 
  48. float3 viewDirection = normalize( float3( float4( _WorldSpaceCameraPos.xyz, 1.0) - mul(unity_ObjectToWorld, v.vertex).xyz ) ); 
  49. float3 lightDirection; 
  50. float atten = 1.0
  51. //光照 
  52. lightDirection = normalize(_WorldSpaceLightPos0.xyz); 
  53. float3 diffuseReflection = atten * _LightColor0.xyz * max( 0.0, dot( normalDirection, lightDirection ) ); 
  54. float3 specularReflection = atten * _LightColor0.xyz * _SpecColor.rgb * max( 0.0, dot( normalDirection, lightDirection ) ) * pow( max( 0.0, dot( reflect( -lightDirection, normalDirection ), viewDirection ) ), _Shininess ); 
  55. float3 lightFinal = diffuseReflection + specularReflection + UNITY_LIGHTMODEL_AMBIENT; 
  56. //计算结果 
  57. o.col = float4(lightFinal * _Color.rgb, 1.0);//颜色 
  58. o.pos = UnityObjectToClipPos(v.vertex);//位置 
  59. return o; 
  60. //--------------片段函数--------------- 
  61. float4 frag(vertexOutput i) : COLOR 
  62. fixed4 tex = tex2D(_MainTex,i.uv); 
  63. i.col*=tex; 
  64. return i.col; 
  65. //-------------------结束CG着色器编程语言段------------------ 
  66. ENDCG 
  67. Fallback "Diffuse" 
  68.  

3.3.4 效果

在世界坐标系中顶点坐标超过限定值则不显示,类似切面效果

 

切面
切面

 

3.3.5 示例代码

  1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 
  2.  
  3. Shader "ShaderCookbook/VertexWorldPos" { 
  4. //------------------------属性-------------------------- 
  5. Properties { 
  6. _Color ("Color", Color) = (1,1,1,1
  7. _MainTex ("Albedo (RGB)", 2D) = "white" {} 
  8. _YLimit("YLimit", float)= 0.0 
  9.  
  10.  
  11. //------------------------子shader-------------------------- 
  12. SubShader { 
  13.  
  14. //开启透明度混合 之一 设置渲染类型和序列 
  15. Tags { "RenderType"="AlphaTest" "IgnoreProjector"="True" "Queue"="Transparent"} 
  16.  
  17. Pass{ 
  18. Tags{"LightMode"="ForwardBase"} 
  19. //开启透明度混合 之二 关闭深度写入 
  20. ZWrite off 
  21. //开启透明度混合 之三 设置混合参数 
  22. Blend SrcAlpha OneMinusSrcAlpha 
  23. //关闭剔除,显示双面 
  24. Cull off 
  25.  
  26. //------------------------开启CG-------------------------- 
  27. CGPROGRAM 
  28. #pragma vertex vert 
  29. #pragma fragment frag 
  30.  
  31. sampler2D _MainTex; 
  32. float4 _MainTex_ST; 
  33. fixed4 _Color; 
  34. float _YLimit; 
  35.  
  36. struct a2v{ 
  37. float4 vertex:POSITION; 
  38. float4 texcoord:TEXCOORD0; 
  39. }; 
  40.  
  41. struct v2f{ 
  42. float4 pos:SV_POSITION; 
  43. float4 uv:TEXCOORD; 
  44. float3 worldPos:TEXCOORD1; 
  45. }; 
  46.  
  47. v2f vert(a2v i){ 
  48. v2f v; 
  49. v.pos=UnityObjectToClipPos(i.vertex); 
  50. v.uv.xy=i.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; 
  51. //获取顶点的世界坐标 
  52. v.worldPos=mul(unity_ObjectToWorld,i.vertex); 
  53.  
  54. return v; 
  55.  
  56. fixed4 frag(v2f v):SV_TARGET{ 
  57. fixed4 Col=_Color*tex2D(_MainTex,v.uv.xy); 
  58. //丢弃的未符合的像素 
  59. if(v.worldPos.y>_YLimit) 
  60. discard
  61.  
  62. return Col; 
  63. //------------------------关闭CG-------------------------- 
  64. ENDCG 
  65.  
  66. //------------------------弃子-------------------------- 
  67. FallBack "Diffuse" 
  68.  

3.3.6 效果

这里我们再次使用顶点片元着色器造一个黑洞一样的效果。物体靠近黑洞时会被黑洞吸引拉扯,逐渐缩成一点,在另一端则会逐渐变大出现。

 

enter description here
BlankHole

 

3.3.7 示例代码

  1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 
  2.  
  3. Shader "ShaderCookbook/黑洞" { 
  4. //------------------------属性-------------------------- 
  5. Properties { 
  6. _Color ("Color", Color) = (1,1,1,1
  7. _MainTex ("Albedo (RGB)", 2D) = "white" {} 
  8. _YLimit("YLimit", float)= 0.0 
  9. _Length("Length",float)=1 
  10.  
  11.  
  12. //------------------------子shader-------------------------- 
  13. SubShader { 
  14.  
  15.  
  16. Pass{ 
  17. Tags{"LightMode"="ForwardBase"} 
  18.  
  19. //------------------------开启CG-------------------------- 
  20. CGPROGRAM 
  21. #pragma vertex vert 
  22. #pragma fragment frag 
  23.  
  24. sampler2D _MainTex; 
  25. float4 _MainTex_ST; 
  26. fixed4 _Color; 
  27. float _YLimit; 
  28. float _Length; 
  29.  
  30. struct a2v{ 
  31. float4 vertex:POSITION; 
  32. float4 texcoord:TEXCOORD0; 
  33. }; 
  34.  
  35. struct v2f{ 
  36. float4 pos:SV_POSITION; 
  37. float4 uv:TEXCOORD; 
  38. float3 worldPos:TEXCOORD1; 
  39. }; 
  40.  
  41. v2f vert(a2v i){ 
  42. v2f v; 
  43. //获取模型顶点坐标在世界坐标下的位置 
  44. v.worldPos=mul(unity_ObjectToWorld,i.vertex); 
  45.  
  46. //如果其距离我们设定的临界值_YLimit小于_Length长度,我们将会按比例缩放其X,Z轴的顶点位置 
  47. if(distance(v.worldPos.y,_YLimit)<_Length) 
  48. float s=(distance(v.worldPos.y,_YLimit)/_Length); 
  49. i.vertex.xz*=s; 
  50.  
  51. //将修改后的顶点坐标转换为裁剪坐标 
  52. v.pos=UnityObjectToClipPos(i.vertex); 
  53. //获取图形的UI 
  54. v.uv.xy=i.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; 
  55. return v; 
  56.  
  57. fixed4 frag(v2f v):SV_TARGET{ 
  58. fixed4 Col=_Color*tex2D(_MainTex,v.uv.xy); 
  59. return Col; 
  60. //------------------------关闭CG-------------------------- 
  61. ENDCG 
  62.  
  63. //------------------------弃子-------------------------- 
  64. FallBack "Diffuse" 
  65.  

開簾頓覺春風暖 滿紙淋漓白雲聲——杨玉香

X Shader实例

X.1.1 热度图

X.1.1.1 效果

运行结果:

 

渐变颜色效果
渐变颜色效果

 

 

梯度颜色效果
梯度颜色效果

热度图纹理:

 

 

渐变ramp图片
渐变ramp图片

 

 

梯度颜色效果
梯度颜色效果

 

X.1.1.2 示例代码

  1.  
  2. Shader "Ellioman/Heatmap" 
  3. //-----------------------属性-------------------- 
  4. Properties 
  5. _HeatTex("Texture", 2D) = "white" {} 
  6.  
  7. //----------------------子shader----------------- 
  8. SubShader 
  9. //设置为透明格式 
  10. Tags{ "Queue" = "Transparent" } 
  11.  
  12. //设置Blend类型 
  13. Blend SrcAlpha OneMinusSrcAlpha  
  14.  
  15. //----------------------通道---------------------- 
  16. Pass 
  17. //-------------------开始CG------------- 
  18. CGPROGRAM 
  19. #pragma vertex vert 
  20. #pragma fragment frag 
  21.  
  22.  
  23. struct vertInput 
  24. float4 pos : POSITION; 
  25. }; 
  26.  
  27. struct vertOutput 
  28. float4 pos : POSITION; 
  29. fixed3 worldPos : TEXCOORD1; 
  30. }; 
  31.  
  32. //自定义变量 
  33. uniform int _Points_Length = 0
  34.  
  35. //cg中只能声明确定长度的数组 
  36. uniform float3 _Points[100]; // (x, y, z) = 位置 
  37. uniform float2 _Properties[100]; // x = 半径, y = 强度 
  38. sampler2D _HeatTex; 
  39.  
  40. vertOutput vert(vertInput input) 
  41. vertOutput o; 
  42. o.pos = UnityObjectToClipPos(input.pos); 
  43. o.worldPos = mul(unity_ObjectToWorld, input.pos).xyz; 
  44. return o; 
  45.  
  46. half4 frag(vertOutput output) : COLOR 
  47. half h = 0
  48. for (int i = 0; i < _Points_Length; i++) 
  49. // 计算每一个分布点位置与当前像素的距离 
  50. half di = distance(output.worldPos, _Points[i].xyz); 
  51.  
  52. half ri = _Properties[i].x; 
  53.  
  54. //抛弃超过半径分布点的影响,距离output.worldPos越近,其值越高 
  55. half hi = 1 - saturate(di / ri); 
  56. //叠加又分布点位置_Points[i].xyz与output.worldPos之间的距离及x半径和y强度值得到的值 
  57. h += hi * _Properties[i].y; 
  58.  
  59.  
  60. h = saturate(h);//限定范围至[0,1] 
  61.  
  62. //通过ramp图,类似toonshader的方式获得阶梯式的颜色效果 
  63. half4 col = tex2D(_HeatTex, fixed2(h, 0.5)); 
  64. return col; 
  65. //-------------------开始CG------------- 
  66. ENDCG 
  67. Fallback "Diffuse" 

X.1.1.3 分析

首先,我们需要将ramp图片的wrapMode格式设为Clamp。
Texture.wrapMode 循环模式:

  1. TextureWrapMode.Clamp:设置纹理充满拉伸使用
  2. TextureWrapMode.Repeat:纹理重复平铺使用

如果采用Repeat,那么等于U>=1的情况就会用纹理图在右边在平铺一张图。

我们使用了类似使用ramp纹理制作toon卡通风格shader效果的方式,来用uv中的u指获得颜色的强度。
uv
这里在对blend混合命令做一个笔记:
Blend SrcAlpha OneMinusSrcAlpha // 传统透明度
Blend One OneMinusSrcAlpha // 预乘透明度
Blend One One // 叠加
Blend OneMinusDstColor One // 柔和叠加
Blend DstColor Zero // 相乘——正片叠底

那海和天空之间星星消失的地方
连时间也没有确切的命运——杨炼

X.1.2 山崖

X.1.2.1 效果

这里主要实现了山崖上草地覆盖面积,高度还有山石高光,平滑度,法线强度的控制。

X1.2.2 示例代码

  1. Shader "Unlit/Cliff" 
  2. //---------------参数--------------------------- 
  3. Properties 
  4. _MainTex ("Texture", 2D) = "white" {} 
  5. _MainNormal("MainNormal",2D)="white"{} 
  6. _GrassTex ("GrassTex", 2D) = "white" {} 
  7. _GrassNormal ("GrassNormal", 2D) = "white" {} 
  8.  
  9. _Smooth("smooth",Range(0,1))=1 
  10. _Gloss("Gloss",Range(0,128))=96 
  11.  
  12. _Height("Height",float)=0.5 
  13.  
  14. _Offset("Offset",float)=0.5 
  15. _BumpScale("bumpscale",float)=0.5 
  16.  
  17. //-------------subshader--------------------------- 
  18. SubShader 
  19. Tags { "LightMode"="ForwardBase" } 
  20. LOD 100 
  21.  
  22. Pass 
  23. CGPROGRAM 
  24. #pragma vertex vert 
  25. #pragma fragment frag 
  26.  
  27. #include "UnityCG.cginc" 
  28. #include "Lighting.cginc" 
  29.  
  30.  
  31. //-----------传递数据用的结构体--------------------------- 
  32. struct appdata 
  33. float4 vertex : POSITION; 
  34. float4 uv : TEXCOORD0; 
  35. float3 normal:NORMAL; 
  36. float4 tangent:TANGENT; 
  37. }; 
  38.  
  39. struct v2f 
  40. float4 uv : TEXCOORD0; 
  41. float4 vertex : SV_POSITION; 
  42. float3 lightDir:TEXCOORD1; 
  43. float3 worldPos:TEXCOORD2; 
  44. float3 tanView:TEXCOORD3; 
  45. }; 
  46.  
  47. //----------传递参数数据--------------------------- 
  48. sampler2D _MainTex; 
  49. float4 _MainTex_ST; 
  50. sampler2D _GrassTex; 
  51. float4 _GrassTex_ST; 
  52. sampler2D _MainNormal; 
  53. sampler2D _GrassNormal; 
  54.  
  55. float _Offset; 
  56. float _BumpScale; 
  57. float _Height; 
  58. float _Gloss; 
  59. float _Smooth; 
  60.  
  61. v2f vert (appdata v) 
  62. v2f o; 
  63. o.vertex = UnityObjectToClipPos(v.vertex); 
  64. o.uv.xy = TRANSFORM_TEX(v.uv.xy, _MainTex); 
  65. o.uv.zw = TRANSFORM_TEX(v.uv.zw, _MainTex); 
  66.  
  67. //----------unity宏定义,用于得到从模型空间矩阵到切线空间矩阵rotation----- 
  68. TANGENT_SPACE_ROTATION; 
  69.  
  70. //------------通过上一步得到rotation用于将模型空间下的光照法线转换到法线贴图对应的空间中去,这里的法线贴图位于切线空间---- 
  71. o.worldPos=UnityObjectToWorldDir(v.vertex); 
  72. //------------需要借助法线贴图计算光照,这里计算出需要用到符合法线贴图空间的光照方向和视角方向 
  73. o.lightDir=normalize(mul(rotation,ObjSpaceLightDir(v.vertex)).xyz); 
  74. o.tanView=normalize(mul(rotation,ObjSpaceViewDir(v.vertex))); 
  75. return o; 
  76.  
  77. fixed4 frag (v2f i) : SV_Target 
  78. float3 halfDir=normalize(i.tanView+i.lightDir); 
  79.  
  80. fixed4 col = tex2D(_MainTex, i.uv.xy); 
  81. fixed4 grass=tex2D(_GrassTex,i.uv.zw); 
  82.  
  83. float3 colNormal=UnpackNormal(tex2D(_MainNormal,i.uv.xy))*_BumpScale; 
  84. float3 GrassNormal=UnpackNormal(tex2D(_GrassNormal,i.uv.zw))*_BumpScale; 
  85.  
  86. colNormal.z=sqrt(1.0-saturate(dot(colNormal.xy,colNormal.xy))); 
  87. GrassNormal.z=sqrt(1.0-saturate(dot(GrassNormal.xy,GrassNormal.xy))); 
  88.  
  89.  
  90. float angleY=1-saturate(dot(UnityObjectToWorldDir(colNormal),float3(0,1,0) )+_Offset); 
  91.  
  92. angleY-=i.worldPos.y>_Height?0:i.worldPos.y-_Height; 
  93.  
  94. fixed4 finalColor=lerp(grass,col,angleY); 
  95.  
  96. //---------------lambert光照--------------------------- 
  97. float3 diffuse=finalColor*max(0.0,dot(i.lightDir,colNormal))*_LightColor0.xyz; 
  98. //---------------Blin-phong光照--------------------------- 
  99. float3 specular= _LightColor0.rgb * pow(saturate(dot(halfDir, colNormal)), _Gloss)*_Smooth; 
  100.  
  101. return fixed4(diffuse+specular+UNITY_LIGHTMODEL_AMBIENT.xyz*finalColor,1); 
  102. ENDCG 
  103. }//end pass 
  104. }//end subshader 
  105.  
  106. Fallback "Diffuse"//阴影 
  107.  
posted @ 2018-07-22 12:12  世纪末の魔术师  阅读(1157)  评论(0编辑  收藏  举报