《一週學完光線追蹤》學習 六 抗鋸齒

蒙特卡洛光線追蹤技術系列 見 蒙特卡洛光線追蹤技術

當一個真正的相機拍攝一張照片,通常沒有沿邊緣的鋸齒,因爲邊緣像素是一些前景和一些背景的混合。我們可以通過平均每個像素內的一組樣本獲得相同的效果。我們不必爲分層而煩惱,這是有爭議的,但對我的節目來說卻是司空見慣的。對於某些光線跟蹤器來說,這是非常關鍵的,但是我們正在編寫的那種一般的光線跟蹤器並不能從中得到太多好處,而且會使代碼更難看。我們把相機類抽象了一點,以便以後可以製作一個更酷的相機。

我們需要的是一個隨機數生成器,它返回真實的隨機數。傳統上,C++沒有標準的隨機數生成器,但是大多數系統都有DRAND48(),它是我在這裏使用的。但是,C++的更新版本已經解決了這個問題。無論您的基礎結構是什麼,都要找到一個函數,它返回一個規範隨機數,按照慣例,該隨機數返回0<=ran<1範圍內的隨機實數。1之前的“小於”很重要,因爲我們有時會利用它。(這裏我們採用的是 rand() / (RAND_MAX + 1.0) 函數來生成0-1之間的隨機數)

對於給定的像素,我們在該像素內有多個採樣,並通過每個採樣發送光線。然後對這些光線的顏色進行平均:

把所有這些放在一起,就產生了一個camera類,它封裝了我們以前的簡單軸對齊相機:

#pragma once
#ifndef __CAMERA_H__
#define __CAMERA_H__

#include "ray.h"

class camera {
public:
	camera() {
		lower_left_corner = vec3(-2.0, -1.0, -1.0);
		horizontal = vec3(4.0, 0.0, 0.0);
		vertical = vec3(0.0, 2.0, 0.0);
		origin = vec3(0.0, 0.0, 0.0);
	}
	ray get_ray(float u, float v) {
		return ray(origin,lower_left_corner+u*horizontal+v*vertical-origin);
	}
	vec3 lower_left_corner;
	vec3 horizontal;
	vec3 vertical;
	vec3 origin;
};


#endif

main函數也要修改一下:

#include <stdlib.h>
#ifdef _WIN64
#define GLUT_NO_LIB_PRAGMA
#pragma comment (lib, "opengl32.lib")  // link with Microsoft OpenGL lib 
#pragma comment (lib, "glut64.lib")    // link with Win64 GLUT lib 
#endif //_WIN64
#ifdef _WIN32
//On Windows, include the local copy of glut.h and glext.h
#include "glut.h"
#include "glext.h"
#define GET_PROC_ADDRESS( str ) wglGetProcAddress( str )
#endif
#include "vec3.h"
#include "ray.h"
#define WIDTH 400
#define HEIGHT 200
unsigned char *Pixels;
#include "hitable.h"
#include "Sphere.h"
#include "hitablelist.h"
#define MAXFLOAT 20.0
#include "camera.h"
#include "time.h"

vec3 color(const ray&r,hitable *world) {
	hit_record rec;
	if (world->hit(r, 0.0, MAXFLOAT, rec))
		return 0.5*vec3(rec.normal.x()+1, rec.normal.y() + 1, rec.normal.z() + 1);
	else {
		vec3 unit_direction = unitVector(r.direction());
		float t = 0.5*(unit_direction.y() + 1.0);
		return (1.0 - t)*vec3(1.0, 1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);
	}
}
void Draw(void) {
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);
	glDrawPixels(WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, Pixels);
	glFlush();
}

int main() {
	
	srand(time(NULL));
	Pixels = (unsigned char*)malloc(WIDTH*HEIGHT*sizeof(unsigned char)*4);
	hitable *list[2];
	list[0] = new sphere(vec3(0.0, 0.0, -2.0), 1.3);
	list[1] = new sphere(vec3(0, 0, -0.5), 0.2);
	hitable *world = new hitable_list(list, 2);
	camera cam;
	int ns = 100;
	for (int j = HEIGHT-1;j >= 0;j--) {
		for (int i = 0;i < WIDTH;i++) {

			vec3 col(0, 0, 0);
			for (int s = 0;s < ns;s++) {
				float u = float(i + rand() / (RAND_MAX + 1.0)) / float(WIDTH);
				float v = float(j + rand() / (RAND_MAX + 1.0)) / float(HEIGHT);
				ray r = cam.get_ray(u, v);
				vec3 p = r.pointAtParameter(2.0);
				col += color(r, world);
			}
			col /= float(ns);
			int offset = (WIDTH*j + i) * 4;
			Pixels[offset + 0] = (unsigned char)255.99*col[0];
			Pixels[offset + 1] = (unsigned char)255.99*col[1];
			Pixels[offset + 2] = (unsigned char)255.99*col[2];
			Pixels[offset + 3] = 255;
		}
	}

	int argc = 0;char *argv = {""};
	glutInit(&argc, &argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
	glutInitWindowSize(WIDTH, HEIGHT);
	glutCreateWindow("bitmap");
	glutDisplayFunc(Draw);
	glutMainLoop();
}

放大生成的圖像時,最大的變化是部分背景和部分前景的邊緣像素:

整體效果如圖:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章