本文主要是介绍SSSSS屏幕空间次表面散射,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- SSSSS
- 代码实现
- Camera Script
- Camera Shader
- Mask 遮罩
refer:
雷火知乎
毛星云总结
参考1
参考2
参考3
(有一个想法,厚度图,通过厚度图来控制SSSSS的范围和强度)
-
严格来讲并不是基于物理的渲染,但是其出发点是对BSSRDF的近似,因此我归到PBR这个专栏了,其实本来就不好分类
-
如果希望理解原理,有更深入的理解的话,欢迎来看我的BSSRDF
-
主要解释都在代码注释里
SSSSS
流程:
- 分别获取Object的 遮罩、 漫反射颜色_A、深度 各自存入RenderTex中
- 对遮罩内的漫反射进行blur
两个要点:1、根据Object到Camera的距离确定一个总体的blur范围
2、根据当前片元的深度值,进行微调 - 再获取一张 漫反射颜色_B
- A和B 相加
- 再加上遮罩内的单独的高光
- 得到最后结果
关于blur的详细介绍可以看本人的另一篇文章
注意:后处理中无法使用模板测试
- 多次尝试后,尚未发现能在后处理中使用模板缓冲的办法,SSSSS的案例考虑采用蒙版
后续发现解决办法再来更新
后处理中是不能使用模板测试的,如果使用则会失效,这是因为后处理是在虚拟的渲染纹理中进行的,而这个渲染纹理并没有继承屏幕空间中的各个缓冲(不确定,但应该是这样)
官方API文档中有说 RenderTexture.GetTemporary(rtW, rtH,24);
第三个参数改成24会保留深度模板缓冲,经测试无效
一篇文章介绍说再添加一个语句
RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH,24);
Graphics.SetRenderTarget(buffer.colorBuffer, src.depthBuffer);
这样会保留缓冲, 但经测试依然无效
代码实现
- 本人学艺不精,现在还没有搞懂遮罩的脚本部分,因此无法完整再现上述流程
但本文的目的是为了理解,因此下面的代码部分,使用了本人自己写的较为基础的版本:没有遮罩,没有深度图和摄像机距离,仅介绍SSSSS中核心的部分
如果有需要完整代码的,Unity的AssetStore中有前辈写好的免费资源
Camera Script
using UnityEngine;public class SSS_Camera : PostEffectsBase
{public Shader SSS_Shader;private Material SSS_Mat = null;public Material material{get{SSS_Mat = CheckShader_CreateMat(SSS_Shader, SSS_Mat);return SSS_Mat;}}//模糊所需参数[Range(0,4)]public int iterations = 3;[Range(0.2f, 10.0f)]public float blurSpread = 0.6f;[Range(1,8)]public int downSample = 2; //bloom截取强度[Range(0.0f, 4f)]public float lumi = 0.6f; //不止到1,是为了HDRvoid OnRenderImage(RenderTexture src, RenderTexture dest) {if (material != null){material.SetFloat("_Lumi", lumi);int rtW = src.width/downSample;int rtH = src.height/downSample;RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH,24); //Graphics.SetRenderTarget(buffer0.colorBuffer, src.depthBuffer);buffer0.filterMode = FilterMode.Bilinear;//先走第一个pass提取需要bloom的部分Graphics.Blit(src, buffer0,material,0);//注意循环内部创建的RenderTex在循环外是访问不到的,因此需要左手倒右手for (int i = 0; i < iterations; i++) {material.SetFloat("_BlurSize", 1.0f + i * blurSpread);RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 24);Graphics.Blit(buffer0, buffer1, material, 1);RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;buffer1 = RenderTexture.GetTemporary(rtW, rtH, 24);Graphics.Blit(buffer0, buffer1, material, 2);RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;}//这一步很重要啊,把bloom好的图像传递给Shadermaterial.SetTexture("_Bloom", buffer0);Graphics.Blit(src, dest, material, 3);RenderTexture.ReleaseTemporary(buffer0);}else{Graphics.Blit(src, dest);}}
}
Camera Shader
Shader "Unlit/SSSSS_Camera"
{Properties{_MainTex ("Base", 2D) = "white" {}_Bloom ("Bloom", 2D) = "black"{} //用于存储亮度图_BlurSize ("Blur Size", float) = 1.0_Lumi ("lumi", float) = 0.6 //这个值是用来控制bloom的阈值}SubShader{ZTest Always Cull Off ZWrite OffPass//提取亮度Pass{Stencil{Ref 1Comp alwaysPass Keep}CGPROGRAM#pragma vertex vertBloom#pragma fragment fragBloom#include "UnityCG.cginc"sampler2D _MainTex;sampler2D _Bloom;half4 _MainTex_TexelSize; //内置方法,可以获取纹素大小float _BlurSize;float _Lumi;struct v2f{float2 uv : TEXCOORD0; float4 vertex : SV_POSITION;};v2f vertBloom(appdata_img v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.texcoord;return o;}fixed Luminance(fixed4 color){ //这个是明度的计算,可以去看本人的文章“色彩知识总结”return 0.2125*color.r + 0.7154*color.g + 0.0721*color.b;}fixed4 fragBloom(v2f i): SV_Target {fixed3 c = tex2D(_MainTex, i.uv);fixed l = clamp(Luminance(c) - _Lumi, 0, 1); //clamp函数是一个截取函数,后面两个参数是范围return fixed4 (c*l,1);//为什么这里要乘?//这里要乘而不是单纯的取0/1,是因为://(1)GPU不擅长分支运算//(2)数值越大的fragment在模糊时,影响的范围更大,符合真实逻辑,乘法保留了这种特性}ENDCG}//模糊Pass//注意这里是直接引用了同工程下的另外一个shader中的Pass,//可以在本人的其他文章中找到,或直接使用《入门精要》附带的文件//UsePass "Unity Shaders Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_HORIZONTAL"UsePass "Unity Shaders Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_HORIZONTAL"UsePass "Unity Shaders Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_VERTICAL"//UsePass "Unity Shaders Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_VERTICAL"Pass//混合Pass{Stencil{Ref 1Comp equalPass Keep}CGPROGRAM#pragma vertex vertMerge#pragma fragment fragMerge#include "UnityCG.cginc"sampler2D _MainTex;sampler2D _Bloom;half4 _MainTex_TexelSize; //内置方法,可以获取纹素大小float _BlurSize;float _Lumi;struct v2fMerg{float4 vertex : SV_POSITION;float4 uv : TEXCOORD0;};v2fMerg vertMerge(appdata_img v){v2fMerg o;o.vertex = UnityObjectToClipPos(v.vertex);//将两张纹理的坐标分开是为了处理平台差异化o.uv.xy = v.texcoord;o.uv.zw = v.texcoord;//平台差异化处理,详见“Unity的一些机制”和《精要》5.6.1//#if UNITY_UV_STARTS_AT_TOP //if (_MainTex_TexelSize.y < 0.0)// o.uv.w = 1.0 - o.uv.w;//#endifreturn o;}fixed4 fragMerge(v2fMerg i): SV_Target{//直接加?//当然直接相加,不然不就是单纯的模糊了,bloom的亮度就是要超额,超额也更适合HDRreturn tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.xy);}ENDCG}}Fallback Off
}
Mask 遮罩
既然不能用模板,那么就来学习遮罩吧,但是呢,对于本人而言有那么一点复杂,我不是很会写脚本,这部分先跳过,以后脚本能力够了回来补
这篇关于SSSSS屏幕空间次表面散射的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!