首页 > 其他分享 >在unity 2020 urp版本中实现 LensFlare功能

在unity 2020 urp版本中实现 LensFlare功能

时间:2023-01-30 17:01:30浏览次数:58  
标签:vh flare Vector3 pos source unity urp new LensFlare


这个官方里面没有支持,只能自己去解决。
效果请查看地址:https://www.bilibili.com/video/BV1w24y1o7pY/

下面说一下我的实现过程。
找来找去,我找到了一篇不错的解决方案,还带源码:
​​​ http://warmcat.org/chai/blog/?p=5279​​​ 这个是一个大佬的个人博客里面的一篇文章。文章和代码里面有很多的可取之处,希望大家也多支持一下大佬。
他在里面列出了四种解决方案:

  1. 后处理
  2. 给Renderer最高的SortingOrder或者Queue
  3. 在Overlay UI上绘制
  4. 使用一个单独的相机并设置最高的priority

他的代码里面使用的是第四种方式,就是额外的增加了一个正交相机,单独去渲染。

我把代码下载下来以后,发现有一些bug,还有这个单独相机渲染也不利于管理。所以,我对此进行了一些修改。也将实现方式修改成了第二种方式,直接绘制在使用的相机的前面。

接下来我讲解一下我是如何修改的,首先我对shader进行了一下处理,因为工程的场景打开以后,没有显示。

在unity 2020 urp版本中实现 LensFlare功能_unity


原来使用了两个对象挂载脚本,一个是挂载FlareSource,这个是制作光晕的一个资源的一个脚本。另一个是拿到设置的光晕的设置(上一个脚本中设置),进行几何体生成,然后给到正交相机去渲染。

麻烦的地方就是在这,所以,我实现是直接将两个脚本都挂载到了一个对象上面,然后把正交相机去掉,多余的对象也删除掉,将这个做成一个预制体,使用的时候,只需要将其放到场景中即可。

FlareSource中的修改,里面的实现是使用了挂载对象位置实现的,比较难操作,我直接将其修改成了,根据光的照射方向,然后加上相机的位置偏移,来计算光晕的出现方向。
然后为了保证性能,我还修改了,除了射线检测,还加入了对光晕位置的是否处于相机视椎体内进行了判断

void Update()
{
// Vector3 position = transform.position;
Vector3 direction = m_MainLight.transform.TransformDirection(new Vector3(0, 0, -1)); //太阳的朝向
Vector3 position = m_GameCamera.transform.position + direction * m_GameCamera.farClipPlane;
m_ViewportPosition = m_GameCamera.WorldToViewportPoint(position);

Ray ray = new Ray(m_GameCamera.transform.position, position);
// Ray ray = new Ray(m_GameCamera.transform.position, position - m_GameCamera.transform.position);

RaycastHit hit;
//射线拾取遮挡或者不处于相机范围内
if (!IsVisableInCamera(position) || Physics.Raycast(ray, out hit))
{
//如果当前在显示,则计算渐变时间,设置渐变效果直至隐藏
if (IsVisible)
{
if (!IsHitLast)
m_FadeTime = (1 - m_AlphaBase) * FadeDuration;

m_FadeTime += Time.deltaTime;
float fac = GetFadeCurveValue(m_FadeTime / FadeDuration);
m_AlphaBase = Mathf.Lerp(1, 0, fac);

if (m_AlphaBase <= 0)
{
IsVisible = false;
m_FadeTime = 0;
m_AlphaBase = 0;
}
}
IsHitLast = true;
}
else
{
if (IsHitLast)
m_FadeTime = m_AlphaBase * FadeDuration;

if (m_AlphaBase < 1)
{
m_FadeTime += Time.deltaTime;
float fac = GetFadeCurveValue(m_FadeTime / FadeDuration);
m_AlphaBase = Mathf.Lerp(0, 1, fac);

if (m_AlphaBase >= 1)
{
m_FadeTime = 0;
m_AlphaBase = 1;
}
}
IsVisible = true;
IsHitLast = false;
}
Debug.DrawLine(position, m_GameCamera.transform.position, IsHitLast ? Color.red : Color.white);
}

判断视椎体的代码也很简单

/// <summary>
/// 判断一个点是否处于视椎体内
/// </summary>
/// <param name="pos">世界坐标下的位置坐标</param>
/// <returns>布尔值,在显示范围为true</returns>
public bool IsVisableInCamera(Vector3 pos)
{
//转化为视角坐标
Vector3 viewPos = m_GameCamera.WorldToViewportPoint(pos);
// z<0代表在相机背后
if (viewPos.z < 0) return false;
//太远了!看不到了!
if (viewPos.z > m_GameCamera.farClipPlane)
return false;
// x,y取值在 0~1之外时代表在视角范围外;
if (viewPos.x < 0 || viewPos.y < 0 || viewPos.x > 1 || viewPos.y > 1) return false;
return true;
}

另一个文件则是FlareBatch,里面有一个mesh顶点合并的辅助函数,而FlareBatch函数主要的就是UpdateGeometry方法,里面是用于更新几何体。

void UpdateGeometry(FlareSource source, List<Vertexhelper> meshes)
{
// Vector3 viewportPos = source.ViewportPosition;
if (!source.IsVisible) //不显示,将不再更新
return;

Vector2 center = source.Center; // 光晕“中心”,后续这个值可以变
Vector2 flareSpacePos = ViewportToPerspFlareSpace(source.ViewportPosition); // 光源在flare space的坐标
Vector2 flareVec = flareSpacePos - center;
float angle = Mathf.Atan2(flareSpacePos.y, flareSpacePos.x) * Mathf.Rad2Deg;
float fac = 1;
if (source.SpreadMaximum != 0)
fac = Mathf.Clamp(flareVec.magnitude / source.SpreadMaximum, 0, 1); // 扩散比例,离中心越远,显得越大

//List<Vertexhelper> meshes = new List<Vertexhelper>();
//遍历创建的每一个资源,根据配置生成顶点信息
for (int i = 0; i < source.Flares.Count; ++i)
{
Flare flare = source.Flares[i];
if (!flare.IsActive)
continue;

Vector2 size = source.GetFlareSize(flare);
float scale = Mathf.Lerp(flare.ScaleRange.x, flare.ScaleRange.y, source.GetScaleCurveValue(fac));
float alpha = Mathf.Lerp(1, 0, source.GetAlphaCurveValue(fac));

//实例化一个顶点数据类
Vertexhelper vh = new Vertexhelper();

//生成颜色
Color col = flare.Color;
col.a *= source.AlphaBase;
col.a *= alpha;
vh.color = new Color[] { col, col, col, col };

//计算出位置
Vector2 pos = flareSpacePos - flare.DistanceAspect * flareVec * source.SpreadAmount;
Vector3 _pos = new Vector3(pos.x, pos.y, 0);

Vector2 halfSize = size / 2;

vh.vertices = new Vector3[]
{
Vec3(-halfSize.x, -halfSize.y, -i*0.01f),
Vec3(-halfSize.x, halfSize.y, -i*0.01f),
Vec3(halfSize.x, halfSize.y, -i*0.01f),
Vec3(halfSize.x, -halfSize.y, -i*0.01f),
};
//对每个点的位置进行缩放旋转和偏移
for (int j = 0; j < vh.vertices.Length; ++j)
{
vh.vertices[j] *= scale;
vh.vertices[j] = Quaternion.Euler(0, 0, flare.Rotation) * vh.vertices[j];
if (flare.RotateWith)
vh.vertices[j] = Quaternion.Euler(0, 0, angle) * vh.vertices[j];
vh.vertices[j] += _pos;

vh.vertices[j].z += 3;

vh.vertices[j] = m_GameCamera.ViewportToWorldPoint(PerspFlareSpaceToViewport(vh.vertices[j]));
// vh.vertices[j] -= m_GameCamera.transform.position;
}

//uv
vh.uv = new Vector2[]
{
Vec2(0,0),
Vec2(0,1),
Vec2(1,1),
Vec2(1,0),
};
for (int j = 0; j < vh.uv.Length; ++j)
{
Vector4 scaleOffset = flare.Atlas.GetScaleOffset(flare.Index);
vh.uv[j] *= new Vector2(scaleOffset.x, scaleOffset.y);
vh.uv[j] += new Vector2(scaleOffset.z, scaleOffset.w);
}

//面的索引
vh.triangles = new int[] { 0, 1, 2, 0, 2, 3 };

meshes.Add(vh);
}
}
/// <summary>
/// 将viewport坐标转换到“透视flare空间”
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
Vector3 ViewportToPerspFlareSpace(Vector3 pos)
{
//将视线空间的位置,转换到了以中心为0 0, -1到1的空间
pos += new Vector3(-0.5f, -0.5f, 0);
pos.x *= m_GameCamera.aspect; //横轴乘以宽高比
pos.y *= 1; //纵轴
pos.z = 0;
pos *= 2;
return pos;
}

/// <summary>
/// 将“透视flare空间”坐标转换到viewport空间
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
Vector3 PerspFlareSpaceToViewport(Vector3 pos)
{
pos /= 2;
pos.x /= m_GameCamera.aspect;
pos += new Vector3(0.5f, 0.5f, 0);
return pos;
}

主要修改的地方,我不再使用正交相机,而是直接将模型绘制使用的相机前面。所以之前是有一个光晕空间,我继续保留了这个空间,在此空间处理完成以后,再转换回主相机视角空间,然后将顶点从视角空间重新转换回世界空间。这样,主相机拿到顶点信息,也能正确渲染出来数据了。


标签:vh,flare,Vector3,pos,source,unity,urp,new,LensFlare
From: https://blog.51cto.com/u_15948039/6027352

相关文章

  • unity中实现ue眼球的渲染
    此图取至ue官方网站在shader里面我们还是需要使用英文,中英文对照:巩膜:sclera角膜缘:limbus虹膜:iris瞳孔:pupil角膜:cornea渲染效果。模型方面:使用一个突出的眼球模型来......
  • Unity 热更新方案
    一、Lua:内置一个Lua虚拟机。典型的框架有xLua,uLua。 二、ILRuntime:内置一个.net字节码解释器,解释执行.net字节码。三、puerts:内置一个JavaScript/TypeScrip......
  • The GPG keys listed for the "MySQL 5.7 Community Server" repository are already
    报错TheGPGkeyslistedforthe"MySQL5.7CommunityServer"repositoryarealreadyinstalledbuttheyarenotcorrectforthispackage.Checkthatthecorrec......
  • 关于blender导入到unity……
    我快被该死的左右手坐标轴搞疯了,于是做了个实验:我在blender里建了个箭头,让箭头朝向z轴,再在y轴正方向上写了个y,x轴正方向写了个x,然后ctrl+a应用全部。然后直接以blender文......
  • 2023 Cisco Designated VIP Program for Cisco Community
    从接触网络技术到现在,已将近10年的时间。岁月如斯,不知有多少技术大神,多少前辈在技术这条路上披星戴月。做技术容易,做好技术不容易!最开始接触网络技术,就是从Cisco开始的,最......
  • Unity中的网络同步解决方案
    网络同步的解决方案有三种:状态同步、帧同步、实时广播同步,实际使用中可以混合使用。网络同步概览知名游戏中的同步方案类型游戏名同步方案MMORPG魔兽世界状态同步+实时广播......
  • burpsuite抓https包的必要准备
    https的原理首先了解什么是https: HTTPS全称为HypertextTransferProtocolSecure,相当于是http协议的安全版本,跟http相比增加了加密这一环节 百度百科 https的具体......
  • VScode中调试Unity【Debugger for Unity】
    我遇到的情况:在点击运行和调试时,出现中间栏让你选择调试器,我点击UnityDebugger没反应。单击创建Launch文件也无效,没有UnityDebugger的选项删除.vscode/launch.json如......
  • Unity2D: 重新加载场景后场景暂停
    SceneManager.LoadSceneAsync(01);所有动画、脚本停止运行,改变timeScale没有用Time.timeScale=1;解决办法:SceneManager.LoadSceneAsync(01,LoadSceneMode.Single......
  • BurpSuite使用指南-综合应用
    综合应用在很多实际的情况下,我们需要使用BP工具进行信息的拦截,使用其他工具进行生产测试用例,分析数据。其中常见的工具为CO2打开BP找到“extender”选择bapp stroe在旁边的......