一、前言距离写下第一篇博客后过了挺久了期间一直在忙项目一直没啥时间做做技术整理或是巩固自己的基础。最近也算是告一段落开始进行一些整理和查漏补缺目前主要是分为开发框架和Shader两个方向因为开发框架涉及知识点很杂需要花点时间整理优化于是就先从Shader开始写起。本系列内容主要针对《Unity Shader入门精要》一书中所涉及的效果实现进行整理第六章前面的基础知识暂时不做重点后面会单开一篇进行整理本篇会从第六章基础光照开始整理其中涉及的知识点和代码实现。二、基础光照1. 标准光照模型标准光照模型也就是为人熟知的Blinn-Phong模型其基本理念只关心光源直接照射到物体表面后经过一次反射进入摄像机的光线它将进入摄像机的光线分为4个部分环境光ambient、漫反射diffuse、高光反射specular和自发光emissive。1.1 环境光在标准光照模型中使用环境光来近似模拟简介光照通常为一个全局变量场景中所有物体都使用该环境光。在Unity中我们通过Window/Rendering/Lighting窗口中的Environment页面对场景的环境光进行修改。1.2 漫反射漫反射用于描述光被物体表面随机散射到各个方向。在漫反射中由于反射完全随机可以近似为在各反射方向上相同因此视角位置并不重要。1.2.1 兰伯特光照模型顾名思义该模型符合兰伯特定律即反射光线强度与表面法线和光源方向间的夹角的余弦值成正比因此入射光线的角度在漫反射中十分重要我们可以用以下公式来描述漫反射其中为表面法线是指向光源的单位向量是材质的漫反射颜色。同时为防止物体被背后的光源照亮需要保持法线和光源方向点乘结果非负。1.2.2 半兰伯特光照模型兰伯特光照模型存在背光区域明暗一样失去了模型细节表现的缺点半兰伯特光照模型是对其的一种改善技术广义的半兰伯特光照模型公式如下相较于原公式半兰伯特光照模型没有使用操作来避免赋值而是对结果进行了缩放和偏移大部分情况下和的值均为0.5以此将点积范围从[-1,1]映射到[0,1]区间使背光面也可以有明暗变化。需要注意的是半兰伯特模型没有任何物理依据只是一种视觉加强技术1.3 高光反射此处的高光反射是一种经验模型不完全符合现实的高光反射现象主要用于计算被完全镜面反射的光线从而模拟材质的光泽效果。高光反射通常涉及四个向量表面法线、视角方向、光源方向和反射方向其中反射方向可以通过其他信息计算获得我们可以用以下公式来描述高光反射其中是反射方向是材质的高光反射颜色是视角方向是材质的光泽度所有向量均使用单位向量。与漫反射相同此处也需要保证视角方向与反射方向的点乘结果非负。为简化计算Blinn对上面的公式进行了修改通过视角方向和光源方向取平均后再归一化获得新的向量从而规避计算反射方向计算公式如下对于上述两种算法当摄像机和光源距离模型足够远时和可以视为定值此时是一个常量Blinn模型效率更高反之Phong模型可能会更快。1.4 逐像素和逐顶点在Shader中我们有两种方式来计算光照在片元着色器中计算称为逐像素光照在顶点着色器中计算称为逐顶点光照。在逐像素光照中会根据每个像素的法线顶点法线插值或法线纹理采样获得来进行光照计算而对顶点法线进行插值的技术被称为Phong着色也被称为Phong插值或法线插值着色技术需要区别于Phong光照模型。逐顶点光照又被称为高洛德着色则是在每个顶点上计算光照然后在渲染图元内部进行线性插值通常顶点数量远小于像素数目因此计算量也会更小但由于依赖线性插值获得像素光照在进行光照中的非线性计算时如高光反射会出现问题而且会导致图元内部颜色总暗于顶点处的最高颜色值有时会产生明显的棱角现象。2. Shader实现由于《Unity Shader入门精要》原书使用的Unity版本较老部分方法或变量在新版Unity中有所变更或废弃可能会出现报错本系列涉及代码均已进行了更改。2.1 漫反射光照模型2.1.1 逐顶点光照// Upgrade NOTE: replaced _World2Object with unity_WorldToObject // Upgrade NOTE: replaced mul(UNITY_MATRIX_MVP,*) with UnityObjectToClipPos(*) Shader Unity Shader/Chapter6-DiffuseVertexLevel { Properties { _Diffuse(Diffuse, Color) (1,1,1,1) } SubShader { Tags { RenderTypeOpaque } LOD 100 Pass { Tags { LightMode ForwardBase } CGPROGRAM #pragma vertex vert #pragma fragment frag #include Lighting.cginc #include UnityCG.cginc struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed3 color : COLOR; }; fixed4 _Diffuse; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); 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 { fixed4 col fixed4(i.color, 1); return col; } ENDCG } } Fallback Diffuse }2.1.2 逐像素光照Shader Unity Shader/Chapter6-DiffusePixelLevel { Properties { _Diffuse (Diffuse, Color) (1,1,1,1) } SubShader { Tags { RenderTypeOpaque } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normal : NORMAL; }; fixed4 _Diffuse; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.normal mul(v.normal, (float3x3)unity_WorldToObject); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal normalize(i.normal); fixed3 worldLightDir normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 col ambient diffuse; return fixed4(col,1); } ENDCG } } Fallback Diffuse }2.1.3 半兰伯特模型左兰伯特光照模型右半兰伯特光照模型Shader Unity Shader/Chapter6-HalfLambert { Properties { _Diffuse (Diffuse, Color) (1,1,1,1) } SubShader { Tags { RenderTypeOpaque } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normal : NORMAL; }; fixed4 _Diffuse; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.normal mul(v.normal, (float3x3)unity_WorldToObject); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal normalize(i.normal); fixed3 worldLightDir normalize(_WorldSpaceLightPos0.xyz); fixed halfLambert dot(worldNormal, worldLightDir) * 0.5 0.5; fixed3 diffuse _LightColor0.rgb * _Diffuse.rgb * halfLambert; fixed3 col ambient diffuse; return fixed4(col,1); } ENDCG } } Fallback Diffuse }2.2 高光反射光照模型2.2.1 逐顶点光照Shader Unity Shader/Chapter6-SpeculerVertexLevel { Properties { _Diffuse(Diffuse, Color) (1,1,1,1) _Specular(Specular, Color) (1,1,1,1) _Gloss(Gloss, Range(8, 256)) 20 } SubShader { Tags { RenderTypeOpaque } LOD 100 Pass { Tags { LightMode ForwardBase } CGPROGRAM #pragma vertex vert #pragma fragment frag #include Lighting.cginc #include UnityCG.cginc struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed3 color : COLOR; }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); 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)); fixed3 reflectDir normalize(reflect(-worldLight, worldNormal)); fixed3 viewDir normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz); fixed3 specular _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)),_Gloss); o.color ambient diffuse specular; return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col fixed4(i.color, 1); return col; } ENDCG } } Fallback Specular }2.2.2 逐像素光照Shader Unity Shader/Chapter6-SpecularPixelLevel { Properties { _Diffuse(Diffuse, Color) (1,1,1,1) _Specular(Specular, Color) (1,1,1,1) _Gloss(Gloss, Range(8, 256)) 20 } SubShader { Tags { RenderTypeOpaque } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normal : NORMAL; float3 worldPos : TEXCOORD1; }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.worldPos mul(unity_ObjectToWorld, v.vertex).xyz; o.normal mul(v.normal, (float3x3)unity_WorldToObject); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal normalize(i.normal); fixed3 worldLightDir normalize(_WorldSpaceLightPos0.xyz); fixed3 diffuse _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 reflectDir normalize(reflect(-worldLightDir, worldNormal)); fixed3 viewDir normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); fixed3 specular _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)),_Gloss); fixed3 col ambient diffuse specular; return fixed4(col,1); } ENDCG } } Fallback Specular }2.3 Blinn-Phong光照模型Shader Unity Shader/Chapter6-BlingPhong { Properties { _Diffuse(Diffuse, Color) (1,1,1,1) _Specular(Specular, Color) (1,1,1,1) _Gloss(Gloss, Range(8, 256)) 20 } SubShader { Tags { RenderTypeOpaque } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include UnityCG.cginc #include Lighting.cginc struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normal : NORMAL; float3 worldPos : TEXCOORD1; }; fixed4 _Diffuse; fixed4 _Specular; float _Gloss; v2f vert (appdata v) { v2f o; o.vertex UnityObjectToClipPos(v.vertex); o.worldPos mul(unity_ObjectToWorld, v.vertex).xyz; //o.normal mul(v.normal, (float3x3)unity_WorldToObject); o.normal UnityObjectToWorldNormal(v.normal); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 worldNormal normalize(i.normal); //fixed3 worldLightDir normalize(_WorldSpaceLightPos0.xyz); fixed3 worldLightDir normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir)); fixed3 reflectDir normalize(reflect(-worldLightDir, worldNormal)); //fixed3 viewDir normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); fixed3 viewDir normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir normalize(worldLightDir viewDir); fixed3 specular _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal, halfDir)),_Gloss); fixed3 col ambient diffuse specular; return fixed4(col,1); } ENDCG } } Fallback Specular }三、总结本篇笔记主要围绕基础光照章节展开记录了光照模型背后的实现原理并给出了在Unity中的Shader实现。光照作为渲染的基础涉及法线、光照方向、视角方向等重要信息巩固本章内容不仅是打下光照渲染的基础更重要的是熟悉各种常用数据在Shader中的获取方式和转换方式才能在后续的Shader学习中更加顺利。作为第一篇正式的技术博客我也在权衡文章的内容和篇幅也希望各路大神给出一些建议代码也均为本人手动复现如有纰漏还望指正。