Bringing HLSL Ray Tracing to Vulkan

2023-11-15

This post was revised March 2020 to reflect newly added support in DXC for targeting the SPV_KHR_ray_tracing multi-vendor extension.

Vulkan logo

DirectX Ray Tracing (DXR) allows you to render graphics using ray tracing instead of the traditional method of rasterization. This API was created by NVIDIA and Microsoft back in 2018.

A few months later, NVIDIA unveiled its Turing GPU architecture with native support in hardware to accelerate ray tracing workloads. Ever since, the ray-tracing ecosystem has been steadily evolving. Multiple AAA game titles using DXR have been announced and released, as well as industry standard visualization tools. 

Along with DXR, NVIDIA shipped the NVIDIA VKRay Vulkan vendor extension with the same level of ray tracing functionality exposed. Several Vulkan titles use NVIDIA VKRay, including Quake2 RTXJX3 (MMO), and Wolfenstein: Youngblood.

Porting DirectX Content to Vulkan

The Vulkan API from The Khronos Group, being cross-platform, can reach a wide audience across a diverse range of platforms and devices. Many developers port content from DirectX to Vulkan to take advantage of this broader market reach. However, porting a title requires porting both the API calls (to Vulkan) and the shaders (to SPIR-V). 

While most ISVs can port 3D API calls with some reasonable effort, rewriting HLSL shaders in another shading language is a significant undertaking. Shader source code may evolve over many years. In some cases, shaders are also generated on the fly. Consequently, a cross-platform compiler that translates HLSL shader source into SPIR-V for execution by Vulkan is very attractive to developers. 

One such tool developed by Google is the SPIR-V backend to Microsoft’s open source DirectXCompiler (DXC). Over the past couple of years, this compiler has become the common, production-ready solution for bringing HLSL content to Vulkan. Khronos recently discussed more background on using HLSL in Vulkan in a recent post, HLSL as a first class Vulkan Shading Language

Now, bringing together the use of HLSL and ray tracing in Vulkan, NVIDIA has added ray tracing support to DXC’s SPIR-V backend by targeting the SPV_NV_ray_tracing extension under the NVIDIA VKRay extension. We have also upstreamed support for the multi-vendor extension, SPV_KHR_ray_tracing. 

NVIDIA VKRay example

Here’s how to use HLSL shaders in an existing app, created in the Vulkan Ray Tracing Tutorial written by NVIDIA engineers Martin-Karl Lefrançois and Pascal Gautron. 

The following code shows  the HLSL closest hit shader that calculates shadows with a single point light from the sample app:

#include "raycommon.hlsl"
#include "wavefront.hlsl"

struct MyAttrib
{
        float3 attribs;
};

struct Payload
{
   bool isShadowed;
};

[[vk::binding(0,0)]] RaytracingAccelerationStructure topLevelAS;

[[vk::binding(2,1)]] StructuredBuffer<sceneDesc> scnDesc;

[[vk::binding(5,1)]] StructuredBuffer<Vertex> vertices[];

[[vk::binding(6,1)]] StructuredBuffer<uint> indices[];


[[vk::binding(1,1)]] StructuredBuffer<WaveFrontMaterial> materials[];

[[vk::binding(3,1)]] Texture2D textures[];
[[vk::binding(3,1)]] SamplerState samplers[];

[[vk::binding(4,1)]] StructuredBuffer<int> matIndex[];

struct Constants
{
        float4 clearColor;
        float3 lightPosition;
        float lightIntensity;
        int lightType;
};

[[vk::push_constant]] ConstantBuffer<Constants> pushC;

[shader("closesthit")]
void main(inout hitPayload prd, in MyAttrib attr)
{
  // Object of this instance
  uint objId = scnDesc[InstanceIndex()].objId;

  // Indices of the triangle
  int3 ind = int3(indices[objId][3 * PrimitiveIndex() + 0],
                    indices[objId][3 * PrimitiveIndex() + 1],
                    indices[objId][3 * PrimitiveIndex() + 2]);
  // Vertex of the triangle
  Vertex v0 = vertices[objId][ind.x];
  Vertex v1 = vertices[objId][ind.y];
  Vertex v2 = vertices[objId][ind.z];

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

  // Computing the normal at hit position
  float3 normal = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + 
  v2.nrm * barycentrics.z;
  // Transforming the normal to world space
  normal = normalize((mul(scnDesc[InstanceIndex()].transfoIT 
           ,float4(normal, 0.0))).xyz);


  // Computing the coordinates of the hit position
  float3 worldPos = v0.pos * barycentrics.x + v1.pos * barycentrics.y 
                    + v2.pos * barycentrics.z;
  // Transforming the position to world space
  worldPos = (mul(scnDesc[InstanceIndex()].transfo, float4(worldPos, 
              1.0))).xyz;

  // Vector toward the light
  float3  L;
  float lightIntensity = pushC.lightIntensity;
  float lightDistance  = 100000.0;

  // Point light
  if(pushC.lightType == 0)
  {
    float3 lDir      = pushC.lightPosition - worldPos;
    lightDistance  = length(lDir);
    lightIntensity = pushC.lightIntensity / (lightDistance * 
                     lightDistance);
    L              = normalize(lDir);
  }
  else  // Directional light
  {
    L = normalize(pushC.lightPosition - float3(0,0,0));
  }

  // Material of the object
  int               matIdx = matIndex[objId][PrimitiveIndex()];
  WaveFrontMaterial mat    = materials[objId][matIdx];


  // Diffuse
  float3 diffuse = computeDiffuse(mat, L, normal);
  if(mat.textureId >= 0)
  {
    uint txtId = mat.textureId + scnDesc[InstanceIndex()].txtOffset;
    float2 texCoord =
        v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + 
                  v2.texCoord * barycentrics.z;
    diffuse *= textures[txtId].SampleLevel(samplers[txtId], texCoord,
            0).xyz;
  }

  float3  specular    = float3(0,0,0);
  float attenuation = 1;

  // Tracing shadow ray only if the light is visible from the surface
  if(dot(normal, L) > 0)
  {
    float tMin   = 0.001;
    float tMax   = lightDistance;
    float3  origin = WorldRayOrigin() + WorldRayDirection() * 
        RayTCurrent();
    float3  rayDir = L;
    uint  flags =
        RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | 
        RAY_FLAG_FORCE_OPAQUE |
        RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;

    RayDesc desc;
    desc.Origin = origin;
    desc.Direction = rayDir;
    desc.TMin = tMin;
    desc.TMax = tMax;

    Payload shadowPayload;
    shadowPayload.isShadowed = true;
    TraceRay(topLevelAS,
             flags,
             0xFF,
             0,
             0,
             1,
             desc,
             shadowPayload
    );

    if(shadowPayload.isShadowed)
    {
      attenuation = 0.9;
    }
    else
    {
      // Specular
      specular = computeSpecular(mat, WorldRayDirection(), L, normal);
    }
  }

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

Translating to SPIR-V

Here are several interesting portions of the translation:

  • Resource binding
  • Entry points
  • Entry point arguments
  • Translation to intrinsics
  • ShaderBufferRecord (also known as user SBT data)
Resource binding 

At the top of the shader, there is a declaration of a new basic type in HLSL for ray tracing:

[[vk::binding(0,0)]] RaytracingAccelerationStructure topLevelAS;

DirectX uses global root signatures as a mechanism for resource binding. For Vulkan, [[vk::binding]] is a special annotation used to set the binding point and descriptor set location for resources. This annotation is translated to SPIR-V Binding and DescriptorSet decorations respectively, which are ignored when generating DXIL.

You can also continue using register(xX, spaceY) semantics which would be mapped to the Binding and DescriptorSet decorations. For information about a full list of annotations and mappings, see HLSL to SPIR-V Feature Mapping Manual.

RaytracingAccelerationStructure maps directly to SPIR-V opcode OpTypeAccelerationStructureNV/OpTypeAcccelerationStructureKHR.

Entry points

Shader entry points look like the following code example: 

[shader("closesthit")]
void main(inout hitPayload prd, in MyAttrib attr)

DXR HLSL shaders do not use a specific profile for compilation and are compiled as shader libraries (lib_6_* profiles). This allows hundreds of entry points for different ray tracing stages to be present in a single file. To specify specific stages, use the following the annotation:  

[shader(“<stage>”)] 

Use it where <stage> can be any of the following values:

raygeneration, intersection, closesthit, anyhit, miss

These shader libraries are translated to SPIR-V with multiple entry points in a single blob. For the above entry point, the SPIR-V code looks like the following:

OpEntryPoint ClosestHitNV %main "main" %gl_InstanceID %gl_PrimitiveID %5 %6 %7
Entry point arguments
void main(inout hitPayload prd, in MyAttrib attr)

DXR HLSL mandates specific rules for the number and type of arguments for each entry point of a ray tracing stage. For example, in closest-hit shaders, both arguments must be a user-defined structure type. The first represents the payload, while the second represents the hit attributes. A complete set of rules is outlined in the DXR specification

SPIR-V does not allow shader entry points to have arguments. During translation, these variables are added to the global scope with storage classes IncomingRayPayloadNV/IncomingRayPayloadKHR and HitAttributeNV/HitAttributeKHR, respectively. The translation also takes care of proper in-out copy semantics.

Translation of intrinsics

There is a one-to-one mapping for system value intrinsics like InstanceIndex() to SPIR-V built-ins. For more information about the full list of the mappings, see HLSL to SPIR-V Feature Mapping Manual. Matrix intrinsics ObjectToWorld3x4() and WorldToObject3x4() in HLSL don’t have a direct mapping to SPIR-V built-ins. For these, use the original non-transposed SPIR-V built-ins and transpose the result during the translation.

The TraceRay() intrinsic in HLSL uses a specific predeclared structure type, RayDesc. This type is populated with geometric information for the ray, such as origin, direction, and parametric min and max. The OpTraceNV/OpTraceRayKHR action needs each of these parameters as separate arguments. The following code example unpacks the RayDesc structure during translation as follows.

OpTraceNV %245 %uint_13 %uint_255 %uint_0 %uint_0 %uint_1 %244 %float_0_00100000005 %192 %191 %uint_0

OpTraceRayKHR %245 %uint_13 %uint_255 %uint_0 %uint_0 %uint_1 %244 %float_0_00100000005 %192 %191 %uint_0

TraceRay() is templated intrinsic, with the last argument being the payload. There are no templates in SPIR-V.  OpTraceNV/OpTraceRayKHR works around this limitation by providing a location number of the RayPayloadNV/RayPayloadKHR decorated variable. This allows different calls to use different payloads and thereby mimics the template functionality. During translation, RayPayloadNV/RayPayloadKHR decorated variables with unique location numbers are generated while doing copy-in and copy-out of data to preserve the semantics of the TraceRay() call.

ShaderBufferRecord (also known as user SBT data)

NVIDIA’s VKRay extension for ray tracing allows read-only access to user data in the shader binding table (SBT) in ray tracing shaders using shader record buffer blocks. For more information, see the Vulkan 1,2 specification. The SBT data is not directly accessible in HLSL shaders.

To expose this feature, add the [[vk::shader_record_nv]]/[[vk::shader_record_ext]] annotation to ConstantBuffer/cbuffers declarations:

struct S { float t; }
[[vk::shader_record_nv]]
ConstantBuffer<S> cbuf;

DXR introduced local root signatures for binding resources for each shader present in the SBT. Instead of emulating local root signature at SPIR-V level and enforcing some contract on the app, we provide access to the user data section inside the SBT. This, along with support for VK_EXT_descriptor_indexing and its corresponding SPIR-V capability RuntimeDescriptorArrayEXT, can achieve the same effect as local root signature while retaining flexibility. Here’s a code example:

[[vk::binding(0,0)] Texture2D<float4> gMaterials[];
struct Payload { float4 Color; };
struct Attribs { float2 value; };
struct MaterialData { uint matDataIdx; };
[[vk::shader_record_nv]]
ConstantBuffer<MaterialData> cbuf;
void main(inout Payload prd, in Attribs bary)
{
    Texture2D tex = gMaterials[NonUniformResourceIndex(matDataIdx)]
    prd.Color += tex[bary.value];
}

From our experience, this mechanism lines up fairly well with the way most DXR applications use the SBT. It’s also simpler to deal with from the application side, compared to other potential approaches for emulating a local root signature.

Generating SPIR-V using the Microsoft DXC compiler

The earlier HLSL code can be converted to SPIR-V targeting the KHR extension by running the following command:

dxc.exe -T lib_6_4 raytrace.rchit.hlsl -spirv -Fo raytrace.rchit.spv -fvk-use-scalar-layout

To target the NV extension, run the following command:

dxc.exe -T lib_6_4 raytrace.rchit.hlsl -spirv -Fo raytrace.rchit.spv -fvk-use-scalar-layout -fspv-extension="SPV_NV_ray_tracing"

The options used are as follows: 

  • -T lib_6_4: Use the standard profile for compiling ray tracing shaders. 
  • -spirv: Generate output in SPIR-V.
  • -Fo <filename>: Generate an output file from <filename>.

That’s pretty much it! You can plug in the generated SPIR-V blob in the sources and see that it runs as expected, as shown in Figure 2. If you compare the SPIR-V generated from HLSL or corresponding GLSL, it looks very similar.


Figure 2: Vulkan Ray Tracing tutorial using HLSL shaders. 

Conclusion

The NVIDIA VKRay extension, with the DXC compiler and SPIR-V backend, provides the same level of ray tracing functionality in Vulkan through HLSL as is currently available in DXR. You can now develop ray-tracing applications using DXR or NVIDIA VKRay with minimized shader re-writing to deploy to either the DirectX or Vulkan APIs.

We encourage you to take advantage of this new flexibility and expand your user base by bringing ray tracing titles to Vulkan.

References

Related resources

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Bringing HLSL Ray Tracing to Vulkan 的相关文章

  • python矩阵教程_numpy教程:矩阵matrix及其运算

    numpy矩阵简介 NumPy函数库中存在两种不同的数据类型 矩阵matrix和数组array 都可以用于处理行列表示的数字元素 虽然它们看起来很相似 但是在这两个数据类型上执行相同的数学运算可能得到不同的结果 其中NumPy函数库中的ma
  • 插入MySQL数据库前去除重复数据的几种方法

    在数据存储过程中 可能会遇到数据主键重复的情况 我们可以通过下面几个方法进行处理 1 若数据不存在插入 存在更新 2 使用duplicate key关键字 如插入数据时发生主键冲突就更新数据 3 使用Ingore关键字 4 使用replac
  • BoxFit(缩放模式、自适应模式)

    类似于Android原生的ImageView ScaleType 以下是Flutter提供的Box缩放类型 fill Box被完全填充 相当于ScaleType的FIT XY contain 保持Box的纵横比至至少有一边填充满父控件 相当
  • 单例模式 -- 懒汉模式&饿汉模式

    目录 一 单例模式是什么 二 饿汉模式 三 懒汉模式 一 单例模式是什么 单例模式是一种设计模式 用于将类的实例化限制为一个对象 它确保一个类只有一个实例 并提供了该实例的全局访问点 这种模式被广泛用于创建对象的唯一实例 例如数据库连接和日
  • LCD(五)Backlight背光子系统

    一 Backlight背光子系统概述 LCD的背光原理主要是由核心板的一根引脚控制背光电源 一根PWM引脚控制背光亮度组成 应用程序可以通过改变PWM的频率达到改变背光亮度的目的 Backlight背光子系统构建过程结构关系图 黑色加粗部分
  • ONNX 运行时报错 ORT_RUNTIME_EXCEPTION Ort::Exception 未经处理的异常

    1 运行报错 前段时候推理时遇到一个非常奇怪的bug ONNX模型在运行时会报ORT RUNTIME EXCEPTION的异常 2 错误排查 继续运行 断点看到是在Session Run 的时候报错 断点逐语句跟踪没有更多详情的信息 重新看
  • jsp 购物车

  • 墨者学院——SQL注入漏洞测试(时间盲注)

    点击链接进入题目 点进网页 在url后加 type 1 发现没有回显 上传 type 1 and sleep 10 发现网页有明显延迟 说明sleep函数被执行 该网页存在时间注入 通过构造payload去获得数据库长度 x为猜想的数据库长
  • 【LSTM预测】基于双向长短时记忆BiLSTM(多输入单输出)数据预测含Matlab源码

    1 简介 针对长短期记忆循环神经网络在对时间序列进行学习时存在早期特征记忆效果差 难以充分挖掘整个网络流量特征等问题 提出一种基于双向长短期记忆循环神经网络的网络流量预测方法 以提高网络流量预测的准确性 对网络流量序列进行双向学习 避免单向
  • Android的手势识别

    首先 在Android系统中 每一次手势交互都会依照以下顺序执行 接触接触屏一刹那 触发一个MotionEvent事件 该事件被OnTouchListener监听 在其onTouch 方法里获得该MotionEvent对象 通过Gestur
  • Variable used in lambda expression should be final or effectively final

    问题描述 在使用java8lambda表达式的时候 有时候会遇到这样的编译报错 这句话的意思是 lambda表达式中使用的变量应该是final或者是有效的final 在Java8之前 匿名类中如果要访问局部变量的话 那个局部变量必须显式的声
  • LeetCode-1488. Avoid Flood in The City

    Your country has an infinite number of lakes Initially all the lakes are empty but when it rains over the nth lake the n
  • 迅雷5引发的Dos Generic SYNFlood网络攻击

    迅雷5引发的Dos Generic SYNFlood网络攻击 使用卡巴斯基的各位 有没有注意到卡巴最近经常会报Dos Generic SYNFlood 网络攻击 而且一报起来就没完没了 网上有人居然收到几千条还没崩溃 真是有定力 今天突然发
  • flutter_html出现蓝底

    Html data div style background color FFFFFF 123 div 原因是不支持大写颜色 替换为小写即可 String upperColor2Lower String text RegExp reg ne
  • 激发云力量--打造我的云端工具集

    0 前言 日常工作中 有很多小需求 作为码农 总喜欢自己动手做点小东西出来 也成为学习与实践的好机会 在使用腾讯云过程中 从环境搭建 各个小需求的构思 前后端技术的琢磨 学习 使用 收获很大 现在整理出来和大家分享 先说说做了哪些事情 都来
  • 深度学习在训练和测试阶段使用显卡的情况是否必须完全一致?

    问题 深度学习模型在进行训练时采用多张显卡进行训练 测试时是不是就与显卡无关了 也就是说可以利用CPU做推理 也可以使用GPU做推理 回答 是的 在深度学习模型训练时采用多张显卡进行训练 测试时模型的预测过程与显卡无关 这意味着在测试过程中
  • netware php_服务器_如何在 Netware 服务器中安装多块网卡,如果网络在扩大时服务器只装 - phpStudy...

    如何在 Netware 服务器中安装多块网卡 如果网络在扩大时服务器只装一块网卡 所有工作站采用总线结构上网 那么访问速度会很慢 另外 如果上网时某台工作站出了故障 所有的工作站都受其影响 不能工作 我们可以在服务器中安装多块网卡来解决问题
  • 【虚拟机连接Xshell详细过程】

    虚拟机连接Xshell详细过程 1 Xshell简介 2 Xftp简介 3 安装下载Xhell和Xftp 下载Xshell和上面的步骤一样 4 使用VMware连接Xhell 5 Xftp的使用 1 Xshell简介 Xshell是Wind
  • CCS软件

    目录 一 如何跳转到函数的定义 二 declaration is incompatible with 常见错误原因 三 symbol cell values redefined first defined in HARDWARE bq pa

随机推荐

  • LRU算法的Java实现

    一 LRU算法介绍 LRU算法全称Least Recently Used 也就是检查最近最少使用的数据的算法 这个算法通常使用在内存淘汰策略中 用于将不常用的数据转移出内存 将空间腾给最近更常用的 热点数据 算法很简单 只需要将所有数据按使
  • 2019 暑期实习面试及准备资料总结

    2019 暑期实习面试及准备资料总结 18年年初开始转型做ML DL 0基础 3个月学习了基础知识 打了一个kaggle比赛 DSB2018 截止投简历的时候成绩top 3 投了三家的算法岗 今日头条图像算法岗 蚂蚁金服机器学习推荐算法 腾
  • Markdown中LaTeX公式编号

    在Markdown中使用 LaTeX LaTeX LATE X公式时 加上 tag 标签可以生成对应的编号 比如这个代码可以生成 x y z tag 1 1 这样的带编号的公式 比如这个代码可以生成 1 1
  • 两直线垂直,斜率乘积为-1的证明

    老早以前在学习初等函数的时候 线性函数中的两直线y m0x b0 y m1x b1如果垂直 则有结论两条直线的斜率乘积为 1即m0 m1 1 以前也只是拿来用 没有证明过 最近在学图形学的时候 突然想起了这个点 因此记一篇笔记 证明一下 如
  • 南科大C++课程知识点

    视频 快速学习C和C 基础语法和优化策略 学了不再怕指针 南科大计算机系原版 哔哩哔哩 bilibili南方科技大学计算机系 C C 程序设计 课程视频 课件 例程等请访问 https github com ShiqiYu CPP 致谢 广
  • Linux Ubuntu Server图形界面(Ubuntu-desktop)安装

    1 更新源 国内用户 我选择的清华源 具体操作自行百度 deb cdrom Ubuntu 16 04 LTS Xenial Xerus Release amd64 20160420 1 xenial main restricted deb
  • 基于tensorflow2.0的GA优化BP神经网络的代码

    首先定义遗传算法 GA 染色体编码部分 实际上就是种群个体初始化 def encode pop dim ub lb 染色体编码 X np zeros pop dim for i in range pop for j in range dim
  • 华为综合测评是什么_信号“学霸”名不虚传,电信发布最新报告:华为手机包揽7项第一...

    了解华为的朋友应该都知道 华为是做通信起家的 华为手机在行业内的信号体验是最好的 堪称 信号学霸 而近日 中国电信发布了 中国电信终端洞察报告2020版 对14个品牌共计62款产品进行了5G全网通测评 华为共包揽7项第一 1 华为Mate4
  • 数据结构 练习题:用LINUX内核函数提供的双向循环链表标准实现重做:用双向循环链表实现顺序递增存储若干自然数,比如输入一个整数10,则建立一个双向循环链表,里面的每个节点分别存放1,2。。。

    数据结构 练习题 用LINUX内核函数提供的双向循环链表标准实现重做 用双向循环链表实现顺序递增存储若干自然数 比如输入一个整数10 则建立一个双向循环链表 里面的每个节点分别存放1 2 3 4 5 6 7 8 9 10 然后通过某些操作
  • endl在c语言中的作用,C++中的endl

    从开始接触C 到现在 一直以为语句 cout lt lt hello world lt lt endl 中的endl只是一个相当于C中的换行 n 直到今天 莫名的搜索了一下endl才发现了一个惊天大秘密 原理endl不仅仅是一个换行那么简单
  • R语言快速学习第一部分(有其他语言基础)

    文章目录 注意事项 1 输出内容 输出到控制台 输出到文件 2 数字运算符 数字运算符 赋值 3 流程控制 if 判断语句 switch 判断语句 循环语句 4 函数 注意事项 R 语言区分大小写 使用的是 TRUE 与 FALSE 而不是
  • 2023最新版本Activiti7系列-流程中的任务

    流程中的任务 1 用户任务 用户任务 用于定义流程中需要人工参与的任务 用户任务可以在流程中创建并分配给特定的用户或用户组 当流程执行到用户任务时 流程将暂停 并等待相应的用户完成该任务 完成用户任务后 流程将继续执行 用户任务可以有以下属
  • 三菱MR-JE-C伺服电机FB功能块 流水线项目,16个MR-JE-C电机

    三菱MR JE C伺服电机FB功能块 适用Q系列PLC 流水线项目 16个MR JE C电机 为了加快编程速度 特意做的一个FB功能块 内部采用局部变量 全局缓冲区的方式进行编程 多次调用不冲突 适用于Q系列PLC和MR JE C的运动控制
  • 模块基础知识总结

    文章目录 1 模块 2 模块的创建 模块的使用 1 模块 模块化指将一个完整的程序分解成一个个的小模块 通过将模块组合 来搭建出一个完整的程序 模块化的有点 方便开发 方便维护 模块可以复用 2 模块的创建 在Python当中一个py文件就
  • spring boot中修改默认json转换器的部分特性,以及如何替换默认json转换器

    用Spring boot构建微服务时 后台接收controller通常用 RestController注解 Restcontroller是 Controller和 ResponseBody的结合体 会将后台返回的Java对象转换为Json字
  • Linux面试题

    1 解释下什么是GPL GNU 自由软件 GPL 通用公共许可证 一种授权 任何人有权取得 修改 重新发布自由软件的权力 GNU 革奴计划 目标是创建一套完全自由 开放的的操作系统 自由软件 是一种可以不受限制地自由使用 复制 研究 修改和
  • matlab elmtrain,ELM代码matlab

    Elmtrain m 训练代码 function IW B LW TF TYPE elmtrain P T N TF TYPE ELMTRAIN Create and Train a Extreme Learning Machine Syn
  • 电脑如何快速截屏技巧 四种常见的电脑截图方法

    经常使用电脑的朋友肯定会经常用到截屏这一功能 那么怎样快速的将电脑桌面上的信息通过截屏保存下来呢 其实截屏的方法有很多种 小编在这里与大家一起分享下四种常见的电脑截图方法 一 Windows系统自带的截图工具 一般的Windows系统都自带
  • git初识

    目录 简介 基本操作 常用命令 简介 Git是一种分布式版本控制系统 用于跟踪文件的更改并协调多个人之间的工作 它最初由Linus Torvalds于2005年创建 现在已成为许多开源和商业项目的首选版本控制系统 Git的主要特点包括分支和
  • Bringing HLSL Ray Tracing to Vulkan

    This post was revised March 2020 to reflect newly added support in DXC for targeting the SPV KHR ray tracing multi vendo