URP - 能量罩实现
目录
一、实现思路
二、效果展示
三、具体实现方法
一、实现思路
- 菲尼尔效果
- 利用深度信息计算交接位置并设置颜色
- 采样蜂窝状纹理并使该纹理在V方向上流动
二、效果展示
三、具体实现方法
-
菲尼尔效果实现
F = F0 + (1 - F0)*(1 - cos∠(N,V))^5
菲尼尔反射的原理是计算反射率的值,具体计算是通过1-物体表面的法线向量和视线向量的点积。
因为当法线向量N和视线向量V重合时,点击后的值为1,但是由于视线垂直于物体表面时,此时的反射率最小,折射率最高,所以要使用1-dot(N,V)来计算反射率
-
利用深度信息计算交接位置并设置颜色
实现思路:将用于实现能量罩球体的Shader代码中添加"Queue"="Transparent",使得能量罩球体在深度图中不可见。然后计算出能量罩球体在视图空间中的Z值,最后通过该Z值和深度图中的深度信息进行计算求出交接处的位置并设置颜色
1. 计算能量罩球体在视图空间中的Z值:此时的o.positionVS.z为负值
//将模型的坐标从本地空间转换到世界空间
float3 positionWS = TransformObjectToWorld(v.positionOS);
//将世界空间的模型顶点转换到视图空间(观察空间)
o.positionVS = TransformWorldToView(positionWS);
2. 获得深度图中的深度信息
(1)声明深度图
TEXTURE2D(_CameraDepthTexture);
SAMPLER(sampler_CameraDepthTexture);
(2)用模型在屏幕空间下的坐标来采样深度图
//模型在屏幕空间下的坐标值
float2 ScreenUV = i.positionCS/_ScreenParams.xy;
//用此坐标值来采样深度图
float4 depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV);
//将深度图转换到线性空间
float4 depth = LinearEyeDepth(depthTex,_ZBufferParams);
3. 通过深度图中的信息与模型的深度值进行计算求出交接处位置
因为越靠近摄像机的位置深度值越小,所以在有物体与能量罩球体接触到时,被球体遮挡的部分深度值大于该位置球体本身的深度值,而交接处两者的深度值差不多一样
所以可以使用深度图中的深度信息减去球体本身的深度信息,这样被球体遮挡的部分的值在0-1的范围内,而交接处的值大约为0 (而球体本身的深度值为负值,所以可以通过两者相加计算)
//depth为深度图中的深度值(此时只有立方体的深度信息) i.positionVS.z为视图空间下模型顶点本身的深度信息
//i.positionVS.z为负值,所以要用两者相加,在立方体被遮挡的区域此时depth的深度值大于此区域上模型本身的深度值
float4 HighLight = depth + i.positionVS.z;
但是需要更改交接处位置的颜色,所以可以使交接处的位置先改为白色,然后乘以颜色即可
-
采样蜂窝状纹理
(1)声明主纹理
TEXTURE2D(_MainTex);
float4 _MainTex_ST;
SAMPLER(sampler_MainTex);
(2)使纹理的重复和平铺有效
o.uv = TRANSFORM_TEX(v.uv,_MainTex);
(3)采样纹理并使纹理在V方向上流动
//i.uv+float2(0,_Time.y*0.3)使纹理的v方向进行流动
float4 mainTex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv+float2(0,_Time.y*0.3));
//mainTex*0.1的目的是使纹理颜色变淡
c+=mainTex*0.1f;
四、代码
Shader"unity/EnergyShield02"
{Properties{_MainTex("MainTex",2D)="white"{}_Color("Color",Color)=(1,1,1,1)_HighLightEdge("HighLightEdge",float)=1_fresnelxs("fresnelxs",float)=1_fresnelColor("fresnelColor",Color)=(1,1,1,1)}SubShader{Tags{"RenderPipeline" = "UniversialPipeline""RenderType" = "Transparent""Queue" = "Transparent"}Blend SrcAlpha OneMinusSrcAlpha// Blend One OnePass{HLSLPROGRAM#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"#pragma vertex vert#pragma fragment fragCBUFFER_START(UnityPerMaterial)float4 _Color;float _HighLightEdge;float _fresnelxs;float4 _fresnelColor;CBUFFER_ENDTEXTURE2D(_MainTex);float4 _MainTex_ST;SAMPLER(sampler_MainTex);TEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);struct Attributes{float4 positionOS : POSITION;float2 uv : TEXCOORD;float3 normal : NORMAL;};struct Varyings {float4 positionCS : SV_POSITION;float2 uv : TEXCOORD;float3 positionVS : TEXCOORD1;float3 normalWS : TEXCOORD2;float3 viewWS : TEXCOORD3;};Varyings vert(Attributes v){Varyings o = (Varyings)0;//将模型的坐标从本地空间转换到世界空间float3 positionWS = TransformObjectToWorld(v.positionOS);//将世界空间的模型顶点转换到视图空间(观察空间)o.positionVS = TransformWorldToView(positionWS);//将视图空间下的顶点转换到齐次裁剪空间o.positionCS = TransformWViewToHClip(o.positionVS);o.normalWS = TransformObjectToWorldNormal(v.normal);//用于计算菲尼尔中的V向量o.viewWS = _WorldSpaceCameraPos - positionWS;o.uv = TRANSFORM_TEX(v.uv,_MainTex);return o;}float4 frag(Varyings i):SV_Target{float4 c;//模型在屏幕空间下的坐标值float2 ScreenUV = i.positionCS/_ScreenParams.xy;//用此坐标值来采样深度图float4 depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,ScreenUV);//将深度图转换到线性空间float4 depth = LinearEyeDepth(depthTex,_ZBufferParams);//depth为深度图中的深度值(此时只有立方体的深度信息) i.positionVS.z为视图空间下模型顶点本身的深度信息//i.positionVS.z为负值,所以要用两者相加,在立方体被遮挡的区域此时depth的深度值大于此区域上模型本身的深度值float4 HighLight = depth + i.positionVS.z; //控制边缘HighLight *= _HighLightEdge; //将颜色翻转,原来交接处为黑色,反转后颜色为白色,方便与_Color相乘HighLight = 1-HighLight;HighLight *=_Color;//将值限制在0-1之间HighLight = saturate(HighLight);c=HighLight;//菲尼尔效果float3 N = normalize(i.normalWS);float3 V = normalize(i.viewWS);float NdotV = dot(N,V);float4 fresnel = pow((1-NdotV),_fresnelxs);fresnel *=_fresnelColor;c+=fresnel;//i.uv+float2(0,_Time.y*0.3)使纹理的v方向进行流动float4 mainTex = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv+float2(0,_Time.y*0.3));//mainTex*0.1的目的是使纹理颜色变淡c+=mainTex*0.1f;return c;}ENDHLSL}}}