当前位置: 首页 > news >正文

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

浅聊 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.aspxfloat 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+tviewReflectDir

我们需要找到 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+tviewReflectDir.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.zcameraNearviewPosition.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+viewReflectDirt;

三、计算反射位置在屏幕空间下的位置
// 屏幕分辨率
uniform vec2 resolution;// 视图空间转屏幕空间
vec2 viewPositionToXY(vec3 viewPosition){vec2 xy;vec4 clip = cameraProjectionMatrix * vec4(viewPosition,1);//clipxy = clip.xy;float clipW = clip.w;//NDCxy /= clipW;//uvxy = (xy + 1.) / 2.;//screenxy *=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<float(MAX_STEP); 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~1float 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.pdffloat 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;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【JavaScript 算法】滑动窗口:处理子数组问题
  • 打造直播工具详解:从零开始开发直播美颜SDK
  • Android14 - 前台Service、图片选择器 、OpenJDK 17、其他适配
  • Flink HA
  • 为啥IT系统日志信息普遍保存6个月呢?
  • Adaboost公式推导
  • (ISPRS,2021)具有遥感知识图谱的鲁棒深度对齐网络用于零样本和广义零样本遥感图像场景分类
  • 使用 tcpdump 进行网络流量捕获与分析
  • Gateway源码分析:路由Route、断言Predicate、Filter
  • ArcGIS Pro SDK (九)几何 6 包络
  • NVidia 的 gpu 开源 Linux Kernel Module Driver 编译 安装 使用
  • Richteck立锜科技电源管理芯片简介及器件选择指南
  • uniapp判断h5/微信小程序/app端+实战展示
  • Ubuntu20.04从零开搭PX4MavrosGazebo环境并测试
  • 【开源库学习】libodb库学习(三)
  • (三)从jvm层面了解线程的启动和停止
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • C++入门教程(10):for 语句
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • css系列之关于字体的事
  • HTML中设置input等文本框为不可操作
  • java8 Stream Pipelines 浅析
  • Shadow DOM 内部构造及如何构建独立组件
  • Travix是如何部署应用程序到Kubernetes上的
  • web标准化(下)
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 精彩代码 vue.js
  • 两列自适应布局方案整理
  • 爬虫模拟登陆 SegmentFault
  • 使用API自动生成工具优化前端工作流
  • 问题之ssh中Host key verification failed的解决
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 译自由幺半群
  • 优秀架构师必须掌握的架构思维
  • 在Mac OS X上安装 Ruby运行环境
  • - 转 Ext2.0 form使用实例
  • 组复制官方翻译九、Group Replication Technical Details
  • ​【已解决】npm install​卡主不动的情况
  • #Linux(权限管理)
  • #pragma multi_compile #pragma shader_feature
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (一)Linux+Windows下安装ffmpeg
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .net6使用Sejil可视化日志
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .NET面试题解析(11)-SQL语言基础及数据库基本原理
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • .php结尾的域名,【php】php正则截取url中域名后的内容