当前位置:   article > 正文

UnityShader入门精要--Unity中的基础光照_unity shader lightmode

unity shader lightmode

光线从光源发射出来,通常把光源当作一个没有体积的点,用l表示方向,用辐照度量化光,辐照度与cosθ成正比,cosθ可以通过光源方向l与表面发现n的点积来得到(games101中有详细解释光照模型)。

散射只改变光的方向,不改变光线的密度与颜色,而吸收只改变光线的密度与颜色。散射到物体内部:折射或投射;散射到物体外部:反射。

为了区分不同的反射方向,高光反射表示物体表面如何反射光线,漫反射表示多少光线会被折射、吸收、散射出表面。出射度描述出射光线的数量与方向,辐照度和出射度满足线性关系。

着色指的是根据材质属性(如漫反射属性)、光源信息、计算某个观察方向出射度的过程。

标准光照模型

自发光:描述当给定一个方向时,一个表面本身会向该方向发射多少辐射量,如果没有使用全局光照,自发光的表面并不会真的照亮周围的物体。

高光反射:描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。

漫反射:用于描述当光线从光源照射到漫反射模型表面时,该表面会向每个方向散射多少辐射量。

环境光:描述其他所有的间接光照。

布林冯模型此处不详细记录,后续上传games101笔记时有详细解释。Blinn-Phong模型是各向同性的,而有些物体表面是各向异性反射材质的,如拉丝金属毛发等,需要基于物理的光照模型。

逐像素光照

在片元着色器中计算,以每个像素为基础得到他的法线,然后进行光照计算,在面片间对顶点发现插值得技术称为Phong着色。

逐顶点光照

Gouraud着色,在顶点着色器中计算,依赖线性插值来得到像素光照,因此当光照模型中有非线性的计算会出现问题。

漫反射光照模型实现

        计算漫反射需要知道四个参数:入射光线的颜色与强度Clight,材质的漫反射系数mdiffuse,表面法线n以及光源方向l。

        为了防止点积结果为负值,需要进行max操作,CG的函数saturate(x)提供了这个功能,用于将x截取在[0,1]范围内,如果x是矢量,会对每一个分量进行同样的操作。

逐顶点光照

代码如下,说明在注释中

  1. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
  2. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
  3. Shader "Shaders/DiffuseVertexLevel"
  4. {
  5. Properties
  6. {
  7. _Diffuse("Diffuse",Color) = (1,1,1,1) //得到并控制材质的漫反射颜色 初始值为白色
  8. }
  9. SubShader
  10. {
  11. Pass
  12. {
  13. Tags{"LigtheMode" = "ForwardBase"}//定义该pass在unity光照流水线中的角色
  14. CGPROGRAM
  15. #pragma vertex vert
  16. #pragma fragment frag
  17. #include "Lighting.cginc"
  18. fixed4 _Diffuse;//使用properties中声明的属性
  19. struct a2v {
  20. float4 vertex :POSITION;
  21. float3 normal:Normal;//获取法线(位于模型空间下)后续使用需要转换到世界空间
  22. };
  23. struct v2f {
  24. float4 pos :SV_POSITION;
  25. fixed3 color : COLOR;
  26. };
  27. v2f vert(a2v v){
  28. v2f o;//定义返回值
  29. o.pos=UnityObjectToClipPos(v.vertex);//顶点着色器的功能,将顶点坐标从模型空间转换到裁剪空间
  30. fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//获得环境光
  31. fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));//得到模型空间到世界空间的变换矩阵的逆矩阵_World2Object,调换他在
  32. //mul函数中的位置,得到和转置矩阵相同的矩阵乘法,由于法线是三维向量,只需要截取_World2Object的前三行三列
  33. fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);//要知道光源的颜色与强度信息及光源方向, 可以以通过内置变量——LightColor()得到,前提是定义了
  34. //合适的lightmode,光源方向可以有_WorldSpaceLightPos0得到(单一光源平行光)
  35. //计算光源方向和法线方向的点积的时候,只有两者处于同一坐标空间才有意义,这里选择世界坐标
  36. fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));
  37. o.color=ambient+diffuse;
  38. return o;
  39. }
  40. fixed4 frag(v2f i):SV_Target{
  41. return fixed4(i.color,1.0);//所有的计算在顶点着色器中完成了,片元着色器中只需将顶点颜色输出
  42. }
  43. ENDCG
  44. }
  45. }
  46. FallBack "Diffuse"
  47. }

逐像素光照

代码如下,逐像素光照可以得到更加平滑的光照效果,但是在光照无法到达的区域,模型的外观通常是全黑的,没有明暗变化,像一个平面一样失去模型细节表现,改善方法--半兰伯特(half-lambert)光照模型。

  1. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
  2. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
  3. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
  4. Shader "Shaders/DiffuseVertexLevel"
  5. {
  6. Properties
  7. {
  8. _Diffuse("Diffuse",Color) = (1,1,1,1) //得到并控制材质的漫反射颜色 初始值为白色
  9. }
  10. SubShader
  11. {
  12. Pass
  13. {
  14. Tags{"LigtheMode" = "ForwardBase"}//定义该pass在unity光照流水线中的角色
  15. CGPROGRAM
  16. #pragma vertex vert
  17. #pragma fragment frag
  18. #include "Lighting.cginc"
  19. fixed4 _Diffuse;//使用properties中声明的属性
  20. struct a2v {
  21. float4 vertex :POSITION;
  22. float3 normal:Normal;//获取法线(位于模型空间下)后续使用需要转换到世界空间
  23. };
  24. struct v2f {
  25. float4 pos :SV_POSITION;
  26. float3 worldNormal:TEXCOORD0;
  27. };
  28. v2f vert(a2v v){
  29. v2f o;//定义返回值
  30. o.pos=UnityObjectToClipPos(v.vertex);//顶点着色器的功能,将顶点坐标从模型空间转换到裁剪空间
  31. o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
  32. return o;//此处顶点着色器不需要计算光照模型,只需要将处理到世界空间的法线传递给片元着色器即可
  33. }
  34. fixed4 frag(v2f i):SV_Target{//由片元着色器
  35. fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//获得环境光
  36. fixed3 worldNormal=normalize(i.worldNormal);
  37. fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
  38. fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));
  39. fixed3 color=ambient+diffuse;
  40. return fixed4(color,1.0);
  41. }
  42. ENDCG
  43. }
  44. }
  45. FallBack "Diffuse"
  46. }

半兰伯特模型

在前面使用的漫反射光照模型为兰伯特光照模型,为了解决光照无法到达区域模型外观为全黑无明暗变化的问题,对公式进行修正,没有用max操作来防止法线n与光源方向I的点积为负值,而是用α倍的缩放加上β大小的偏移,绝大多数下用0.5 。没有物理依据,仅仅是视觉加强技术。

  1. v2f vert(a2v v){
  2. v2f o;//定义返回值
  3. o.pos=UnityObjectToClipPos(v.vertex);//顶点着色器的功能,将顶点坐标从模型空间转换到裁剪空间
  4. o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
  5. return o;//此处顶点着色器不需要计算光照模型,只需要将处理到世界空间的法线传递给片元着色器即可
  6. }
  7. fixed4 frag(v2f i):SV_Target{//由片元着色器
  8. fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;//获得环境光
  9. fixed3 worldNormal=normalize(i.worldNormal);
  10. fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
  11. fixed3 halfLambert=dot(worldNormal,worldLight)*0.5+0.5;
  12. fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halfLambert;
  13. fixed3 color=ambient+diffuse;
  14. return fixed4(color,1.0);
  15. }

高光反射光照模型

计算高光反射需要知道四个参数:入射光线颜色和强度Clight,材质的高光反射系数,视角的方向与反射方向,反射方向可以有法线与光源方向计算得到。CG提供函数reflect(i,n),给定入射方向i与法线方向n时,返回反射方向。Gloss越大,高光范围越小。

逐顶点光照

  1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
  2. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
  3. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
  4. Shader "Shaders/SpecularVertexLevel"
  5. {
  6. Properties
  7. {
  8. _Diffuse("Diffuse",Color)=(1,1,1,1)
  9. _Specular("Specular",Color)=(1,1,1,1) //控制高光反射颜色
  10. _Gloss("Gloss",Range(8.0,256))=20 //控制高光区域的大小
  11. }
  12. SubShader
  13. {
  14. Pass
  15. {
  16. Tags { "RenderType"="ForwardBase" }
  17. CGPROGRAM
  18. #pragma vertex vert
  19. #pragma fragment frag
  20. // make fog work
  21. #include "Lighting.cginc"
  22. fixed4 _Diffuse;
  23. fixed4 _Specular;
  24. float _Gloss;
  25. struct a2v{
  26. float4 vertex :POSITION;
  27. float3 normal: NORMAL;
  28. };
  29. struct v2f{
  30. float4 pos:SV_POSITION;
  31. fixed3 color:COLOR;
  32. };
  33. v2f vert(a2v v ){
  34. v2f o;
  35. o.pos =UnityObjectToClipPos(v.vertex);
  36. fixed3 ambient =UNITY_LIGHTMODEL_AMBIENT.xyz;
  37. fixed3 worldNormal =normalize(mul(v.normal,(float3x3)unity_WorldToObject));
  38. fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
  39. fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
  40. fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal));
  41. fixed3 viewDir =normalize(_WorldSpaceCameraPos.xyz-mul(unity_ObjectToWorld,v.vertex).xyz);
  42. fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir,viewDir)),_Gloss);
  43. o.color =ambient+diffuse+specular;
  44. return o;
  45. };
  46. fixed4 frag(v2f i):SV_Target {
  47. return fixed4 (i.color,1.0);
  48. }
  49. ENDCG
  50. }
  51. }
  52. FallBack "Diffuse"
  53. }

逐像素光照

  1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
  2. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
  3. // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
  4. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
  5. Shader "Shaders/SpecularPixelLevel"
  6. {
  7. Properties
  8. {
  9. _Diffuse("Diffuse",Color)=(1,1,1,1)
  10. _Specular("Specular",Color)=(1,1,1,1) //控制高光反射颜色
  11. _Gloss("Gloss",Range(8.0,256))=20 //控制高光区域的大小
  12. }
  13. SubShader
  14. {
  15. Pass
  16. {
  17. Tags { "RenderType"="ForwardBase" }
  18. CGPROGRAM
  19. #pragma vertex vert
  20. #pragma fragment frag
  21. // make fog work
  22. #include "Lighting.cginc"
  23. fixed4 _Diffuse;
  24. fixed4 _Specular;
  25. float _Gloss;
  26. struct a2v{
  27. float4 vertex :POSITION;
  28. float3 normal: NORMAL;
  29. };
  30. struct v2f{
  31. float4 pos:SV_POSITION;
  32. float3 worldNormal:TEXCOORD0;
  33. float3 worldPos:TEXCOORD1;
  34. };
  35. v2f vert(a2v v ){
  36. v2f o;
  37. o.pos =UnityObjectToClipPos(v.vertex);
  38. o.worldNormal =normalize(mul(v.normal,(float3x3)unity_WorldToObject));
  39. o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
  40. return o;
  41. };
  42. fixed4 frag(v2f i):SV_Target {
  43. fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
  44. fixed3 worldNormal=i.worldNormal;
  45. fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
  46. fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
  47. fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
  48. fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
  49. fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir,viewDir)),_Gloss);
  50. return fixed4 (ambient+diffuse+specular,1.0);
  51. }
  52. ENDCG
  53. }
  54. }
  55. FallBack "Diffuse"
  56. }

BlinnPhong模型

Blinn模型没有使用反射方向,而是引入了新的矢量h半程向量,描述视角方向与光照方向的中间向量,由视角方向与光照方向相加后再归一化得到。实现代码与上面类似,仅呈现不同的部分。

  1. fixed4 frag(v2f i):SV_Target {
  2. fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
  3. fixed3 worldNormal=i.worldNormal;
  4. fixed3 worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
  5. fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
  6. fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
  7. fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
  8. fixed3 halfDir=normalize(worldLightDir+viewDir);
  9. fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);
  10. // BlinnPhong与phone的区别就在于高光反射部分
  11. return fixed4 (ambient+diffuse+specular,1.0);
  12. }

Unity的内置函数

unity中一些内置函数可以辅助计算。

 

使用内置函数完成的Blinn-Phong模型

内置函数得到的方向是没有归一化的,仍需要使用normalize函数归一化

  1. v2f vert(a2v v ){
  2. v2f o;
  3. o.pos =UnityObjectToClipPos(v.vertex);
  4. o.worldNormal=UnityObjectToWorldNormal(v.normal);
  5. // o.worldNormal =normalize(mul(v.normal,(float3x3)unity_WorldToObject));
  6. o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
  7. return o;
  8. };
  9. fixed4 frag(v2f i):SV_Target {
  10. fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
  11. fixed3 worldNormal=normalize(i.worldNormal);
  12. fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));
  13. fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLightDir));
  14. fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
  15. fixed3 viewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));
  16. fixed3 halfDir=normalize(worldLightDir+viewDir);
  17. fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);
  18. // BlinnPhong与phone的区别就在于高光反射部分
  19. return fixed4 (ambient+diffuse+specular,1.0);
  20. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/127358
推荐阅读
相关标签
  

闽ICP备14008679号