定义
漫反射,是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这种反射的光称为漫射光。很多物体,如植物、墙壁、衣服等,其表面粗看起来似乎是平滑,但用放大镜仔细观察,就会看到其表面是凹凸不平的,所以本来是平行的太阳光被这些表面反射后,弥漫地射向不同方向。
光照模型
效果
兰伯特光照模型(Lambert)
公式
漫反射Diffuse = 平行光颜色 * max(0, 平行光方向·法线方向)
漫反射Diffuse = 平行光颜色 * max(0, cosθ)
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
fixed4 frag(v2f f) : SV_Target { //法线方向由模型空间转世界空间 half3 worldNormalDir = UnityObjectToWorldNormal(f.normal); //法线方向由模型空间转世界空间 第二种写法 // half3 worldNormalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); //获取世界空间的平行光方向 half3 worldLightDir = _WorldSpaceLightPos0.xyz; //标准漫反射(兰伯特)法线和光线方向的点积 //颜色不会小于0,所以用max取得不小于0的值 half nl = max(0, dot(worldNormalDir, worldLightDir)); //取得漫反射颜色 half3 diffuse = nl * _LightColor0; //得出最后光照颜色,加上环境光亮度会增加,去掉环境光则只有平行光光照颜色 //叠加自身颜色,因为自身颜色这里不考虑透明通道 所以用rgb f.color = diffuse * _MainColor.rgb; return fixed4(f.color,1); } |
半兰伯特光照模型(Half Lambert)
公式
漫反射Diffuse = 平行光颜色 * (平行光方向·法线方向 * 0.5 + 0.5)
漫反射Diffuse = 平行光颜色 * (cosθ * 0.5 + 0.5)
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
fixed4 frag(v2f f) : SV_Target { //法线方向由模型空间转世界空间 half3 worldNormalDir = UnityObjectToWorldNormal(f.normal); //法线方向由模型空间转世界空间 第二种写法 // half3 worldNormalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); //获取世界空间的平行光方向 half3 worldLightDir = _WorldSpaceLightPos0.xyz; //漫反射(半兰伯特)法线和光线方向的点积 half nl = dot(worldNormalDir, worldLightDir) * 0.5 + 0.5; //取得漫反射颜色 half3 diffuse = nl * _LightColor0; //得出最后光照颜色,加上环境光亮度会增加,去掉环境光则只有平行光光照颜色 //叠加自身颜色,因为自身颜色这里不考虑透明通道 所以用rgb f.color = diffuse * _MainColor.rgb; return fixed4(f.color,1); } |
顶点计算/片元计算
示例使用的是兰伯特光照模型
效果
逐顶点漫反射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
Shader "WCC/Diffuse Vertex" { SubShader { Pass { //需要指定通道是前向渲染管线中 Tags {"LightMode" = "ForwardBase"} CGPROGRAM //引入平行光相关的库 //通过这个库,获取平行光的方向(_WorldSpaceLightPos0) #include "UnityLightingCommon.cginc" //或者这个库也可以 // #include "Lighting.cginc" //获取UnityObjectToWorldNormal #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag struct a2v { //顶点坐标 float4 vertex : POSITION; //顶点法线 float3 normal : NORMAL; }; struct v2f { float4 position : SV_POSITION; fixed3 color : COLOR; }; //这里把光照算法写在顶点函数里,由于顶点数量少,所以颜色转到片元函数,会经过差值计算 //因为顶点数量少,所以运算次数少 会提高shader性能 v2f vert(a2v v) { v2f f; //顶点坐标由模型空间转世界空间 f.position = UnityObjectToClipPos(v.vertex); //顶点坐标由模型空间转世界空间 第二种写法 // f.position = mul(UNITY_MATRIX_MVP, v.vertex); //法线方向由模型空间转世界空间 half3 worldNormalDir = UnityObjectToWorldNormal(v.normal); //法线方向由模型空间转世界空间 第二种写法 // half3 worldNormalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); //获取世界空间的平行光方向 half3 worldLightDir = _WorldSpaceLightPos0.xyz; //标准漫反射(兰伯特)法线和光线方向的点积 //颜色不会小于0,所以用max取得不小于0的值 half nl = max(0, dot(worldNormalDir, worldLightDir)); //取得漫反射颜色 half3 diffuse = nl * _LightColor0; //环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; //得出最后光照颜色,加上环境光亮度会增加,去掉环境光则只有平行光光照颜色 f.color = diffuse + ambient; return f; } fixed4 frag(v2f f) : SV_Target { return fixed4(f.color,1); } ENDCG } } } |
逐像素漫反射(逐片元漫反射)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
Shader "WCC/Diffuse Fragment" { Properties { _MainColor("Main Color", Color) = (1,1,1,1) } SubShader { Pass { //需要指定通道是前向渲染管线中 Tags {"LightMode" = "ForwardBase"} CGPROGRAM //引入平行光相关的库 //通过这个库,获取平行光的方向(_WorldSpaceLightPos0) #include "UnityLightingCommon.cginc" //或者这个库也可以 // #include "Lighting.cginc" //获取UnityObjectToWorldNormal #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag fixed4 _MainColor; struct a2v { //顶点坐标 float4 vertex : POSITION; //顶点法线 float3 normal : NORMAL; }; struct v2f { float4 position : SV_POSITION; float3 normal : TEXCOORD0; fixed3 color : COLOR; }; v2f vert(a2v v) { v2f f; //顶点坐标由模型空间转世界空间 f.position = UnityObjectToClipPos(v.vertex); //顶点坐标由模型空间转世界空间 第二种写法 // f.position = mul(UNITY_MATRIX_MVP, v.vertex); f.normal = v.normal; return f; } //这里把光照算法写在片元函数里,效果更平滑一些,但性能会比顶点函数里执行要差 fixed4 frag(v2f f) : SV_Target { //法线方向由模型空间转世界空间 half3 worldNormalDir = UnityObjectToWorldNormal(f.normal); //法线方向由模型空间转世界空间 第二种写法 // half3 worldNormalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); //获取世界空间的平行光方向 half3 worldLightDir = _WorldSpaceLightPos0.xyz; //标准漫反射(兰伯特)法线和光线方向的点积 //颜色不会小于0,所以用max取得不小于0的值 half nl = max(0, dot(worldNormalDir, worldLightDir)); //取得漫反射颜色 half3 diffuse = nl * _LightColor0; //环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; //得出最后光照颜色,加上环境光亮度会增加,去掉环境光则只有平行光光照颜色 //叠加自身颜色,因为自身颜色这里不考虑透明通道 所以用rgb f.color = (diffuse + ambient) * _MainColor.rgb; return fixed4(f.color,1); } ENDCG } } } |
区别
- 逐顶点计算:顶点数量少,所以计算量少,性能会高,不在顶点位置像素会根据差值计算,效果会差
- 逐片元计算:片元会每个像素点计算,计算量多,性能稍差,但更平滑
- 本文固定链接: http://www.u3d8.com/?p=2570
- 转载请注明: 网虫虫 在 u3d8.com 发表过
[…] Shader漫反射 […]