• 全局光照RSM


    Reflective Shadow Maps(RSM)

    一切被直接光照照到的物体,会作为次级光源。

    问题1:哪些面片被直接照亮

    使用ShadowMap就可以知道哪些面片被直接照亮

    问题2:各个面片对P点的贡献分别是多少。

    在这里插入图片描述
    对渲染方程代入如上计算,得到如下结果
    在这里插入图片描述
    在上式中, L i ( q → p ) L_i(q\rightarrow p) Li(qp)可优化为如下函数
    因为:
    f r = ρ / π , ρ 为反射率 L i = f r ⋅ ϕ d A , ϕ 是直接光照的光强 f_r = \rho / \pi,\rho 为反射率\\ \quad\\ L_i = f_r \cdot \frac{\phi}{dA},\phi 是直接光照的光强 fr=ρ/πρ为反射率Li=frdAϕϕ是直接光照的光强
    因此有次级光源光强
    E p ( x , n ) = ϕ p m a x { 0 , d o t ( n p , ( x − x p ) } m a x { 0 , d o t ( n , x p − x ) } ∣ ∣ x − x p ∣ ∣ 4 E_p(x,n)=\phi_p \frac{max\{0,dot(n_p,(x-x_p)\}max\{0,dot(n,x_p-x)\}}{||x-x_p||^4} Ep(x,n)=ϕp∣∣xxp4max{0,dot(np,(xxp)}max{0,dot(n,xpx)}

    L 0 = E p ( x , n ) ∗ f r ( p , p → q , w o ) L_0 = E_p(x,n) * f_r(p,p\rightarrow q,w_o) L0=Ep(x,n)fr(p,pq,wo)

    其他近似

    1. 不考虑Visbility项,认为次级光源不会被遮挡。
    2. 不对所有次级光源采样,只对该点在ShadowMap中周围的部分像素采样。

    ShadowMap记录值

    1. 深度
    2. 世界坐标
    3. 法线
    4. 次级光源强度 ϕ \phi ϕ

    特点与效果

    对于特定情况效果比较好,如手电筒(用RSM做手电筒!!!?)
    好处:非常好写,第一个Path生成RSM,第二个path用眼睛看向场景。
    问题:

    1. 直接光源较多时,间接光源也会成倍增多
    2. 不会计算反射光的可见性检查
    3. 次级光源效果和运行速度,会根据采样率的不同的不同

    如何实现

    第一步,先找到哪些物体表面能够被直接照亮,使用ShadowMap,认为每一个ShadowMap的像素就是一个次级光源。
    第二步,次级光源如何贡献到点P:我们认为所有的反射物(次级光源),都是Diffuse的

    RSM与VPL(Virtual Point Light 虚拟点光源)

    RSM与离线渲染中的VPL方式是比较相似的,RSM是硬件加速版本的VPL(Virtual Point Light 虚拟点光源)。

    RSM实现步骤

    第一个Pass

    在摄像机视角渲染整个场景,并记录场景中可见的像素点的颜色视口矩阵下的法线视口矩阵下的坐标

    得到一组场景信息,可以理解为GBuffer。
    在这里插入图片描述

    第2个Pass

    从光源视口下看向场景,生成RSM图。
    记录的从光源视口下可见像素点的反射光颜色值相机视角下的法线相机视角下的坐标光源视角下的坐标
    得到的结果是一组256x256的RSM图
    在这里插入图片描述在这里插入图片描述
    ············反射光颜色值 ······························· 相机视角下的法线 ··················
    在这里插入图片描述在这里插入图片描述

    ··········· 相机视角下的坐标 ·························· 光源视角下的坐标(主要保存z轴深度)·······························

    第3个Pass

    1.将Pass1中保存的像素坐标转化到与RSM相同的光源坐标

    	ivec2 FragPos = ivec2(gl_GlobalInvocationID.xy);//当前执行单元在全局工作组中的位置的有效索引
    	vec3 FragViewNormal = normalize(texelFetch(u_NormalTexture, FragPos, 0).xyz);
    	vec3 FragAlbedo = texelFetch(u_AlbedoTexture, FragPos, 0).xyz;
    	vec3 FragViewPos = texelFetch(u_PositionTexture, FragPos, 0).xyz;
    
    	// 获取 摄像机视口下像素 在光源视口下的位置坐标
    	vec4 FragPosInLightSpace = u_LightVPMatrixMulInverseCameraViewMatrix * vec4(FragViewPos, 1);
    	FragPosInLightSpace /= FragPosInLightSpace.w;
    	vec2 FragNDCPos4Light = (FragPosInLightSpace.xy + 1) / 2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. 计算该像素点的直接光照

    如果该像素点在光照空间外,则只提供一个基本的环境光照。
    当该像素在光照空间内,首先判断该点是否在阴影中

    • 如果在阴影中,则只计算环境光照
    • 如果不在阴影中,则计算直接光照
    // 直接光照&环境光照
    	vec3 testOutput;
    	vec3 DirectIllumination;
    	// 如果该像素在RSM范围之外,以0.1倍源像素作为其环境光照
    	// FragPosInLightSpace.z范围在【-1,0】的区间中.属于可被光照直接照射的范围
    	// 这里可通过与RSM中的深度进行比较,来确定是否有阴影生成,或是否可被作为次级光源
    	if(	FragPosInLightSpace.z < -1.0f || FragPosInLightSpace.z > 0.0f||\
    		FragPosInLightSpace.x >=  1.0f || FragPosInLightSpace.x <= -1.0f||\
    		FragPosInLightSpace.y >=  1.0f || FragPosInLightSpace.y <= -1.0f )//
    	{
    		DirectIllumination = vec3(0.1) * FragAlbedo;
    		testOutput = vec3(0.f,0.f,0.f);
    	}
    	else//反之,将原像素经过夹角衰减后的值作为直接光照
    	{	
    		// view space下的光照坐标
    		vec3 RSWLightPosition =texture(u_RSMLightPositionTexture,FragNDCPos4Light.xy).xyz;
    		
    		// 判断是否在阴影中(近小【-1】远大【0】)
    		if(RSWLightPosition.z + 0.001 > FragPosInLightSpace.z){//不在阴影中
    			DirectIllumination = FragAlbedo* max(dot(-u_LightDirInViewSpace, FragViewNormal), 0.1);// ;
    		}
    		else{//在阴影中
    		   DirectIllumination = vec3(0.1) * FragAlbedo;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述

    3. 计算该像素点的间接光照

    将该着色点投影到光照视口下的点,则已知该点在RSM纹理上的xy轴位置。

    在RSM图中,在(x,y)像素上以R为半斤的周围进行随机采样,每一个采样点作为次级光源进行光照计算。

    // 计算间接光照
    	vec3 IndirectIllumination = vec3(0);
    	float RSMTexelSize = 1.0 / u_RSMSize;
    	for(int i = 0; i < u_VPLNum; ++i)
    	{
    		if(FragPosInLightSpace.z > -1.0f && FragPosInLightSpace.z <0.0f){
    			//获取随机采样数组
    			vec3 VPLSampleCoordAndWeight = u_VPLsSampleCoordsAndWeights[i].xyz;
    			// 在光源视口下的该像素点周围一圈进行采样
    			vec2 VPLSamplePos = FragNDCPos4Light + u_MaxSampleRadius * VPLSampleCoordAndWeight.xy * RSMTexelSize;
    			// 在RSM图中获取采样点的次级光源颜色值
    			vec3 VPLFlux = texture(u_RSMFluxTexture, VPLSamplePos).xyz;
    			// 获取采样点在摄像机视口坐标下的的法线和位置坐标
    			vec3 VPLNormalInViewSpace = normalize(texture(u_RSMNormalTexture, VPLSamplePos).xyz);
    			vec3 VPLPositionInViewSpace = texture(u_RSMPositionTexture, VPLSamplePos).xyz;
    			// 计算该采样点对该像素的间接光照值
    			IndirectIllumination += calcVPLIrradiance(VPLFlux, VPLNormalInViewSpace, VPLPositionInViewSpace, FragViewPos, FragViewNormal, VPLSampleCoordAndWeight.z);
    		}
    	}
    	IndirectIllumination *= FragAlbedo;
    
    	//间接光
    	vec3 Result = IndirectIllumination / u_VPLNum;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    4. 将直接光照与间接光照加起来

    得到结果,渲染!

    vec3 Result = DirectIllumination  + IndirectIllumination / u_VPLNum ;
    
    • 1

    在这里插入图片描述
    区别【好像没太大区别 dog.jpg】
    在这里插入图片描述

  • 相关阅读:
    π161E60 Pai161E60 5.0kVrms 200Mbps 六通道数字隔离器代替纳芯微数字隔离NSi8261W0
    C++性能分析工具gperftools安装教程与使用案例分析
    数据分析实战 | 多元回归——广告收入数据分析
    oracle官网下载早期jdk版本
    记一次公司内部技术分享—DDD
    swiperJS滑动时监听事件
    服务端业务设计及源码
    程序员的注释:编程艺术与沟通工具
    电脑为何只有一个C盘?
    ERP采购管理系统软件
  • 原文地址:https://blog.csdn.net/weixin_44518102/article/details/132790107