【RayTracingInOneWeekend】1

非常著名的光线追踪教程。
先看画出一个类似于天空的图像需要哪些内容。
具体的解析都在注释里:

//vec3.h
//vec3 类的头文件,它定义了一个三维向量类 vec3。这个类有三个成员变量 e0、e1 和 e2 表示向量的三个分量,
//即向量在三个坐标轴上的投影。同时还定义了一些向量运算符,如向量加减法、点乘、叉乘、标量乘法和除法等,
//以及一些向量的操作函数,如向量的长度、单位向量等。这些函数和运算符可以对 vec3 类对象进行操作,返回一个
//新的 vec3 类对象或修改原有的 vec3 类对象。同时,这个头文件还定义了一些别名,如 point3 表示三维空间中的一个点,color 表示颜色。
#ifndef VEC3_H
#define VEC3_H

#include<cmath>
#include<iostream>

using std::sqrt;

class vec3
{
public:
	vec3() : e{0,0,0}{}
	vec3(float e0, float e1, float e2) : e{ e0,e1,e2 } {}

	float x() const { return e[0]; }
	float y() const { return e[1]; }
	float z() const { return e[2]; }

	vec3 operator~() const { return vec3(-e[0], -e[1], -e[2]); }
	float operator[](int i) const { return e[i]; }//返回向量的某个分量的值
	float& operator[](int i) { return e[i]; }//返回向量的某个分量的引用,可以修改这个分量的值

	//将对当前向量对象进行修改,并返回修改后的对象
	//将另一个向量 v 的分量加到当前向量对象 e 中,实现了向量加法的功能
	vec3& operator +=(const vec3& v)
	{
		e[0] += v.e[0];
		e[1] += v.e[1];
		e[2] += v.e[2];
		return *this;
	}

	vec3& operator *=(const float t)
	{
		e[0] *= t;
		e[1] *= t;
		e[2] *= t;
		return *this;
	}

	vec3& operator/=(const float t) 
	{
		return *this *= 1 / t;
	}

	float length() const
	{
		return sqrt(length_squared());
	}

	float length_squared() const
	{
		return e[0] * e[0] + e[1] * e[1] + e[2] * e[2];
	}
	

public:
	float e[3];

};

//type aliases for vec3
using point3 = vec3;
using color = vec3;

//vec3 utility functions
inline std::ostream& operator << (std::ostream& out, const vec3& v)
{
	return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
}

inline vec3 operator + (const vec3& u, const vec3& v)
{
	return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]);
}

inline vec3 operator - (const vec3& u, const vec3& v)
{
	return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]);
}

inline vec3 operator * (const vec3& u, const vec3& v)
{
	return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]);
}

inline vec3 operator * (float t, const vec3& v)
{
	return vec3(t * v.e[0], t * v.e[1], t * v.e[2]);
}

inline vec3 operator * (const vec3& v, float t)
{
	return t * v;
}

inline vec3 operator / (vec3& u, float t)
{
	return (1 / t) * u;
}

inline float dot(const vec3& u, const vec3& v)
{
	return u.e[0] * v.e[0] + u.e[1] * v.e[1] + u.e[2] * v.e[2];
}

inline vec3 cross(const vec3& u, const vec3& v)
{
	return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],
		u.e[2] * v.e[0] - u.e[0] * v.e[2],
		u.e[0] * v.e[1] - u.e[1] * v.e[0]);
}

inline vec3 unit_vector(vec3 v)
{
	return v / v.length();
}

#endif // ! VEC3_H



//color.h
#pragma once
#ifndef COLOR_H
#define COLOR_H

#include"vec3.h"

#include<iostream>


void write_color(std::ostream& out, color pixel_color)
{
	//write the translated[0,255] value of each color component;
	//在渲染图像时,我们通常需要将颜色值从浮点数格式转换为整数格式,并将其存储为图像文件中每个像素的 RGB 值。
	//而 C++ 中的 int 类型也是有符号的 32 位整数,因此在写入图像时,我们需要将浮点数值缩放到 0 到 255 的整数
	//范围内,并将其强制转换为 int 类型。在此处,我们选择将颜色乘以 255.999,而不是 255,这是为了解决浮点数精
	//度损失的问题,因为 255.999 在 IEEE 754 浮点数规范下可以精确表示。
	out << static_cast<int>(255.999 * pixel_color.x()) << ' '
		<< static_cast<int>(255.999 * pixel_color.y()) << ' '
		<< static_cast<int>(255.999 * pixel_color.z()) << '\n';
}

#endif
//ray.h
#pragma once
#ifndef RAY_H
#define RAY_H
#include"vec3.h"

class ray
{
public:
	ray() {}
	ray(const point3& origin, const vec3& direction) : orig(origin),dir(direction){}

	point3 origin() const { return orig; }
	vec3 direction() const { return dir; }

	//光线可以看做是P(t) A + tb的方程,P是沿着这条线的3d位置,A是光线原点,b是光线方向
	point3 at(float t) const
	{
		return orig + t * dir;
	}

public:
	point3 orig;
	vec3 dir;
};

#endif // !RAY_H

//main.cpp
#include"vec3.h"
#include"ray.h"
#include"color.h"

#include<iostream>

color ray_color(const ray& r)
{
	vec3 unit_direction = unit_vector(r.direction());
	auto t = 0.5f * (unit_direction.y() + 1.0f);
	//(1−t)×c1 + t×c2 线性插值

	return (1.f - t) * color(1.f, 1.f, 1.f) + t * color(0.5f, 0.7f, 1.f);
}

int main()
{
	//定义了一个关于图像和相机的基本参数:

	//图像部分:定义了图像的宽高比,以及宽度和高度。宽高比是在之前的代码中定义的,宽度为400像素,高度根据宽高比计算得出。
	//相机部分:定义了视口的宽高以及焦距。视口是指相机能够看到的范围,这里的视口宽度是根据宽高比和视口高度计算得出的;
	// 焦距表示了相机的聚焦程度,这里设为1。还定义了相机的位置,以及一个水平方向向量、垂直方向向量和左下角的位置,这些都是
	// 计算相机拍摄视角时需要用到的基本参数。
	//image 
	const auto aspect_ratio = 16.f / 9.f;
	const int image_width = 400;
	const int image_height = static_cast<int>(image_width / aspect_ratio);

	//camera
	auto viewport_height = 2.f;
	auto viewport_width = aspect_ratio * viewport_height;
	auto focal_length = 1.f;

	auto origin = point3(0, 0, 0);
	auto horizontal = vec3(viewport_width, 0, 0);
	auto vertical = vec3(0, viewport_height, 0);
	auto lower_left_corner = origin - horizontal / 2 - vertical / 2 - vec3(0, 0, focal_length);

	//render
	std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";

	//由于PPM文件格式规定像素行是从文件的底部开始写入的,而每一行的像素是从左向右依次排列的,
	//因此在遍历时需要先从最后一行开始循环遍历,再往上逐行遍历,以保证输出的像素数据符合PPM文件格式的要求
	for (int i = image_height - 1; i >= 0; --i)
	{
		//通过一个外层循环,从图像的最后一行开始,往前逐行扫描。
		//在内层循环中,对于每行中的每个像素,计算出其在图像中的位置(即相对于左下角的水平和垂直方向上的偏移量),从而确定了该像素对应的射线方向。
		//使用 ray_color() 函数计算出该射线的颜色,即该像素的颜色。
		//将该像素的颜色输出到标准输出流中,以便最终能够将其保存为图像文件。
		std::cerr << "\r Scanlines remaining: " << i << ' ' << std::flush;
		for (int j = 0; j < image_width; ++j)
		{
			auto u = float(j) / (image_width - 1);
			auto v = float(i) / (image_height - 1);
			ray r(origin, lower_left_corner + u * horizontal + v * vertical - origin);
			color pixel_color = ray_color(r);
			write_color(std::cout, pixel_color);
		}
	}
	std::cerr << "\nDone.\n";
}

最好在Release下运行,生成了exe后,在exe所在的文件夹唤出cmd,并输入

[文件名] > image.ppm

通过查看图像的软件打开ppm文件,会得到如下图:
在这里插入图片描述

这个渐变效果得益于ray_color函数中的插值计算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值