這個(gè)官方里面沒有支持,只能自己去解決。
效果請(qǐng)查看地址:https://www.bilibili.com/video/BV1w24y1o7pY/
下面說一下我的實(shí)現(xiàn)過程。
找來找去,我找到了一篇不錯(cuò)的解決方案,還帶源碼:
http://warmcat.org/chai/blog/?p=5279
這個(gè)是一個(gè)大佬的個(gè)人博客里面的一篇文章。文章和代碼里面有很多的可取之處,希望大家也多支持一下大佬。
他在里面列出了四種解決方案:
- 后處理
- 給Renderer最高的SortingOrder或者Queue
- 在Overlay UI上繪制
- 使用一個(gè)單獨(dú)的相機(jī)并設(shè)置最高的priority
他的代碼里面使用的是第四種方式,就是額外的增加了一個(gè)正交相機(jī),單獨(dú)去渲染。
我把代碼下載下來以后,發(fā)現(xiàn)有一些bug,還有這個(gè)單獨(dú)相機(jī)渲染也不利于管理。所以,我對(duì)此進(jìn)行了一些修改。也將實(shí)現(xiàn)方式修改成了第二種方式,直接繪制在使用的相機(jī)的前面。
接下來我講解一下我是如何修改的,首先我對(duì)shader進(jìn)行了一下處理,因?yàn)楣こ痰膱?chǎng)景打開以后,沒有顯示。
原來使用了兩個(gè)對(duì)象掛載腳本,一個(gè)是掛載FlareSource,這個(gè)是制作光暈的一個(gè)資源的一個(gè)腳本。另一個(gè)是拿到設(shè)置的光暈的設(shè)置(上一個(gè)腳本中設(shè)置),進(jìn)行幾何體生成,然后給到正交相機(jī)去渲染。
麻煩的地方就是在這,所以,我實(shí)現(xiàn)是直接將兩個(gè)腳本都掛載到了一個(gè)對(duì)象上面,然后把正交相機(jī)去掉,多余的對(duì)象也刪除掉,將這個(gè)做成一個(gè)預(yù)制體,使用的時(shí)候,只需要將其放到場(chǎng)景中即可。
FlareSource中的修改,里面的實(shí)現(xiàn)是使用了掛載對(duì)象位置實(shí)現(xiàn)的,比較難操作,我直接將其修改成了,根據(jù)光的照射方向,然后加上相機(jī)的位置偏移,來計(jì)算光暈的出現(xiàn)方向。
然后為了保證性能,我還修改了,除了射線檢測(cè),還加入了對(duì)光暈位置的是否處于相機(jī)視椎體內(nèi)進(jìn)行了判斷
void Update()
{
// Vector3 position = transform.position;
Vector3 direction = m_MainLight.transform.TransformDirection(new Vector3(0, 0, -1)); //太陽(yáng)的朝向
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;
//射線拾取遮擋或者不處于相機(jī)范圍內(nèi)
if (!IsVisableInCamera(position) || Physics.Raycast(ray, out hit))
{
//如果當(dāng)前在顯示,則計(jì)算漸變時(shí)間,設(shè)置漸變效果直至隱藏
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);
}
判斷視椎體的代碼也很簡(jiǎn)單
/// <summary>
/// 判斷一個(gè)點(diǎn)是否處于視椎體內(nèi)
/// </summary>
/// <param name="pos">世界坐標(biāo)下的位置坐標(biāo)</param>
/// <returns>布爾值,在顯示范圍為true</returns>
public bool IsVisableInCamera(Vector3 pos)
{
//轉(zhuǎn)化為視角坐標(biāo)
Vector3 viewPos = m_GameCamera.WorldToViewportPoint(pos);
// z<0代表在相機(jī)背后
if (viewPos.z < 0) return false;
//太遠(yuǎn)了!看不到了!
if (viewPos.z > m_GameCamera.farClipPlane)
return false;
// x,y取值在 0~1之外時(shí)代表在視角范圍外;
if (viewPos.x < 0 || viewPos.y < 0 || viewPos.x > 1 || viewPos.y > 1) return false;
return true;
}
另一個(gè)文件則是FlareBatch,里面有一個(gè)mesh頂點(diǎn)合并的輔助函數(shù),而FlareBatch函數(shù)主要的就是UpdateGeometry方法,里面是用于更新幾何體。文章來源:http://www.zghlxwxcb.cn/news/detail-435777.html
void UpdateGeometry(FlareSource source, List<Vertexhelper> meshes)
{
// Vector3 viewportPos = source.ViewportPosition;
if (!source.IsVisible) //不顯示,將不再更新
return;
Vector2 center = source.Center; // 光暈“中心”,后續(xù)這個(gè)值可以變
Vector2 flareSpacePos = ViewportToPerspFlareSpace(source.ViewportPosition); // 光源在flare space的坐標(biāo)
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); // 擴(kuò)散比例,離中心越遠(yuǎn),顯得越大
//List<Vertexhelper> meshes = new List<Vertexhelper>();
//遍歷創(chuàng)建的每一個(gè)資源,根據(jù)配置生成頂點(diǎn)信息
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));
//實(shí)例化一個(gè)頂點(diǎn)數(shù)據(jù)類
Vertexhelper vh = new Vertexhelper();
//生成顏色
Color col = flare.Color;
col.a *= source.AlphaBase;
col.a *= alpha;
vh.color = new Color[] { col, col, col, col };
//計(jì)算出位置
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),
};
//對(duì)每個(gè)點(diǎn)的位置進(jìn)行縮放旋轉(zhuǎn)和偏移
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坐標(biāo)轉(zhuǎn)換到“透視flare空間”
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
Vector3 ViewportToPerspFlareSpace(Vector3 pos)
{
//將視線空間的位置,轉(zhuǎn)換到了以中心為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空間”坐標(biāo)轉(zhuǎn)換到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;
}
主要修改的地方,我不再使用正交相機(jī),而是直接將模型繪制使用的相機(jī)前面。所以之前是有一個(gè)光暈空間,我繼續(xù)保留了這個(gè)空間,在此空間處理完成以后,再轉(zhuǎn)換回主相機(jī)視角空間,然后將頂點(diǎn)從視角空間重新轉(zhuǎn)換回世界空間。這樣,主相機(jī)拿到頂點(diǎn)信息,也能正確渲染出來數(shù)據(jù)了。文章來源地址http://www.zghlxwxcb.cn/news/detail-435777.html
到了這里,關(guān)于在unity 2020 urp版本中實(shí)現(xiàn) LensFlare功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!