当前位置:   article > 正文

【Unity渲染——屏幕后处理】简单图像处理(亮度、色相、饱和度、对比度、晕影效果)_unity后处理效果

unity后处理效果

简介

       屏幕后处理(Post-Processing)是从渲染完成后再进行特定的处理技术,以改变游戏画面的颜色、饱和度、对比度、景深和运动模糊等等。这些特效可以增强游戏场景的观感,利用具有高分辨率和带宽等优势的纹理贴图,能够进一步增强特效的真实感。本次项目中,将使用Unity Shader,ASE与Cg语言进行屏幕后处理,项目包括简单图像处理(亮度、色相、饱和度、对比度、晕影效果);模糊处理(均值模糊、高斯模糊、双向模糊、Kawase模糊等);Bloom效果和ToneMapping算法。大概会分为三个小节进行更新。

  1. 后处理在渲染管线流程

        在渲染管线中,后处理通常位于渲染过程的末尾,即在所有的渲染通道(例如顶点着色器、片段着色器等)完成之后执行后处理操作。后处理操作是在已经渲染的图像上进行的,它不会影响到场景的几何形状或光照等因素。

        一般来说,后处理操作会在渲染目标纹理(例如帧缓存或渲染纹理)上进行。在每一帧的渲染完成后,后处理操作会将渲染目标纹理作为输入,并应用各种效果和滤镜来修改图像的外观。

简单图像处理

        简单图像处理实现了一些简单的修图功能,包括亮度处理、色相调整、饱和度与对比度调整和晕影效果的实现。

亮度处理

        亮度处理就是通过调整图像中像素的亮度值来改变图像的整体亮度,以下使用线性的亮度调整,通过对每个像素乘上一个亮度值常熟来增加或减少亮度。

        定义属性_Brightness用于控制亮度变换,在fragment shader中进行计算

  1. //获取图像颜色值
  2. half4 col = tex2D(_MainTex, i.uv);
  3. //亮度处理
  4. half3 final_color = col.rgb * _Brightness;

创建C#脚本控制后处理Shader

        OnRenderImage() 是Unity 中用于在渲染每一帧图像之前或之后执行自定义后处理操作的函数。它是 MonoBehaviour 类的一个方法,可以在脚本中实现。通过OnRenderImage(),控制每一帧渲染图像之后需要执行的执行自定义的后处理效果。

        创建C#脚本EasyImageEffect.cs,传入对应的Material材质用于控制各种后处理的效果。编写OnRenderImage()函数。

        注意:只能搭载在Main Camera上才能正确执行OnRenderImage()方法。

  1. [ExecuteInEditMode()]
  2. public class EasyImageEffect : MonoBehaviour
  3. {
  4.     public Material material;
  5.     public float Brightness = 1;
  6. private void OnRenderImage(RenderTexture source, RenderTexture destination)
  7.     {
  8.         material.SetFloat("_Brightness", Brightness);
  9.         Graphics.Blit(source, destination, material);
  10.     }
  11. // Start is called before the first frame update
  12. void Start()
  13.     {
  14.         if(material == null || SystemInfo.supportsImageEffects == false
  15.             || material.shader ==null || material.shader.isSupported == false)
  16.         {
  17.             enabled = false;
  18.             return;
  19.         }
  20.       
  21.     }
  22.    
  23. }

        声明一个公共材质变量 material,用于指定要应用效果的材质。还声明了一个公共浮点数变量 Brightness,用于控制图像的亮度。

        在OnRenderImage()中设置材质的 _Brightness 属性,然后,使用 Graphics.Blit 函数将源纹理 source 绘制到目标纹理 destination 上,并应用材质。

        在Start()方法中检查材质是否有效(不为空且支持图像效果),以及材质的 Shader 是否有效(不为空且支持)。如果不满足条件,则禁用脚本。使用[ExecuteInEditMode()]规定Unity在编辑器模式下也能执行该脚本的代码,方便执行后处理操作。完成上述操作后,我们就获得了一个简单的后处理效果——控制图像的亮度。

图 1 亮度处理

色相(Hue)处理

  •         RGB / HSV

        RGB颜色模型:RGB 颜色模型使用三个通道(红色、绿色和蓝色)来表示颜色。每个通道的值范围从 0 到 255,其中 0 表示最暗的颜色,255 表示最亮的颜色。通过组合这三个通道的值,可以生成各种颜色。

        HSV颜色模型:HSV 颜色模型由三个参数组成:色相(Hue)、饱和度(Saturation)和亮度(Value)。

        HSV 颜色模型更适合用于颜色选择和调整,因为它可以更直观地表示颜色的感知特性。例如在本节项目中进行的色相调整,可以改变颜色的种类,而不改变亮度与对比度。

图 2 HSV模型

        在Unity中,图片通常以RGB模型进行保存,因此要调整色相时,需要将RGB格式转换为HSV格式后再进行修改。‘修改完成后再将HSV转化为RGB方便存储。具体的转换算法分析见

https://blog.csdn.net/shandianfengfan/article/details/120600453?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170131384916800227446861%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170131384916800227446861&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-120600453-null-null.142^v96^pc_search_result_base6&utm_term=RGB%E8%BD%ACHSV&spm=1018.2226.3001.4187icon-default.png?t=N7T8https://blog.csdn.net/shandianfengfan/article/details/120600453?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170131384916800227446861%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170131384916800227446861&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-120600453-null-null.142^v96^pc_search_result_base6&utm_term=RGB%E8%BD%ACHSV&spm=1018.2226.3001.4187https://blog.csdn.net/shandianfengfan/article/details/120600453?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170131384916800227446861%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170131384916800227446861&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-120600453-null-null.142^v96^pc_search_result_base6&utm_term=RGB%E8%BD%ACHSV&spm=1018.2226.3001.4187

        以下转换函数截取自ASE

  1. float3 HSVToRGB( float3 c )
  2. {
  3.     float4 K = float4( 1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0 );
  4.     float3 p = abs( frac( c.xxx + K.xyz ) * 6.0 - K.www );
  5.     return c.z * lerp( K.xxx, saturate( p - K.xxx ), c.y );
  6. }
  7.            
  8. float3 RGBToHSV(float3 c)
  9. {
  10.     float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
  11.     float4 p = lerp( float4( c.bg, K.wz ), float4( c.gb, K.xy ), step( c.b, c.g ) );
  12.     float4 q = lerp( float4( p.xyw, c.r ), float4( c.r, p.yzx ), step( p.x, c.r ) );
  13.     float d = q.x - min( q.w, q.y );
  14.     float e = 1.0e-10;
  15.     return float3( abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
  16. }

        声明float _HueShift变量用以更改HSV的x分量。调用转换函数进行操作。

  1. half3 hsv = RGBToHSV(rgb);
  2. rgb = HSVToRGB(float3((_HueShift.x + hsv.x),hsv.y,+ hsv.z));

        在c#脚本中,添加新变量用于控制色相变换。并在Update()上实时更新色相值

  1. void Update()
  2. {
  3.     HueShift = HueShift + (0.2f * Time.deltaTime);
  4. }

图 色相变换

        可以额外声明两个变量用作HSV的饱和度(Saturation)和亮度(Value)的调整。在这里不做赘述。

饱和度处理

        饱和度是指色彩的鲜艳程度,也称色彩的纯度。在色彩学中,原色饱和度最高,随着饱和度降低,色彩变得暗淡直至成为无彩色,即失去色相的色彩。

        在HSV中,只需调整S变量即可控制图像的饱和度。在RGB模型下则是求得原图像的灰度图像,再从原图像与灰度图像之间做一个0-1的插值即可得出。

  1. //饱和度处理
  2. float3 Cginc_GammaProcess(float3 col, float satur)
  3. {
  4. //gamma空间求灰度图像lumin
  5. float lumin = dot(col, float3(0.22, 0.707, 0.071));
  6. //原图像与灰度图像做插值计算
  7. col = lerp(lumin, col, satur);
  8. return col;
  9. }

        

饱和度处理


      注:Unity默认使用gamma空间。有关gamma空间与线性空间的概念详细见

伽马空间与线性空间_gamma空间和线性空间-CSDN博客文章浏览阅读1.2k次,点赞2次,收藏7次。随着真实性更高的基于物理渲染(PBR)的到来,线性空间(Linear space)光照计算也越来越被经常提及。虽然线性空间和与之“对立”的伽马空间(gamma space)是简单而重要的概念,但很多开发者对它们的真正意义并不了解。这篇文档将会介绍伽马空间和线性空间、它们之间的区别以及在Unity引擎中的应用。 线性空间是什么?简单的说,在线性空间对数字化的颜色和光照强度进行相加相乘计算得......_gamma空间和线性空间https://blog.csdn.net/lejian/article/details/125313878?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170150211516800226583966%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170150211516800226583966&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-125313878-null-null.142^v96^pc_search_result_base6&utm_term=%E7%BA%BF%E6%80%A7%E7%A9%BA%E9%97%B4%E4%B8%8E%E4%BC%BD%E9%A9%AC%E7%A9%BA%E9%97%B4&spm=1018.2226.3001.4187

对比度处理

        对比度是指图像中最亮和最暗区域之间的差异程度。它是一个视觉上的概念,用于描述图像中颜色和亮度的变化程度。对比度越低,颜色越趋于一致。

        取颜色中值(0.5, 0.5, 0.5),使用中值与原图像进行插值处理。

  1. //对比度处理
  2. float3 ContrastProcess(float3 col, float contr)
  3. {
  4. float3 midPoint = float3(0.5, 0.5, 0.5);
  5. col = lerp(midPoint, col, contr);
  6. return col;
  7. }

 

对比度处理

暗角晕影效果

        暗角一词属于摄影术语。对着亮度均匀景物,画面四角有变暗的现象,叫做“失光”,俗称“暗角”。

        定义一个暗角图像Mask,计算每个uv坐标离中心点的远近,离中心点越远则越暗。定义一个变量_VignetteIntensity控制暗角的大小。对暗角图像做升幂处理,计算后原本的椭圆形暗角将朝圆角方形逼近。最后使用smoothstep函数实现暗角的平滑过渡。

  1. float VignetteProcess(float2 uv, float vigIntensity, float vigPow, float vigSmoothness)
  2. {
  3. float2 d = abs(uv - half2(0.5, 0.5)) * vigIntensity;
  4. d = pow(saturate(d), vigPow);
  5. float dist = length(d);
  6. float vFactor = pow(saturate(1.0 - dist * dist), vigSmoothness);
  7. return vFactor;
  8. }

 

项目源码

Shader源码

  1. Shader "Hidden/EasyImage"
  2. {
  3. Properties
  4. {
  5. _MainTex ("Texture", 2D) = "white" {}
  6. _AddTex ("Add Tex", 2D) = "black" {}
  7. _Brightness ("Brightness", Float) = 1
  8. _Saturation ("Saturation", Float) = 0
  9. _Contrast ("Contrast", Float) = 1
  10. _VignetteIntensity ("VignetteIntensity", Range(0.05, 3)) = 3
  11. _VignetteRoundness ("VignetteRoundness", Range (1, 6)) = 2
  12. _VignetteSmoothness ("VignetteSmoothness", Range (0.05, 5)) = 5
  13. _HueShift("HueShift", Float) = 1
  14. }
  15. SubShader
  16. {
  17. // No culling or depth
  18. Cull Off ZWrite Off ZTest Always
  19. Pass
  20. {
  21. CGPROGRAM
  22. #pragma vertex vert_img
  23. #pragma fragment frag
  24. #include "UnityCG.cginc"
  25. #include "../../Cginc/IncludeForShader.cginc"
  26. sampler2D _MainTex;
  27. sampler2D _AddTex;
  28. float _Brightness;
  29. float _Saturation;
  30. float _Contrast;
  31. float _VignetteIntensity;
  32. float _VignetteRoundness;
  33. float _VignetteSmoothness;
  34. float _HueShift;
  35. //使用unity自带的顶点着色器vert_img与结构体v2f_img
  36. //不需要使用顶点着色器时可以使用unity自带的vert_img
  37. fixed4 frag (v2f_img i) : SV_Target
  38. {
  39. //获取图像颜色值
  40. half4 col = tex2D(_MainTex, i.uv);
  41. //亮度处理
  42. half3 final_color = col.rgb * _Brightness;
  43. //色相Hue。先将RGB图像改成HSV的存储格式,通过修改HSV的H(Hue)通道进行色相的调整
  44. //随后再将HSV转回RGB
  45. half3 hsv = Cginc_RGBToHSV(final_color);
  46. final_color = Cginc_HSVToRGB(float3((_HueShift.x + hsv.x),hsv.y,+ hsv.z));
  47. //饱和度处理
  48. //gamma空间求明度(固定算法)→处理后得出灰度图像lumin
  49. final_color = Cginc_GammaProcess(final_color, _Saturation);
  50. //对比度处理
  51. final_color = Cginc_ContrastProcess(final_color, _Contrast);
  52. //暗角/晕影
  53. final_color = final_color * Cginc_VignetteProcess(i.uv, _VignetteIntensity, _VignetteRoundness, _VignetteSmoothness);
  54. //return vFactor.xxxx;
  55. return half4(final_color, col.a);
  56. }
  57. ENDCG
  58. }
  59. }
  60. }
  1. #ifndef IncludeForShader
  2. #define IncludeForShader
  3. float3 Cginc_HSVToRGB( float3 c )
  4. {
  5. float4 K = float4( 1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0 );
  6. float3 p = abs( frac( c.xxx + K.xyz ) * 6.0 - K.www );
  7. return c.z * lerp( K.xxx, saturate( p - K.xxx ), c.y );
  8. }
  9. float3 Cginc_RGBToHSV(float3 c)
  10. {
  11. float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
  12. float4 p = lerp( float4( c.bg, K.wz ), float4( c.gb, K.xy ), step( c.b, c.g ) );
  13. float4 q = lerp( float4( p.xyw, c.r ), float4( c.r, p.yzx ), step( p.x, c.r ) );
  14. float d = q.x - min( q.w, q.y );
  15. float e = 1.0e-10;
  16. return float3( abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
  17. }
  18. //饱和度处理
  19. //gamma空间求明度(固定算法)→处理后得出灰度图像lumin
  20. float3 Cginc_GammaProcess(float3 col, float satur)
  21. {
  22. float lumin = dot(col, float3(0.22, 0.707, 0.071));
  23. col = lerp(lumin, col, satur);
  24. return col;
  25. }
  26. //对比度处理
  27. float3 Cginc_ContrastProcess(float3 col, float contr)
  28. {
  29. float3 midPoint = float3(0.5, 0.5, 0.5);
  30. col = lerp(midPoint, col, contr);
  31. return col;
  32. }
  33. //暗角效果
  34. float Cginc_VignetteProcess(float2 uv, float vigIntensity, float vigPow, float vigSmoothness)
  35. {
  36. float2 d = abs(uv - half2(0.5, 0.5)) * vigIntensity;
  37. d = pow(saturate(d), vigPow);
  38. float dist = length(d);
  39. float vFactor = pow(saturate(1.0 - dist * dist), vigSmoothness);
  40. return vFactor;
  41. }
  42. #endif

C#脚本源码

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using Unity.VisualScripting;
  4. using UnityEngine;
  5. [ExecuteInEditMode()]
  6. public class EasyImageEffect : MonoBehaviour
  7. {
  8. public Material material;
  9. [Range(0.0f, 7.0f)]
  10. public float Brightness = 1;
  11. [Range(0.0f, 1.0f)]
  12. public float Saturation = 1;
  13. [Range(0.0f, 2.0f)]
  14. public float Contrast = 1;
  15. [Range(0.05f, 3.0f)]
  16. public float vigIntensity = 3.0f;
  17. [Range(1.0f, 6.0f)]
  18. public float vigPow = 2.0f;
  19. [Range(0.05f, 5.0f)]
  20. public float vigSmooth = 5.0f;
  21. public float HueShift = 0;
  22. public float ShiftSpeed = 2;
  23. // Start is called before the first frame update
  24. void Start()
  25. {
  26. if(material == null || material.shader ==null
  27. || material.shader.isSupported == false)
  28. {
  29. enabled = false;
  30. return;
  31. }
  32. HueShift = 0;
  33. }
  34. // Update is called once per frame
  35. void Update()
  36. {
  37. //自动控制色相变换
  38. HueShift += Time.deltaTime * ShiftSpeed * 0.1f;
  39. }
  40. private void OnRenderImage(RenderTexture source, RenderTexture destination)
  41. {
  42. material.SetFloat("_Brightness", Brightness);
  43. material.SetFloat("_Saturation", Saturation);
  44. material.SetFloat("_Contrast", Contrast);
  45. material.SetFloat("_VignetteIntensity", vigIntensity);
  46. material.SetFloat("_VignetteRoundness", vigPow);
  47. material.SetFloat("_VignetteSmoothness", vigSmooth);
  48. material.SetFloat ("_HueShift", HueShift);
  49. Graphics.Blit(source, destination, material);
  50. }
  51. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/632957
推荐阅读
相关标签
  

闽ICP备14008679号