【Unity3D】Bloom特效

2024-01-20 01:40
文章标签 特效 unity3d bloom

本文主要是介绍【Unity3D】Bloom特效,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 Bloom 特效原理

        Bloom 特效是指:将画面中较亮的区域向外扩散,造成一种朦脓的效果。实现 Bloom 特效,一般要经过 3 个阶段处理:亮区域检测、高斯模糊、Bloom 合成。

        本文完整资源见→Unity3D Bloom 特效。

        1)亮区域检测

        根据亮度阈值检测亮区,如下从原图中提取亮区域。

原图
亮区域

        2)高斯模糊

        对亮区域进行高斯模糊(原理见→高斯模糊特效),使得亮区域往外扩散,并产生朦脓效果。

亮区域高斯模糊

        3)Bloom 合成

        将高斯模糊处理后的亮区域图像与原图像叠加。 

Bloom图像合成

2 代码实现

        Bloom.cs

using UnityEngine;[ExecuteInEditMode] // 编辑态可以查看脚本运行效果
[RequireComponent(typeof(Camera))] // 需要相机组件
public class Bloom : MonoBehaviour {private Material material = null; // 材质[Range(0, 4)]public int iterations = 3; // 高斯模糊迭代次数[Range(0.2f, 3.0f)]public float blurSpread = 0.6f; // 每次迭代纹理坐标偏移的速度[Range(1, 8)]public int downSample = 2; // 降采样比率[Range(0.0f, 4.0f)]public float luminanceThreshold = 0.6f; // 亮度阈值private void Start() {material = new Material(Shader.Find("MyShader/Bloom"));material.hideFlags = HideFlags.DontSave;}void OnRenderImage(RenderTexture src, RenderTexture dest) {if (material != null) {material.SetFloat("_LuminanceThreshold", luminanceThreshold); // 设置亮度阈值int rtW = src.width/downSample; // 降采样的纹理宽度int rtH = src.height/downSample; // 降采样的纹理高度RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);buffer0.filterMode = FilterMode.Bilinear; // 滤波模式设置为双线性Graphics.Blit(src, buffer0, material, 0);for (int i = 0; i < iterations; i++) {material.SetFloat("_BlurSize", 1.0f + i * blurSpread);RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);Graphics.Blit(buffer0, buffer1, material, 1); // 渲染垂直的Pass(高斯模糊)RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);Graphics.Blit(buffer0, buffer1, material, 2); // 渲染垂直的Pass(高斯模糊)RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;}material.SetTexture("_Bloom", buffer0); // 将高斯模糊处理后的纹理设置给_BloomGraphics.Blit(src, dest, material, 3);RenderTexture.ReleaseTemporary(buffer0);} else {Graphics.Blit(src, dest);}}
}

        Bloom.shader

Shader "MyShader/Bloom" {Properties {_MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理_Bloom ("Bloom (RGB)", 2D) = "black" {} // Bloom处理需要的纹理(即高斯模糊处理后的纹理)_LuminanceThreshold ("Luminance Threshold", Float) = 0.5 // 亮度阈值_BlurSize ("Blur Size", Float) = 1.0 // 模糊尺寸(纹理坐标的偏移量)}SubShader {CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex; // 主纹理half4 _MainTex_TexelSize; // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)sampler2D _Bloom; // Bloom处理需要的纹理(即高斯模糊处理后的纹理)float _LuminanceThreshold; // 亮度阈值float _BlurSize; // 模糊尺寸(纹理坐标的偏移量)fixed luminance(fixed4 color) { // 计算亮度return  0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b; }// 采样纹理的亮度减去亮度阈值, 小于0的值将取0fixed4 fragExtractBright(v2f_img i) : SV_Target { // v2f_img为内置结构体, 里面只包含pos和uvfixed4 c = tex2D(_MainTex, i.uv);fixed val = saturate(luminance(c) - _LuminanceThreshold);return c * val;}struct v2fBloom { // v2fBloom之所以不用v2f_img替代, 因为v2fBloom.uv是四维的, 而v2f_img.uv是二维的float4 pos : SV_POSITION; // 裁剪空间顶点坐标half4 uv : TEXCOORD0; // 纹理uv坐标};v2fBloom vertBloom(appdata_img v) {v2fBloom o;o.pos = UnityObjectToClipPos (v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)o.uv.xy = v.texcoord;o.uv.zw = v.texcoord;#if UNITY_UV_STARTS_AT_TOPif (_MainTex_TexelSize.y < 0.0)o.uv.w = 1.0 - o.uv.w; // 平台差异化处理#endifreturn o;}fixed4 fragBloom(v2fBloom i) : SV_Target {return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);}ENDCGZTest Always Cull Off ZWrite OffPass {  CGPROGRAM#pragma vertex vert_img // 使用内置的vert_img顶点着色器#pragma fragment fragExtractBrightENDCG  }UsePass "MyShader/GaussianBlur/GAUSSIAN_BLUR_VERTICAL" // 垂直高斯模糊处理UsePass "MyShader/GaussianBlur/GAUSSIAN_BLUR_HORIZONTAL" // 水平高斯模糊处理Pass {  CGPROGRAM  #pragma vertex vertBloom#pragma fragment fragBloom  ENDCG  }}FallBack Off
}

        说明: vert_img 是 Unity 内置的顶点着色器,v2f_img 是 Unity 内置的结构体变量,vert_img 和 v2f_img 的实现见→Shader常量、变量、结构体、函数。 

        GaussianBlur.shader

Shader "MyShader/GaussianBlur" { // 高斯模糊Properties{_MainTex("Base (RGB)", 2D) = "white" {} // 主纹理_BlurSize("Blur Size", Float) = 1.0 // 模糊尺寸(纹理坐标的偏移量)}SubShader{CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex; // 主纹理half4 _MainTex_TexelSize; // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)float _BlurSize; // 模糊尺寸(纹理坐标的偏移量)struct v2f {float4 pos : SV_POSITION; // 模型空间顶点坐标half2 uv[5]: TEXCOORD0; // 5个邻域的纹理坐标};v2f vertBlurVertical(appdata_img v) { // 垂直模糊顶点着色器v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)half2 uv = v.texcoord;o.uv[0] = uv;o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;return o;}v2f vertBlurHorizontal(appdata_img v) { // 水平模糊顶点着色器v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)half2 uv = v.texcoord;o.uv[0] = uv;o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;return o;}fixed4 fragBlur(v2f i) : SV_Target {float weight[3] = {0.4026, 0.2442, 0.0545}; // 大小为5的一维高斯核,实际只需记录3个权值fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];for (int j = 1; j < 3; j++) {sum += tex2D(_MainTex, i.uv[j * 2 - 1]).rgb * weight[j]; // 中心右侧或下侧的纹理*权值sum += tex2D(_MainTex, i.uv[j * 2]).rgb * weight[j]; // 中心左侧或上侧的纹理*权值}return fixed4(sum, 1.0);}ENDCGZTest Always Cull Off ZWrite OffPass {NAME "GAUSSIAN_BLUR_VERTICAL"CGPROGRAM#pragma vertex vertBlurVertical  #pragma fragment fragBlurENDCG}Pass {NAME "GAUSSIAN_BLUR_HORIZONTAL"CGPROGRAM#pragma vertex vertBlurHorizontal  #pragma fragment fragBlurENDCG}}FallBack "Diffuse"
}

3 运行效果

        调整模糊迭代次数 iterations 由 0 ~ 4 变化,效果如下: 

这篇关于【Unity3D】Bloom特效的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/624390

相关文章

Unity3D自带Mouse Look鼠标视角代码解析。

Unity3D自带Mouse Look鼠标视角代码解析。 代码块 代码块语法遵循标准markdown代码,例如: using UnityEngine;using System.Collections;/// MouseLook rotates the transform based on the mouse delta./// Minimum and Maximum values can

Unity3D 运动之Move函数和translate

CharacterController.Move 移动 function Move (motion : Vector3) : CollisionFlags Description描述 A more complex move function taking absolute movement deltas. 一个更加复杂的运动函数,每次都绝对运动。 Attempts to

『功能项目』战士的平A特效【35】

我们打开上一篇34武器的切换实例的项目, 本章要做的事情是在战士的每次按A键时在指定位置生成一个平A特效 首先将之前下载的技能拖拽至场景中 完全解压缩后重命名为AEffect 拖拽至预制体文件夹 进入主角动画的战士动画层级 双击第一次攻击 选择Animation 创建事件 创建的动画事件帧放在攻击动画挥剑指定处 命名为PerpetualAtt

第49课 Scratch入门篇:骇客任务背景特效

骇客任务背景特效 故事背景:   骇客帝国特色背景在黑色中慢慢滚动着! 程序原理:  1 、 角色的设计技巧  2 、克隆体的应用及特效的使用 开始编程   1、使用 黑色的背景: ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/7d74c872f06b4d9fbc88aecee634b074.png#pic_center)   2

【生日视频制作】酒吧一群美女车展模特大屏幕视频改字AE模板修改文字软件生成器教程特效素材【AE模板】

生日视频制作教程酒吧一群美女车展模特大屏幕视频改字AE模板修改文字特效广软件告生成神器素材祝福玩法AE模板工程 怎么如何做的【生日视频制作】酒吧一群美女车展模特大屏幕视频改字AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤: 安装AE软件 下载AE模板 把AE模板导入AE软件 修改图片或文字 渲染出视频

Unity3D在2D游戏中获取触屏物体的方法

我们的需求是: 假如屏幕中一个棋盘,每个棋子是button构成的,我们希望手指或者鼠标在哪里,就显示那个位置的button信息。 网上有很多获取触屏物体信息的信息的方法如下面代码所示: Camera cam = Camera.main; // pre-defined...if (touch.phase == TouchPhase.Bagan)){ // 如果触控点状态为按下Ray

【生日视频制作】劳斯莱斯库里南中控改名软件AE模板修改文字软件生成器教程特效素材【AE模板】

生日视频制作教程豪车劳斯莱斯库里南中控改名软件AE模板修改文字特效广告生成神器素材祝福玩法AE模板工程 怎么如何做的【生日视频制作】劳斯莱斯库里南中控改名软件AE模板修改文字软件生成器教程特效素材【AE模板】 生日视频制作步骤: 下载AE模板 安装AE软件 把AE模板导入AE软件 修改图片或文字 渲染出视频

印度再现超级大片,豪华阵容加顶级特效

最近,印度影坛再次掀起了风潮,一部名为《毗湿奴降临》的神话大片强势登陆各大影院,上映首周票房就飙升至105亿卢比,成功占据了票房榜首的位置。之后,这部电影也在北美上映,海外市场的表现同样不俗,收获了相当亮眼的票房成绩。作为一部印度神话科幻大片,《毗湿奴降临》不仅在本土大火,在国际市场上也引发了不小的关注。 《毗湿奴降临》由印度著名导演纳格·阿什温执导,卡司阵容极其豪华,集结了迪皮卡·帕度柯妮

Unity3D Shader详解:只画顶点或只画线框

在Unity3D开发中,Shader是控制渲染过程的关键组件,它允许开发者自定义物体的渲染方式。有时,为了特定的视觉效果,我们可能需要只渲染模型的顶点或者只显示其线框。下面,我们将详细探讨这两种效果的技术实现,并给出相应的代码示例。 对惹,这里有一个游戏开发交流小组,大家可以点击进来一起交流一下开发经验呀! 只画顶点 在Unity中直接“只画顶点”的概念可能不是非常直观,因为顶点本身只是模型

Flink实例(六十八):布隆过滤器(Bloom Filter)的原理和实现

什么情况下需要布隆过滤器? 先来看几个比较常见的例子 字处理软件中,需要检查一个英语单词是否拼写正确在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上在网络爬虫里,一个网址是否被访问过yahoo, gmail等邮箱垃圾邮件过滤功能 这几个例子有一个共同的特点: 如何判断一个元素是否存在一个集合中? 常规思路 数组链表树、平衡二叉树、TrieMap (红黑树)哈希表 虽然上面描述的