初始圖形學(7)

上一章完成了相機類的實現,對之前所學的內容進行了封裝與整理,現在要學習新的內容。

抗鋸齒

我們放大之前渲染的圖片,往往會發現我們渲染的圖像邊緣有尖銳的"階梯"性質。這種階梯狀被稱為"鋸齒"。當真實的相機拍照時,邊緣通常沒有鋸齒,這是因為真實的邊緣時前景和背景顏色的混合,而不是簡單的二值化。而且真實的世界是連續的,而我們渲染的圖像是離散的,也就是說真實世界具有無限的分辨率 ,而我們的=圖像的分辨率是有限的。我們可以通過對每個像素進行多次采樣并取平均值,來近似實現此效果。

我們目前采用的采樣方式被稱為”點采樣“,即在每個像素的中心發射一條射線來進行采樣,但是同時也面臨一個問題,當我們對較遠的地方的圖像進行采樣時,可能會出現"非黑即白"的情況。但實際上我們應該看到的是灰色,而不是黑白分明的點。這是我們的眼睛很自然的對遠處的圖形進行了處理,而這種處理正是我們想要的效果。

所以為了解決這個問題,我們采用"多采樣"的方式,來對我們的圖片實現采樣。我們需要對像素周圍的光線進行采樣,然后對采樣的結果進行整合,以近似真實世界的連續效果。

為了實現這種效果,我們采用最為簡單的方式,我們對以像素為中心的正方形區域進行采樣,這個正方形區域會延伸到每個相鄰的像素的一般位置。這個方法可能效果一般,但是便于實現,具體的內容可以參考文獻像素不是一個小方塊,下面是一個多采樣草圖

image.png

數學工具:隨機數生成

為了實現函數的多采樣,我們需要一個能夠返回真實隨機數的隨機數生成器。這個函數為我們返回一個(0,1)的隨機數,我們可以使用標準庫<cstdlib>中的std::rand()函數,這個函數會返回一個[0,RAND_MAX]之間的隨機整數。我們通過以下改動,可以得到真正的隨機數函數,我們寫在rtweekend.h中:

#include <cmath>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <memory>
...
//實用函數
inline double degree_to_radius(double degrees){return degrees * pi / 180.0;
}
inline double random_double(){//返回[0,1)的數return std::rand() / (RAND_MAX + 1.0);
}
inline double random_double(double min,double max){//返回[min,max)的數return min + (max - min)*random_double();
}

不過由于rand()具有隨機性較差等特點,所以在C++11標準下有其他的隨機數函數寫法:

...
#include <random>
...
inline double random_double(){static std::uniform_real_distribution<double> distribution(0.0,1.0);static std::mt19937 generator;return distribution(generator);
}
...

不過看不太懂就是了

使用多采樣式生成像素

對于由多個樣本組成的像素,我們將從像素周圍的區域選擇樣本,并將顏色(光值)平均在一起

我們需要更新我們的write_color()函數以考慮我們的樣本數量,不過在此之前,我們需要添加一個用于區間判斷的輔助函數interval::clamp(x),以確保最終的結果的顏色分量保持在正確的[0,1]范圍:

class interval{
public:
...
//包含double clamp(double x) const{if(x < min) return min;if(x > max) return max;return x;}

接下來我們更新write_color()函數,其包含區間的限制功能:

 ?void write_color(std::ostream& out,const color& pixel_color){auto r = pixel_color.x();auto g = pixel_color.y();auto b = pixel_color.z();//使用區間RGB[0,1]計算RGB值static const interval intensity(0.000,0.999);int rbyte = int (256*intensity.clamp(r));int gbyte = int (256*intensity.clamp(g));int bbyte = int (256*intensity.clamp(b));out << rbyte << ' ' << gbyte << ' ' << bbyte << '\n';
}

這樣保證了我們的的rgb分量不會超出[0,1]的范圍,這樣更加安全。

接下來我們需要更新相機類,以定義和使用一個新的camera::get_ray(i,j)函數,該函數將為每個像素生成不同的樣本。這個函數將使用一個新的輔助函數sample_squred(),該函數在以原點為中心的正方形內生成一個隨機樣本點。然后我們將這個正方形中的隨機樣本轉換為我們當前正在采樣的特定像素。

?
#ifndef RENDER_C___CAMERA_H
#define RENDER_C___CAMERA_H
?
#include "hittable.h"
?
class camera{
public:double aspect_radio = 1.0; ? ? ?//圖像的寬高比int ? ?image_width = 100; ? ? ? //圖像寬度的像素數int ? ?samples_per_pixel = 10; ?//每個像素的采樣次數
?void render(const hittable& world){initialize();
?std::cout << "P3\n" << image_width << " " << image_height << "\n255\n";for(int j=0;j<image_height;j++){std::clog << "\rScanlines remaining: " << (image_height - j) << ' ' << std::flush;for(int i=0;i<image_width;i++){color pixel_color(0,0,0);for(int sample = 0;sample < samples_per_pixel; sample++){ray r = get_ray(i,j);pixel_color += ray_color(r,world);}write_color(std::cout,pixel_color*pixel_samples_scale);}}std::clog << "\rDone. ? ? ? ? ? ? ? ? ? \n";}
?
private:int image_height; ? ? ? ? ? //渲染圖像的高度double pixel_samples_scale; //每次采樣的顏色權重point3 camera_center; ? ? ? //相機的中心point3 pixel00_loc; ? ? ? ? //像素(0,0)的位置vec3 pixel_delta_u; ? ? ? ? //向右的偏移值vec3 pixel_delta_v; ? ? ? ? //向下的偏移值
?void initialize(){image_height = int(image_width/aspect_radio);image_height = (image_height < 1) ? 1 : image_height;
?pixel_samples_scale = 1.0 / samples_per_pixel;
?camera_center = point3 (0,0,0);
?//確認視窗的設置auto focal_length = 1.0; ? ?//焦距設置auto viewport_height = 2.0;auto viewport_width = viewport_height*(double (image_width)/image_height);
?//視圖邊緣的向量計算auto viewport_u = vec3(viewport_width,0,0);auto viewport_v = vec3(0,-viewport_height,0);//計算視圖的像素間的水平豎直增量pixel_delta_u = viewport_u/image_width;pixel_delta_v = viewport_v/image_height;
?//計算左上角第一個像素中心的坐標auto viewport_upper_left = camera_center - vec3(0,0,focal_length) - viewport_v/2 - viewport_u/2;pixel00_loc = viewport_upper_left + 0.5*(pixel_delta_u+pixel_delta_v);}
?ray get_ray(int i,int j){//構造一個從原點開始的隨機采樣射線,指向(i,j)像素
?auto offset = sample_square();auto pixel_sample = pixel00_loc + ((i+offset.x())*pixel_delta_u) + ((j+offset.y())*pixel_delta_v);
?auto ray_origin = camera_center;auto ray_direction = pixel_sample - ray_origin;
?return ray(ray_origin,ray_direction);}
?vec3 sample_square() const {//返回一個一個隨機的點,在[-0.5,-0.5]~[+0.5,+0.5]的單位矩陣內return {random_double() - 0.5, random_double() - 0.5,0};}
?color ray_color(ray & r,const hittable& world){hit_record rec;if(world.hit(r,interval(0,infinity),rec)){return 0.5*(rec.normal + color(1,1,1));}
?vec3 unit_direction = unit_vector(r.direction());auto a = 0.5*(unit_direction.y()+1.0);return (1.0 - a)*color(1.0,1.0,1.0) + a*color(0.5,0.7,1.0);}
};
?
#endif //RENDER_C___CAMERA_H

這是新的camera,我們更新了get_ray()sample_square(),還有新的公有私有屬性

接下來設置一下主函數的參數:

?
int main(){hittable_list world;world.add(make_shared<sphere>(point3(0,0,-1),0.5));world.add(make_shared<sphere>(point3(0,-100.5,-1),100));
?camera cam;
?cam.aspect_radio = 16.0/9.0;cam.image_width = 400;cam.samples_per_pixel = 100;    // 設置采樣次數
?cam.render(world);
}

image.png

這里可以看到左圖的鋸齒明顯減少了,我們的抗鋸齒設置的十分成功,今天就到此為止了

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80509.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80509.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80509.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vllm筆記

目錄 vllm簡介vllm解決了哪些問題&#xff1f;1. **瓶頸&#xff1a;KV 緩存內存管理低效**2. **瓶頸&#xff1a;并行采樣和束搜索中的內存冗余**3. **瓶頸&#xff1a;批處理請求中的內存碎片化** 快速開始安裝vllm開始使用離線推理啟動 vLLM 服務器 支持的模型文本語言模型生…

訪問網站提示“不安全”“有風險”怎么辦?

訪問網站提示“不安全”“有風險”有以下幾種解決方案 一、理解警告類型 1.“不安全”提示&#xff08;HTTP網站&#xff09; 原因&#xff1a;網站未使用HTTPS加密&#xff0c;傳輸數據&#xff08;如密碼、支付信息&#xff09;可能被竊取。 表現&#xff1a;瀏覽器地址欄顯…

vue3的響應式設計原理

Vue 3 的響應式設計是其核心特性之一&#xff0c;依賴于 Proxy 和 依賴收集機制&#xff0c;相比 Vue 2 的 Object.defineProperty&#xff0c;Vue 3 的響應式系統更加高效、靈活且易于維護。 以下是 Vue 3 響應式設計的核心原理&#xff1a; 一、核心機制概覽 使用 Proxy 實現…

C++模板筆記

Cpp模板筆記 文章目錄 Cpp模板筆記1. 為什么要定義模板2. 模板的定義2.1 函數模板2.1.1 函數模板的重載2.1.2 頭文件與實現文件形式&#xff08;重要&#xff09;2.1.3 模板的特化2.1.4 模板的參數類型2.1.5 成員函數模板2.1.6 使用模板的規則 2.2 類模板2.3 可變參數模板 模板…

遞歸函數(斐波那契數列0,1,1,2,3,5,8,13,21,34,55...)

目錄 一、斐波那契數列&#xff08;兔子問題&#xff09; 二、迭代法&#xff08;用while循環推下一項 ) 三、遞歸函數 (函數的定義中調用函數自身的一種函數定義方式) 四、遞歸函數的底層邏輯推理 (二叉樹推倒最左下節點回退法) 一、斐波那契數列&#xff08;兔子問題&…

光的本質(以暗物質維度粒子為介質的能量傳導)

一、光的概要描述 1、光的本質是能量傳導 空間中均勻分布著暗物質維度粒子。光不是粒子也不是波,而是沒有質量和形態的能量,在臨近暗物質粒子之間的一種能量傳遞。 2、光能傳遞類似牛頓鐘擺(空間中的牛頓鐘擺) 當光能能量騷動一個暗物質粒子后,該暗物質粒…

Open CASCADE學習|管道殼體生成

一、引言 在計算機輔助設計&#xff08;CAD&#xff09;和計算機圖形學領域&#xff0c;OpenCASCADE 是一款功能強大的開源 3D 建模庫。它提供了豐富的幾何和拓撲建模工具&#xff0c;其中管道殼體&#xff08;Pipe Shell&#xff09;生成是其重要功能之一。管道殼體廣泛應用于…

JS正則表達式介紹(JavaScript正則表達式)

文章目錄 JavaScript正則表達式完全指南正則表達式基礎元字符與特殊字符基本元字符. - 點號\d - 數字\D - 非數字\w - 單詞字符\W - 非單詞字符\s - 空白字符\S - 非空白字符 正則表達式標志常用標志詳解g - 全局匹配i - 忽略大小寫m - 多行匹配s - 點號匹配所有字符u - Unicod…

Kubernetes 使用 containerd 實現 GPU 支持及 GPU Operator 部署指南

目錄 Kubernetes 使用 containerd 實現 GPU 支持及 GPU Operator 部署指南 一、為什么 containerd 是趨勢&#xff1f; 二、目標 三、前提條件 四、方式一&#xff1a;containerd nvidia-container-toolkit&#xff08;基礎方式&#xff09; 1?? 安裝 NVIDIA Containe…

leetcode 2918. 數組的最小相等和 中等

給你兩個由正整數和 0 組成的數組 nums1 和 nums2 。 你必須將兩個數組中的 所有 0 替換為 嚴格 正整數&#xff0c;并且滿足兩個數組中所有元素的和 相等 。 返回 最小 相等和 &#xff0c;如果無法使兩數組相等&#xff0c;則返回 -1 。 示例 1&#xff1a; 輸入&#xf…

猿人學第十二題-js入門

1. 鏈接 https://match.yuanrenxue.cn/match/12 2. 抓包分析 2.1. m參數 通過觀察&#xff0c;只有m參數要解決&#xff1a; 3. 逆向分析 3.1. 跟棧 直接跟棧吧&#xff0c;一下就出結果了&#xff1a; 可以看到m其實很簡單&#xff0c;就是固定字符串 當前頁數&#xf…

雙系統電腦中如何把ubuntu裝進外接移動固態硬盤

電腦&#xff1a;win11 ubuntu22.04 實體機 虛擬機&#xff1a;VMware17 鏡像文件&#xff1a;ubuntu-22.04.4-desktop-amd64.iso 或者 ubuntu20.4的鏡像 外接固態硬盤1個 一、首先win11中安裝vmware17 具體安裝方法&#xff0c;網上很多教程 二、磁盤分區 1.在筆…

202535| Kafka架構與重要概念+冪等性+事務

好的&#xff01;以下是關于 Kafka 架構 以及其 重要概念 的詳細介紹&#xff0c;結合 Mermaid 圖形 和 表格&#xff0c;幫助你更好地理解各個概念的關系和作用。 Kafka 架構與重要概念 Kafka 是一個分布式消息系統&#xff0c;廣泛應用于日志收集、流處理、事件驅動架構等場…

從0開始學習大模型--Day05--理解prompt工程

提示詞工程原理 N-gram&#xff1a;通過統計&#xff0c;計算N個詞共同出現的概率&#xff0c;從而預測下一個詞是什么。 深度學習模型&#xff1a;有多層神經網絡組成&#xff0c;可以自動從數據中學習特征&#xff0c;讓模型通過不斷地自我學習不斷成長&#xff0c;直到模型…

Amazing晶焱科技:系統級 EOS 測試方法 - System Level EOS Testing Method

系統上常見的EOS測試端口以AC電源、電話線&#xff08;RJ11&#xff09;、同軸電纜&#xff08;coaxial cable&#xff09;以及以太網絡&#xff08;RJ45&#xff09;最常見&#xff0c;這些端口因有機會布線至戶外的關系&#xff0c;受到EOS/Surge沖擊的幾率也大大提升。因此電…

數據結構—(概述)

目錄 一 數據結構&#xff0c;相關概念 1. 數據結構&#xff1a; 2. 數據(Data): 3. 數據元素(Data Element): 4. 數據項&#xff1a; 5. 數據對象(Data Object): 6. 容器&#xff08;container&#xff09;&#xff1a; 7. 結點&#xff08;Node&#xff09;&#xff…

Vue 兩種導航方式

目錄 一、聲明式導航 二、編程式導航 三、兩句話總結 一、聲明式導航 1. 傳參跳轉&#xff1a; <router-link :to"/user?nameCHEEMS&id114514">Query傳參 </router-link><router-link :to"/user?參數名1參數值1&參數名2參數值2&a…

QTableWidget實現多級表頭、表頭凍結效果

最終效果&#xff1a; 實現思路&#xff1a;如果只用一個表格的話寫起來比較麻煩&#xff0c;可以考慮使用兩個QTableWidget組合&#xff0c;把復雜的表頭一個用QTableWidget顯示&#xff0c;其他內容用另一個QTableWidget。 #include "mainwindow.h" #include &qu…

2025年客運從業資格證備考單選練習題

客運從業資格證備考單選練習題 1、從事道路旅客運輸活動時&#xff0c;應當采取必要措施保證旅客的人身和財產安全&#xff0c;發生緊急情況時&#xff0c;首先應&#xff08; &#xff09;。 A. 搶救財產 B. 搶救傷員 C. 向公司匯報 答案&#xff1a;B 解析&#xff1a;…

python打卡day21

常見的降維算法 知識點回顧&#xff1a; LDA線性判別PCA主成分分析t-sne降維 之前學了特征降維的兩個思路&#xff0c;特征篩選&#xff08;如樹模型重要性、方差篩選&#xff09;和特征組合&#xff08;如SVD/PCA&#xff09;。 現在引入特征降維的另一種分類&#xff1a;無/有…