文章目录
- 前言
- 一、实现思路
- 二、实现原理
- 我们可以由下图直观的感受到 N 与 L夹角越小,点积越接近(白色)1。越趋近90°,点积越接近0(黑色)
- 三、实现URP下的菲涅尔效果
- 1、我们新建一个Shader,修改为最简
- 2、获取世界空间下的顶点法线 N
- 3、获取顶点指向摄像机的视线单位向量 L
- 4、在片元着色器中,计算得到 NdotL 值
- 5、用1 - NdotL 值得到菲尼尔效果
- 四、测试代码
前言
我们在这篇文章中,了解一下URP中Shader怎么实现菲涅尔效果,同时学习一下URP下怎么获取法线 和 视线向量。
一、实现思路
Lambert光照模型公式:
Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L))
-
实现灯光照射中间亮 周围暗的效果,核心是dot(N,L)
-
Unity中Shader的Lambert光照的实现
-
光照效果下, 视线单位向量 点积 法线单位向量的效果是 中间亮周围暗。我们需要的效果刚好相反,用 1 减去该结果即可得到菲尼尔效果。
-
所以,我们主要要获取 N 和 L
我们在之前的文章中,实现过一次菲涅尔效果(模型中间暗周围亮的效果)
- Unity中Shader的XRay透视效果
二、实现原理
为什么 NdotL 可以得到中间亮,周围亮的的效果
我们可以由下图直观的感受到 N 与 L夹角越小,点积越接近(白色)1。越趋近90°,点积越接近0(黑色)
三、实现URP下的菲涅尔效果
我们这里用一个 BRP 下的Shader来修改为 URP 下的该效果
1、我们新建一个Shader,修改为最简
Shader "MyShader/URP/P3_2_4"
{Properties{}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);return o;}fixed4 frag (v2f i) : SV_Target{return 1;}ENDCG}}
}
- 在SubShader的Tags中,告诉引擎这是URP下的Shader
“RenderPipeline” = “UniversalPipeline”
- 替换代码块申明
CGPROGRAM -> HLSLPROGRAM
ENDCG -> ENDHLSL
- 替换我们的引入库为HLSL常用的几个
#include “Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl”
#include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl”
#include “Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”
#include “Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl”
2、获取世界空间下的顶点法线 N
- 在应用程序传入顶点着色器的 Attributes(appdata)结构体 加入本地法线
struct Attributes
{float3 vertexOS : POSITION;float3 normalOS : NORMAL;
};
- 在顶点着色器传入片元着色器的 Varyings(v2f)结构体 加入世界法线
struct Varyings
{float4 vertexCS : SV_POSITION;float3 normalWS : TEXCOORD0;
};
- 在顶点着色器进行法线坐标转化
o.normalWS = TransformObjectToWorld(v.normalOS);
3、获取顶点指向摄像机的视线单位向量 L
要获取该向量,需要知道 摄像机的世界空间坐标 和 我们顶点的世界空间坐标
- 摄像机的世界空间坐标
_WorldSpaceCameraPos
- 顶点世界空间下的坐标
- 在顶点着色器传入片元着色器的 Varyings(v2f)结构体 加入世界顶点坐标
float3 vertexWS : TEXCOORD1;
- 在顶点着色器进行顶点坐标的空间转化
o.vertexWS = TransformObjectToWorld(v.vertexOS);
- 我们在片元着色器输出看看效果
- 用世界空间下的 摄像机坐标 减去 模型顶点坐标 得到 L
half3 L = normalize(_WorldSpaceCameraPos - i.vertexWS);
4、在片元着色器中,计算得到 NdotL 值
half NdotL = dot(N,L);
- 我们输出看看效果
5、用1 - NdotL 值得到菲尼尔效果
需要调节效果强弱的话,我们使用pow函数即可
return 1 - NdotL;
四、测试代码
//URP下的菲涅尔效果
Shader "MyShader/URP/P3_2_4"
{Properties{}SubShader{Tags{//告诉引擎,该Shader只用于 URP 渲染管线"RenderPipeline"="UniversalPipeline"//渲染类型"RenderType"="Opaque"//渲染队列"Queue"="Geometry"}Pass{Cull Back Blend One Zero ZTest LEqual ZWrite OnHLSLPROGRAM#pragma vertex vert#pragma fragment frag#pragma target 2.0#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"struct Attributes{float3 vertexOS : POSITION;float3 normalOS : NORMAL;};struct Varyings{float4 vertexCS : SV_POSITION;float3 normalWS : TEXCOORD0;float3 vertexWS : TEXCOORD1;};Varyings vert (Attributes v){Varyings o;o.vertexWS = TransformObjectToWorld(v.vertexOS);o.vertexCS = TransformWorldToHClip(o.vertexWS);o.normalWS = TransformObjectToWorldNormal(v.normalOS);return o;}half4 frag (Varyings i) : SV_Target{//菲涅尔效果 1 - dot(N,L)half3 N = i.normalWS;half3 L = normalize(_WorldSpaceCameraPos - i.vertexWS);half NdotL = dot(N,L);return 1 - NdotL;}ENDHLSL}}
}