本文主要参考NVIDIA Vulkan Ray Tracing Tutorial教程,环境配置与程序均可参照此文档执行(个人水平有限,如有错误请参照原文)。

一、简单光照

当前最近的命中着色器仅返回单一颜色。为了添加光照,我们需要引入表面法线的概念。但是,光线追踪仅提供击中点的重心坐标。为了获得法线和其他顶点属性,我们需要在顶点缓冲区中找到它们并使用重心坐标对它们进行插值。这就是为什么我们在创建光线追踪描述符集时扩展了顶点和索引缓冲区的原因。

1.1 最近命中着色器 (raytrace.rchit)

当我们创建光线追踪描述符集时,我们已经包含了几何定义。因此,我们可以通过场景描述直接在最近的命中着色器中引用顶点和索引缓冲区: binding = 2

我们首先引用有效光路载荷的定义和 OBJ-Wavefront 结构体

#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"

然后我们根据描述符集布局来描述资源

layout(location = 0) rayPayloadInEXT hitPayload prd;layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {ivec3 i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };

在main函数中,gl_InstanceCustomIndexEXT告诉我们哪个对象被击中,并且gl_PrimitiveID允许我们找到被射线击中的三角形的顶点

void main()
{// Object dataObjDesc    objResource = objDesc.i[gl_InstanceCustomIndexEXT];MatIndices matIndices  = MatIndices(objResource.materialIndexAddress);Materials  materials   = Materials(objResource.materialAddress);Indices    indices     = Indices(objResource.indexAddress);Vertices   vertices    = Vertices(objResource.vertexAddress);// Indices of the triangleivec3 ind = indices.i[gl_PrimitiveID];// Vertex of the triangleVertex v0 = vertices.v[ind.x];Vertex v1 = vertices.v[ind.y];Vertex v2 = vertices.v[ind.z];

通过以下方式完成计算重心坐标:

  const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);

世界空间位置可以通过两种方式计算

  • 第一种:使用来自命中着色器的信息。但如果命中点很远,这可能会出现精度问题。
  vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
  • **另一种:**更精确的方法是通过插值计算位置。我们使用命中着色器提供的材料信息。这些矩阵是使用我们设置 TLAS 和 BLAS 提供的信息计算的。请注意,我们所有的 BLAS 都没有应用任何转换,仅使用的是实例数据。
// 计算命中位置的坐标
const vec3 pos      = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;// 将位置坐标转换到世界空间
const vec3 worldPos = vec3(gl_ObjectToWorldEXT * vec4(pos, 1.0));

同方的方式计算法线

// 计算击中位置的法线
const vec3 nrm      = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
// 将法线转换到世界空间
const vec3 worldNrm = normalize(vec3(nrm * gl_WorldToObjectEXT));

然后可以使用常量中指定的光源来计算法线与光照方向的点积,给出简单的漫射照明效果:

  // Vector toward the lightvec3  L;float lightIntensity = pcRay.lightIntensity;float lightDistance  = 100000.0;// Point lightif(pcRay.lightType == 0){vec3 lDir      = pcRay.lightPosition - worldPos;lightDistance  = length(lDir);lightIntensity = pcRay.lightIntensity / (lightDistance * lightDistance);L              = normalize(lDir);}else  // Directional light{L = normalize(pcRay.lightPosition);}

如下图所示:

二、材质

通过添加对材质的支持,可以使上面的渲染更有趣。导入的 OBJ 对象提供了简化的 Alias Wavefront 材质定义。

2.1 最近命中着色器 (raytrace.rchit)

这些材质使用简单的颜色系数定义了它们的基本反射特性,并且还支持纹理。我们已经为光栅化创建了包含材质的缓冲区,并且也已将其添加到光线跟踪描述符集中。

因此在着色器中添加纹理采样器数组的绑定(材质的定义与用于光栅化器的定义相同):

layout(set = 1, binding = eTextures) uniform sampler2D textureSamplers[];

顶点数组结构中包含一个材质索引,所以我们使用它来查找缓冲区中的相应材质。

我们首先在main()末尾删除这些行 :

float dotNL = max(dot(normal, L), 0.2);
prd.hitValue = vec3(dotNL);

取而代之的是获取材料定义:

  // 物体的材质int               matIdx = matIndices.i[gl_PrimitiveID];WaveFrontMaterial mat    = materials.m[matIdx];

其中,每个对象有一个材质缓冲区,每个材质都可以通过索引访问。每个三角形都有一个材料索引。

根据材质定义,我们使用漫反射和镜面反射来计算漫射光照。此代码还支持纹理来调节表面反射率。

  // Diffusevec3 diffuse = computeDiffuse(mat, L, normal);if(mat.textureId >= 0){uint txtId = mat.textureId + scnDesc.i[gl_InstanceCustomIndexEXT].txtOffset;vec2 texCoord =v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;diffuse *= texture(textureSamplers[nonuniformEXT(txtId)], texCoord).xyz;}// Specularvec3 specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, normal);

最终的光照计算如下

  prd.hitValue = vec3(lightIntensity * (diffuse + specular));

2.2 更改模型

在main.cpp中我们通过调用helloVk.loadModel来加载一些比立方体更复杂的OBJ 模型:

helloVk.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths, true));
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true));

由于该模型更大,我们可以将CameraManip.setLookat调用更改为

CameraManip.setLookat(nvmath::vec3f(4, 4, 4), nvmath::vec3f(0, 1, 0), nvmath::vec3f(0, 1, 0));

三、阴影

以上代码可以让我们对场景进行光线追踪并应用一些光照,但它仍然缺少阴影。为此,我们将添加一个新的光线类型,并从最近命中着色器发射光线。这种新的光线类型需要我们在代码中添加一个新的未命中着色器。

3.1 修改createRaytracingPipeline

对于简单的阴影光线,我们只需要沿光线计算光路是否击中了某些几何体。这可以通过使用初始化一个bool有效载荷值来实现代表是否存在遮挡物体,并且光线跟踪仅需要使用额外的一个未命中着色器将有效载荷bool值设置为未命中来传递结果。

在 createRtPipeline 函数中,我们需要在之前的未命中着色器之后立即定义一个新的未命中着色器:

enum StageIndices
{eRaygen,eMiss,eMiss2,eClosestHit,eShaderGroupCount
};

之后创建组件

// 当阴影射线没有击中几何体时,第二个未命中着色器被调用。 它只是表明没有发现咬合
stage.module =nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytraceShadow.rmiss.spv", true, defaultSearchPaths, true));
stage.stage   = VK_SHADER_STAGE_MISS_BIT_KHR;
stages[eMiss2] = stage;

在添加未命中着色器之后,我们继续添加阴影光线的未命中着色器:

// Shadow Miss
group.type          = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eMiss2;
m_rtShaderGroups.push_back(group);

光追管线现在必须允许从最近命中着色器发射光线,因此需要将递归级别增加到 2:

//光线追踪过程可以从相机发射光线,阴影光线可以从相机光线的命中点发射,因此递归级别为2。
// 出于性能考虑,这个数字应该尽可能地保持在较低的水平。 即使是递归射线追踪也应该在射线生成中被平展成一个循环,以避免递归深度过大。
rayPipelineInfo.maxPipelineRayRecursionDepth = 2;  // Ray depth

递归深度受限于硬件,需进行判断由于我们现在需要 2 的递归深度,我们应该检查设备是否支持所需的递归级别:

  // 根据本机硬件限制进行判断if (m_rtProperties.maxRayRecursionDepth <= 1) {throw std::runtime_error("Device fails to support ray recursion (m_rtProperties.maxRayRecursionDepth <= 1)");}

3.2 修改createRtShaderBindingTable

新miss shader组的添加使得我们的shader绑定表也需要对应修改,新的布局如下:

因此,我们必须更改HelloVulkan::createRtShaderBindingTable以标识有两个未命中着色器。

uint32_t missCount{2};

3.3 修改createRtDescriptorSet

对于描述符集中的每个资源实例,分别代表了哪个着色器阶段将能够使用它。由于阴影光线将从最近的命中着色器发出,因此我们需要添加VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR到加速结构绑定中去:

  // TLAS加速结构,可用于射线生成和最近的命中(发射阴影射线)  m_rtDescSetLayoutBind.addBinding(RtxBindings::eTlas, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);  // TLAS

3.4 修改最近命中着色器(raytrace.rchit)

最近命中着色器现在需要能够知道TLAS加速结构才能发射光线:

layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;

这些射线还将保存一个有效光路载荷值,它需要在与当前射线有效光路不同的位置进行重定义 。此处有效光路载荷将是一个简单的布尔值,指示是否命中遮挡物:

layout(location = 1) rayPayloadEXT bool isShadowed;

在main函数中,我们将发出一条新射线,而不是简单地将有效载荷设置为prd.hitValue = c。要选择未命中阴影着色器,我们需要将函数traceRayEXT()中的参数missIndex设置为1而不再是0 。有效光路荷载值的定义与上面的声明布局(location = 1)相匹配。 。另外在调用traceRayEXT() 时我们设置标识为:

  • gl_RayFlagsSkipClosestHitShaderKHR: 不会调用命中着色器,只会调用未命中着色器

  • gl_RayFlagsOpaqueKHR :不会调用任意命中着色器,因此所有对象都将是不透明的

  • gl_RayFlagsTerminateOnFirstHitKHR : 第一击中点总是有效的。

由于我们跳过了阴影命中组,因此在命中表面时不会调用任何代码。因此,我们将有效载荷isShadowed初始化为true,如果没有击中任何几何体表面,我们将使用未命中着色器将其设置为 false。

此外,我们还设置了光线标志来优化光线追踪:由于这些简单的阴影光线只需要返回光线是否与任何几何体表面相交的结果,我们可以控制光线追踪引擎在找到第一个交点后停止遍历,而无需继续执行最近命中着色器。

因为仅当光线位于表面前方时才需要投射阴影光线,如果我们处于阴影中,则不应计算镜面反射(因为从着色点看不到光源)。所以之前计算镜面反射项的代码将如下所示修改一下:

  vec3  specular    = vec3(0);float attenuation = 1;// Tracing shadow ray only if the light is visible from the surfaceif(dot(normal, L) > 0){float tMin   = 0.001;float tMax   = lightDistance;vec3  origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;vec3  rayDir = L;uint  flags =gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT;isShadowed = true;traceRayEXT(topLevelAS,  // acceleration structureflags,       // rayFlags0xFF,        // cullMask0,           // sbtRecordOffset0,           // sbtRecordStride1,           // missIndexorigin,      // ray origintMin,        // ray min rangerayDir,      // ray directiontMax,        // ray max range1            // payload (location = 1));if(isShadowed){attenuation = 0.3;}else{// Specularspecular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, normal);}}

然后可以根据阴影射线的结果调整最终的有效光路载荷值:

prd.hitValue = vec3(lightIntensity * attenuation * (diffuse + specular));

Vulkan_Ray Tracing 08_光照、材质、阴影相关推荐

  1. 【OpenGL ES】光影(光照与阴影)效果

    1 前言 Blinn改进的冯氏光照模型 中只展示了光照效果,本文将进一步展示阴影效果. 绘制阴影,需要用到深度纹理,即从光源角度看模型并绘制一张纹理图,纹理图的颜色代表了模型上的点离光源的深度,只有离 ...

  2. (六)unity自带的着色器源码剖析之——————Unity3D的全局光照和阴影:上篇(全局照明GI和局部照明)

    一.全局照明和局部照明 全局照明(global illumination,GI)是用于向三维场景中添加更为逼真的光照效果的一组算法总称.全局算法不仅考虑光源发出的光与被照亮物体之间的关系,即直接照明( ...

  3. Maya与Substance Painter风格化材质阴影和照明学习教程

    大小解压后:619 M 1920X1080 mp4 时长:第一部分 48分55秒 第二部分 32分29秒 语言:英语+中英文字幕(根据原英文字幕机译更准确) 第一部分介绍Substance Paint ...

  4. 对光照、阴影和反光具有鲁棒性的变化检测算法及实现

    对光照.阴影和反光具有鲁棒性的变化检测算法及实现 http://www.cqvip.com/Main/Detail.aspx?id=8293930

  5. Opengl-光照-基本光照-材质(有了材质一个物体才算是完整了)

    在现实世界里,每个物体会对光产生不同的反应.比如说,钢看起来通常会比陶瓷花瓶更闪闪发光,木头箱子也不会像钢制箱子那样对光产生很强的反射.每个物体对镜面高光也有不同的反应.有些物体反射光的时候不会有太多 ...

  6. Vulkan_Shader—高级光照_阴影_原理

    高级光照_阴影原理概述 阴影是光线被阻挡的结果:当一个光源的光线由于其他物体的阻挡不能够达到一个物体的表面的时候,那么这个物体就在阴影中了.阴影能够使场景看起来真实得多,并且可以让观察者获得物体之间的 ...

  7. Vulkan_Shader—高级光照_阴影_实现

    高级光照_阴影_代码实现 根据上节阴影原理及附源码,主要是创建一个深度缓冲视图,然后将其使用到主场景Shader中,以此来实现阴影,大部分工作是在原有代码基础上进行改动,并新增了部分hpp文件来快捷创 ...

  8. Vulkan_Ray Tracing 05_光线追踪管线

    本文主要参考NVIDIA Vulkan Ray Tracing Tutorial教程,环境配置与程序均可参照此文档执行(个人水平有限,如有错误请参照原文). 一.光线追踪管线 在光线追踪时,与光栅化不 ...

  9. 基本光照与阴影(一)

    (一)基本光照 光照 -- 即根据场景中光源的分布及物体的形状.朝向等信息,为物体"涂"上阴影.高光等一系列增加真实感的色彩. 为了给物体着色,我们需要一个"模型&quo ...

  10. 【计算机视觉】光照及阴影(持续更新中)

    学习的时候来csdn搜发现这里很少有关于光照及阴影部分的介绍,为了方便自己复习,也方便uu们学习,在这里发笔记啦. 三种反射: 漫反射: 入射光线均匀的被反射后散射在外向的整个半球面上 镜面反射: 就 ...

最新文章

  1. Windows8 下安装 Materials Studio 6.0 全过程
  2. 使用Windows Server Backup备份恢复Exchange Server 2010数据库
  3. linux route命令深入浅出与实战案例精讲
  4. 4. Leetcode 18. 四数之和 (数组-双向双指针)
  5. ORACLE下载当中的gateways,companion,clusterware都是什么用途?
  6. 一天搞定HTML----标签语义化04
  7. Linux而不必进入password登陆自己主动sshserver方法
  8. 超简单让.NET Core开发者快速拥有CI/CD的能力-Docker版本
  9. oracle 造数据脚本_Oracle数据库shell脚本--统计所有数据库用户信息及明细
  10. 服务器虚拟机密度,服务器整合:虚拟机密度大未必是好事
  11. 带网格的_雨花区井圭路社区开展消防安全网格化实战演练活动
  12. Alex 的 Hadoop 菜鸟教程: 第20课 工作流引擎 Oozie
  13. Cocos2dx--Cocos2dx与Android平台的跨平台调用
  14. java代码复数包括虚部和实部,C++ complex复数类用法详解
  15. 民国歌曲 - 毛毛雨
  16. ietester测试本地html,网站浏览器兼容测试工具IETester
  17. 为什么Word文档无响应,Word文档无响应的解决方法
  18. 我看:“不患寡而患不均” 以及 “饥寒起盗心”
  19. 优缺点 快速扫描 硬盘监测_有了这6款mac硬盘检测工具 你就能够快速检测磁盘的状态和错误情况...
  20. papers-06-07

热门文章

  1. 已知三角形顶点坐标,求其外接圆的公式
  2. WordPress模板iDowns1.8.3+支持对接Erphpdown
  3. FastDFS的安装及上传下载(二)
  4. 《产品经理面试攻略》PART 6:产品群面题
  5. SQL教程——常见的约束类型
  6. Himawari-8葵花八HSD数据处理——几何校正
  7. 单片机c语言中断延时,单片机中C语言延时函数
  8. vue3小兔鲜儿项目文档,视频
  9. 在python3中、下列输出变量a的正确写法是_超星尔雅大数据Python答案免费微信公众号...
  10. 简单实现内外网自由切换、指定网卡上网