浅聊 Three.js 屏幕空间反射SSR-SSRShader
创始人
2024-12-28 02:09:56
0

浅聊 Three.js 屏幕空间反射SSR(2)-SSRShader

前置基础
渲染管线中的相机和屏幕示意图

 -Z  (相机朝向的方向)  |  |  |       +--------------+  <- 屏幕/投影平面  |       |              |  |       |              |  |       |     (f)      |  <- 焦距  |       |              |  |       |              |  |       +--------------+  |              |  |              |  |              O  <- 相机原点 (也称为视点)  |              |  |  |  +---------------------- X (水平轴) 
一、计算 viewPosition

根据深度图计算屏幕空间上的 视图位置。

float clipW = cameraProjectionMatrix[2][3] * viewZ+cameraProjectionMatrix[3][3]; vec3 viewPosition = getViewPosition( vUv, depth, clipW ); 
二、计算反射位置 d1viewPosition
vec3 viewNormal=getViewNormal( vUv );  // 入射光线方向 vec3 viewIncidentDir=normalize(viewPosition);  // 反射光线方向 vec3 viewReflectDir=reflect(viewIncidentDir, viewNormal);  // 反射光线最大长度 float maxReflectRayLen=maxDistance/dot(-viewIncidentDir, viewNormal);  // 反射位置 vec3 d1viewPosition = viewPosition + viewReflectDir * maxReflectRayLen; 

请添加图片描述

处理反射位置在近平面(即 -cameraNear)之的情况

目标:
确保反射光线的目标位置 (d1viewPosition) 不在近平面之前。如果在近平面之前,则将其调整到近平面上。

if(d1viewPosition.z > -cameraNear){   //https://tutorial.math.lamar.edu/Classes/CalcIII/EqnsOfLines.aspx   float t= (-cameraNear - viewPosition.z) / viewReflectDir.z;   d1viewPosition = viewPosition + viewReflectDir * t; } 
 ^ -z  |  |  |  |      * 视点(viewPosition)  |       \  |        \  |------------ (近平面,z = -cameraNear)  |          \  |           \  |            *  |             \  |              \  |               * d1viewPosition (初始位置)  |   -------------------------------------> x 

解释:
反射光线的参数方程:
P ( t ) = v i e w P o s i t i o n + t ∗ v i e w R e f l e c t D i r P(t) = viewPosition + t * viewReflectDir P(t)=viewPosition+t∗viewReflectDir

我们需要找到 t t t 使得:
P ( t ) . z = − c a m e r a N e a r P(t).z = -cameraNear P(t).z=−cameraNear

因此,我们需要解方程:
v i e w P o s i t i o n . z + t ∗ v i e w R e f l e c t D i r . z = − c a m e r a N e a r viewPosition.z + t * viewReflectDir.z = -cameraNear viewPosition.z+t∗viewReflectDir.z=−cameraNear

解这个方程,得到:
t = − c a m e r a N e a r − v i e w P o s i t i o n . z v i e w R e f l e c t D i r . z t = \frac{-cameraNear - viewPosition.z}{ viewReflectDir.z} t=viewReflectDir.z−cameraNear−viewPosition.z​

最后, 调整反射后目标位置:
d 1 v i e w P o s i t i o n = v i e w P o s i t i o n + v i e w R e f l e c t D i r ∗ t ; d1viewPosition = viewPosition + viewReflectDir * t; d1viewPosition=viewPosition+viewReflectDir∗t;

三、计算反射位置在屏幕空间下的位置
// 屏幕分辨率 uniform vec2 resolution;  // 视图空间转屏幕空间 vec2 viewPositionToXY(vec3 viewPosition){   vec2 xy;    vec4 clip = cameraProjectionMatrix * vec4(viewPosition,1);    //clip   xy = clip.xy;    float clipW = clip.w;    //NDC   xy /= clipW;    //uv   xy = (xy + 1.) / 2.;    //screen   xy *=resolution;    return xy; }  vec2 d1 = viewPositionToXY(d1viewPosition); 
四、屏幕空间光线步进(Ray Marching)

参考: DDA 画直线算法

// 片段着色器中的当前像素坐标 vec2 d0 = gl_FragCoord.xy;  vec2 d1 = viewPositionToXY(d1viewPosition);  // x 和 y 方向上的距离 float xLen = d1.x-d0.x; float yLen = d1.y-d0.y;  // 两个点之间的欧几里得距离 float totalLen = length(d1-d0);  // 在 x 和 y 方向上步数的最大值,用于决定采样的步数 float totalStep = max(abs(xLen), abs(yLen));  // 每一步在 x 和 y 方向上的增量 float xSpan = xLen / totalStep; float ySpan = yLen / totalStep;  for(float i = 0.; i    if(i >= totalStep) break;    vec2 xy = vec2(d0.x + i * xSpan, d0.y + i * ySpan);    if(xy.x < 0. || xy.x > resolution.x || xy.y < 0. || xy.y > resolution.y) break;    // 比例进度, 0~1   float s = length(xy - d0) / totalLen;    vec2 uv = xy / resolution;    float d = getDepth(uv);    // 当前像素的视图空间深度值   float vZ = getViewZ(d);    if(-vZ >= cameraFar) continue;    float cW = cameraProjectionMatrix[2][3] * vZ+cameraProjectionMatrix[3][3];   vec3 vP = getViewPosition( uv, d, cW );    // https://comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf   float recipVPZ = 1. / viewPosition.z;    // 基于插值得到的透视矫正后的深度值   float viewReflectRayZ = 1. / (recipVPZ + s * (1. / d1viewPosition.z - recipVPZ));    if(viewReflectRayZ <= vZ){     // 只处理无限厚度的情况     vec3 vN = getViewNormal(uv);     if(dot(viewReflectDir,vN) >= 0.) continue;      float distance = pointPlaneDistance(vP, viewPosition, viewNormal);     if(distance > maxDistance) break;      vec4 reflectColor = texture2D(tDiffuse, uv);     gl_FragColor = reflectColor;   } } 

只处理 viewReflectRayZ <= vZ的情况

^ -z  |  |  |            * viewPosition (反射的起始位置)  |             \  |              \  |               \  |                \  |                 \  |                  * viewReflectRayZ  (矫正后的深度值)  |                   \  |                    \  |                     * vZ (当前像素深度值)  |                      \  |                       \  |                        \  |                         \  |   -------------------------------------> x 
  • 在透视投影下,深度值绝对值越小,表示距离相机越近。在进行光线行进时,我们希望光线从起点出发,经过所有可能的深度值,直到目标位置

  • viewReflectRayZ 是矫正后的深度值,它应该始终小于或等于 vZ,以确保光线距离起点从近到远进行插值和计算。

  • 如果 viewReflectRayZ 大于 vZ, 这种情况可能导致光线跳过当前像素,直接到达更远的像素,产生穿透问题

  • 通过确保 viewReflectRayZ <= vZ,可以保证光线在行进过程中深度值是连续变化的,从而提高插值的精度,避免因不连续的深度值变化而产生的伪影

只处理钝角的情况
点积大于或等于零,表示这两个单位向量的夹角小于或等于 90 度。

点到平面距离

float pointPlaneDistance(vec3 point,vec3 planePoint,vec3 planeNormal){   // https://mathworld.wolfram.com/Point-PlaneDistance.html    https://en.wikipedia.org/wiki/Plane_(geometry)    http://paulbourke.net/geometry/pointlineplane/    float a = planeNormal.x;   float b = planeNormal.y;   float c = planeNormal.z;    float x0 = point.x;   float y0 = point.y;   float z0 = point.z;    float x = planePoint.x;   float y = planePoint.y;   float z = planePoint.z;    float d = -(a * x + b * y + c * z);    float distance = (a * x0 + b * y0 + c * z0 + d)/sqrt(a * a + b * b + c * c);   return distance; } 

相关内容

热门资讯

专业讨论!德扑之星真破解套路(... 专业讨论!德扑之星真破解套路(辅助挂)软件透明挂(有挂了解)-哔哩哔哩;人气非常高,ai更新快且高清...
每日必看!智星德州菠萝外挂检测... 每日必看!智星德州菠萝外挂检测(辅助挂)软件透明挂(有挂教学)-哔哩哔哩1、玩家可以在智星德州菠萝外...
透视透明挂!轰趴十三水有后台(... 轰趴十三水有后台赢率提升策略‌;透视透明挂!轰趴十三水有后台(辅助挂)软件透明挂(有挂详情)-哔哩哔...
发现玩家!德扑ai助手软件(辅... 发现玩家!德扑ai助手软件(辅助挂)透视辅助(有挂教学)-哔哩哔哩;玩家在德扑ai助手软件中需先进行...
一分钟了解!x-poker辅助... 一分钟了解!x-poker辅助软件(辅助挂)辅助透视(有挂攻略)-哔哩哔哩1、每一步都需要思考,不同...
一分钟揭秘!德州最新辅助器(辅... 一分钟揭秘!德州最新辅助器(辅助挂)透视辅助(有挂攻略)-哔哩哔哩;德州最新辅助器最新版本免费下载安...
玩家攻略推荐!德州辅助(辅助挂... 玩家攻略推荐!德州辅助(辅助挂)辅助透视(有挂了解)-哔哩哔哩是由北京得德州辅助黑科技有限公司精心研...
揭秘真相!pokernow德州... 《揭秘真相!pokernow德州(辅助挂)辅助透视(有挂介绍)-哔哩哔哩》 pokernow德州软件...
五分钟了解!德州之星辅助器(辅... 五分钟了解!德州之星辅助器(辅助挂)辅助透视(有挂透明)-哔哩哔哩1、很好的工具软件,可以解锁游戏的...
推荐一款!pokermaste... 1、推荐一款!pokermaster有外挂(辅助挂)透视辅助(有挂教学)-哔哩哔哩;详细教程。2、p...