找回密码
 立即注册
查看: 391|回复: 1

[笔记] [译] 实现Unity 后处理特效 (1)

[复制链接]
发表于 2023-2-14 08:31 | 显示全部楼层 |阅读模式
前阵子我尝试了一下在Unity中实现后处理特效,顺手写了这篇文章以做记录。我会在这篇文章中简单介绍下我的方法。
首先我们要了解后处理是什么?后处理是对当前画面的特殊处理或者实现一些有趣、特殊效果的技法。简而言之,可以理解为Photoshop的滤镜,AE的Effect功能。Unity中也支持后处理堆栈。(在下篇文章中会讲到)




我们可以把自定义后处理添加至堆栈(Stack)中,这部分我之后再说。
先打开Unity,依次选择Create - Shader - Image Effect Shader,生成一个Image Effect着色器。


只需点几次,我们就可以得到一个基础Image effect shader,非常方便!然后把命名改成ShadowThresholdCustomEffect.shader吧。(今天我要实现这个shader)


初次点开Shader代码的话,代码应如上图所示,把Hidden换成ImageEffect吧。


如此一来,我们生成的材质就可以按这种方法来选择shader了(如果是Hidden的话,就无法在shader目录中看到材质了)。假如之后我们要在脚本文件中动态生成材质的话,用Hidden可能会更好,但今天我的目标是做一个简单的实现,所以这里我先把名字改了。


Cull Off,ZWrite Off,ZTest Always这些都是后处理中的默认选项,默认自动开启,大家知道有这些功能就好,不用刻意去记。其实就是关闭剔除(设成Cull back也行...),不使用Z值,让它一直出现在最上方的意思。虽然实质性的计算都要在这个Shader里完成,但这部分我先跳过,之后再处理。创建材质的话,会发现里面只有一个放Main texture的贴图槽,其他啥也没有。


主纹理我先空着,因为我会用脚本来修改shader,所以现在可以先不动材质。做好了用于内部计算的shader之后,现在我还需写一个控制脚本,我把它命名为ShadowThresholdCustomEffect.cs,然后在里面创建一个OnRenderImage函数,写上Graphics.Blit(source, destination, shadowMaterial),这里的shadowMaterial是我随便取的变量名。OnRenderImage是在渲染目标(Render target)上完成了所有的渲染后才调用的函数。


而Graphics.Blit是在渲染目标(destination)上用我刚做好的Shader(Material)计算源图像(source)值的函数,两者有所不同。这样一来,我就构建好了生成render target的函数,接着就要声明在函数中使用的变量。(先声明变量也没问题)我目前只需要材质,那就先声明下材质吧。


接着把相机添加至脚本...把材质声明为public的话,就可以在inspector面板中看到它。在inspector中,把它和刚做好的材质连接起来的话...




会发现源图像发生了“反相”,重新打开shader,看下实现“反相”的代码。


图像效果的计算基本都在frag shader中完成,因为这是像素处理(pixel processing)。
实现“反相”的代码是 c.rgb = 1 - c.rgb,去掉1-,就可以得到原始图像了。基础的设置到这里就结束了。大家可以尝试下使用不同的shader计算,做出更多好玩的效果出来。因为这是我写的Unity 后处理系列文章中的第一篇帖子,今天就先简单地对图像做下黑白处理。第一步,先把float3 彩色图像变为float黑白图像,再创建Luminance变量计算。


当把彩色图像变成黑白图像时,需要注意因为“亮度”对每个色彩通道的影响不同,所以若按上面的公式计算的话,就可以得到明度保持不变的黑白图像。


因为黑白图像的取值在0~1之间,0.5作为基准值,值大于0.5的话就是亮的,小于0.5的话就是暗的。如此一来,就可以得到黑/ 白值。


实际上,计算应该要返回float4(luminance, luminance, luminance, 1),但写这么长实在太麻烦了,在没有alpha的情况下,直接返回float就行,Unity会自动帮我们处理的。啊!请注意,这里不是要返回 luminance,应该要返回col才是。我是为了debug所以写成了 luminance,忘记改过来了,捂脸...


运行之后发现,额...太黑了吧!(魔鬼orz)
看来把0.5作为基准值不是很合理,得换成一个变量才行。我可以在shader中修改值,但在摄像机中做一次性修改会更方便些,所以我会用脚本来控制shader的值。在shader的属性中创建了一个_ShadowThreshold变量,并用它来代替if语句中的0.5。




然后在脚本中,创建一个名为shadowThreshold的public float变量。


写一个shadowMaterial.SetFloat("_ ShadowThreshold",shadowThreshold)命令。


这部分非常重要。
通过这样地处理,我就能够在脚本中动态地更改材质的属性。不仅是SetFloat,SetColor, SetTexture, SetBool等Shader的属性也可以全部把值变成Set!
接着在脚本中,把[Range(0,1)]属性写在shadowThreshold变量上面,这样我们就能使用滑动条来更方便地控制效果了。(这个功能大家应该经常在shader中看到吧?)


我现在可以通过来回拖动滑条来控制阴影区域了。


拖动滑动条的时候,大家会发现暗部划分的标准不一样了。
我想试着把原来的颜色添加至白色区域里面。


因为col会被用做进行各种计算,所以我创建了一个tex,让它带有源图像的颜色,然后用lerp,把tex放入白色(1)区域进行插值。这样一来,暗部会依然维持之前的黑色(col),而白色区域会变成源图像的颜色(tex)。


Zang zang! 是不是有点像美国漫画风,也有点像<暗黑地牢>加了黑色墨水的明暗效果。
最后,我想给暗部加个颜色,就在脚本中加了阴影颜色。Zang zang!


在shader中也要添加上去。




计算lerp的时候,要用_ShadowColor.rgb而不是col.rgb。
(因为值小于_ShadowThreshold的话,就为0,所以这里我用的是ShadowColor而不是0)




现在我可以通过输入颜色来控制明暗了。这就是我今天的分享呢,下次计划给大家分享如何利用色差来实现chromatic aberration。

[原文链接] https://blog.naver.com/mnpshino/221468814028|作者 Madumpa
本文仅限于学习参考交流,请勿做商业用途和随意转载。

译 Qinfei
20200605

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2023-2-14 08:38 | 显示全部楼层
手机没有炸吗
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-4 15:49 , Processed in 0.091814 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表