前言

今天我们来做LensFlare的另类奇葩模拟实现(通过绘制2D—UI的方式来实现LensFlare)。不需要读者编写任何特效Shader,不涉及到任何跟渲染相关的知识点。

实现尽可能的模拟光晕,基于本思路实现的光晕效果如何最终取决于“光晕”纹理的设计如何,以及光晕的摆放问题,后面会说。效果当然可能比不上使用高级图形学知识去通过shader渲染的效果。本文重在分享这种奇葩方式。如文章有错误的地方,还望诸位大神指出。


什么是Lens Flare?

Lens Flare 又称为”镜头光晕“。在游戏中镜头指的是经过视椎体做可视裁剪过后将可视物体渲染到RenderTarget中所呈现出来的画面。RenderTarget可以简单理解成当前游戏窗口的画面。光晕则指的是当镜头中存在光源时,光源会在镜头中会产生一种带有颜色的连续”光圈“特效。看下图就明白了。

呃,这个是网上的图,我们做的效果肯定没那么逼真,因为PS不好做的素材不行,如果你有逼真点的光晕素材的话,效果绝对杠杠的。


对读者的要求

虽然这次模拟LensFlare不涉及到任何shader特效,但是Demo基于Unit3D开发。
因此笔者假设诸位读者具备以下技能:

1、对”向量”有一定了解。
2、熟悉C#或者其他编程语言
3、熟悉Unity3D
4、Photoshop(可有可无,会的话可以自己做素材)


Step1:【光晕素材】

1、在PS中新建一张1600X900的透明图,使用PS工具的多边形工具摁住左Shift键拖动鼠标绘制出一个正五边形,颜色要白色。

2、新建图层使用PS工具的椭圆工具摁住左Shift键拖动鼠标绘制一个正圆,颜色也要白色。


3、好了到这里我们就把素材做完了。
What?这么简单?因为我们只需要模拟光晕,不强调逼真度,不然你可以给上述2个形状添加透明渐变。可以实现更逼真的效果。现在我们只需要将素材导入到Unity3D中去。


Step2:【Unit3D资源准备】

1、新建3D工程(默认工程里面会有一个Main Camera和一个Directional Light)


2、新建一个Light->Point Light, 虽然太阳光是用方向光来表示,但是在这里我们不打算用这个,而是用一个Point Light来模拟光源位置,命名为Sun,老油条自己决定拿什么做光源。

将Sun移动到下图屏幕的大致位置来模拟太阳,并设置如下属性。
Range = 4;
Intensity = 8;
Draw Halo[打钩];


3、将上面完成的素材导入到Unity3D中去,并做一些配置.

点击图片在右边的编辑栏里面设置相应的图片属性,然后保存
Texture Type 设置为 Sprite(2D and UI)
Sprite Mode 设置为 Multiple

打开Sprite Editor

如下图直接点击(1步骤)按钮Slice,会发现2个素材出现绿色矩形边框(2步骤),然后点击(步骤3)Apply

可以看到图片被切分成2张图,Flare1和Flare2


4、接着我们开始创建光晕UI

首先创建一个画布Canvas,在Canvas下通过Create Empty创建一个GameObject并改名为LensFlareManager.如下图所示

接着在LensFlareManager下创建UI->Image用来存放光晕精灵图片。多复制几个,具体多少看自己想做多少个光晕了。我们在这里创建10个。并全部改名为Flare1~10,如下图所示

在Scene场景编辑中切换到2D,开始摆放这些Flare,摆放序号从左下到右上依次1->10.如下图所示

将已经切分出来的素材(正五边形和正圆)分别放入这些UI中,随便放看自己的设计了。同时调整UI的大小、RGB颜色和透明度。对于位置粗略摆放即可,最终位置将会由代码计算出来,现在设置位置只是为了做个大致参考而已。笔者的摆法如下图。

相比较上图是不是有点感觉了?哈哈。不过现在只是摆放而已,现在光晕还不会根据太阳的方位动起来,因此我们需要编写代码来计算出这10个光晕的具体位置在哪里。在编写代码之前我们需要在原理的基础上介绍Flare的位置选取原理。不然直接上代码可能有些人会被搞晕。


Step3:【解析实现原理】

1、首先我们来看下面这张图,要想计算出10个光晕摆放的位置,首先我们需要求出图中所示的关键点位置。

  • 2D-SunPos:太阳在当前摄像机中的2D屏幕位置。
    获取方式Camera.WorldToScreenPoint(Vector3 position);
    在函数参数中传入3D-Sun的位置即可获取太阳在屏幕上的位置。
  • 2D-Mid-ScreenPos:2D屏幕空间中心位置。
    获取方式:Vector2 midScreenPos = new Vector2( Screen.Width * 0.5f , Screen.Height * 0.5f );
  • LensDir : 是一条向量,方向是从2D屏幕中心点指向太阳在屏幕中的2D位置点。长度为两点间距离,后面我们也要用到这条向量的长度来根据比例算出对应光晕在这条向量的方向上的对应坐标。
  • LensDir2 : 也是一条向量,方向与LensDir相反,长度不确定(因为我们要通过程序动态控制)

2、在计算出关键点位置之后我们就要开始计算10个光晕对应的位置了,计算的原理是基于LensDir的方向上根据给定的比例来计算对应的坐标,这些坐标都会坐落在LensDir这条向量上。

  • 接下来我们要给定10个光晕的比例值,如下图中的X1~X10.
  • 由上面计算出信息可知,设P1~P10为对应10个光晕的位置点
    则有: P(x=1~10) = 2D_mid_ScreenPos + ( Dis * X(x=1~10) * LensDir.Normalized )

    • Dis:2D太阳在屏幕位置2D_SunPos和2D屏幕空间位置2D_Mid_ScreenPos之间的距离。
    • X(x=1~10) : 对应光晕在LensDir上的比例值应该在(-1 <= X <= 1 ) 区间内。值为负数时方向与LensDir2一致
    • LensDir.Normalized : LensDir的单位方向向量。

3、需要注意的是,当太阳离开屏幕时我们不希望看到太阳光晕,因此我们还需要对太阳的位置进行判断。当太阳不在屏幕中时将不会显示LensFlare.

  • 通过上面的操作之后我们能获取到太阳在屏幕中的2D位置”2D_SunPos”,我们只需要判断这个点在不在2D屏幕中即可,当太阳离开屏幕时(太阳还在摄像机正面时),其2D坐标值必然不在屏幕中,而屏幕的范围横向是(0~Width)的范围,纵向是(0~Height)的范围,也就是说我们需要判断(0<=x<=Width)&&(0 <=y <=Height)条件满足即可。

  • 除了做以上操作以外,我们还需要判断太阳是不是处在摄像机的前面,如下图Sun_Pos1很明显映射后会出现在屏幕中,Sun_Pos2已经超出屏幕位置,Sun_Pos3虽然在后面,但是X-Y映射还是会在屏幕上,因此会出现屏幕被朝太阳时还是会出现光晕,这个情况是我们不希望看到的,因此我们需要判断太阳是不是在摄像机的前面,庆幸的是这个条件很好判断。摄像机当前的Forward向量是已知的,然后再计算出另一条从摄像机位置指向太阳位置的就可以得到向量Dir.然后将Forward和Dir做点积运算结果大于0时则表示太阳在摄像机前方。
  • 从下图可以看出Forward跟P1、P2、P3的夹角小于90°因此点积结果大于0
    说明对应位置的太阳在摄像机前面,而Forward和P4的夹角大于90°因此点积结果小于0。以此来判定太阳和摄像机的位置即可。

Step4:【编写代码实现】

  1. 首先我们需要创建一个用来管理LensFlares的脚本命名为LensFlareManager。
  2. 逻辑脚本的类成员变量声明如下
    public Camera      _Camera;//主摄像机public Transform   Sun;//太阳public Transform[] LensFlares;//镜头光晕的数组public float[]     Rates;//各个对应光晕的位置比例

3. 回到检视面板,将脚本拖到LensFlaresManager的对象上去(注意:在这里名字虽然一样但是一个是脚本,另一个是对象别搞混了),然后将对应的Object拖动到脚本对应的变量槽位中去,并设置对应的值如下图。

4.完全代码编写如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LensFlareManager : MonoBehaviour {public Camera      _Camera;//主摄像机public Transform   Sun;//太阳public Transform[] LensFlares;//镜头光晕的数组public float[]     Rates;//各个对应光晕的位置比例// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {float width  = Screen.width;//屏幕宽度float height = Screen.height;//屏幕高度Vector2 Sun_2D_Pos = _Camera.WorldToScreenPoint(Sun.position);//太阳2D位置Vector2 Mid_2D_Pos = new Vector2(width * 0.5f, height * 0.5f);//屏幕中心位置Vector2 LensDir    = Sun_2D_Pos - Mid_2D_Pos;//屏幕中心指向太阳2D位置的向量//摄像机到太阳3D位置的向量,用来判断3D空间中太阳是否处在摄像机的前面。Vector3 SunDir     = Sun.position - _Camera.transform.position;//判断太阳是否在摄像机的前面bool isFront       = Vector3.Dot(_Camera.transform.forward, SunDir) > 0 ? true : false;//屏幕空间中,屏幕中心点到太阳2D点的距离。用来计算各个光晕的位置float   dis        = Vector2.Distance(Sun_2D_Pos, Mid_2D_Pos);//循环遍历整个(这里是10个)光晕列表for (int i = 0; i < LensFlares.Length; i++){//当太阳2D位置在屏幕范围中,并且在3D空间中也处在摄像机的前面if ((Sun_2D_Pos.x >= 0 && Sun_2D_Pos.x <= width && Sun_2D_Pos.y >= 0 && Sun_2D_Pos.y <= height) && isFront){//根据Rates[i]中的对应比例计算出当前光晕的位置LensFlares[i].position = Mid_2D_Pos + (dis * Rates[i] * LensDir.normalized);//开启光晕LensFlares[i].gameObject.SetActive(true);}else //不在2D屏幕范围中{//关闭光晕LensFlares[i].gameObject.SetActive(false);}}}
}

Step5:【最终特效】

为了更好的观看LensFlare你还需要一个摄像机的移动旋转控制脚本。不然摄像机在不移动的情况下很难观察到LensFlare做出来的最终结果。



Step6:【总结】

- 优点:

  • 灵活性高,实现简单,易于操作和维护。
  • 如果有好一点的光晕纹理可以实现接近真实光晕的效果
  • 不需要编写shader就可以实现

- 缺点

  • 太依赖美工的素材,如果素材不好做出来的特效会很蹩脚。
  • 等等

- PS

  • 这里为了测试才使用Point light作为光源,实际情况请根据自身需要来实现

  • 没有做Alpha过渡,例如一般情况下太阳越接近屏幕中心光晕越强,反之则会变弱。这里由于篇幅原因就不写了。最简单的Alpha过渡就是根据太阳2D位置到屏幕中心的距离跟一个常量数值做除法算比例然后让当前各个光晕的Alpha乘以这个比率即可。

  • 实际实现中计算光晕位置的方式有很多,这里只是其中一种,有些游戏的光晕是太阳越靠近屏幕中心点,则光晕会从两边聚起来,反之则散开。

Step7:【感谢】

如果你有什么好的意见或者建议,欢迎在评论区一起交流。最后谢谢大家来到我的博客^_^。

【LensFlare镜头光晕】Unity3D奇葩实现相关推荐

  1. Three开发笔记(二)

    文章目录 1. 学习使用Three.js 中的光源 THREE. AmbientLight THREE. Color THREE. PointLight THREE. SpotLight THREE. ...

  2. UE官方教程笔记02-实时渲染基础下

    对官方教程视频[官方培训]02-实时渲染基础下 | 陈拓 Epic的笔记 没听懂的地方就瞎写 反射 实时渲染中反射是一个非常有挑战的特性 UE中有多种不同的方案,各有各的优势和缺点 反射捕获 屏幕空间 ...

  3. 第五节:Three.js光源的类型【Three.js整理】

    Three.js 光源类型:环境光 THREE.AmbientLight .点光源THREE.PointLight.聚光灯 THREE.SpotLight.定向光 THREE.DirectionalL ...

  4. 总索引Classes 类

    转自:http://game.ceeger.com/Script/index.Classes.html A B C D E F G H I J K L M N O P Q R S T U V W Y ...

  5. LensFlare Studio for Mac 6.3 镜头光晕特效软件 破解版下载

    LensFlare Studio for Mac 是摄影爱好者的终极工具.可以方便的将漂亮的光晕和灯光效果添加到您的照片. LensFlare Studio for Mac 6.3 镜头光晕特效软件 ...

  6. 【three.js:语法】光源使用详解2-3(聚光灯 SpotLight、平行光 DirectionLight 、环境光 HemisphereLight、镜头光晕 LensFlare)

    注意点:SpotLight.target 的使用. 1.SpotLight.target= object 或者是 THREE.Object3D()才行.不能只是一个position. 2.target ...

  7. 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 amp; 纹理混合...

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接: http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星 ...

  8. 猫都能学会的Unity3D Shader入门指南(一)

    动机 自己使用Unity3D也有一段时间了,但是很多时候是流于表面,更多地是把这个引擎简单地用作脚本控制,而对更深入一些的层次几乎没有了解.虽然说Unity引擎设计的初衷就是创建简单的不需要开发者操心 ...

  9. PhotoShop - 滤色模式(screen) 的 响应曲线(关于加镜头光晕的思考)

    看了一篇在新的图层中加镜头光晕的博文:[url]http://www.photoshopessentials.com/photo-effects/lens-flare/[/url] ,其中光晕图层的混 ...

  10. VR学习(Demo)以及在Unity3D上的项目

    筑基 为了准备好学习使用Unity开发VR应用,我们首先要检查下自己的电脑硬件和软件配置是否满足要求.简单来说,显卡要NVIDIA GTX970或AMD290以上,CPU要Intel i5-459以上 ...

最新文章

  1. typedef、setw()
  2. (二期)IOS调试技巧
  3. (轉貼) Jolt 2007得獎名單 (News) (.NET)
  4. mysql 优化表的作用_mysql实战优化之三:表优化
  5. java 中 image 和 byte[] 相互转换
  6. 网络层网络层服务及其 IP 地址
  7. 关于application title一直是untitled的问题
  8. Github Page 绑定域名
  9. 在sphinx中处理使用特殊字符时所引起错误的办法
  10. Does Rails Hurt?
  11. Face alignment at 3000 FPS via Regressing Local Binary Features
  12. 剑指offer(C++)-JZ55:二叉树的深度(数据结构-树)
  13. 设计模式---责任链模式(C++实现)
  14. tiny4412 linux-4.2 移植(十一)LCD驱动移植
  15. Virtualbox主机与虚拟机相互访问
  16. keil与proteus联调C语言,51keil与proteus联调,实现在线仿真
  17. Vue中的keep-alive组件
  18. Android城市列表
  19. c语言变量周围堆栈损坏csdn,围绕变量“输入”的堆栈已损坏(Stack around the variable 'input' was corrupted)...
  20. QQ API设计说明书

热门文章

  1. jq如何改变html页面,jq同一页面内容切换
  2. c语言中int转string,C++中int型与string型互相转换
  3. ”小糊涂“与美女网站的收费机制
  4. opengl学习笔记
  5. wordpress调用the_excerpt()不带p标签
  6. 弘辽科技:淘宝店铺违规再也不用怕了 这个新规能抵消扣分处罚
  7. 2006年10大变态站名网站排名
  8. Android下拉状态栏 快捷开关的添加与删除
  9. LT.852二分法查找指定数字,绝对值最小的数
  10. 网络计算机amd,AMD多屏显示设置指南_计算机硬件和网络_IT /计算机_信息