Unity Shader 学习之旅

unityshader图形图像

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

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

一、渲染流水线

示例图

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. Shader "ShaderName"{ 

  2. Properties{ 

  3. //属性,暴露在inspector面板上 




  4. SubShader{ 

  5. //针对显卡A的SubShader 

  6. Pass{ 

  7. //设置渲染状态和标签 


  8. CGPROGRAM 

  9. //该代码的编译指令,如: 

  10. #pragma vertex vert 

  11. #pragma fragment frag 


  12. //CG代码 



  13. ENDCG 

  14. //其他设置 






  15. SubShader{ 

  16. //针对显卡B的SubShader 




  17. //上述SubShader都失败后调用回调的Unity Shader 

  18. Fallback "VertexLit" 



2.1.2一个简单的示例

  1. Shader "UnityShaderBoook/SimplerShader"{ 


  2. //Properties并非必须,此shader中不声明任何材质属性 

  3. SubShader{ 


  4. Pass{  


  5. CGPROGRAM 

  6. //编译命令,指定顶点/片元着色器处理函数 

  7. #pragma vertex vert 

  8. #pragma fragment frag 


  9. //顶点着色器代码,逐个顶点执行 

  10. //使用POSITION语义指定函数的输入参数v为顶点的位置信息 

  11. //SV_POSITION语义指明函数的返回值为顶点着色器输出的是裁剪空间中的位置 

  12. //UnityObjectToClipPos则是Unity内置的模型-观察-投影矩阵,帮我们完成操作(mul(UNITY_MATRIX_MVP,*)' 现已更新为'UnityObjectToClipPos(*)') 

  13. float4 vert(float4 v:POSITION):SV_POSITION{ 

  14. return UnityObjectToClipPos(v); 




  15. //片元着色器,逐个面片执行 

  16. //此片元着色器没有任何输入参数 

  17. //SV_TARGET语义指定函数输出颜色存储一个渲染目标上 

  18. //这里仅仅简单的返回表示白色的fixed4的变量 

  19. fixed4 frag():SV_TARGET{ 

  20. return fixed4(1.0,1.0,1.0,1.0); 




  21. ENDCG 









当我们把使用该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. Shader "UnityShaderBoook/vert2frag"{ 

  3. Properties { 

  4. _Color ("Color Tint", Color) = (1, 1, 1, 1) 



  5. //Properties并非必须,此shader中不声明任何材质属性 

  6. SubShader{ 



  7. Pass{  


  8. CGPROGRAM 

  9. //编译命令,指定顶点/片元着色器处理函数 

  10. #pragma vertex vert 

  11. #pragma fragment frag 


  12. uniform fixed4 _Color; 

  13. //结构体用于指定向顶点着色器传递的信息 

  14. struct a2v{ 

  15. float4 vertex:POSITION;//模型的顶点坐标 

  16. float4 normal:NORMAL;//模型的法线信息 

  17. float4 texcoord:TEXCOORD0;//模型的第一套纹理 

  18. }; 


  19. //结构体指定了顶点着色器向片元着色器传出的信息 

  20. struct v2f{ 

  21. float4 pos:SV_POSITION;//裁剪空间中的坐标信息 

  22. fixed3 color:COLOR0;//COLOR0语义可用来存储颜色信息 

  23. }; 


  24. v2f vert(a2v v) { 

  25. v2f o; 

  26. o.pos = UnityObjectToClipPos(v.vertex); 

  27. o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5); 

  28. return o; 





  29. //接收传入的信息 

  30. fixed4 frag(v2f i):SV_TARGET{ 

  31. fixed3 c = i.color; 

  32. c *= _Color.rgb; 

  33. return fixed4(c, 1.0); 




  34. ENDCG 









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

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. Lighting On 

  30. //开启独立镜面反射 

  31. SeparateSpecular On 

  32. //设置纹理并进行纹理混合 

  33. SetTexture[_MainTex] 



  34. Combine texture* primary DOUBLE,texture * primary 




  35. SetTexture[_SecTex]{ 

  36. 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" 




3.3顶点片元着色器

3.3.1 简介

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

3.3.2效果

顶点片元着色器

3.3.3示例代码:

  1. shader "ShaderCookbook/简单顶点片元着色器"{ 


  2. //-------------------------------【属性】-------------------------------------- 

  3. Properties 



  4. _Color ("Color", Color) = (1.0,1.0,1.0,1.0) 

  5. _MainTex("MainTex",2D)="white"{} 

  6. _SpecColor ("Specular Color", Color) = (1.0,1.0,1.0,1.0) 

  7. _Shininess ("Shininess", Float) = 10 



  8. //--------------------------------【子着色器】-------------------------------- 

  9. SubShader 



  10. //-----------子着色器标签---------- 

  11. Tags { "LightMode" = "ForwardBase" } 

  12. //----------------通道--------------- 

  13. Pass 



  14. //-------------------开始CG着色器编程语言段-----------------  

  15. CGPROGRAM 

  16. #pragma vertex vert 

  17. #pragma fragment frag 

  18. #include "UnityCG.cginc" 

  19. //---------------声明变量-------------- 

  20. float4 _Color; 

  21. sampler2D _MainTex; 

  22. float4 _MainTex_ST; 

  23. float4 _SpecColor; 

  24. float _Shininess; 

  25. //--------------定义变量-------------- 

  26. float4 _LightColor0; 

  27. //--------------顶点输入结构体------------- 

  28. struct vertexInput  



  29. float4 vertex : POSITION; 

  30. float3 normal : NORMAL; 

  31. float4 texcoord : TEXCOORD0; 

  32. }; 

  33. //--------------顶点输出结构体------------- 

  34. struct vertexOutput  



  35. float4 pos : SV_POSITION; 

  36. float4 col : COLOR; 

  37. float2 uv :TEXCOORD0; 

  38. }; 

  39. //--------------顶点函数-------------- 

  40. vertexOutput vert(vertexInput v) 



  41. vertexOutput o; 

  42. //获取纹理信息 

  43. o.uv= v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;; 


  44. //一些方向 

  45. float3 normalDirection = normalize( mul( float4(v.normal, 0.0), unity_WorldToObject ).xyz ); 

  46. float3 viewDirection = normalize( float3( float4( _WorldSpaceCameraPos.xyz, 1.0) - mul(unity_ObjectToWorld, v.vertex).xyz ) ); 

  47. float3 lightDirection; 

  48. float atten = 1.0; 

  49. //光照 

  50. lightDirection = normalize(_WorldSpaceLightPos0.xyz); 

  51. float3 diffuseReflection = atten * _LightColor0.xyz * max( 0.0, dot( normalDirection, lightDirection ) ); 

  52. float3 specularReflection = atten * _LightColor0.xyz * _SpecColor.rgb * max( 0.0, dot( normalDirection, lightDirection ) ) * pow( max( 0.0, dot( reflect( -lightDirection, normalDirection ), viewDirection ) ), _Shininess ); 

  53. float3 lightFinal = diffuseReflection + specularReflection + UNITY_LIGHTMODEL_AMBIENT; 

  54. //计算结果 

  55. o.col = float4(lightFinal * _Color.rgb, 1.0);//颜色 

  56. o.pos = UnityObjectToClipPos(v.vertex);//位置 

  57. return o; 



  58. //--------------片段函数--------------- 

  59. float4 frag(vertexOutput i) : COLOR 



  60. fixed4 tex = tex2D(_MainTex,i.uv); 

  61. i.col*=tex; 

  62. return i.col; 



  63. //-------------------结束CG着色器编程语言段------------------ 

  64. ENDCG 





  65. Fallback "Diffuse" 




3.3.4 效果

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

切面

3.3.5 示例代码

  1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 


  2. Shader "ShaderCookbook/VertexWorldPos" { 

  3. //------------------------属性-------------------------- 

  4. Properties { 

  5. _Color ("Color", Color) = (1,1,1,1) 

  6. _MainTex ("Albedo (RGB)", 2D) = "white" {} 

  7. _YLimit("YLimit", float)= 0.0 





  8. //------------------------子shader-------------------------- 

  9. SubShader { 


  10. //开启透明度混合 之一 设置渲染类型和序列 

  11. Tags { "RenderType"="AlphaTest" "IgnoreProjector"="True" "Queue"="Transparent"} 


  12. Pass{ 

  13. Tags{"LightMode"="ForwardBase"} 

  14. //开启透明度混合 之二 关闭深度写入 

  15. ZWrite off 

  16. //开启透明度混合 之三 设置混合参数 

  17. Blend SrcAlpha OneMinusSrcAlpha 

  18. //关闭剔除,显示双面 

  19. Cull off 


  20. //------------------------开启CG-------------------------- 

  21. CGPROGRAM 

  22. #pragma vertex vert 

  23. #pragma fragment frag 


  24. sampler2D _MainTex; 

  25. float4 _MainTex_ST; 

  26. fixed4 _Color; 

  27. float _YLimit; 


  28. struct a2v{ 

  29. float4 vertex:POSITION; 

  30. float4 texcoord:TEXCOORD0; 

  31. }; 


  32. struct v2f{ 

  33. float4 pos:SV_POSITION; 

  34. float4 uv:TEXCOORD; 

  35. float3 worldPos:TEXCOORD1; 

  36. }; 


  37. v2f vert(a2v i){ 

  38. v2f v; 

  39. v.pos=UnityObjectToClipPos(i.vertex); 

  40. v.uv.xy=i.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; 

  41. //获取顶点的世界坐标 

  42. v.worldPos=mul(unity_ObjectToWorld,i.vertex); 


  43. return v; 




  44. fixed4 frag(v2f v):SV_TARGET{ 

  45. fixed4 Col=_Color*tex2D(_MainTex,v.uv.xy); 

  46. //丢弃的未符合的像素 

  47. if(v.worldPos.y>_YLimit) 

  48. discard; 


  49. return Col; 



  50. //------------------------关闭CG-------------------------- 

  51. ENDCG 






  52. //------------------------弃子-------------------------- 

  53. FallBack "Diffuse" 





3.3.6 效果

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

BlankHole

3.3.7 示例代码

  1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' 


  2. Shader "ShaderCookbook/黑洞" { 

  3. //------------------------属性-------------------------- 

  4. Properties { 

  5. _Color ("Color", Color) = (1,1,1,1) 

  6. _MainTex ("Albedo (RGB)", 2D) = "white" {} 

  7. _YLimit("YLimit", float)= 0.0 

  8. _Length("Length",float)=1 





  9. //------------------------子shader-------------------------- 

  10. SubShader { 



  11. Pass{ 

  12. Tags{"LightMode"="ForwardBase"} 


  13. //------------------------开启CG-------------------------- 

  14. CGPROGRAM 

  15. #pragma vertex vert 

  16. #pragma fragment frag 


  17. sampler2D _MainTex; 

  18. float4 _MainTex_ST; 

  19. fixed4 _Color; 

  20. float _YLimit; 

  21. float _Length; 


  22. struct a2v{ 

  23. float4 vertex:POSITION; 

  24. float4 texcoord:TEXCOORD0; 

  25. }; 


  26. struct v2f{ 

  27. float4 pos:SV_POSITION; 

  28. float4 uv:TEXCOORD; 

  29. float3 worldPos:TEXCOORD1; 

  30. }; 


  31. v2f vert(a2v i){ 

  32. v2f v; 

  33. //获取模型顶点坐标在世界坐标下的位置 

  34. v.worldPos=mul(unity_ObjectToWorld,i.vertex); 


  35. //如果其距离我们设定的临界值_YLimit小于_Length长度,我们将会按比例缩放其X,Z轴的顶点位置 

  36. if(distance(v.worldPos.y,_YLimit)<_Length) 



  37. float s=(distance(v.worldPos.y,_YLimit)/_Length); 

  38. i.vertex.xz*=s; 




  39. //将修改后的顶点坐标转换为裁剪坐标 

  40. v.pos=UnityObjectToClipPos(i.vertex); 

  41. //获取图形的UI 

  42. v.uv.xy=i.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw; 

  43. return v; 




  44. fixed4 frag(v2f v):SV_TARGET{ 

  45. fixed4 Col=_Color*tex2D(_MainTex,v.uv.xy); 

  46. return Col; 



  47. //------------------------关闭CG-------------------------- 

  48. ENDCG 






  49. //------------------------弃子-------------------------- 

  50. FallBack "Diffuse" 





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

X Shader实例

X.1.1 热度图

X.1.1.1 效果

运行结果:

渐变颜色效果

梯度颜色效果

热度图纹理:

渐变ramp图片

梯度颜色效果

X.1.1.2 示例代码


  1. Shader "Ellioman/Heatmap" 



  2. //-----------------------属性-------------------- 

  3. Properties 



  4. _HeatTex("Texture", 2D) = "white" {} 




  5. //----------------------子shader----------------- 

  6. SubShader 



  7. //设置为透明格式 

  8. Tags{ "Queue" = "Transparent" } 


  9. //设置Blend类型 

  10. Blend SrcAlpha OneMinusSrcAlpha  


  11. //----------------------通道---------------------- 

  12. Pass 



  13. //-------------------开始CG------------- 

  14. CGPROGRAM 

  15. #pragma vertex vert 

  16. #pragma fragment frag 



  17. struct vertInput 



  18. float4 pos : POSITION; 

  19. }; 


  20. struct vertOutput 



  21. float4 pos : POSITION; 

  22. fixed3 worldPos : TEXCOORD1; 

  23. }; 


  24. //自定义变量 

  25. uniform int _Points_Length = 0; 


  26. //cg中只能声明确定长度的数组 

  27. uniform float3 _Points[100]; // (x, y, z) = 位置 

  28. uniform float2 _Properties[100]; // x = 半径, y = 强度 

  29. sampler2D _HeatTex; 


  30. vertOutput vert(vertInput input) 



  31. vertOutput o; 

  32. o.pos = UnityObjectToClipPos(input.pos); 

  33. o.worldPos = mul(unity_ObjectToWorld, input.pos).xyz; 

  34. return o; 




  35. half4 frag(vertOutput output) : COLOR 



  36. half h = 0; 

  37. for (int i = 0; i < _Points_Length; i++) 



  38. // 计算每一个分布点位置与当前像素的距离 

  39. half di = distance(output.worldPos, _Points[i].xyz); 


  40. half ri = _Properties[i].x; 


  41. //抛弃超过半径分布点的影响,距离output.worldPos越近,其值越高 

  42. half hi = 1 - saturate(di / ri); 

  43. //叠加又分布点位置_Points[i].xyz与output.worldPos之间的距离及x半径和y强度值得到的值 

  44. h += hi * _Properties[i].y; 





  45. h = saturate(h);//限定范围至[0,1] 


  46. //通过ramp图,类似toonshader的方式获得阶梯式的颜色效果 

  47. half4 col = tex2D(_HeatTex, fixed2(h, 0.5)); 

  48. return col; 



  49. //-------------------开始CG------------- 

  50. ENDCG 





  51. 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指获得颜色的强度。

这里在对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. _Smooth("smooth",Range(0,1))=1 

  9. _Gloss("Gloss",Range(0,128))=96 


  10. _Height("Height",float)=0.5 


  11. _Offset("Offset",float)=0.5 

  12. _BumpScale("bumpscale",float)=0.5 




  13. //-------------subshader--------------------------- 

  14. SubShader 



  15. Tags { "LightMode"="ForwardBase" } 

  16. LOD 100 


  17. Pass 



  18. CGPROGRAM 

  19. #pragma vertex vert 

  20. #pragma fragment frag 


  21. #include "UnityCG.cginc" 

  22. #include "Lighting.cginc" 



  23. //-----------传递数据用的结构体--------------------------- 

  24. struct appdata 



  25. float4 vertex : POSITION; 

  26. float4 uv : TEXCOORD0; 

  27. float3 normal:NORMAL; 

  28. float4 tangent:TANGENT; 

  29. }; 


  30. struct v2f 



  31. float4 uv : TEXCOORD0; 

  32. float4 vertex : SV_POSITION; 

  33. float3 lightDir:TEXCOORD1; 

  34. float3 worldPos:TEXCOORD2; 

  35. float3 tanView:TEXCOORD3; 

  36. }; 


  37. //----------传递参数数据--------------------------- 

  38. sampler2D _MainTex; 

  39. float4 _MainTex_ST; 

  40. sampler2D _GrassTex; 

  41. float4 _GrassTex_ST; 

  42. sampler2D _MainNormal; 

  43. sampler2D _GrassNormal; 


  44. float _Offset; 

  45. float _BumpScale; 

  46. float _Height; 

  47. float _Gloss; 

  48. float _Smooth; 


  49. v2f vert (appdata v) 



  50. v2f o; 

  51. o.vertex = UnityObjectToClipPos(v.vertex); 

  52. o.uv.xy = TRANSFORM_TEX(v.uv.xy, _MainTex); 

  53. o.uv.zw = TRANSFORM_TEX(v.uv.zw, _MainTex); 


  54. //----------unity宏定义,用于得到从模型空间矩阵到切线空间矩阵rotation----- 

  55. TANGENT_SPACE_ROTATION; 


  56. //------------通过上一步得到rotation用于将模型空间下的光照法线转换到法线贴图对应的空间中去,这里的法线贴图位于切线空间---- 

  57. o.worldPos=UnityObjectToWorldDir(v.vertex); 

  58. //------------需要借助法线贴图计算光照,这里计算出需要用到符合法线贴图空间的光照方向和视角方向 

  59. o.lightDir=normalize(mul(rotation,ObjSpaceLightDir(v.vertex)).xyz); 

  60. o.tanView=normalize(mul(rotation,ObjSpaceViewDir(v.vertex))); 

  61. return o; 




  62. fixed4 frag (v2f i) : SV_Target 



  63. float3 halfDir=normalize(i.tanView+i.lightDir); 


  64. fixed4 col = tex2D(_MainTex, i.uv.xy); 

  65. fixed4 grass=tex2D(_GrassTex,i.uv.zw); 


  66. float3 colNormal=UnpackNormal(tex2D(_MainNormal,i.uv.xy))*_BumpScale; 

  67. float3 GrassNormal=UnpackNormal(tex2D(_GrassNormal,i.uv.zw))*_BumpScale; 


  68. colNormal.z=sqrt(1.0-saturate(dot(colNormal.xy,colNormal.xy))); 

  69. GrassNormal.z=sqrt(1.0-saturate(dot(GrassNormal.xy,GrassNormal.xy))); 



  70. float angleY=1-saturate(dot(UnityObjectToWorldDir(colNormal),float3(0,1,0) )+_Offset); 


  71. angleY-=i.worldPos.y>_Height?0:i.worldPos.y-_Height; 


  72. fixed4 finalColor=lerp(grass,col,angleY); 


  73. //---------------lambert光照--------------------------- 

  74. float3 diffuse=finalColor*max(0.0,dot(i.lightDir,colNormal))*_LightColor0.xyz; 

  75. //---------------Blin-phong光照--------------------------- 

  76. float3 specular= _LightColor0.rgb * pow(saturate(dot(halfDir, colNormal)), _Gloss)*_Smooth; 


  77. return fixed4(diffuse+specular+UNITY_LIGHTMODEL_AMBIENT.xyz*finalColor,1); 



  78. ENDCG 

  79. }//end pass 

  80. }//end subshader 


  81. Fallback "Diffuse"//阴影 




Unity Shader 学习之旅的更多相关文章

  1. Unity Shader 学习之旅之SurfaceShader

    Unity Shader 学习之旅之SurfaceShader unity shader 图形图像  如果大地的每个角落都充满了光明 谁还需要星星,谁还会 在夜里凝望 寻找遥远的安慰——江河 官方文档 ...

  2. 第四章 开始Unity Shader学习之旅(2)

    目录 1. 强大的援手:Unity提供的内置文件和变量 1.1 内置的包含文件 1.2 内置的变量 2. Unity提供的Cg/HLSL语义 2.1 什么是语义 2.2 Unity支持的语义 2.3 ...

  3. 第四章 开始Unity Shader学习之旅(1)

    1. 一个最简单的顶点/片元着色器 现在,我们正式开始学习如何编写Unity Shader,更准确的说是,学习如何编写顶点/片元着色器 2.顶点/片元着色器的基本结构 我们在以前已经讲过了Unity ...

  4. Unity Shader入门精要学习笔记 - 第5章 开始 Unity Shader 学习之旅

    一个顶点/片元 着色器的结构大概如下: Shader "MyShaderName" { Properties { //属性 } SubShader { //针对显卡A的SubSha ...

  5. 第四章 开始Unity Shader学习之旅(3)

    1. 程序员的烦恼:Debug 调试(debug),大概是所有程序员的噩梦.而不幸的是,对一个Shader进行调试更是噩梦中的噩梦.这也是造成Shader难写的原因之一--如果发现得到的效果不对,我们 ...

  6. Unity Shader学习笔记-1

    本篇文章是对Unity Shader入门精要的学习笔记,插图大部分来自冯乐乐女神的github 如果有什么说的不正确的请批评指正 目录 渲染流水线 流程图 Shader作用 屏幕映射 三角形遍历 两大 ...

  7. 【Unity Shader学习笔记】Unity基础纹理-单张纹理

    1 单张纹理 1.1 纹理 使用纹理映射(Texture Mapping)技术,我们把一张图片逐纹素(Texel)地控制模型的颜色. 美术人员建模时,会在建模软件中利用纹理展开技术把纹理映射坐标(Te ...

  8. Unity shader学习之屏幕后期处理效果之高斯模糊

    高斯模糊,见 百度百科. 也使用卷积来实现,每个卷积元素的公式为: 其中б是标准方差,一般取值为1. x和y分别对应当前位置到卷积中心的整数距离. 由于需要对高斯核中的权重进行归一化,即使所有权重相加 ...

  9. 【Unity Shader学习笔记】Unity基础纹理-渐变纹理

    纹理可以用来存储任何表面属性. 可以通过使用渐变纹理来实现插画风格的渲染效果. 这项技术是由Valve公司提出的.Valve使用它来渲染游戏中具有插画风格的角色. 我们使用半兰伯特模型计算漫反射. 因 ...

随机推荐

  1. [MongoDB]------windos下的安装部署与基础使用

    1.安装 首先前往官网进行下载,这里贴个地址https://www.mongodb.com/download-center#community 点击大大的原谅色的DOWNLOAD(msi)按钮进行下载 ...

  2. PHP设计模式系列 - 解释器模式

    解释器模式 解释器模式 用于分析一个实体的关键元素,并且针对每个元素提供自己的解释或相应动作.解释器模式非常常用,比如PHP的模板引擎 就是非常常见的一种解释器模. 代码: <?php //解释 ...

  3. Codeforces 1118 F2. Tree Cutting (Hard Version) 优先队列+树形dp

    题目要求将树分为k个部分,并且每种颜色恰好在同一个部分内,问有多少种方案. 第一步显然我们需要知道哪些点一定是要在一个部分内的,也就是说要求每一个最小的将所有颜色i的点连通的子树. 这一步我们可以将所 ...

  4. sort与sorted

    Python list内置sort()方法用来排序,也可以用python内置的全局sorted()方法来对可迭代的序列排序生成新的序列. 1.list.sort()方法仅被定义在list中,相反地so ...

  5. find 的一些用法

    find的一些用法 例1:find . -type f -exec chmod -R 644 {} \ ;   #{}代表签名的输出,\;代表结束命令操作结束 例2: find -print0 |xa ...

  6. windows同时安装python2和python3

    系统之前安装了python2.7,现在准备装个python3.6 1:首先下载一个python3.6适合windows32位的包python-3.6.5.exe 然后直接默认双击安装,安装的时候勾选a ...

  7. locust

    from locust import HttpLocust,TaskSet,task class UserVue(TaskSet): #tasks = {buy:1,consume:2} #设置权重 ...

  8. FFmpeg中几个结构体的意义

    AVCodec是存储编解码器信息的结构体,特指一个特定的解码器,比如H264编码器的名字,ID,支持的视频格式,支持的采样率等: AVCodecContext是一个描述编解码器采用的具体参数,比如采用 ...

  9. Lambda 表达式语法

    本主题介绍 lambda 表达式的语法. 它演示提供 lambda 表达式的结构元素的示例,这些元素与示例. Lambda 表达式语法 下面用于定义显示语法,ISO C++11 从标准,lambda ...

  10. #leetcode刷题之路42-接雨水

    给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水.上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 ...