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

news2024/9/21 22:09:26

浅聊 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+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);

  //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<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~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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1934685.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新文件覆盖旧文件还能复原吗?八大excel文档修复软件免费

新文件覆盖旧文件还能复原吗&#xff1f;文件操作失误&#xff0c;尤其是新文件意外覆盖旧文件的情况时有发生&#xff0c;面对文件被覆盖的情况&#xff0c;我们不仅需要冷静应对&#xff0c;更需要掌握一系列有效的恢复策略。本文将深入探讨八种免费方法&#xff0c;旨在帮助…

JavaWeb笔记_Response对象

一.Response对象 1.1 Response对象概述 a.专门负责给浏览器响应信息&#xff08;响应行&#xff0c;响应头&#xff0c;响应体&#xff09;的对象 b.我们主要使用的是跟HTTP协议相关的Response对象&#xff1a;HTTPServletResponse&#xff0c;继承了ServletResponse&#x…

谷粒商城-商品上架

1.sku在es中的存储模型分析(spring整和es) es中所有数据存在内存中,内存产品贵,能节省就节省,只保存有用的信息 两种保存方法:(空间换时间,时间换空间): 我们选空间换时间 ES中放这些东西: "mappings": { "properties": { "skuId"…

ClickHouse 入门(二)【基础SQL操作】

1、ClickHouse 1.1、SQL 操作 这里只介绍一些和我们之前 MySQL 不同的语法&#xff1b; 1.1.1、Update 和 Delete ClickHouse 提供了 Delete 和 Update 的能力&#xff0c;这类操作被称为 Mutation 查询&#xff08;可变查询&#xff09;&#xff0c;它可以看 做 Alter 的一…

国产化低功耗HDMI转VGA方案,大量出货产品,广泛应用在显示器以及广告机产品

芯片描述&#xff1a; 兼具高性能和低成本效益的优点&#xff0c;是一款可以将高清视频 HDMI1.4 数字信号转换成 VGA 模拟信号输出的芯片。不需要提供外部电源&#xff0c;ICNM7301 就可以在正常模式下使用&#xff1b;ICNM7301 广 泛适用于各种市场系统和显示应用体系&#x…

2024-07-18 Unity插件 Odin Inspector8 —— Type Specific Attributes

文章目录 1 说明2 特定类型特性2.1 AssetList2.2 AssetSelector2.3 ChildGameObjectsOnly2.4 ColorPalette2.5 DisplayAsString2.6 EnumPaging2.7 EnumToggleButtons2.8 FilePath2.9 FolderPath2.10 HideInInlineEditors2.11 HideInTables2.12 HideMonoScript2.13 HideReferenc…

【 FPGA 线下免费体验馆】高端 AMD- xilinx 16 nm UltraScale +系列

在复杂的FPGA 开发的过程中&#xff0c;需要能够满足高速、高精度、多通道等的复杂应用。而一个简单的 FPGA 开发板是不具备这些的&#xff0c;因此需要用更高端&#xff0c;大容量&#xff0c;高速IO的 FPGA 芯片与其他硬件组成一个完整的系统开发。这里就产生了高端 FPGA 开发…

拍视频麦克风什么牌子好,什么麦克风音质好,无线麦克风品牌推荐

​当我们想要记录生活中的精彩瞬间&#xff0c;或是在工作中展现专业的一面时&#xff0c;声音的质量显得尤为重要。想象一下&#xff0c;在一间教室中&#xff0c;老师使用无线领夹麦克风&#xff0c;让每一个知识点都能准确无误地传达给学生&#xff1b;在一场激烈的体育赛事…

基于springboot新生宿舍管理系统

系统背景 在当今高等教育日益普及的时代背景下&#xff0c;高校作为知识传播与创新的重要基地&#xff0c;其基础设施的智能化管理显得尤为重要。新生宿舍作为大学生活的起点&#xff0c;不仅是学生日常生活与学习的重要场所&#xff0c;也是培养学生独立生活能力和团队合作精神…

从安装Node到TypeScript到VsCode的配置教程

从安装Node到TypeScript到VsCode的配置教程 1.下载Node安装包&#xff0c; 链接 2.双击安装包&#xff0c;选择安装路径&#xff0c;如下&#xff1a; 3.一直点击下一步&#xff0c;直至安装结束即可&#xff1a; 这个时候&#xff0c;node会默认配置好环境变量&#xff0c;并且…

Kotlin泛型实化

内联函数 reified实现 1. 内联函数 内联函数中的代码会在编译的时候自动被替换到调用它的地方&#xff0c;这样的话也就不存在什么泛型擦除的问题了&#xff0c;因为代码在编译之后会直接使用实际的类型来替代内联函数中的泛型声明。 2. reified关键字 在Kotlin中&#xff0…

webrtc QOS方法十三(视频渲染平滑)

一、背景介绍 视频渲染时间的确定需要考虑三方面的因素&#xff1a;网络抖动、网络延时、音视频同步 网络抖动&#xff1a;视频帧在网络上传输&#xff0c;会受到网络抖动的影响&#xff0c;不能收到立刻播放&#xff0c;需要进行适当的平滑 网络延时&#xff1a;一些报文在…

Axure 教程 | 设置文本框背景透明

​在AXURE软件中&#xff0c;部件样式可以编辑&#xff0c;但有时却无法满足所有个性化原型的需求。例如文本框部件&#xff0c;可以设置是否隐藏边框&#xff0c;但即使隐藏边框之后&#xff0c;文本框还会有白色的背景。 当界面需要一个无背景色的输入框时&#xff0c;对于完…

h5点击电话号跳转手机拨号

需要使用到h5的 <a>标签 我们首先在<head>标签中添加代码 <meta name"format-detection" content"telephoneyes"/>然后再想要的位置添加代码 <a href"tel:10086"> 点击拨打&#xff1a;10086 </a> 这样功能就实现…

QtC++ 设计模式(五)——状态模式

状态模式 序言理解源码 序言 设计模式只是一个抽象的设计模式方法&#xff0c;并不是一个固定使用的搭配&#xff0c;就算是普通switch语句&#xff0c;Map&#xff0c;乃至状态机都是状态模式的其中一种实现方法 状态模式看起来好像和策略模式差不多&#xff0c;主要是其的侧…

FastAPI -- 第三弹(自定义响应、中间件、代理、WebSockets)

路径操作的高级配置 OpenAPI 的 operationId from fastapi import FastAPIapp FastAPI()# 通过 operation_id 参数设置 app.get("/items/", operation_id"some_specific_id_you_define") async def read_items():return [{"item_id": "F…

基于Llama Index构建RAG应用

前言 Hello&#xff0c;大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者&#xff0c;本文参与活动是2024 DataWhale AI夏令营&#xff1b;&#x1f632; 在本文中作者将通过&#xff1a; Gradio、Streamlit和LlamaIndex介绍 LlamaIndex 构…

与VR融合的LED显示屏

随着技术的飞速发展&#xff0c;广告行业已经迎来了3.0时代&#xff0c;这标志着户外LED显示屏不再仅仅局限于空间展示&#xff0c;而是转向了一个全新的维度——“空间时间人”的场景化营销。这种转变要求LED显示屏行业不仅要增强显示技术&#xff0c;还要将消费者的个性化特征…

【中项】系统集成项目管理工程师-第2章 信息技术发展-2.1信息技术及其发展-2.1.1计算机软硬件与2.1.2计算机网络

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

Spring框架之DI依赖注入

Di(Dependence Injection)依赖注入,在spring框架负责创建bean对象时,动态地将依赖对象注入到其它对象中 一、什么是依赖注入。 我们在下面构建spring的过程中体会依赖注入&#xff1b; 从上面的图中我们知道&#xff0c;在ssm框架中服务层&#xff08;server&#xff09;无法直…