江苏省建设厅网站施工员证查询,响应式网站字体大小,网站运营技巧,网站建设的想法和意见大家好#xff0c;我是阿赵。 继续介绍屏幕后处理#xff0c;这一期介绍一下Tonemapping色调映射
一、Tone Mapping的介绍 Tone Mapping色调映射#xff0c;是一种颜色的映射关系处理#xff0c;简单一点说#xff0c;一般是从原始色调#xff08;通常是高动态范围我是阿赵。 继续介绍屏幕后处理这一期介绍一下Tonemapping色调映射
一、Tone Mapping的介绍 Tone Mapping色调映射是一种颜色的映射关系处理简单一点说一般是从原始色调通常是高动态范围HDR映射到目标色调通常是低动态范围LDR。 由于HDR的颜色值是能超过1的但实际上在LDR范围颜色值最大只能是1。如果我们要在LDR的环境下尽量模拟HDR的效果超过1的颜色部分怎么办呢 最直接想到的是两种可能 1、截断大于1的部分 大于1的部分直接等于1小于1的部分保留。这种做法会导致超过1的部分全部变成白色在原始图片亮度比较高的情况下转换完之后可能就是一片白茫茫的效果。 2、对颜色进行线性的缩放 把原始颜色的最大值看做1然后把原始的所有颜色进行整体的等比缩放。这样做能保留一定的效果。但由于原始的HDR颜色的跨度可能比0到1大很多所以整体缩小之后整个画面就会变暗很多了没有了HDR的通透光亮的感觉。 为了能让HDR颜色映射到LDR之后还能保持比较接近的效果上面两种方式的处理显然都是不好的。 Tonemapping也是把HDR颜色范围映射到0-1的LDR颜色范围但它并不是线性缩放而是曲线的缩放。 从上面这个例子可以看出来Tonemapping映射之后的颜色有些地方是变暗了比如深颜色的裤子但有些地方却是变亮了的比如头发和肩膀衣服上的阴影。整体的颜色有一种电影校色之后的感觉。 很多游戏美工在没有技术人员配合的情况下都很喜欢自己挂后处理其中Tonemapping应该是除了Bloom以外美工们最喜欢的一种后处理了虽然不知道为什么但就是觉得颜色好看了。 虽然屏幕后处理看着好像很简单实现挂个组件调几个参数就能化腐朽为神奇把原本平淡无奇的画面变得好看。但其实后处理都是有各种额外消耗的所以我一直不是很建议美工们只会依靠后处理来扭转画面缺陷的特别是做手游。
二、Tonemapping的代码实现
1、C#代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class TonemappingCtrl : MonoBehaviour
{private Material toneMat;public bool isTonemapping false;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}private bool TonemappingFun(RenderTexture source, RenderTexture destination){if (toneMat null){toneMat new Material(Shader.Find(Hidden/ToneMapping));}if (toneMat null || toneMat.shader null || toneMat.shader.isSupported false){return false;}Graphics.Blit(source, destination, toneMat);return true;}private void OnRenderImage(RenderTexture source, RenderTexture destination){if(isTonemapping false){Graphics.Blit(source, destination);return;}RenderTexture finalRt source;if (TonemappingFun(finalRt,finalRt)false){Graphics.Blit(source, destination);}else{Graphics.Blit(finalRt, destination);}}
}C#部分的代码和其他后处理没区别都是通过OnRenderImage里面调用Graphics.Blit
2、Shader
Shader Hidden/ToneMapping
{Properties{_MainTex (Texture, 2D) white {}}SubShader{// No culling or depthCull Off ZWrite Off ZTest AlwaysPass{CGPROGRAM#pragma vertex vert_img#pragma fragment frag#include UnityCG.cgincsampler2D _MainTex; float3 ACES_Tonemapping(float3 x){float a 2.51f;float b 0.03f;float c 2.43f;float d 0.59f;float e 0.14f;float3 encode_color saturate((x*(a*x b)) / (x*(c*x d) e));return encode_color;}fixed4 frag (v2f_img i) : SV_Target{fixed4 col tex2D(_MainTex, i.uv);half3 linear_color pow(col.rgb, 2.2);half3 encode_color ACES_Tonemapping(linear_color);col.rgb pow(encode_color, 1 / 2.2);return col;}ENDCG}}
}需要说明一下 1.色彩空间的转换 由于默认显示空间是Gamma空间所以先通过pow(col.rgb, 2.2)把颜色转换成线性空间然后再进行Tonemapping映射最后再pow(encode_color, 1 / 2.2)把颜色转回Gamma空间 2.Tonemapping映射算法
float3 ACES_Tonemapping(float3 x){float a 2.51f;float b 0.03f;float c 2.43f;float d 0.59f;float e 0.14f;float3 encode_color saturate((x*(a*x b)) / (x*(c*x d) e));return encode_color;}把颜色进行Tonemapping映射。这个算法是网上都可以百度得到的。
三、Tonemapping和其他后处理的配合 一般来说Tonemapping只是一个固定颜色映射效果所以应该是需要配合着其他的效果一起使用才会得到比较好的效果。比如我之前介绍过的校色、暗角、Bloom等。 可以做出各种不同的效果不同于原始颜色的平淡调整完之后的颜色看起来会比较有电影的感觉。 这也是我为什么要在Unity有PostProcessing后处理插件的情况下还要介绍使用自己写Shader实现屏幕后处理的原因。PostProcessing作为一个插件它可能会存在很多功能会有很多额外的计算你可能只需要用到其中的某一个小部分的功能和效果。 而我们自己写Shader实现屏幕后处理自由度非常的高喜欢在哪里添加或者修改一些效果都可以。 比如我可以写一个脚本把之前介绍过的所有后处理效果都加进去
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[ExecuteInEditMode]
public class ImageEffectCtrl : MonoBehaviour
{//--------调色-----------private Material colorMat;public bool isColorAjust false;[Range(-5,5)]public float saturation 1;[Range(-5,5)]public float contrast 1;[Range(0,1)]public float hueShift 0;[Range(0,5)]public float lightVal 1;[Range(0,3)]public float vignetteIntensity 1.8f;[Range(0,5)]public float vignetteSmoothness 5;//-------模糊-----------private Material blurMat;public bool isBlur false;[Range(0, 4)]public float blurSize 0;[Range(-3,3)]public float blurOffset 1;[Range(1,3)]public int blurType 3;//-----光晕----------private Material brightMat;private Material bloomMat;public bool isBloom false;[Range(0,1)]public float brightCut 0.5f;[Range(0, 4)]public float bloomSize 0;[Range(-3, 3)]public float bloomOffset 1;public int bloomType 3;[Range(1, 3)]//---toneMapping-----private Material toneMat;public bool isTonemapping false;// Start is called before the first frame updatevoid Start(){//if(colorMat null||colorMat.shader null||colorMat.shader.isSupported false)//{// this.enabled false;//}}// Update is called once per framevoid Update(){}private bool AjustColor(RenderTexture source, RenderTexture destination){if(colorMat null){colorMat new Material(Shader.Find(Hidden/AzhaoAjustColor));}if(colorMat null||colorMat.shader null||colorMat.shader.isSupported false){return false;}colorMat.SetFloat(_Saturation, saturation);colorMat.SetFloat(_Contrast, contrast);colorMat.SetFloat(_HueShift, hueShift);colorMat.SetFloat(_Light, lightVal);colorMat.SetFloat(_VignetteIntensity, vignetteIntensity);colorMat.SetFloat(_VignetteSmoothness, vignetteSmoothness);Graphics.Blit(source, destination, colorMat, 0);return true;}private Material GetBlurMat(int bType){if(bType 1){return new Material(Shader.Find(Hidden/AzhaoBoxBlur));}else if(bType 2){return new Material(Shader.Find(Hidden/AzhaoGaussianBlur));}else if(bType 3){return new Material(Shader.Find(Hidden/AzhaoKawaseBlur));}else{return null;}}private bool CheckNeedCreateBlurMat(Material mat,int bType){if(mat null){return true;}if(mat.shader null){return true;}if(bType 1){if(mat.shader.name ! Hidden/AzhaoBoxBlur){return true;}else{return false;}}else if(bType 2){if (mat.shader.name ! Hidden/AzhaoGaussianBlur){return true;}else{return false;}}else if (bType 3){if (mat.shader.name ! Hidden/AzhaoKawaseBlur){return true;}else{return false;}}else{return false;}}private bool BlurFun(RenderTexture source, RenderTexture destination,float blurTime,int bType,float offset ){if(CheckNeedCreateBlurMat(blurMat,bType)true){blurMat GetBlurMat(bType);}if (blurMat null || blurMat.shader null || blurMat.shader.isSupported false){return false;}blurMat.SetFloat(_BlurOffset, offset);float width source.width;float height source.height;int w Mathf.FloorToInt(width);int h Mathf.FloorToInt(height);RenderTexture rt1 RenderTexture.GetTemporary(w, h);RenderTexture rt2 RenderTexture.GetTemporary(w, h);Graphics.Blit(source, rt1);for (int i 0; i blurTime; i){ReleaseRT(rt2);width width / 2;height height / 2;w Mathf.FloorToInt(width);h Mathf.FloorToInt(height);rt2 RenderTexture.GetTemporary(w, h);Graphics.Blit(rt1, rt2, blurMat, 0);width width / 2;height height / 2;w Mathf.FloorToInt(width);h Mathf.FloorToInt(height);ReleaseRT(rt1);rt1 RenderTexture.GetTemporary(w, h);Graphics.Blit(rt2, rt1, blurMat, 1);}for (int i 0; i blurTime; i){ReleaseRT(rt2);width width * 2;height height * 2;w Mathf.FloorToInt(width);h Mathf.FloorToInt(height);rt2 RenderTexture.GetTemporary(w, h);Graphics.Blit(rt1, rt2, blurMat, 0);width width * 2;height height * 2;w Mathf.FloorToInt(width);h Mathf.FloorToInt(height);ReleaseRT(rt1);rt1 RenderTexture.GetTemporary(w, h);Graphics.Blit(rt2, rt1, blurMat, 1);}Graphics.Blit(rt1, destination);ReleaseRT(rt1);rt1 null;ReleaseRT(rt2);rt2 null;return true;}private bool BrightRangeFun(RenderTexture source, RenderTexture destination){if(brightMat null){brightMat new Material(Shader.Find(Hidden/BrightRange));}if (brightMat null || brightMat.shader null || brightMat.shader.isSupported false){return false;}brightMat.SetFloat(_BrightCut, brightCut);Graphics.Blit(source, destination, brightMat);return true;}private bool BloomAddFun(RenderTexture source,RenderTexture destination, RenderTexture brightTex){if(bloomMat null){bloomMat new Material(Shader.Find(Hidden/AzhaoBloom));}if (bloomMat null || bloomMat.shader null || bloomMat.shader.isSupported false){return false;}bloomMat.SetTexture(_brightTex, brightTex);Graphics.Blit(source, destination, bloomMat);return true;}private bool TonemappingFun(RenderTexture source, RenderTexture destination){if(toneMat null){toneMat new Material(Shader.Find(Hidden/ToneMapping));}if (toneMat null || toneMat.shader null || toneMat.shader.isSupported false){return false;}Graphics.Blit(source, destination, toneMat);return true;}private void CopyRender(RenderTexture source,RenderTexture destination){Graphics.Blit(source, destination);}private void ReleaseRT(RenderTexture rt){if(rt!null){RenderTexture.ReleaseTemporary(rt);}}private void OnRenderImage(RenderTexture source, RenderTexture destination){ RenderTexture finalRt source;RenderTexture rt2 RenderTexture.GetTemporary(source.width, source.height);RenderTexture rt3 RenderTexture.GetTemporary(source.width, source.height);if (isBloom true){if(BrightRangeFun(finalRt, rt2)true){if(BlurFun(rt2, rt3, bloomSize,bloomType,bloomOffset)true){if(BloomAddFun(source, finalRt, rt3)true){} }}}if(isBlur true){if (blurSize 0){if (BlurFun(finalRt, finalRt, blurSize,blurType,blurOffset) true){}}}if (isTonemapping true){if (TonemappingFun(finalRt, finalRt) true){}}if (isColorAjust true){ if (AjustColor(finalRt, finalRt) true){}}CopyRender(finalRt, destination);ReleaseRT(finalRt);ReleaseRT(rt2);ReleaseRT(rt3);}
}一个脚本控制所有后处理。当然这样的做法只是方便也不见得很好我还是比较喜欢根据实际用到多少个效果单独去写对应的脚本那样我觉得性能才是最好的。