问题四十一:怎么用ray tracing画任意圆柱面(generalized cylinder)
我们之前在“35.2”章节中画过椭圆柱面:
我们还在“36.4”章节中画过圆柱面的Inverse Mapping图:
但是,这些柱面都是:底面与ZOX平面平行,中心线与Y轴(方向向量(0,1,0))平行。
这一章节,我们将画空间中任意位置、中心线方向任意的圆柱面。
41.1 数学推导
思路:根据圆柱面的中心线建立新的坐标系vuw,在vuw坐标系中,圆柱底面与wov平面平行,中心线与u轴平行;然后,将圆柱面中心点坐标、光线起点和方向向量的坐标转换到vuw坐标系。
这样就可以用之前画“特殊”圆柱面的方法来画了。
关键是建立新的坐标系vuw。
设圆柱面中心线的方向向量u的坐标为(Xu, Yu, Zu)。
ZOX平面内会有两个向量与u垂直:(-Zu, 0,Xu)和(Zu, 0, -Xu)
我们取v=(-Zu, 0, Xu)
w= cross(v, u)
这样很容易建立了vuw坐标系。
另外,我们希望引入theta角表示:圆柱面围绕中心线旋转的角度
(如果圆柱面是纯色的,则旋转是看不出来的;如果圆柱面有多种颜色,则可以看到旋转的效果)
在已经建立的vuw坐标系中:
旋转theta角后,v轴的方向向量转到v1位置,设v1为vuw坐标系中的单位向量,则v1在vuw坐标系中的坐标为(cos(theta), 0, sin(theta))
将v1的坐标转换到xyz坐标系vector_trans_back(v1, v, u, w)
然后在xyz坐标系中w1 = cross(v1, u)
所以,v1, u, w1,即为旋转theta角后的最终的坐标系的基。
另外,补充一点:u.x()=0(即在YOZ平面)时,由于v固定为(1,0,0)
41.2 看C++代码实现
----------------------------------------------vec3.cpp ------------------------------------------
vec3.cpp
bool get_vector_vw(vec3& u, float angle, vec3& v, vec3& w) {
/*determine the v axis and w axis of u-v-w space*/vec3 v1, w1;u = unit_vector(u);float theta = angle*M_PI/180;
if (u.x() == 0.0) {
//u在ZOY平面时,v1为x轴上的单位向量(这里的v1对应“数学推导”中的v)v1 = vec3(1.0, 0.0, 0.0);theta = (angle)*M_PI/180;}else {v1 = unit_vector(vec3(-u.z(), 0.0, u.x()));theta = (angle)*M_PI/180;}w1 = cross(v1, u);//这里的w1对应“数学推导”中的wfloat x = cos(theta);float z = sin(theta);
if (fabs(x) < 0.0001) {
/*这里是考虑到,计算机中计算结果中的0有时候表示为一个10-8数量级的数,而不是0,这样当theta为90*N度时,cos(theta)并不是真正的0,导致后面计算出错。*/x = 0.0;}if (fabs(z) < 0.0001) {z = 0.0;}vec3 v_uv1w1 = vec3(x, 0, z);v = unit_vector(vector_trans_back(v_uv1w1, v1, u, w1));w = cross(v, u);return true;
}
----------------------------------------------vec3.h ------------------------------------------
vec3.h
bool get_vector_vw(vec3& u, float angle, vec3& v, vec3& w);
----------------------------------------------quadratic_cylinder_all.h ------------------------------------------
quadratic_cylinder_all.h
#ifndef QUADRATIC_CYLINDER_ALL_H
#define QUADRATIC_CYLINDER_ALL_H#include <hitable.h>
#include "material.h"
#include "log.h"class quadratic_cylinder_all : public hitable
{public:quadratic_cylinder_all() {}quadratic_cylinder_all(vec3 cen, float a, float b, float c, float hy, material *m, vec3 u, float an) {center = cen;intercept_x = a;intercept_y = b;intercept_z = c;height_half_y = hy;ma = m;vector_u = u;get_vector_vw(vector_u, an, vector_v, vector_w);}
/*
(x-xc)^2/a^2 + 0*(y-yc)^2/b^2 + (z-zc)^2/c^2 = 1
*/virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;vec3 center;float intercept_x;float intercept_y;float intercept_z;float height_half_y;material *ma;vec3 vector_u;vec3 vector_v;vec3 vector_w;
};#endif // QUADRATIC_CYLINDER_ALL_H
----------------------------------------------quadratic_cylinder_all.cpp ------------------------------------------
quadratic_cylinder_all.cpp
#include "quadratic_cylinder_all.h"#include <iostream>
using namespace std;bool quadratic_cylinder_all::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
#if QUADRATIC_CYLINDER_ALL_LOG == 1std::cout << "-------------quadratic_cylinder_all::hit----------------" << endl;
#endif // QUADRATIC_CYLINDER_ALL_LOGvec3 direction = vector_trans(r.direction(), vector_v, vector_u, vector_w);vec3 origin = vector_trans(r.origin(), vector_v, vector_u, vector_w);vec3 center_vuw = vector_trans(center, vector_v, vector_u, vector_w);
/*这里就是将光线的起点和方向向量、圆柱面的中心从xyz坐标系转换到vuw坐标系*/float ab_square = intercept_x*intercept_x*intercept_y*intercept_y;float bc_square = intercept_y*intercept_y*intercept_z*intercept_z;float ac_square = 0.0*intercept_x*intercept_x*intercept_z*intercept_z;float abc_square = 1.0*intercept_x*intercept_x*intercept_y*intercept_y*intercept_z*intercept_z;vec3 inter_square = vec3(bc_square, ac_square, ab_square);vec3 rd_square = vec3(direction.x()*direction.x(),direction.y()*direction.y(),direction.z()*direction.z());float A = dot(inter_square, rd_square);vec3 r0_c = origin - center_vuw;vec3 r0_c_rd = vec3(r0_c.x()*direction.x(),r0_c.y()*direction.y(),r0_c.z()*direction.z());float B = 2*dot(r0_c_rd, inter_square);vec3 r0_c_square = vec3(r0_c.x()*r0_c.x(),r0_c.y()*r0_c.y(),r0_c.z()*r0_c.z());float C = dot(r0_c_square, inter_square) - abc_square;float temp, temp1, temp2;vec3 pc;if(A == 0) {if (B == 0) {return false;}else {temp = -C/B;if (temp < t_max && temp > t_min) {rec.t = temp;rec.p = vector_trans(r.point_at_parameter(rec.t), vector_v, vector_u, vector_w); //将撞击点坐标转换到vuw坐标系if (((rec.p.y()-center_vuw.y()) > -height_half_y) && ((rec.p.y()-center_vuw.y()) < height_half_y)) {pc = rec.p - center_vuw;rec.normal = unit_vector(vec3(2*bc_square*pc.x(), 2*ac_square*pc.y(), 2*ab_square*pc.z()));if (dot(rec.normal, direction) > 0) {rec.normal = -rec.normal;}rec.normal = vector_trans_back(rec.normal, vector_v, vector_u, vector_w);//最终的撞击点的法向量需要从vuw坐标系转回到xyz坐标系。rec.mat_ptr = ma;if ((intercept_x == intercept_y) && (intercept_y == intercept_z)) {//0/1:cylinderrec.v = (rec.p.y() - (center_vuw.y() - height_half_y)) / (2*height_half_y);vec3 pc1 = vec3(rec.p.x()-center_vuw.x(), 0, rec.p.z()-center_vuw.z());vec3 vx = vec3(0, 0, 1);float u = acos(dot(pc1, vx) / (pc1.length()*vx.length())) / (2*M_PI);
// float u = acos((rec.p.x() - center_vuw.x()) / intercept_x) / (2*M_PI);if ((rec.p.x() - center_vuw.x()) < 0) {rec.u = 1-u;}else {rec.u = u;}rec.p = vector_trans_back(rec.p, vector_v, vector_u, vector_w);return true;}else {rec.u = -1.0;rec.v = -1.0;rec.p = vector_trans_back(rec.p, vector_v, vector_u, vector_w);return true;}}else {return false;}}}}else {float discriminant = B*B - 4*A*C;if (discriminant >= 0) {temp1 = (-B - sqrt(discriminant)) / (2.0*A);temp2 = (-B + sqrt(discriminant)) / (2.0*A);if (temp1 > temp2) {//make sure that temp1 is smaller than temp2temp = temp1;temp1 = temp2;temp2 = temp;}if (temp1 < t_max && temp1 > t_min) {rec.t = temp1;rec.p = vector_trans(r.point_at_parameter(rec.t), vector_v, vector_u, vector_w); //将撞击点坐标转换到vuw坐标系if (((rec.p.y()-center_vuw.y()) > -height_half_y) && ((rec.p.y()-center_vuw.y()) < height_half_y)) {pc = rec.p - center_vuw;rec.normal = unit_vector(vec3(2*bc_square*pc.x(), 2*ac_square*pc.y(), 2*ab_square*pc.z()));if (dot(rec.normal, direction) > 0) {rec.normal = -rec.normal;}rec.normal = vector_trans_back(rec.normal, vector_v, vector_u, vector_w); //最终的撞击点的法向量需要从vuw坐标系转回到xyz坐标系。rec.mat_ptr = ma;if ((intercept_x == intercept_y) && (intercept_y == intercept_z)) {//cylinderrec.v = (rec.p.y() - (center_vuw.y() - height_half_y)) / (2*height_half_y);vec3 pc1 = vec3(rec.p.x()-center_vuw.x(), 0, rec.p.z()-center_vuw.z());vec3 vx = vec3(0, 0, 1);float u = acos(dot(pc1, vx) / (pc1.length()*vx.length())) / (2*M_PI);
// float u = acos((rec.p.x() - center_vuw.x()) / intercept_x) / (2*M_PI);if ((rec.p.x() - center_vuw.x()) < 0) {rec.u = 1-u;}else {rec.u = u;}rec.p = vector_trans_back(rec.p, vector_v, vector_u, vector_w);return true;}else {rec.u = -1.0;rec.v = -1.0;rec.p = vector_trans_back(rec.p, vector_v, vector_u, vector_w);return true;}}else {
// return false;}}if (temp2 < t_max && temp2 > t_min) {rec.t = temp2;rec.p = vector_trans(r.point_at_parameter(rec.t), vector_v, vector_u, vector_w); //将撞击点坐标转换到vuw坐标系if (((rec.p.y()-center_vuw.y()) > -height_half_y) && ((rec.p.y()-center_vuw.y()) < height_half_y)) {pc = rec.p - center_vuw;rec.normal = unit_vector(vec3(2*bc_square*pc.x(), 2*ac_square*pc.y(), 2*ab_square*pc.z()));if (dot(rec.normal, direction) > 0) {rec.normal = -rec.normal;}rec.normal = vector_trans_back(rec.normal, vector_v, vector_u, vector_w); //最终的撞击点的法向量需要从vuw坐标系转回到xyz坐标系。rec.mat_ptr = ma;if ((intercept_x == intercept_y) && (intercept_y == intercept_z)) {//cylinderrec.v = (rec.p.y() - (center_vuw.y() - height_half_y)) / (2*height_half_y);vec3 pc1 = vec3(rec.p.x()-center_vuw.x(), 0, rec.p.z()-center_vuw.z());vec3 vx = vec3(0, 0, 1);float u = acos(dot(pc1, vx) / (pc1.length()*vx.length())) / (2*M_PI);
// float u = acos((rec.p.x() - center_vuw.x()) / intercept_x) / (2*M_PI);if ((rec.p.x() - center_vuw.x()) < 0) {rec.u = 1-u;}else {rec.u = u;}rec.p = vector_trans_back(rec.p, vector_v, vector_u, vector_w);return true;}else {rec.u = -1.0;rec.v = -1.0;rec.p = vector_trans_back(rec.p, vector_v, vector_u, vector_w);return true;}}else {
// return false;}}}return false;}
}
----------------------------------------------main.cpp ------------------------------------------
main.cpp
hitable *list[2];
list[0] = new quadratic(vec3(-3.75, 3.25, 0), 1.25, 1.25, 1.25, 0, 1,1.25, new lambertian(vec3(0.8, 0.8, 0.0)));
list[1] = newquadratic_cylinder_all(vec3(3.75, 3.25, 0), 1.25, 1.25, 1.25, 1.25,
new lambertian(vec3(0.8,0.8, 0.0)), vec3(1, 1, 0), 0);
hitable *world = new hitable_list(list,2);
vec3 lookfrom(0, 5, 20);
vec3 lookat(0, 3, 0);
float dist_to_focus = (lookfrom - lookat).length();
float aperture = 0.0;
camera cam(lookfrom, lookat, vec3(0,1,0),20, float(nx)/float(ny), aperture, 0.7*dist_to_focus);
输出图片如下:
咦~咦~咦
我们设置的旋转角度为0,为什么会发生旋转呢???
41.3 补偿旋转角度
u向量在ZOX平面的投影向量为u_zox的坐标应该为(Xu, 0,Zu),u_zox向量和-Z轴的单位方向向量z_n(0,0,-1)的夹角为α,
v向量和与XOY平面的夹角为β,这β=α
β即为vuw坐标系相对于xyz坐标系旋转的角度。
如果要使圆柱面的实际效果是旋转角度为0,则需要补偿一个β角度。
具体做法:
u.x()=0(即在YOZ平面)时,由于v固定为(1,0,0),所以,不管β是0度还是180度,都不需要补偿。
u.x()>0,需要减去一个β
u.x()<0,需要加上一个β
代码修改如下:
----------------------------------------------vec3.cpp ------------------------------------------
vec3.cpp
bool get_vector_vw(vec3& u, float angle, vec3& v, vec3& w) {
/*determine the v axis and w axis of u-v-w space*/vec3 v1, w1;u = unit_vector(u);float theta = angle*M_PI/180;if (u.x() == 0.0) {v1 = vec3(1.0, 0.0, 0.0);theta = (angle)*M_PI/180;}else {v1 = unit_vector(vec3(-u.z(), 0.0, u.x()));vec3 z_n = vec3(0, 0, -1);vec3 u_xoz = vec3(u.x(), 0, u.z());float phi = acos(dot(z_n, u_xoz) / (z_n.length()*u_xoz.length()));if (u.x() > 0) {theta = (angle)*M_PI/180 - phi;}else {theta = (angle)*M_PI/180 + phi;}}w1 = cross(v1, u);float x = cos(theta);float z = sin(theta);if (fabs(x) < 0.0001) {x = 0.0;}if (fabs(z) < 0.0001) {z = 0.0;}vec3 v_uv1w1 = vec3(x, 0, z);v = unit_vector(vector_trans_back(v_uv1w1, v1, u, w1));w = cross(v, u);return true;
}bool roots_quadratic_equation2(float a, float b, float c, float (&roots)[3]) {//the first element is the number of the real roots, and other elements are the real roots.if (a == 0.0) {if (b == 0.0) {roots[0] = 0.0;}else {roots[1] = -c/b;roots[0] = 1.0;}}else {float d = b*b - 4*a*c;if (d < 0.0) {roots[0] = 0.0;}else {roots[1] = (-b + sqrt(d)) / (2*a);roots[2] = (-b - sqrt(d)) / (2*a);roots[0] = 2.0;}}return true;
}
旋转角度补偿前后图片对比:
补偿前:(之前已经贴出图片,这里再贴一次)
补偿后:
接下来,我们测几组图片:
vec3(0, 0, 1), 0
vec3(0, 0, -1), 0
vec3(1, 0, 0), 0
vec3(-1, 0, 0), 0
vec3(1, 1, 1), 0
vec3(1, 1, -1), 0
vec3(1, -1, 1), 0
vec3(1,- 1, -1), 0
vec3(-1, 1, 1), 0
vec3(-1, 1, -1), 0
vec3(-1, -1, 1), 0
vec3(-1, -1, -1), 0
vec3(-1, 1, 0), 0
vec3(-1, 0,-1), 0
vec3(-1, -1, 0), 0
vec3(0, 1, 1), 0
vec3(1, 1, 1), 0
vec3(1, 1, 1), 45
vec3(1, 1, 1), 90
vec3(1, 1, 1), 135
问题四十一:怎么用ray tracing画任意圆柱面(generalized cylinder)相关推荐
- 问题三十四:怎么用ray tracing画任意长方体(generalized box)
34.1 思路分析 这个内容书上没有,但是觉得实际应用中的长方体的位置应该是任意的(表面法向量不一定平行坐标轴). 怎么画? 1,光线撞击到长方体 2,撞击点到光线起点的距离 3,撞击点的法向量 怎么 ...
- 问题五十四:怎么用ray tracing画参数方程表示的曲面(2)—— bezier surface
首先,需要说明的是: 这一章节可以看作"问题五十三"的另一个例子--bicubic bezier surface: 之前已经用"球面"和"牛角面&qu ...
- 问题四十四:怎么用ray tracing画空间任意位置的圆环的任意片段
44.1 数学推导 同"41.1" 44.2 看C++代码实现 ----------------------------------------------tori_part_al ...
- 问题四十二:怎么用ray tracing画任意圆环片段
42.1 数学推导 引入两个参数theta1.theta2 撞击点P和中心点O连线的向量OP与+X轴的夹角theta在[theta1, theta2]范围内时,才有可能撞击到圆环段. 当然,考虑到th ...
- 问题五十三:怎么用ray tracing画参数方程表示的曲面(1)
首先,以球面为例学习怎么用ray tracing画参数方程表示的曲面:然后,再画一个牛角面. 特别说明:这一章节所画的曲面只是示意性的,所以先不care图片上的瑕疵. 53.1 数学推导 球面的参数方 ...
- 问题三十三:怎么用ray tracing画特殊长方体(box)
33.1 怎么用ray tracing画特殊长方体 在光线追踪中被用到的一种常见形态是长方体盒子.这种基本物体被用于可见物体和包围盒,包围盒被用于加速复杂物体的相交测试. 吐槽:单词都认识,就是不知道 ...
- Q77:怎么用Ray Tracing画仿射变换之后的图形
77.1 理论说明 如上图所示,椭球面是球面通过仿射变换T得到的. 现在我们的问题是:怎么ray trace变换后的椭球面? (先重申一点:经过仿射变换之后,直线还是直线,光线还是光线) 我们应该这么 ...
- 问题六十六:怎么用ray tracing画CSG(Constructive Solid Geometry 构造实体几何)图形
66.1 概述 什么是CSG图形? 若干简单图形通过集合运算后得到的复杂图形,被称为"CSG图形". 其中"简单图形",包括:sphere, box, cyli ...
- 问题四十八:怎么用ray tracing画superhyperboloid(超级双曲面)
48.1 数学推导 Superhyperboloid的方程和"问题四十六"中描述的superellipsoid的如下方程非常接近. 到获得迭代格式,超级双曲面和超级椭圆面的推导是相 ...
最新文章
- golang通过RSA算法生成token,go从配置文件中注入密钥文件,go从文件中读取密钥文件,go RSA算法下token生成与解析;go java token共用
- python中的chr和ord函数_python chr()和ord() | 学步园
- jl计算机二级c语言考什么,计算机等级考试二级C语言考前密卷(9)2
- 【嵌入式Linux】STM32MP157开发板上Linux启动流程
- linux之lsof使用技巧
- mysql数据库创建表时通过设置什么属性可以设置字段编号自动增加_Mysql数据库创建表样例和解释...
- 由sock引起的感想
- Mac新手使用技巧——设置Finder(访达)快捷键
- 【手写数字识别】基于matlab GUI知识库手写数字识别(写字板+图片)【含Matlab源码 1227期】
- 后台job批量停用和开启
- 线性代数之——矩阵乘法和逆矩阵
- 修改IP的cmd命令
- 产品的概念:提出与筛选--第三章人人都是产品经理
- 什么是cookie?cookie的优缺点。
- 人力外派和猎头的区别是什么?哪个行业更赚钱?
- Bootstrap入门使用
- 学画画软件app推荐_最好用的绘画软件APP,学画画必备
- 2017语义分割综述
- Windows驱动开发工具 WDK 学习笔记
- CRM系统和OA系统是否可以共用一个系统,如何操作?
热门文章
- File Manipulation
- 第二次作业 项目质量管理重点知识梳理
- Java Swing 开发之JTable中在添加组件(JCheckBox)
- [转]AAuto编程语言官方站 网站服务条款
- android学习笔记之Fragment(一)
- 【链表相加】程序员面试金典——2.5链式A+B
- 程序员面试金典——2.4链表分割
- 剑指offer——面试题38:数字在排序数组中出现的次数
- 大白话5分钟带你走进人工智能-第二十四节决策树系列之分裂流程和Gini系数评估(3)...
- Day14 字符编码