入门图形学:光照模型(三)

       surfaceColor = emissive + ambient + diffuse + specular;

         这次我们就来实际在图形引擎中用shader cg实现一下,看下具体效果,效果图如下:

        

        这里我做了一个放射光为鲜红色的高亮材质球,其中fragment函数是自己实现的通用光照模型计算,cg代码如下:

        


 


 
 
  1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
  2. Shader "Unlit/FragLightUnlitShader"
  3. {
  4. Properties
  5. {
  6. _EmissiveColor( "emissive",Color) = ( 1, 1, 1, 1)
  7. _AmbientColor( "embient",Color) = ( 1, 1, 1, 1)
  8. _AmbientFactor( "e_factor",Color) = ( 1, 1, 1, 1)
  9. _LightColor( "light",Color) = ( 1, 1, 1, 1)
  10. _DiffuseFactor( "d_factor",Color) = ( 1, 1, 1, 1)
  11. _SpecularFactor( "s_factor",Color) = ( 1, 1, 1, 1)
  12. _SpecShininess( "s_shininess",Range( 10, 100)) = 10
  13. }
  14. SubShader
  15. {
  16. //LightMode标注问ForwardBase是为了告诉unity引擎我们开始使用光照计算了
  17. //需要为我们开发准备好光照计算所有数据
  18. Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
  19. LOD 100
  20. Pass
  21. {
  22. CGPROGRAM
  23. #pragma vertex vert
  24. #pragma fragment frag
  25. #include "UnityCG.cginc"
  26. #include "Lighting.cginc"
  27. struct app2vert
  28. {
  29. float4 vertex : POSITION;
  30. float3 normal : NORMAL;
  31. };
  32. struct vert2frag
  33. {
  34. float4 pos : SV_POSITION;
  35. float3 worldNormal : TEXCOORD0;
  36. float3 worldPos : TEXCOORD1;
  37. };
  38. float3 _EmissiveColor; /*自己定义的放射颜色*/
  39. float3 _AmbientColor; /*自己定义的环境光颜色,一般我们都是用unityLighting中的自带字段*/
  40. float4 _AmbientFactor; /*自己定义的环境反射系数,为什么用一个color字段储存呢?博客中我会谈到*/
  41. float3 _LightColor; /*自己定义的太阳光颜色,当然这个我们也是一般使用unityLighting.cginc中自带的场景中directionlight的颜色了*/
  42. float4 _DiffuseFactor; /*自己定义的Diffuse漫反射系数,float4值*/
  43. float4 _SpecularFactor; /*自己定义的Sepcular镜面反射系数,float4值*/
  44. float _SpecShininess; /*自己定义的specular的强度指数*/
  45. vert2frag vert(app2vert av)
  46. {
  47. vert2frag vf;
  48. //使用mvp矩阵将顶点位置变换到投影空间位置
  49. vf.pos = UnityObjectToClipPos(av.vertex);
  50. //使用M矩阵将法线位置变换到模型空间法线位置的单位向量
  51. vf.worldNormal = normalize(mul(UNITY_MATRIX_M,av.normal));
  52. //使用M矩阵将顶点位置变换到模型空间位置的单位向量
  53. vf.worldPos = normalize(mul(UNITY_MATRIX_M,av.vertex).xyz);
  54. return vf;
  55. }
  56. fixed4 frag(vert2frag vf) : SV_Target
  57. {
  58. //根据lighting.cginc中的字段准备一些参数
  59. float3 unity_buildin_ambient_light_color = UNITY_LIGHTMODEL_AMBIENT.xyz; /*unity自带的环境光颜色*/
  60. float3 unity_buildin_world_p2sun_dir = normalize(WorldSpaceLightDir(float4(vf.worldPos, 1))); /*unity自己的光线入射方向计算相反的的p点到太阳的朝向单位向量*/
  61. float3 unity_buildin_light_color = _LightColor0.rgb; /*unity自带的入射光颜色*/
  62. //float3 unity_buildin_light_color = _LightColor;/*或者自己指定lightColor*/
  63. float3 unity_buildin_eye2p_dir = normalize(_WorldSpaceCameraPos.xyz - vf.worldPos.xyz); /*unity自带的eye眼睛到p点朝向单位向量*/
  64. float3 h_dir_or_call_VplusL_dir = normalize(unity_buildin_eye2p_dir + unity_buildin_world_p2sun_dir); /*计算出的H向量的单位向量*/
  65. /*surfaceColor = emissive + ambient + diffuse + specular根据这个公式一步一步来计算*/
  66. //先计算emissive
  67. float3 emissive = _EmissiveColor;
  68. //再计算ambient
  69. float3 ambient = _AmbientFactor.rgb * unity_buildin_ambient_light_color;
  70. /* 或者float3 ambient = _AmbientFactor.rgb * _AmbientColor; */
  71. //再计算diffuse
  72. float3 diffuse = _DiffuseFactor.rgb * unity_buildin_light_color * max(dot(vf.worldNormal,unity_buildin_world_p2sun_dir), 0);
  73. //再计算specular
  74. float3 specular = _SpecularFactor.rgb * unity_buildin_light_color * pow(max(dot(vf.worldNormal,h_dir_or_call_VplusL_dir), 0),_SpecShininess);
  75. //最终计算surfaceColor
  76. fixed4 sCol = fixed4(emissive + ambient + diffuse + specular, 1.0);
  77. return sCol;
  78. }
  79. ENDCG
  80. }
  81. }
  82. }

        这里我来着重解释一下cg代码中可能导致小伙伴们看不懂的地方。

        一.properties定义字段讲解。

        我依次将放射颜色(_EmissiveColor),环境光颜色(_AmbientColor),材质环境反射系数(_AmbientFactor),场景光源颜色(_LightColor),漫反射系数(_DiffuseFactor),镜面反射系数(_SpecularFactor),镜面反射强度指数(_SpecShininess)人为定义完毕。

        这里需要特别注意的是,我将系数都定义为了Color的float4类型,这是为什么呢?这些系数难道不应该是一个float类型么?

        其实是这样考虑的,我们获取颜色rgb(float3)*系数(float4或者float)后分两种情况:

        ①.rgb(float3) * 系数(float4).xyz这种乘法得到的结果是float3(rgb.x*系数.x,rgb.x*系数.y,rgb.x*系数.z)

        ②.rgb(float3) * 系数(float) = float3(rgb.x*系数,rgb.x*系数,rgb.x*系数)

        第一种细节更加丰富,因为rgb颜色的每个分量分别乘上了不同的系数分量值,并不像第二种一样分量的系数不变,第二种就类似于一个等比例系数,太固定死板了。

        二.vert顶点函数讲解

        vert顶点函数中的输入参数app2vert结构体,这个就是UnityCG.cginc文件中提供的unitybuildin字段,我们可以直接定义成输入参数结构体拿来用,这些都是unity帮我计算储存好的,此时我就使用了vertex顶点坐标和normal顶点法向量(当然注意,这是最原始的数据)

        随后,在顶点函数中,我依次用矩阵处理了

       ①.UNITY_MATRIX_MVP去处理顶点变换到裁剪空间

      ②.UNITY_MATRIX_M去处理法向量到3D模型空间(这个法向量无所谓平移或者不平移,你可以用matrix3x3或者4x4的M矩阵去处理都行)并且单位化它,以便后续使用

       ③.UNITY_MATRIX_M去处理顶点坐标到3D模型空间坐标,后面也需要用

        ps:如果现在还是不懂这些矩阵运算意义的同学,建议从我博客第一篇看起。

       三.frag片段函数讲解

       frag片段函数就开始我们最关键的光照模型数学计算讲解了。

       首先我们准备一些参数,如下:

       ①.float3 unity_buildin_ambient_light_color = UNITY_LIGHTMODEL_AMBIENT.xyz;

         这个字段是unity buildin定义字段,记录的是当前环境光颜色,可以通过editor - window-lighting-settings中的Environment Lighting手动设置。

        ②.float3 unity_buildin_world_p2sun_dir = normalize(-WorldSpaceLightDir(float4(vf.worldPos,1)));

        先根据unity buildin的获取光线射线朝向向量的函数WorldSpaceLightDir函数获取光线朝向向量-L,然后p2sun就是-L的反向量,就是照射点P到太阳光源的朝向向量L,同时单位化。

        ③.float3 unity_buildin_light_color = _LightColor0.rgb;

        一目了然,这就是unity buildin的入射光线颜色值

        ④.float3 unity_buildin_eye2p_dir = normalize(_WorldSpaceCameraPos.xyz - vf.worldPos.xyz);

        这个就是计算的观察者眼睛到照射点P的朝向向量,根据场景中camera在3D建模空间xyz坐标-顶点在3D建模空间中xyz坐标。

        ⑤.float3 h_dir_or_call_VplusL_dir = normalize(unity_buildin_eye2p_dir + unity_buildin_world_p2sun_dir);

        计算出H单位向量的值,就是V+L。这里不懂原因的同学可以返回上一篇看我写的镜面反射数学计算。

        接下来就简单了,依次计算出:emissive、ambient、diffuse、specular,然后计算出surfaceColor。

        这些基本是相当详细的讲解了cg代码的每一个步骤的作用,我相信小伙伴能看懂,demo我就不放了csdn要分,这个cg代码就是最核心的东西,有兴趣自己动手实现看看。

        下一篇我们学习一种自定义通用光照模型的cg写法。

 

        

 

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值