Ray_Tracing_In_One_Weekend下

news2024/11/25 14:12:27

1·Lambertian漫反射材质

一个物体的材质,可以分成两部分来看,因为物体没有绝对光滑和绝对粗糙

漫反射:由于物体粗糙,那么对于微小平面,光线会向四周反射,光源的一部分光线传回人眼

镜面反射:假设物体绝对光滑,那么反射方向都是一致的,因此当特定角度观察,光线很大一部分传到人眼

物体越粗糙,漫反射材质部分占比越大

步骤:

为了模拟漫反射材质,我们生成随机反射方向,这个方向根据,交点的法线终点坐标为中心的单位球体内,生成随机点,求得反射光线(随机点 - 交点)

如何在单位球体生成随机点?

首先根据在一个-1---1的单位立方体内,生成随机点,如果这个点在球外(向量长度>1,超出了单位球的半径)就重新生成直到该点在球内:

然后 ,交点法线 + 随机点向量 = 反射方向

vec3 random_in_unit_sphere() {
    while (true) {
        auto p = vec3::random(-1,1);
        if (p.length_squared() >= 1) continue;
        return p;
    }
}

递归求光线颜色

  • 如果有交点,生成随机反射光线,递归 * 0.5,因此次数越多会越暗
  • 如果递归超过50次,那么返回0,防止一直有有交点不断反射的情况
  • 如果没有交点,返回背景颜色

结果:

2·伽马校正(gamma corrected)

 我们看到的球体比较暗,这其实和计算的数据不符合,因为屏幕CRT2.2会自动校正颜色,因此我们应该进行gamma校正(1/2.2次幂),让我们看到实际颜色值

这里简单的应用x^1/2次幂,也就是 sqrt(x)

3`优化

为了防止自相交,t==0.000……1的情况 ,我们将t的范围控制在>0.001

我们生成的随机点是单位球体积内, 这样生成的向量大概率上会和法线方向相近, 并且极小概率会沿着入射方向反射回去。

但是真正的反射分布率会更加均衡。这是因为我们选取的是单位球面上的点。我们可以通过在单位球内选取一个随机点, 然后将其单位化来获得该点。

vec3 random_unit_vector() {
    auto a = random_double(0, 2*pi);
    auto z = random_double(-1, 1);
    auto r = sqrt(1 - z*z);
    return vec3(r*cos(a), r*sin(a), z);
}

兰伯特球体: 

还有一种方式,选取随机反射方向:通过在交点取单位球体的随机方向,然后再和物体的normal点乘,判断是否在物体表面的半球 

4·金属材质

我们将材质类抽象出来,虚函数scatter散射函数,它会返回反射率(albedo),和散射光线

Lambertian材质继承material材质基类,散射光线是随机生成的

另外创建一个新的metal材质继承material材质基类,散射光线遵守反射定律,通过:

首先点乘vn,因为n是单位向量,点乘表示向量v在这个单位向量方向n上的投影长度,||v||costheta(三角函数)

因为vn方向相反,因此点乘cos结果为负数,即在n的反向方向的投影,因此需要加符号

反射向量 = v + 2(v在n 的投影长度)

vec3 reflect(const vec3& v, const vec3& n) {
    return v - 2*dot(v,n)*n;
}

可以让金属模糊,方法是以反射向量终点为单位球心,生成随机点,用这个点作为最终的反射方向 

通过fuzz变量控制模糊程度,随机向量 * fuzz,fuzz越大随机球的半径越大

可以看到越来越接近漫反射材质,左边0.3,右边1.0

5·绝缘体材质

 透明的材料, 例如水, 玻璃, 和钻石都是绝缘体。当光线击中这类材料时, 一条光线会分成两条, 一条发生反射, 一条发生折射。我们假设它每次要么是反射,要么是折射

斯内尔定律/折射定律:eta和eta prime是折射率,theta和theta prime是入射光线与折射光线距离法相的夹角

我们要表示折射向量,首先将折射向量Rprimer分解为,两个投影方向的加法,下面是一个经过推导后的结论,其中costheta(- 入射向量 * 法线)

 根据这个结论,写出refract折射函数,返回折射向量,

创建一个新的dielectric绝缘体材质,ref_idx是入射介质折射率,如果法线是正面的,那么eta / eta prime = 1 / ref_idx(空气折射率为1),否则法线反面,代表光线从物体内部折射出去,也就是ref_idx / 1 = ref_idx

设置散射光线为折射光线,但是这是会出现黑点,这是由于eta > eta prime导致的,比如从玻璃1.3进入空气,如果eta /  eta prime * sintheta >1,也就是sintheta primer>1这根本不可能求解,

因此无法计算折射光线的结果,我们应该在不会发生折射时进行反射

全内反射:所有的光线都不发生折射, 转而发生了反射,它经常在实心物体的内部发生

double cos_theta = ffmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
if (etai_over_etat * sin_theta > 1.0)
{
    vec3 reflected = reflect(unit_direction, rec.normal);
    scattered = ray(rec.p, reflected);
    return true;
}

优化:

最左侧是玻璃材质,这和现实世界的玻璃还不同,现实世界中的玻璃, 发生折射的概率会随着入射角而改变,从一个很狭窄的角度去看玻璃窗, 它会变成一面镜子

有个数学上近似的等式, 它是由Christophe Schlick提出的(几何函数:微平面间相互遮蔽的比率):

其中如果光线和物体法线,以一定角度观察,也会执行反射

double schlick(double cosine, double ref_idx) {
    auto r0 = (1-ref_idx) / (1+ref_idx);
    r0 = r0*r0;
    return r0 + (1-r0)*pow((1 - cosine),5);
}
double reflect_prob = schlick(cos_theta, etai_over_etat);
if (random_double() < reflect_prob)
{
    vec3 reflected = reflect(unit_direction, rec.normal);
    scattered = ray(rec.p, reflected);
    return true;
}

对于想要渲染通透的玻璃,需要两个球体,把一个小球套在大球里,半径设置为负数

6·摄像机 

优化我们的摄像机类,使得在创建相机时,可以轻松调整高度方向的视角fov theta,并且指定aspect 屏幕宽高比,和调整摄像机位置和旋转

那么屏幕(摄像机)一半高度可以由这两个参数表示 ==  half_height(==tan(fov theta / 2) == h / z,假设-z为1时省略,z越大,h越大        ),一半宽度 == aspect  * half_height

 调整摄像机位置和旋转,为了描述位置,我们建立lookfrom摄像机的位置,lookat看向的点,为了描述旋转,建立正上方向向量vup,可以绕着lookfrom--lookat轴旋转

class camera
{
public:
    camera(
        vec3 lookfrom, vec3 lookat, vec3 vup, /* 摄像机位置,屏幕的中心,上向量 */
        double vfov,                          /* 高度视角 */
        double aspect)                        /* 宽高比 */
    {
        origin = lookfrom; /* 摄像机原点 */
        vec3 u, v, w;
        /* 根据fov视角,计算宽高的长度 */
        auto theta = degrees_to_radians(vfov);
        auto half_height = tan(theta / 2);
        auto half_width = aspect * half_height;

        w = unit_vector(lookfrom - lookat); /* lookfrom-lookat */
        u = unit_vector(cross(vup, w));     /* 屏幕x坐标方向 */
        v = cross(w, u);                    /* 屏幕y坐标方向 */
        /*
            摄像机起点到屏幕平面形成锥形区域,我们计算左下点向量,实际是从摄像机向屏幕左下的向量
            左下角:屏幕一半的宽高分别*uv向量,有了两个从lookfrom出发的向量
            origin - 宽向量,向量取反,  -高向量,获得从uv平面lookfrom指向左下的向量,
            -w获得从lookfrom指向以lookat为中心屏幕的左下角
         */
        lower_left_corner = origin - (half_width * u) - (half_height * v) - w; /* */

        horizontal = 2 * half_width * u; /* 水平和垂直向量 */
        vertical = 2 * half_height * v;
    }

    ray get_ray(double s, double t)
    {
        /*
            向量位置无关性,那么为什么摄像机的origin的位置会影响渲染结果?
            因为ray的创建指定了origin,在世界空间从origin位置发射光线,求这个光线的交点
         */
        /*
            左下向量 + s向量 + t向量 获得从origin为起点,指向新的像素坐标
         */
        return ray(origin, lower_left_corner + (s * horizontal) + (t * vertical) - origin);
    }

public:
    vec3 origin;            /* 原点 */
    vec3 lower_left_corner; /* 左下 */
    vec3 horizontal;        /* 宽度 */
    vec3 vertical;          /* 高度 */
};

不同fov大小 

 

7·散焦模糊

不在从lookfrom发射光线,而是指定aperture孔径大小,随机球面生成lookfrom光线起点方向

focus_dist交点距离

8·渲染大场景

利用之前所有的框架,渲染多个球体,使用不同的材质,设置球体大小和位置

 

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

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

相关文章

C++ 类和对象的初步介绍

文章目录 1.面向过程和面向对象的初步认识2.类的引入3.类的定义4. 类的访问限定符及封装4.1 访问限定符4.2 封装 5.类的作用域6.类的实例化 1.面向过程和面向对象的初步认识 C语言是面向过程的&#xff0c;关注的过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用…

OpenCV Canny()函数

OpenCV Canny()函数被用来检测图像物体的边缘。其算法原理如下&#xff1a; 高斯滤波&#xff1a;使用高斯滤波器平滑图像以减少噪声。高斯滤波器是一种线性滤波器&#xff0c;可以消除图像中的高频噪声&#xff0c;同时保留边缘信息。计算梯度强度和方向&#xff1a;使用Sobe…

教资备考--高中数学(仅为高中数学梳理)

按照高中学习数学梳理的方案进行整理

爬虫——同步与异步加载

一、同步加载 同步模式--阻塞模式&#xff08;就是会阻止你浏览器的一个后续加载&#xff09;停止了后续的解析 因此停止了后续的文件加载&#xff08;图像&#xff09; 比如hifini音乐网站 二、异步加载 异步加载--xhr(重点) 比如腾讯新闻&#xff0c;腾讯招聘等 三、同…

组合框QGroupBox设置checked属性

我们可以给组合框设置checked属性 //获取和设置checked属性是否开启 bool isCheckable() const void setCheckable(bool checkable)//获取和设置选中还是非选中 bool isChecked() const void setChecked(bool checked)效果如下&#xff1a; 如果checked属性使能之后&#xff0…

Open3D实现点云数据的序列化与网络传输

转载自个人博客&#xff1a;Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候&#xff0c;有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输&#xff0c;但Open3D源码在实现RPC功能时就提供了一套序列化及传输的…

用langchain+streamlit应用RAG实现个人知识库助手搭建

RAG原理概述 RAG&#xff08;Retrieval-Augmented Generation&#xff09; 是一种结合了信息检索和生成式人工智能技术的模型架构&#xff0c;旨在让模型生成更有根据和更准确的回答。通俗来讲&#xff0c;它让模型不只是凭借自己的“记忆”&#xff08;预训练数据&#xff09…

Java中的依赖注入(Dependency Injection, DI)详解

Java中的依赖注入&#xff08;Dependency Injection, DI&#xff09;是软件工程中的一种重要设计模式。它有助于提高系统的可测试性、可维护性和灵活性。通过依赖注入&#xff0c;组件不再负责创建它们所需的对象&#xff0c;而是通过外部的设置来提供这些对象。这种方式也与控…

无人机+无人车+机器狗:综合管控系统技术详解

无人机、无人车、机器狗的综合管控系统技术是一个集成了多种先进技术和设备的复杂系统&#xff0c;旨在实现高效、精准、协同的作业与管理。以下是对该系统技术的详细解析&#xff1a; 一、系统概述 综合管控系统通过集成无人机、无人车和机器狗等智能设备&#xff0c;结合物…

OSDU轻量化单机部署

首先更新系统 sudo apt update sudo apt upgrade -y安装docker sudo apt install -y docker.io sudo systemctl start docker sudo systemctl enable docker安装minikube curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 …

vmware Workstation16设置批量虚拟机开机自启 vmAutoStart

文章目录 前言解压压缩包一、使用步骤1.获取虚拟机所在目录2.获取vmware所在目录3.测试启动4.开机自启 二、gitee总结 前言 vmware workstation16不支持虚拟机开机自启&#xff0c;通常的办法是写脚本&#xff0c;但是有个问题就是不能启动多台虚拟机&#xff0c;因为有时候会…

Python | Leetcode Python题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; class Solution:def findContentChildren(self, g: List[int], s: List[int]) -> int:g.sort()s.sort()m, n len(g), len(s)i j count 0while i < m and j < n:while j < n and g[i] > s[j]:j 1if j < n:count 1i …

Spring框架使用Api接口实现AOP的切面编程、两种方式的程序示例以及Java各数据类型及基本数据类型的默认值/最大值/最小值列表

一、Spring框架使用Api接口-继承类实现AOP的切面编程示例 要使用Spring框架AOP&#xff0c;除了要导入spring框架包外&#xff0c;还需要导入一个织入的包org.aspectj&#xff0c;具体maven依赖如下&#xff1a; <dependency><groupId>org.springframework</gr…

JAVA-异常(通俗易懂)

目录 一、异常的概念 1.算术异常 2.数组越界异常 3.空指针异常 二、异常体系结构 三、异常的分类 1. 编译时异常 2. 运行时异常 四、异常处理 1.防御式编程 2.异常的抛出 3.异常的捕获 4.try-catch捕获并处理 5.finally 五、异常的处理流程 六. 自定义异常类…

ade20k 街景图像【数据集】及其【论文出处】ADE20K数据集 超过25000张图像的语义分割数据集

ade20k 街景图像【数据集】及其【论文出处】ADE20K数据集介绍 是一个包含超过25000张图像的语义分割数据集&#xff0c;这些图像被密集注释&#xff0c;覆盖室内和室外场景。 它由MIT发布&#xff0c;包含100个事物类别和50个物质类别&#xff0c; 用于训练和验证的图像数量分别…

(16)MATLAB仿真Nakagami-m分布1

文章目录 前言一、Nakagami分布二、MATLAB建模代码三、仿真结果画图四、总结 前言 Nakagami衰落模型最初是由于该模型与短波电离层传播的经验结果相匹配而提出的。它还用于仿真来自多个干扰源的情况&#xff0c;因为多个独立且同分布&#xff08;i.i.d&#xff09;的瑞利分布随…

线程池的实现和讲解:解决多线程并发服务器创建销毁线程消耗过大的问题

1.前言 多进程/线程并发服务器、多路I/O转接服务器的简单实现-CSDN博客 原先的多线程并发服务器&#xff0c;有多少个客户端连接服务器就有多少个线程&#xff0c;CPU需要在多个线程之间来回切换处理客户端的请求&#xff0c;系统消耗比较大(每次创建和消耗线程在操作系统内部…

linux学习--第七天(多路复用IO)

多路复用IO -阻塞IO与非阻塞IO -IO模型 IO的本质时基于操作系统接口来控制底层的硬件之间数据传输&#xff0c;并且在操作系统中实现了多种不同的IO方式&#xff08;模型&#xff09;比较常见的有下列三种&#xff1a; 1.阻塞型IO模型 2.非阻塞型IO模型 3.多路复用IO模型 -阻…

开源2+1链动模式AI智能名片O2O商城小程序源码:线下店立体连接的超强助力器

摘要&#xff1a;本文将为您揭示线下店立体连接的重大意义&#xff0c;您知道吗&#xff1f;线上越火&#xff0c;线下就得越深入经营。现代门店可不再只是卖东西的地儿&#xff0c;还得连接KOC呢&#xff01;咱们来看看门店要做的那些超重要的事儿&#xff0c;还有开源21链动模…

Authentication Lab | CVE-2019-7644 - JWT Signature Disclosure

关注这个靶场的其他相关笔记&#xff1a;Authentication Lab —— 靶场笔记合集-CSDN博客 0x01&#xff1a;JWT Signature Disclosure 前情提要 本关的考点是 JWT&#xff08;Json Web Token&#xff09;漏洞&#xff0c;JWT 是一个用于跨域认证的技术。如果你不了解 JWT&…