本文由@唐三十胖子出品,转载请注明出处。
文章链接:https://blog.csdn.net/iceSony/article/details/84334899
这篇文章将总结和提炼《Unity Shader入门精要》的第六章“Unity中的基础光照”的内容。
通过这篇文章,你可以知道
1)标准光照模型对光线的处理
2)漫反射的逐顶点光照
3)漫反射的逐像素光照
4)漫反射的半兰伯特光照
一.标准光照模型对光线的处理
下面四种都是模型对光的处理
自发光emissive 不会改变周围物体颜色
模型表现颜色为自发光颜色
高光反射 specular 完全反射光
phong模型:高光反射颜色 = 光线颜色*材质高光反射系数*max(0,法线在发射光线的投影)^反光度
漫反射 duffuse 吸收波长,反射颜色波长
兰伯特定律:漫反射颜色 = 光线颜色*材质漫反射系数*max(0,法线在光源方向的投影)
环境光 ambient 所有物体颜色受环境光影响
模型表现颜色为环境光 (Unity设置,Window->Lighting->Ambient Source->Ambient Color->Ambient Intensity)
接下来我们将重点介绍最常见的漫反射与高光反射光照Shader编写。
二.漫反射光照Shader编写
对漫反射光照的处理有两种方式
逐顶点光照 or 逐像素光照
逐顶点光照:单位为顶点 通过顶点进行计算 计算量小 图元内部顶点着色可能产生棱角
逐像素光照:单位为像素 通过法线进行计算 计算量大
逐顶点光照
注意这里我们需要设置tag,获取光照颜色与光照方向
_LightColor0与_WorldSpaceLightPos0(注意这里只有一个光源)☞ Tags {“LightMode”=”ForwardBase”}
Tags具体使用:https://blog.csdn.net/iceSony/article/details/84258594
代码中首先设置一个属性_Diffuse,作为模型漫反射的系数
Properties { _Diffuse("Color", Color) = (1.0,1.0,1.0,1.0) }
之后就是常见的Pass语义定义
SubShader { Tags { "RenderType" = "ForwardBase" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse;
在这之后就是结构体
struct a2v { float4 position:POSITION; float3 normal:NORMAL; }; struct v2f { float4 pos : SV_POSITION; fixed3 color : COLOR; };
最重要的着色器函数_LightColor0代表光照颜色,_WorldSpaceLightPos0代表世界坐标中光照方向
v2f vert (a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.position); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object)); fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight)); o.color = ambient + diffuse; return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(i.color,1.0); } ENDCG
重点解说顶点着色器
首先将o.pos从模型空间坐标转换到屏幕坐标
ambient是环境光的方向的获取
这里进行了两步操作v.normal代表的是模型空间坐标下的顶点法线
我们可以获取到光照方向,diffuse下进行光照在法线上的强度计算。
World2Object是什么
这是计算方法,Unity提供的变换矩阵
可以通过mul(v.normal, (float3x3)_World2Object)方法转化法线到空间坐标
其中因为法线类型为float3 所以取float4x4矩阵的一部分就够了
Saturate函数目的是获取[0-1]内的参数
最后在和diffuse颜色相乘得到最终漫反射结果
果然边缘看起来很凹凸不平
这里你可能奇怪
1.为什么要和光照颜色进行相乘?
如果你调整光照颜色为红色且不和光照颜色相乘,这时候你的物体颜色不会受到影响,还是白色。
2.那为什么不是加呢?
左加右乘,高下立盼,这时候你调整属性会发现右边的类似黄+蓝=绿的效果
完整代码如下
Shader "sony/Shader144" { Properties { _Diffuse("Color", Color) = (1.0,1.0,1.0,1.0) } SubShader { Tags { "RenderType" = "ForwardBase" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; struct a2v { float4 position:POSITION; float3 normal:NORMAL; }; struct v2f { float4 pos : SV_POSITION; fixed3 color : COLOR; }; v2f vert (a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.position); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight)); o.color = ambient + diffuse; return o; } fixed4 frag (v2f i) : SV_Target { return fixed4(i.color,1.0); } ENDCG } } Fallback "Diffuse" }
三.逐像素光照
计算方式与逐顶点光照类似,这里不重复介绍了。
Shader "sony/Shader147" { Properties { _Diffuse("Color", Color) = (1.0,1.0,1.0,1.0) } SubShader { Tags{ "RenderType" = "ForwardBase" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; struct a2v { float4 position:POSITION; float3 normal:NORMAL; } ; struct v2f { float4 pos : SV_POSITION; float3 texcoord : TEXCOORD0; }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.position); o.texcoord = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); return o; } fixed4 frag(v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(i.texcoord); fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLightDir)); fixed3 color = ambient + diffuse; return fixed4(color,1.0); } ENDCG } } Fallback "Diffuse" }
具体效果比逐顶点光照shader好。
四.半兰伯特光照
虽然我们可以通过添加环境光让角色背面亮起来,但是那种背面的阴影是一个深度的
在半条命中提出了这样的一种光照模式,具体操作是原本反射的光在光照法线上的曲线
这样就解决了判断0-1的问题:)
好处也很明显,背面的光照显得很真实。
这是背面对比图
贴个代码
Shader "sony/Shader148" { Properties { _Diffuse("Color", Color) = (1.0,1.0,1.0,1.0) } SubShader { Tags{ "RenderType" = "ForwardBase" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse; struct a2v { float4 position:POSITION; float3 normal:NORMAL; } ; struct v2f { float4 pos : SV_POSITION; float3 texcoord : TEXCOORD0; }; v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.position); o.texcoord = normalize(mul(v.normal, (float3x3)unity_WorldToObject)); return o; } fixed4 frag(v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal = normalize(i.texcoord); fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 halfLambert = dot(worldNormal, worldLightDir)*0.5 + 0.5; fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*halfLambert; fixed3 color = ambient + diffuse; return fixed4(color,1.0); } ENDCG } } Fallback "Diffuse" }
其实在日常开发中这是最常用的一种光照计算方式。
下一章节,我们将介绍高光反射。