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

这篇具有很好参考价值的文章主要介绍了在unity 2020 urp版本中实现 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功能
原来使用了两个对象挂载脚本,一个是挂载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;
    }

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

到了这里,关于在unity 2020 urp版本中实现 LensFlare功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 场景烘培 ——LensFlare镜头光晕(三)

    提示:文章有错误的地方,还望诸位大神指出! 一般情况下都会忽略的东西,镜头光晕。理论上不加镜头光晕,也不会有什么影响,但是有时候为了追求一些特殊的要求效果,也会加上镜头光晕。 比如 下图这种效果: 镜头光晕 (Lens Flares) 是模拟相机镜头内的折射光线的效果

    2024年02月05日
    浏览(43)
  • unity urp 实现丝绸渲染

    首先看一下实际上真实的效果 再来一张 这是专门去找的。 可以看到丝绸渲染使用了各向异性的GGX去实现,有点仿头发的感觉,接下来看一下怎么实现的。 首先,准备实现双向反射率分布函数(BRDF)的DVF项。 D项使用UE里面的各项异性GGX: V项使用配合D项的Vis_SmithJointAniso F项

    2024年02月10日
    浏览(41)
  • Unity中URP下实现深度贴花

    在游戏中,有很多用到贴画的地方。比如:地面污渍、地面喷漆、地面血迹、魔法阵、地裂等效果。 我们在这篇文章中,来用深度图实现一下贴画的效果。 使用之前的棋盘格设置一个场景,且在场景中,增加一些物体,来给贴花吸附。 然后,我们创建一个面片用于承载贴花

    2024年01月21日
    浏览(43)
  • 【VR】【unity】如何在VR中实现远程投屏功能?

    目前主流的VD应用,用于娱乐很棒,但是用于工作还是无法效率地操作键鼠。用虚拟键盘工作则显然是不现实的。为了让自己的头显能够起到小面积代替多显示屏的作用,自己动手开发投屏VR应用。 先实现C#的投屏应用。 研究如何将C#投屏应用用Unity 3D项目转写。 将Unity3D项目

    2024年02月08日
    浏览(64)
  • 【Unity3D小功能】Unity3D中实现UI擦除效果、刮刮卡功能

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师 ☆恬静的小魔龙☆ ,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 使用Unity3D实现UI的擦拭效果、刮刮卡功能的效果实现方式比较多,比如说用Shader、Texture渲染都是可以

    2024年02月04日
    浏览(246)
  • Unity中URP下实现能量罩(外发光)

    2024年01月13日
    浏览(67)
  • 【Unity】URP屏幕后处理UI模糊效果实现

     这里Canvas(1)设置为Overlay能渲染出指定UI高清,其他UI模糊,然而这做法非常不好,如果此时再打开UI 以及 关闭模糊效果 要将这些置顶UI 恢复到原本Canvas里,也就是要管理2套Canvas Shader代码实现模糊  1个Canvas和2个摄像机 主要以上内容,实际上就是因为Render Pass Event是只能Af

    2024年02月10日
    浏览(41)
  • 【Unity URP】风格化草地01:实现方法概述

    写在前面 最近本专业开始多很多事情了,要开始建模写论文了(不然研究生毕不了业),TA方面的学习进度更慢了,,so sad。 废话不多说,这篇文章其实是个小总结,毕竟学习新东西就是先要当一只copy cat(不是)。 至于草地交互,把草地做出来再说! 2021.5 【Unity】ShaderG

    2023年04月21日
    浏览(58)
  • Unity中URP下的菲涅尔效果实现(URP下的法线和视线向量怎么获取)

    我们在这篇文章中,了解一下URP中Shader怎么实现菲涅尔效果,同时学习一下URP下怎么获取法线 和 视线向量。 Lambert光照模型公式: Diffuse = Ambient + Kd * LightColor * max(0,dot(N,L)) 实现灯光照射中间亮 周围暗的效果,核心是dot(N,L) Unity中Shader的Lambert光照的实现 光照效果下, 视线单

    2024年02月02日
    浏览(46)
  • 在unity中实现视频的暂停播放和拖拽进度条的功能

    #Unity中实现视频的暂停播放和拖拽进度条的功能 在UI上,视频包含一个播放、暂停和停止按钮,以及一个拖动条,可以使用这些按钮来控制视频的播放,使用拖动进度条来调整视频的播放进度。 1.建立一个UI,导入视频素材,然后将视频拖放到场景中。 2.建立一个Canvas对象作

    2024年02月07日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包