• Unity Shader - 兰伯特漫反射


    兰伯特漫反射公式:

    漫反射(Diffuse)= 光源颜色 * max(0,cos(光方向和法线的夹角))

    公式原理:

     从上面图片可以看出光照方向 L 与物体法相 N形成的 余弦值越大,反射光越大,角度为0度的时候最强 Cos(0) = 1,大于等于90度的时候为0 Cos(90) = 0;

    所以我们首先需要计算出法向量N入射光方向L的角度的余弦值。

    我们可以通过他们的点乘来计算,公式如下:

    把向量归一化处理后,|L| 和 |N| 都是1,可以简化为:

    让我们来实现以下:

    逐顶点漫反射:

    1. Shader "Unlit/001"
    2. {
    3. Properties
    4. {
    5. // 漫反射颜色
    6. _Diffuse ("_Diffuse",Color) = (1,1,1,1)
    7. }
    8. SubShader
    9. {
    10. Pass
    11. {
    12. CGPROGRAM
    13. #pragma vertex vert
    14. #pragma fragment frag
    15. #include "UnityCG.cginc"
    16. #include "Lighting.cginc"
    17. float4 _Diffuse;
    18. struct v2v
    19. {
    20. float4 vertex : POSITION;
    21. float3 normal : NORMAL;
    22. };
    23. struct v2f
    24. {
    25. float4 vertex : POSITION;
    26. float3 color : Color;
    27. };
    28. v2f vert (v2v v)
    29. {
    30. v2f o;
    31. // 将对象空间中的点变换到齐次坐标中的摄像机裁剪空间
    32. o.vertex = UnityObjectToClipPos(v.vertex);
    33. // 光源方向
    34. float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
    35. // 将法向量转到世界坐标法向量
    36. float3 normal = UnityObjectToWorldNormal(v.normal);
    37. // 环境光颜色
    38. float3 ambinet = UNITY_LIGHTMODEL_AMBIENT.xyz;
    39. // 光源颜色 0 表示第一套光源 ,场景里可以有多个光源
    40. float3 lightColor = _LightColor0.xyz;
    41. // 漫反射公式 计算
    42. float3 diffuse = _Diffuse * lightColor * max(0,dot(lightDir,normal));
    43. o.color = diffuse + ambinet;
    44. return o;
    45. }
    46. fixed4 frag (v2f i) : SV_Target
    47. {
    48. float4 color = float4(i.color,1);
    49. return color;
    50. }
    51. ENDCG
    52. }
    53. }
    54. }

    逐片源漫反射:

    1. Shader "Unlit/002"
    2. {
    3. Properties
    4. {
    5. // 漫反射颜色
    6. _Diffuse ("_Diffuse",Color) = (1,1,1,1)
    7. }
    8. SubShader
    9. {
    10. Pass
    11. {
    12. CGPROGRAM
    13. #pragma vertex vert
    14. #pragma fragment frag
    15. #include "UnityCG.cginc"
    16. #include "Lighting.cginc"
    17. float4 _Diffuse;
    18. struct v2v
    19. {
    20. float4 vertex : POSITION;
    21. float3 normal : NORMAL;
    22. };
    23. struct v2f
    24. {
    25. float4 vertex : POSITION;
    26. float3 normal : NORMAL;
    27. };
    28. v2f vert (v2v v)
    29. {
    30. v2f o;
    31. // 将对象空间中的点变换到齐次坐标中的摄像机裁剪空间
    32. o.vertex = UnityObjectToClipPos(v.vertex);
    33. // 将法向量转到世界坐标法向量
    34. o.normal = UnityObjectToWorldNormal(v.normal);
    35. return o;
    36. }
    37. fixed4 frag (v2f i) : SV_Target
    38. {
    39. // 光源方向
    40. float3 lightDir = _WorldSpaceLightPos0.xyz;
    41. // 光源颜色
    42. float lightColor = _LightColor0.rgb;
    43. // 漫反射公式 计算
    44. float3 diffuse = lightColor * lightDir * max(0,dot(lightDir,i.normal));
    45. float3 ambinet = UNITY_LIGHTMODEL_AMBIENT.rgb;
    46. float3 color = diffuse + ambinet;
    47. return float4(color,1);
    48. }
    49. ENDCG
    50. }
    51. }
    52. }

    让我们看以下效果:左边是逐顶点漫反射,右边是逐片元漫反射
    逐顶点的漫反射与逐片元的漫反射的区别在于顶点漫反射在阴影切换处会有明显的锯齿反应(不过在我这电脑上看并不明显),可以看的到明显的顶点。而片元漫反射则相对切换平滑。相对的,逐片元就比逐顶点好性能。

    背光效果:

     我们看到背光的地方非常暗,完全就看不见模型纹理了,这样的话效果在游戏里太影响体验了,于是就有了 半兰伯特光照模型 ,该技术是Valve公式在开发游戏《半条命》时提出的,由于该技术是在原兰伯特光照模型的基础上修改的,所以被称为半兰伯特光照模型 

    漫反射(Diffuse)= 光源颜色 * (cos(光方向和法线的夹角)* 0.5 + 0.5)

    其实就是把结果范围从 [-1,1] 映射到 [0,1]的范围内。这样的话在背光面也会有明暗变化,不会完全黑掉。

    整体代码我就不贴出来了,把公式部分贴出来

    1. // 漫反射公式 计算
    2. //float3 diffuse = _Diffuse * lightColor * max(0,dot(lightDir,normal));
    3. float3 diffuse = _Diffuse * lightColor * (dot(lightDir,normal) * 0.5 + 0.5);

    正面:

    背面:

  • 相关阅读:
    web3.0入门及学习路径
    Docker资源控制管理
    Tortoise SVN 察看本地缓存密码
    有偿找proteus51单片机仿真代做
    [正确重装docker] Win10 重装 Docker 提示 Exising installation is up to date 的正确姿势
    【车辆分类】基于matlab的视频中车辆跟踪监测分类算法仿真,包括背景差分与帧间差分以及形态学处理
    电工特种作业操作证登高作业等报名考试费用是怎么样的
    通过数据可观测性进行价值工程和数据成本优化
    px转rem插件postcss-plugin-px2rem使用方法(浏览器缩放页面自适应)
    【历史上的今天】8 月 14 日:新浪微博开始内测;阿塔纳索夫完成论文;登上太空的计算机病毒
  • 原文地址:https://blog.csdn.net/weixin_41316824/article/details/131141255