【简介与代码下载】

本文将通过一个示例来介绍光线生成模块,详细介绍光线生成,以及绘制一个三角形,对三角形在shader中实施着色从而介绍添加三角网在OptiX中的操作,并介绍当光线与三角形无交时,会调用相交丢失模块(Miss Program)的设置流程。光线的生成和场景的类似Trackball的操作器里包含着一个重要而又基础的知识点:与相机相关的空间变换。

本文示例代码:

链接:https://pan.baidu.com/s/19YUxZ3SFIDZposKIzpMHbQ 
提取码:hhs3 
下载解压后,请使用VS2015打开工程,并把当前配置调整为Debug和x64。另需要在默认路径安装OptiX6.0与CUDA10.0,可以参考我的这篇博文:【Optix】Optix介绍与示例编译

下面是运行结果,可以使用鼠标左键进行拖拽和右键进行拉近拉远。

【光线生成模块讲解】

本示例我们书写了一个三角形,其流程也较为简单,在光线生成模块程序camera.cu中,调用trTrace进行光线跟踪,在rtTrace中有对哪些物体进行跟踪,这个物体是场景树的顶层物体,示例中叫做top_object,rtTrace一旦调用,因此这个场景是我们传入的三角网,不是实时计算的物体,因此OptiX会自动帮我们计算交点,rtTrace调用时,会调用其关联的属性和材质point2color.cu,在rtTrace中相交,则实时调用point2color.cu中的triangle_attributes计算交点处的属性,而后若是离相机最近的交点则调用材质中的closest_hit_radiance来计算颜色放置在光线结构体prd_radiance的result成员中。在rtTrace结束后,其出参prd中的result就是材质中closest_hit_radiance设置的颜色。那么我们来首先来看第一步:生成光线。

生成光线按说是根据相机的位置,与要渲染的窗口上的每个像素相连,生成一条条光线与场景求交。在camera.cu中有一段很巧的代码:

    //缓存尺寸,也就是屏幕尺寸size_t2 screen = output_buffer.size();//rtPrintf("%d-%d-", screen.x, screen.y);float2 d = make_float2(launch_index) / make_float2(screen) * 2.f - 1.f;float3 ray_origin = eye;float3 ray_direction = normalize(d.x*U + d.y*V + W);//生成光线optix::Ray ray(ray_origin, ray_direction, RADIANCE_RAY_TYPE, scene_epsilon);

其中launch_index是当前像素的行列号。之前说过camera.cu做为光线发生程序,是要渲染的每个像素调用一次,每次的launch_index均不相同代表不同的像素,以生成不同的光线。我们来看d的值是什么,首先来看laun_index/screen,这个除了之后整个屏幕(本文中所说的屏幕都是渲染窗口)的范围是[0, 1],而又乘以2,范围是[0, 2],而又减去1则范围是[-1, 1],也即屏幕的[0, 0]到[width, height]被化成了d,也就是[-1, -1]到[1, 1]之间。每个像素均匀排列。

两个参数ray_origin代表光线的起点,就是眼睛的位置,由外部传入。而ray_direction的求解需要好好的解释解释。我们首先来看图:

其中e就是eye, l就是lookat,eye是光线的起点,方框代表要渲染的屏幕,分辨率是width/height,橙色的代表光线的方向,每个像素的中心点穿过一个光线。其中yv, zv, xv是相机的局部坐标轴,可注意是朝着z轴负方向的。而xw, yw, zw是世界坐标轴。现在要求每个光线的方向我们第一步是要求出相机的局部坐标系(xv, yv, zv)。再根据相机的fov和长宽比aspect_ratio计算出UVW,而后根据shader中d的值逐个遍历。和图中的d没有关系。

首先来看正交基xv, yv, zv的计算,其实很简单,因为eye已知,lookat已知,就如图图中的e和l,则两者相交就得到了一个向量,而后默认的up向量是(0, 1, 0)两者cross又得到了另一个向量,有了两个向量直接cross就得到了第三个向量,代码如下:

void calculateCameraVariables(optix::float3 eye, optix::float3 lookat, optix::float3 up,float fov, float aspect_ratio,optix::float3& U, optix::float3& V, optix::float3& W)
{float ulen, vlen, wlen;W = lookat - eye;wlen = length(W);U = normalize(cross(W, up));V = normalize(cross(U, W));vlen = wlen*tanf(0.5f * fov * M_PIf / 180.f);V *= vlen;ulen = vlen * aspect_ratio;U *= ulen;
}

要注意看里面还有个tanf操作,求出的UVW是正交基,还要得到其视域的范围与之相乘才是放入到shader中要计算的UVW。参考fov与aspect_ratio的定义很容易就能够理解vlen和ulen的求法,如图:

其中红色的是wlen,而vlen是绿色的,蓝色的是ulen,而红色的与蓝色的夹角是fov的一半是已知量。aspect_ratio是整 个长宽的比率,也是已知量。由此可以求出UVW,传入到camera.cu中,从[-1, -1]到[1, 1]逐像素的遍历,就生成了光线。

【相机操作】

可见要实现对相机的操作就是求出UVW,要求出UVW就要计算eye, lookat, up这三个变量。假如平移操作,很好办,只要修改eye的位置就可以了,同步修改lookat的位置也行,其至lookat不改,eye不管走到哪里始终向它看也可以。复杂就复杂在鼠标左键的旋转上了。首先我们鼠标在屏幕上划动,计算出起始位置与结束位置,根据这两个位置要生成相机的旋转矩阵,而后与相机的矩阵相乘,最终实现变换。

而从起始位置到结整位置的计算我们使用到了两个技巧,一个是把屏幕坐标映射到球的大圆上,算是绕轴操作:

    else if(mouse_button == GLUT_LEFT_BUTTON){const optix::float2 from = { static_cast<float>(mouse_prev_pos.x), static_cast<float>(mouse_prev_pos.y) };const optix::float2 to = { static_cast<float>(x), static_cast<float>(y) };const optix::float2 a = { from.x / width, from.y / height };const optix::float2 b = { to.x / width, to.y / height };camera_rotate = arcball.rotate(b, a);camera_dirty = true;}

可以看到a,和b是归一化范围在[0, 1]的from和to,百后arcball实施了一个rotate操作得到了相机的旋转矩阵。那么rotate究竟做了什么。

optix::Matrix4x4 Arcball::rotate(const optix::float2& from, const optix::float2& to) const
{optix::float3 a = toSphere(from);optix::float3 b = toSphere(to);Quaternion q = Quaternion(a, b);q.normalize();return q.rotationMatrix();
}

首先模拟球的大圆,得到了两个点a, b,相当于a, b点共球,然后从a点移到b点求旋转矩阵。这也就是这种操作器叫做trackball的原因。是把屏幕上的二维点模拟了一个三维球上的大圆的点,通过另外一维永远为0来实现的。

那么最后一个问题,四元数Quaternion 是如何实现根据起始点和结束点来计算旋转矩阵的。

【四元数】

参考这篇文章:彻底搞懂四元数

在计算完成旋转矩阵后,在updateCamera中更新UVW:

void updateCamera()
{const float vFov = 30.0f;const float aspect_radio = static_cast<float>(width) / static_cast<float>(height);optix::float3 camera_u, camera_v, camera_w;calculateCameraVariables(camera_eye, camera_lookat, camera_up, vFov, aspect_radio,camera_u, camera_v, camera_w);//相机坐标系,UVW是正交基const optix::Matrix4x4 frame = optix::Matrix4x4::fromBasis(normalize(camera_u),normalize(camera_v),normalize(-camera_w),optix::make_float3(0));//相机坐标系的逆变换const optix::Matrix4x4 frame_inv = frame.inverse();//frame_inv代表将点变换到相机空间,再乘以camera_rotate代表相机的旋转,再乘以frame代表再变到世界空间const optix::Matrix4x4 trans = frame*camera_rotate*frame_inv;//将camera_rotate施加的变换后重新计算eye, lookat, up,再重新计算相机的正交基camera_eye = optix::make_float3(trans * make_float4(camera_eye, 1.0f));camera_lookat = optix::make_float3(trans * make_float4(camera_lookat, 1.0f));camera_up = optix::make_float3(trans * make_float4(camera_up, 0.0f));calculateCameraVariables(camera_eye, camera_lookat, camera_up, vFov, aspect_radio,camera_u, camera_v, camera_w);camera_rotate = optix::Matrix4x4::identity();context["eye"]->setFloat(camera_eye);context["U"]->setFloat(camera_u);context["V"]->setFloat(camera_v);context["W"]->setFloat(camera_w);camera_dirty = false;}

中间最关键的部分就是施加camera_rotate变换。关于此还可以参考这篇文章:详解MVP矩阵之ViewMatrix

【场景构建-输入三角形】

这属于API级的知识,在定义完顶点和顶点索引之后,直接通过API设置即可:

    optix::GeometryTriangles geom_tri = context->createGeometryTriangles();geom_tri->setPrimitiveCount(num_faces);geom_tri->setTriangleIndices(index_buffer, RT_FORMAT_UNSIGNED_INT3);geom_tri->setVertices(num_vertices, vertex_buffer, RT_FORMAT_FLOAT3);geom_tri->setBuildFlags(RTgeometrybuildflags(0));

其中setPrimitiveCount设置的是三角形的数量。注意OptiX是针对元数据也就是每个primitive来重新构建加速结构的。

【计算属性与材质】

当场景中输入三角形后,OptiX会自动的计算交点,当有交点时,会需要实时的计算交点的属性,比如法线、纹理坐标等,然后再调用材质中的相关内容根据这些属性计算出最终要显示的颜色。

上面解释的是属性的调用时机与纹理的调用时机。通过如下代码设置属性shader与材质shader以及它们的启动函数。

    //创建三角形的属性生成模块const char* ptx = getPtxString("point2color.cu");geom_tri->setAttributeProgram(context->createProgramFromPTXString(ptx, "triangle_attributes"));geom_tri["index_buffer"]->setBuffer(index_buffer);geom_tri["vertex_buffer"]->setBuffer(vertex_buffer);//创建材质optix::Program bary_ch = context->createProgramFromPTXString(ptx, "closest_hit_radiance");optix::Material bary_matl = context->createMaterial();bary_matl->setClosestHitProgram(0, bary_ch);

【光线相交丢失模块】

当光线与场景不相交时,什么材质属性都不好使了,那么屏幕要显示什么颜色呢,OptiX通过miss program来实现:

#include <optix_world.h>using namespace optix;rtDeclareVariable(float3, bg_color, , );struct PerRayData_radiance
{float3 result;
};rtDeclareVariable(PerRayData_radiance, prd_radiance, rtPayload, );RT_PROGRAM void miss()
{prd_radiance.result = bg_color;
}

在CPU端通过如下代码设置shader和回调函数:

    context->setMissProgram(0, context->createProgramFromPTXString(getPtxString("miss.cu"), "miss"));context["bg_color"]->setFloat(1.0f, 0.0f, 1.0f);

【OptiX】第1个示例 光线生成模块(RayGenerationProgram), 相机操作、添加三角网以及相交丢失模块(Miss Program)相关推荐

  1. coco数据集格式示例_OpenAI的光辉可以从一些示例中生成训练数据集

    coco数据集格式示例 I recently started a new newsletter focus on AI education. TheSequence is a no-BS( meani ...

  2. c++ 获取64位进程模块地址_针对银行木马BokBot核心模块的深入分析

    一.概述 BokBot恶意软件由LUNAR SPIDER恶意组织开发和运营,在2017年首次出现,CrowdStrike的Falcon Overwatch和Falcon Intelligenc团队对被 ...

  3. python自己创建模块_创建并发布自己的python模块

    python通过模块来共享组织代码,python不仅内置了丰富的标准库,而且python社区还贡献了大量的第三方模块,正是由于第三方模块大大扩展了python的应用领域,成就了如今python的江湖地 ...

  4. 模块版网站与html网站,网站新闻模块代码html

    网站新闻模块代码html [2020-09-05 21:12:43]  简介: php去除nbsp的方法:首先创建一个PHP代码示例文件:然后通过"preg_replace("/( ...

  5. java 模块设计模式_Java9模块化学习笔记二之模块设计模式

    模块设计的原则: 1.防止出现编译时循环依赖(主要是编译器不支持),但运行时是允许循环依赖的,比如GUI应用 2.明确模块的边界 几种模块设计: API模块,聚合模块(比如java.base) 可选依 ...

  6. ide 两个模块的jdk版本不一样_Java平台模块系统(3)- JDK工具

    在完成项目模块的源代码之后,我们需要编译和运行这些模块.大部分时候,我们都是在IDE上进行开发和测试,可以把编译和运行的工作交给IDE来完成.不过我们仍然可以用javac和java来分别编译和运行代码 ...

  7. node.js 模块_如何创建Node JS可重用模块

    node.js 模块 In my previous post, we have discussed about "How to export and import a Node JS Mod ...

  8. python中常用模块_工作中用过的Python常用模块:(基于3.x)

    内置模块: sys 用于提供对解释器相关的访问及维护.例如:sys.argv --传参 sys.platform --返回系统平台名称 sys.version --查看python版本 os 用于提供 ...

  9. python函数和模块有什么特性_python-函数包和模块

    python函数的作用: 在Python代码段中如果有一段几十行的代码,需要多次重复使用这几十行代码时,为了提高代码的可用性,将代码段放进函数体内,以后在使用中直接调用该函数模块即可,函数是一个独立的 ...

最新文章

  1. android 设置允许http请求_网络请求框架----OkHttp原理
  2. 使用Vue动态生成form表单的实例代码
  3. 函数中使用栈与使用堆时函数执行效率浅对比
  4. XAMPP配置httpd-vhosts.conf后无法启动
  5. hdu-2209 翻纸牌游戏
  6. 求中位数_图解面试题:如何分析中位数?
  7. 基于HMM的中文分词
  8. Java设计模式之策略设计模式
  9. 国家开放大学2021春1295社会心理学(本)题目
  10. Cognitive Security的异常检测技术
  11. 错误: 找不到或无法加载主类
  12. 华为怎么删掉android,华为手机怎么卸载软件 华为手机卸载应用软件教程
  13. 我为NET狂~群福利:逆天书库
  14. 思源黑体(魅族)、方正兰亭(小米)、冬青黑体(锤子)比较
  15. 小白能读懂的 《手把手教你学DSP(TMS320X281X)》第四章(2) gel文件
  16. Jmeter编码格式
  17. 常用DOS命令参数的中文详解3
  18. linux学习步骤(从入门到精通)
  19. 四足机器人技术及进展
  20. 山东大学软件学院考试回忆——大一上

热门文章

  1. JQuery Ajax后台无刷新验证用户名重复,前台验证两次密码一致,后台创建并验证 验证码
  2. ssh mysql购物商场_基于HTML5的汽车配件购物商城设计与实现(SSH,MySQL)(含录像)
  3. python计算机二级含金量-计算机二级证书含金量到底有多高?你真的知道吗?
  4. Fast Neural Style在win10上运行
  5. 一款带 Wi-Fi 功能的产品/模组可能需要通过哪些认证?
  6. 看完项目经理平均工资,看看你拉后腿了吗?
  7. python 百度云不限速版_GitHub - wdpython/pan-light: 百度网盘不限速客户端, golang + qt5, 跨平台图形界面...
  8. 高性能音频放大器OPA134PA的分析及应用
  9. SONiC中的SAI接口使用方法和原理
  10. 分享四款协作方便的平面设计软件