球形体积雾

2024-01-10 00:20
文章标签 体积 球形

本文主要是介绍球形体积雾,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

本Blog的体积雾散射算法借鉴自Miles Macklin Simulation and computer graphics,如需原文参照,可转至链接。

球形体积雾

球形体积雾,即通过一个球体,配备一个雾效Shader,从而模拟出球状雾效。

主要包括:

  1. 首先球体得是透明物体,而且需要在其他透明物体渲染之后进行。
  2. 传入数据包括:
    • 体积雾颜色
    • 体积雾中心坐标和半径
    • 体积雾强度,最大雾效因子(好像没用到),雾效衰减
  3. 在vert中将摄像机之后的片元放置到摄像机近处,防止片元被clip后雾效错误。(借鉴ShadowMap的做法)
#if UNITY_REVERSED_ZpositionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#elsepositionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
  1. 向fragment传入顶点的屏幕坐标Position(o.uv = ComputeScreenPos(o.vertex);
    ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,其范围为[0, w]。
    在Unity内置Shader中,获取阴影UV使用如下代码
float4 GetShadowCoord(VertexPositionInputs vertexInput)
{
#if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT)return ComputeScreenPos(vertexInput.positionCS);
#elsereturn TransformWorldToShadowCoord(vertexInput.positionWS);
#endif
}
  1. 在fragment中获取到该片元的深度,通过深度计算得到该点在WorldSpace下的pos
// 通过深度计算得到直接坐标
real depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.texcoord);
float3 worldPos = ComputeWorldSpacePosition(i.texcoord, depth, UNITY_MATRIX_I_VP);

ComputeWorldSpacePosition定义在Common.hlsl

float3 ComputeWorldSpacePosition(float2 positionNDC, float deviceDepth, float4x4 invViewProjMatrix)
{float4 positionCS  = ComputeClipSpacePosition(positionNDC, deviceDepth);float4 hpositionWS = mul(invViewProjMatrix, positionCS);return hpositionWS.xyz / hpositionWS.w;
}
float3 ComputeWorldSpacePosition(float4 positionCS, float4x4 invViewProjMatrix)
{float4 hpositionWS = mul(invViewProjMatrix, positionCS);return hpositionWS.xyz / hpositionWS.w;
}

ComputeClipSpacePosition(float2 positionNDC, float deviceDepth)将NDC空间转移到Clip空间

float4 ComputeClipSpacePosition(float2 positionNDC, float deviceDepth)
{float4 positionCS = float4(positionNDC * 2.0 - 1.0, deviceDepth, 1.0);#if UNITY_UV_STARTS_AT_TOP// Our world space, view space, screen space and NDC space are Y-up.// Our clip space is flipped upside-down due to poor legacy Unity design.// The flip is baked into the projection matrix, so we only have to flip// manually when going from CS to NDC and back.positionCS.y = -positionCS.y;
#endifreturn positionCS;
}// Use case examples:
// (position = positionCS) => (clipSpaceTransform = use default)
// (position = positionVS) => (clipSpaceTransform = UNITY_MATRIX_P)
// (position = positionWS) => (clipSpaceTransform = UNITY_MATRIX_VP)
float4 ComputeClipSpacePosition(float3 position, float4x4 clipSpaceTransform = k_identity4x4)
{return mul(clipSpaceTransform, float4(position, 1.0));
}
  1. 当下,我们已经获取到了片元的深度点的世界空间坐标值,我们还通过CPU传入了球形雾效的世界空间中心点坐标和半径

之后,我们计算球形体积雾雾效因子。

球形体积雾计算方法1

由此,我们可以计算远处片元反射的光线,要穿过多厚的雾,才能到达我们的眼睛。因为雾效是局部的而不是全局的,因此我们需要计算穿过的雾效厚度L

在这里插入图片描述
根据上图有:
d i r → = ( P − E ) . n o r m a l i z e d ∣ E X ∣ = d o t ( d i r → , E C → ) ∣ C X ∣ 2 = ∣ E C ∣ 2 − ∣ E X ∣ 2 \overrightarrow{dir} = (P-E).normalized\\ |EX| = dot(\overrightarrow{dir}, \overrightarrow{EC})\\ |CX|^2 = |EC|^2 - |EX|^2 dir =(PE).normalizedEX=dot(dir ,EC )CX2=EC2EX2
射线与圆的相交的判定条件为:
∣ C X ∣ 2 < R 2 ( 直线穿过球内 ) |CX|^2<R^2(直线穿过球内) CX2<R2(直线穿过球内)
当条件符合后,再进行雾效计算,有:
L 2 = R 2 − ∣ C X ∣ 2 \frac{L}{2} = \sqrt{R^2-|CX|^2} 2L=R2CX2
当摄像机在球形范围外,即 ∣ E X ∣ > L 2 ∣ ∣ ∣ E X ∣ < − L 2 |EX|>\frac{L}{2} \quad||\quad |EX|<-\frac{L}{2} EX>2L∣∣EX<2L

  • ∣ E X ∣ > L 2 |EX|>\frac{L}{2} EX>2L ,光线经过整个雾球,雾球内距离为L
    L = 2 R 2 − ∣ C X ∣ 2 L = 2\sqrt{R^2-|CX|^2} L=2R2CX2
  • ∣ E X ∣ < − L 2 |EX|<-\frac{L}{2} EX<2L ,整个雾球在光线背后,雾球内距离为0

当相机在球形范围内,雾球内距离为 L/2 + |EX|

  • ∣ E X ∣ > 0 |EX|>0 EX>0 ,光线经过大半个雾球,雾球内距离为 L/2 + |EX|
  • ∣ E X ∣ < 0 |EX|<0 EX<0 ,光线经过小半个雾球,雾球内距离为 L/2 + |EX|(与上面相同,因为|EX|为负)

综上:有代码如下

/// <summary>
/// describe:
///		BallFogFactor的另一种算法
/// params:
///		depthPosWS:				深度图中记录深度点的世界坐标
///		fogBallCenterRadiusWS:  世界空间中球形体积雾的信息(位置+半径)
///		fogDensity:				雾效强度
///		fogMaxFactor:			最大雾效因子
///		falloffDist:			衰减系数 * 半径 * 0.5f
///	<summary>
half CalculateBallFogFactor2(float3 depthPosWS, float4 fogBallCenterRadiusWS,float fogDensity, float fogMaxFactor, float falloffDist
)
{// 数据提取float R = fogBallCenterRadiusWS.w;//Rfloat3 C = fogBallCenterRadiusWS.xyz;//Cfloat3 E = GetCameraPositionWS();//E// 数据计算float3 cameraToDepthPoint = depthPosWS - E;float depthT = length(cameraToDepthPoint);// 深度距离float3 dir = normalize(cameraToDepthPoint);// dirfloat3 EC = center - camPosWS;float EX = dot(dir,EC);float CX_2 = dot(EC,EC) - EX * EX;//判定float R_2 =  R * R;if(CX_2 >= R_2) return 0;//直线不穿过球内,没有雾效float LDiv2 = sqrt(R_2 - CX_2);//sqrt{L}{2}float L = 2 * LDiv2;if(EX < - LDiv2)return 0;//射线不穿过球内,没有雾效// 判定通过,计算雾效距离float FogDistance;if(EX > LDiv2)  FogDistance = L;				//光线经过整个雾球else            FogDistance = LDiv2 + EX;		//摄像机在雾效范围内// 如果雾是均匀雾,我们就可以返回雾效因子为float FogFactor = clamp(0, fogMaxFactor, FogDistance * fogDensity);return FogFactor;
}

运行后发现,我们忘记处理了一种情况,当深度点在雾效球内,或雾效之前,我们需要删掉被遮挡的雾效。
增加代码:// 如果最大深度小于雾效深度,需要减去被遮挡的雾距离。

/// <summary>
/// describe:
///		BallFogFactor的另一种算法,该方法使用几何信息递推求解
///     但这种方法暂时只支持均匀分布的雾效
///     具体推导过程见Blog《光在雾效中的散射》
/// params:
///		depthPosWS:				深度图中记录深度点的世界坐标
///		fogBallCenterRadiusWS:  世界空间中球形体积雾的信息(位置+半径)
///		fogDensity:				雾效强度
///		fogMaxFactor:			最大雾效因子
///		falloffDist:			衰减系数 * 半径 * 0.5f
///	<summary>
half CalculateBallFogFactor2(float3 depthPosWS, float4 fogBallCenterRadiusWS,float fogDensity, float fogMaxFactor, float falloffDist
)
{// 设://    float R: 球的半径//    float3 C:球的中心世界坐标//	  float3 E:摄像机的世界坐标//    float3 X:光线所在的直线上距离球心最近的点//    float3 dir:光线的方向(摄像机到像素点的方向)// 设定://    XX_2:表示XX的平方,如果XX为向量,则表示距离的平方。// 数据提取float R = fogBallCenterRadiusWS.w;float3 C = fogBallCenterRadiusWS.xyz;float3 E = GetCameraPositionWS();// 数据计算float3 cameraToDepthPoint = depthPosWS - E;float depthT = length(cameraToDepthPoint);// 深度距离float3 dir = normalize(cameraToDepthPoint);// dirfloat3 EC = C - E;float EX = dot(dir,EC);float CX_2 = dot(EC,EC) - EX * EX;//判定float R_2 =  R * R;if(CX_2 >= R_2) return 1;//直线不穿过球内,没有雾效float LDiv2 = sqrt(R_2 - CX_2);//sqrt{L}{2}float L = 2 * LDiv2;if(EX < - LDiv2)return 1;//射线不穿过球内,没有雾效// 判定通过,计算雾效距离float RayDistance = EX + LDiv2;//光线从摄像机发出,到穿过雾时移动的距离float FogDistance = 1;if(EX > LDiv2)  FogDistance = L;				//光线经过整个雾球else            FogDistance = LDiv2 + EX;		//摄像机在雾效范围内// 如果最大深度小于雾效深度,需要减去被遮挡的雾距离。float DivDistance = 0;if(depthT < RayDistance){DivDistance = RayDistance - depthT;}FogDistance -= DivDistance;if(FogDistance<0)FogDistance = 0;// 如果雾是均匀雾,我们就可以返回雾效因子为half FogFactor = clamp(0,1-fogMaxFactor,FogDistance * fogDensity);return 1-FogFactor;
}

然而,再增加条件:如果雾效并不是均匀分布的,那我们如何处理。
首先我们知道进入点距离球心为R,退出点也距离球心为R。
如果衰减函数为 y = − k x + 1 ( k > 0 ) y = -kx + 1(k>0) y=kx+1k>0;
球心边缘y为0,球心中心y为1,则进入点雾效距离x = R,中心点雾效距离为x = CX。
中间任意一点雾效距离为:
x = t 2 + C X 2 x = \sqrt{t^2 + CX^2} x=t2+CX2
故整体雾效强度为
2 ∫ t = 0 t = L 2 − k t 2 + C X 2 + 1 ( d t ) 2\int_{t=0}^{t=\frac{L}{2}} -k \sqrt{t^2 + CX^2} + 1 (dt) 2t=0t=2Lkt2+CX2 +1(dt)
在这里插入图片描述
但是这样求解的是全部雾效的强度,但是片元有可能在雾效内,雾效前。所以不能全部积分。
根据t的取值,对积分区间求解,最终得到最后的结果。

那既然我们需要一个参数t得知光线在其中的位置,何不直接在计算时,得到光线在雾内传播的起始t值,和结束t值。


球形体积雾计算方法2

// 设:
//    float r: 球的半径
//    float3 C:球的中心世界坐标
//	  float3 E:摄像机的世界坐标
//    float3 X:光线所在的直线上的点
//    float3 dir:光线的方向(摄像机到像素点的方向)

设:光线函数为
X ( t ) = E + D i r ∗ t ( D i r 为单位向量 ) X(t) = E + Dir * t(Dir为单位向量) X(t)=E+Dirt(Dir为单位向量)
当光线和球面相交,公式为:
∣ X ( t ) − C ∣ 2 = r 2 |X(t) - C|^2 = r^2 X(t)C2=r2
代入公式,得:
∣ E + D i r ∗ t − C ∣ 2 = r 2 |E + Dir * t - C|^2 = r^2 E+DirtC2=r2
注意:因为这里 r 为float,而 E、Dir、C 为向量,故不能将 r 放入平方内。
我们展开公式,并整理得:
∣ D i r ∣ 2 t 2 + 2 ( ∣ E − C ∣ ⋅ D i r ) ∗ t + ( ∣ E − C ∣ 2 − r 2 ) = 0 |Dir|^2t^2 + 2(|E-C| \cdot Dir) * t + (|E-C|^2-r^2) = 0 Dir2t2+2(ECDir)t+(EC2r2)=0
求解2次方程:公式我居然忘了!!!!
在这里插入图片描述
得到 t m i n t_{min} tmin, t m a x t_{max} tmax
继续计算得到场景中实际的t值。
0 < = t m i n < = d e p t h T 0<=t_{min}<=depthT 0<=tmin<=depthT

t m i n < = t m a x < = d e p t h T t_{min}<=t_{max}<=depthT tmin<=tmax<=depthT

最后得到传播距离为
f o g D i s t = t m a x − t m i n ; fogDist = t_{max} - t_{min}; fogDist=tmaxtmin;

同样我们需要考虑雾效衰减的问题。

雾效衰减该部分内容属于公司文件,这里就不再阐述。(怕收到律师函)

这篇关于球形体积雾的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/588914

相关文章

黑盒闪清 v2.9.9 体积小巧,简洁高效的手机清理神器

黑盒闪清APP是安卓手机上的一款优质文件管理器,拥有存储分析、文件分类、大文件扫描、空文件夹扫描等功能,应用无广告、无推送,完全免费使用,让你手机中的文件管理就跟在电脑上管理一样简单。 链接:https://pan.quark.cn/s/5ed59be1d94c 📁大小:9M 🏷标签:#黑盒闪清 #文件管理 #Andriod #内存清理 #无广告 夸克网盘: https://pan.

【Java】—— Java面向对象进阶:继承小练习-Java中实现圆柱体类及其体积计算

目录 1. 定义圆类(Circle) 2. 定义圆柱体类(Cylinder) 3. 测试圆柱体类 4. 总结         在Java中,我们可以通过面向对象的方式来模拟现实世界中的物体,比如圆柱体。本篇文章将通过一个简单的示例来展示如何定义一个圆柱体类(Cylinder),并计算其体积。此外,我们还将创建一个圆类(Circle)作为基类,因为圆柱体的底面本质上是一个圆。 根

优化 Webpack 打包体积的思路

在现代前端开发中,优化 Webpack 打包体积是提升应用性能的重要手段。以下是一些有效的优化思路: 提取第三方库:将第三方库单独打包,并通过 CDN 引入。这样不仅减少了打包体积,还利用了 CDN 的缓存优势,提高加载速度。 使用代码压缩插件:引入如 UglifyJsPlugin 等代码压缩插件,可以有效地压缩 JavaScript 代码,减小文件体积。 启用 Gzip 压缩:通过服务器

正方体挖出几小块后的体积

如图,有一个边长为20厘米的大正方体,分别在它的角上、棱上、面上各挖掉一个大小相同的小立方体后,表面积变为2454平方厘米,那么挖掉的小立方体的边长是多少厘米?() A 2 B 2.5 C 3【正确答案】 D 3.5 这道题和上一期《立体空间想象题》 类似,都是考察空间想象能力,好在这道题给出了图示,可以看出,表面积变多了54立方厘米,这多出来的几个小面需要找到具体的位置和数量,右上角的凹面可

图片体积,图片分辨率,图片尺寸之间是啥关系?

图片体积,图片分辨率,图片尺寸之间是啥关系? 有好几个词,叫法不一样,不过可能拿个实例说一下就能清楚: 500K、1M 这样的描述;是指图片的大小、图片的体积吧?240 × 320、480 × 800 这样的描述;是指图片的尺寸、图片的分辨率吧?高、中、低这样的描述;是指图片质量吧? 上述列举的 3 个描述之间是啥关系? 还有,像素和分辨率呢? 解答 图片体积(size)指的是图片文件

商品详情API返回值中的商品重量与体积信息

商品详情API(Application Programming Interface)的返回值中关于商品重量与体积的信息,通常会以结构化的数据格式呈现,比如JSON(JavaScript Object Notation)或XML(Extensible Markup Language)。这些信息对于电商平台、物流系统或任何需要处理商品物理属性的应用来说至关重要。下面是一个简化的JSON示例,展示了商品

Open3D mesh 计算点云模型的表面积及体积

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 三、实现效果 3.1原始点云 3.2数据显示 Open3D点云算法汇总及实战案例汇总的目录地址: Open3D点云算法与点云深度学习案例汇总(长期更新)-CSDN博客 一、概述         在 Open3D 中,计算三维网格模型的表面积和体积是一个常见的任务,尤其在几何分析、形状优化、工

Unity引擎制作球形地面

Unity引擎制作球形地面3 大家好,我是阿赵。   之前有个朋友问我,有些游戏,角色好像走在一个球形的地面上,离角色远的东西会往下沉,直到看不见。   这种效果的做法肯定不止一种的。   最直观的做法,如果本身每张地图的范围很小,是固定只能走一定的圆弧范围的地面。这种情况我觉得直接做一个弯曲的3D模型作为地表,也可以做到。   不过这种做弯曲3D模型的方式,局限性是比较大的

常见气体在0℃时的体积磁化率

磁氧的磁化率 参考链接 特此记录 anlog 2024年8月22日 参考链接  常见气体的相对磁化率_文档下载 (docsou.com)https://doc.docsou.com/b736e89849bf7582edd3cfd26.html 特此记录 anlog 2024年8月22日

惠海H4312 dcdc同步整流降压恒压IC 30V 40V转3.3V/5V/12V小体积大电流单片机供电

1.产品描述 H4312是一种内置30V耐压MOS,并且能够实现精确恒压以及恒流的同步降压型 DC-DC 转换器: 支持 3.1A 持续输出电流输出电压可调,最大可支持 100%占空比;通过调节FB 端口的分压电阻,可以输出2.5V到 24V的稳定电压。 H4312 采用高端电流模式的环路控制原理,实现了快速的动态响应。H4312工作开关频率为 170kHz,具有良好的 EMI 特性。H43