SSSSS屏幕空间次表面散射

2023-10-09 16:20

本文主要是介绍SSSSS屏幕空间次表面散射,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • SSSSS
  • 代码实现
    • Camera Script
    • Camera Shader
    • Mask 遮罩

refer:
雷火知乎
毛星云总结
参考1
参考2
参考3

(有一个想法,厚度图,通过厚度图来控制SSSSS的范围和强度)

  • 严格来讲并不是基于物理的渲染,但是其出发点是对BSSRDF的近似,因此我归到PBR这个专栏了,其实本来就不好分类

  • 如果希望理解原理,有更深入的理解的话,欢迎来看我的BSSRDF

  • 主要解释都在代码注释里

SSSSS

流程:

  1. 分别获取Object的 遮罩漫反射颜色_A深度 各自存入RenderTex中
  2. 遮罩内的漫反射进行blur
    两个要点:1、根据Object到Camera的距离确定一个总体的blur范围
         2、根据当前片元的深度值,进行微调
  3. 再获取一张 漫反射颜色_B
  4. A和B 相加
  5. 再加上遮罩内的单独的高光
  6. 得到最后结果

流程示意

关于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屏幕空间次表面散射的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/dogman_/article/details/130079949
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/174065

相关文章

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程

【高等代数笔记】线性空间(一到四)

3. 线性空间 令 K n : = { ( a 1 , a 2 , . . . , a n ) ∣ a i ∈ K , i = 1 , 2 , . . . , n } \textbf{K}^{n}:=\{(a_{1},a_{2},...,a_{n})|a_{i}\in\textbf{K},i=1,2,...,n\} Kn:={(a1​,a2​,...,an​)∣ai​∈K,i=1,2,...,n

【电子通识】半导体工艺——保护晶圆表面的氧化工艺

在文章【电子通识】半导体工艺——晶圆制造中我们讲到晶圆的一些基础术语和晶圆制造主要步骤:制造锭(Ingot)、锭切割(Wafer Slicing)、晶圆表面抛光(Lapping&Polishing)。         那么其实当晶圆暴露在大气中或化学物质中的氧气时就会形成氧化膜。这与铁(Fe)暴露在大气时会氧化生锈是一样的道理。 氧化膜的作用         在半导体晶圆

Weex入门教程之4,获取当前全局环境变量和配置信息(屏幕高度、宽度等)

$getConfig() 获取当前全局环境变量和配置信息。 Returns: config (object): 配置对象;bundleUrl (string): bundle 的 url;debug (boolean): 是否是调试模式;env (object): 环境对象; weexVersion (string): Weex sdk 版本;appName (string): 应用名字;

一款支持同一个屏幕界面同时播放多个视频的视频播放软件

GridPlayer 是一款基于 VLC 的免费开源跨平台多视频同步播放工具,支持在一块屏幕上同时播放多个视频。其主要功能包括: 多视频播放:用户可以在一个窗口中同时播放任意数量的视频,数量仅受硬件性能限制。支持多种格式和流媒体:GridPlayer 支持所有由 VLC 支持的视频格式以及流媒体 URL(如 m3u8 链接)。自定义网格布局:用户可以配置播放器的网格布局,以适应不同的观看需求。硬

win7系统中C盘空间缩水的有效处理方法

一、深度剖析和完美解决   1、 休眠文件 hiberfil.sys :   该文件在C盘根目录为隐藏的系统文件,隐藏的这个hiberfil.sys文件大小正好和自己的物理内存是一致的,当你让电脑进入休眠状态时,Windows 7在关闭系统前将所有的内存内容写入Hiberfil.sys文件。   而后,当你重新打开电脑,操作系统使用Hiberfil.sys把所有信息放回内存,电脑

求空间直线与平面的交点

若直线不与平面平行,将存在交点。如下图所示,已知直线L过点m(m1,m2,m3),且方向向量为VL(v1,v2,v3),平面P过点n(n1,n2,n3),且法线方向向量为VP(vp1,vp2,vp3),求得直线与平面的交点O的坐标(x,y,z): 将直线方程写成参数方程形式,即有: x = m1+ v1 * t y = m2+ v2 * t

[Linux]:环境变量与进程地址空间

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 环境变量 1.1 概念 **环境变量(environment variables)**一般是指在操作系统中用来指定操作系统运行环境的一些参数,具有全局属性,可以被子继承继承下去。 如:我们在编写C/C++代码的时,在链接的时候,我们并不知

【编程底层原理】方法区、永久代和元空间之间的关系

Java虚拟机(JVM)中的内存布局经历了几个版本的变更,其中方法区、永久代和元空间是这些变更中的关键概念。以下是它们之间的关系: 一、方法区: 1、方法区是JVM规范中定义的一个概念,它用于存储类信息、常量、静态变量、即时编译器编译后的代码等数据。 3、它是JVM运行时数据区的一部分,与堆内存一样,是所有线程共享的内存区域。 二、永久代(PermGen): 1、在Java SE 7之前,