zoukankan      html  css  js  c++  java
  • 【Ray Tracing in One Weekend 超详解】 光线追踪1-9 景深

    今天我们来学最后一章

     Chapter11:Defocus Blur

     Preface

    散焦模糊 也称 景深

    首先,我们来了解一下散焦模糊,我们在真实相机中散焦模糊的原因是因为它们需要一个大圈(而不仅仅是一个针孔)来聚光。这会使所有东西都散焦,但是如果用小孔的话,那么通过前后调整相机镜头,就会使得一切景色都会聚焦到相机镜头中,也就是会汇聚到那个孔内。物体聚焦的那个平面的距离由镜头和胶片/传感器之间的距离控制。这就是为什么当你改变焦点时可以看到镜头相对于相机移动的原因。

    光圈是一个可以有效控制镜头大小的孔。对于真正的相机,如果你需要更多光线,你可以使光圈更大,同时也会获得更多的散焦模糊。对于我们的虚拟相机,我们也需要一个光圈

    真正的相机具有复杂的复合镜头。对于我们的代码,我们可以模拟顺序:传感器,然后是镜头,然后是光圈,并找出发送光线的位置并在计算后翻转图像(图像在胶片上倒置投影)。人们通常使用薄透镜模拟近似。

    引用书上一张图(相机聚焦成像)

    我们不需要这么复杂,我们通常从镜头表面开始射线,并将它们发送到虚拟胶片平面,方法是找到胶片在焦点平面上的投影(在距离focus_dist处)。

     正文

    前面说了一大堆,看着比较复杂,其实并没有那么难

    前言说了三件事情:

    第一点,生活中的相机成像分为两个部分,inside和outside,涉及3个物:film(胶片)、lens(镜片)、focusPlane(焦点平面),而我们只需要outside部分

    其次第二点,我们的眼睛(或者相机)不再是一个点而是眼睛所在的周围圆盘上的随机点,因为实际的相机是有摄像镜头的,摄像镜头是一个大光圈(很大一个镜片),并不是针孔类的东东,所以,我们要模拟镜头,就要随机采针孔周围的光圈点。

    可能有人不明白这里,可以回去看看1-3

    看一下这张图

    这是之前我们讲的光线追踪的成像过程,从eye开始发射视线,这个扫描屏幕中的每个点,如果中间被物体遮挡,那么计算,计算之后的像素值为屏幕上该点的像素值,如果没有遮挡,那么屏幕上那个点的像素值就是背景对应的值

    我们这里只不过是把eye变为周围单位圈内的随机点,仅此模拟实际相机镜头

    第三点,这个应该是上一章节提到的问题:

    上一章节我们说了,为了方便,上一章节假定成像平面位于z = -1(或者是-w平面,按w基向量算)

    所以 tan(theta/2) = (h/2) / dis ,其中dis为1

    而这一章,我们使dis真正变成了一个变量,即:焦距(镜片到成像平面之间的距离)

    (图片来自百度百科:)

     随之,我们的成像平面也就到了z = -focus,或者是-focus * w平面(按w基向量算)

    所以,构造函数,我们就需要加两个参数,改两行行即可

    还有就是单位圆盘取随机点函数

    const rtvec random_unit_disk()            //find a random point in unit_disk
        {
            rtvec p;
            do
            {
                p = 2.0*rtvec(rtrand01(), rtrand01(), 0) - rtvec(1, 1, 0);
            } while (dot(p, p) >= 1.0);
            return p;
        }

    下面是所有的camera类

    /// camera.h
    
    // -----------------------------------------------------
    // [author]        lv
    // [begin ]        2019.1
    // [brief ]        the camera-class for the ray-tracing project
    //                from the 《ray tracing in one week》
    // -----------------------------------------------------
    
    #ifndef CAMERA_H
    #define CAMERA_H
    
    #include "ray.h"
    
    namespace rt
    {
    
    class camera
        {
    public:
        camera(rtvec lookfrom, rtvec lookat, rtvec vup, rtvar vfov, rtvar aspect, rtvar aperture, rtvar focus)
            :_eye(lookfrom)
            ,_lens_radius(aperture/2)
        {
            rtvar theta = vfov * π / 180;
            rtvar half_height = tan(theta / 2) * focus;        //tan(theta/2) = (height/2) / 焦距
            rtvar half_width = aspect * half_height;
            _w = (lookfrom - lookat).ret_unitization();
            _u = cross(vup, _w).ret_unitization();
            _v = cross(_w, _u);
    
            //向量运算
            _start = _eye - half_width * _u - half_height * _v - focus * _w;//高和宽都乘了焦距,w也要乘,不然公式是错的
            _horizontal = 2 * half_width * _u;
            _vertical = 2 * half_height * _v;
        }
    
        inline const ray get_ray(const rtvar u,const rtvar v)const
            {
                rtvec rd = _lens_radius * random_unit_disk();
                rtvec offset = _u * rd.x() + _v * rd.y();
                return ray{ _eye + offset, _start + u*_horizontal + v*_vertical - (_eye + offset) };
            }
    
        inline const ray get_ray(const lvgm::vec2<rtvar>& para)const
            {    return get_ray(para.u(), para.v());    }
    
        inline const rtvec& eye()const { return _eye; }
    
        inline const rtvec& start()const { return _start; }
    
        inline const rtvec& horizontal()const { return _horizontal; }
    
        inline const rtvec& vertical()const { return _vertical; }
    
        inline const rtvec& u()const { return _u; }
    
        inline const rtvec& v()const { return _v; }
    
        inline const rtvec& w()const { return _w; }
    
        inline const rtvar lens_r()const { return _lens_radius; }
    
    private:
        rtvec _u;
    
        rtvec _v;
    
        rtvec _w;
    
        rtvec _eye;
    
        rtvec _start;        //left-bottom
    
        rtvec _horizontal;
    
        rtvec _vertical;
    
        rtvar _lens_radius;  //the radius of lens
    
        };
    
    }
    
    #endif
    camera.h

    所以,我们用上一章的球体设置,把相机改一下,渲染一把

    渲染效果就是开篇那张图

     晚安

  • 相关阅读:
    Unix domain sockets
    python异常处理
    php注册登录源代码
    div,css命名规范!
    html、css和js注释的规范用法
    PHPstrom的Disable Power Save Mode
    开通了博客园
    O(1)时间删除链表中的节点 13
    打印1到最大的n位数 12
    自己实现一个数的整数次方 11
  • 原文地址:https://www.cnblogs.com/lv-anchoret/p/10223222.html
Copyright © 2011-2022 走看看