【OptiX】第0个示例 OptixHello 学习Optix的工程配置以及基本框架

article/2025/11/9 13:13:55

首先需要查看本博客的这篇文章:【Optix】Optix介绍与示例编译 把该安装的工程都安装好。可以按照本文所说的顺序创建和理解代码,也可以在本文末尾下载到已经配置好的代码。建议首先在本文末尾处下载代码,编译通过,这样配合文件看心情会舒畅一些。再一步一步的建内心也不慌乱。

首先来看本文的输出效果。

【建工程】

打开VS2015,新建工程取名为OptiXHello,按照编程习惯,路径中不要有中文。尽量也不要有空格。

选择Win32控制台应用程序,应用程序设置中选空项目

默认情况下是X86程序,注意这里很多配置都有选择X86还是X64,是DEBUG还是RELEASE,这里拿X64来示例。首先把当前编译环境修改为X64:

其次我们要新建一个main.cpp的源文件:并编写以下代码:

#include <iostream>int main()
{std::cout << "Hello World" << std::endl;return 0;
}

确保可以正常运行后,配置工程环境,选菜单->项目->属性,在配置->VC++目录中,右侧的包含目录中新增CUDA和OPTIX的包含目录:注意当前配置是DEBUG平台是X64

C:\ProgramData\NVIDIA Corporation\OptiX SDK 6.0.0\include;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\include;D:\9.PRO\rtx\optix\OptiX SDK 6.0.0\SDK\support\freeglut\include;$(ProjectDir)

为什么要加$(ProjectDir)呢,是因为后面我们要把glew进行编译,而glew.c的包含使用的是#include <gl/glew.h>,使用的是<>而不是"",若是#include "gl/glew.h"则会在当前工程目录下寻找,而使用<>只会在上面配置的目录中寻找。$(ProjectDir)就是当前目录,意思是把当前目录加入到包含目录当中。

在链接器->输入->附加依赖项中新增如下依赖:

C:\ProgramData\NVIDIA Corporation\OptiX SDK 6.0.0\lib64\optix.6.0.0.lib;C:\ProgramData\NVIDIA Corporation\OptiX SDK 6.0.0\SDK\support\freeglut\win64\Release\freeglut.lib;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\lib\x64\nvrtc.lib;

因为代码中用到了glew,需要将glew拷贝到工程中,glew在SDK中的位置是:

C:\ProgramData\NVIDIA Corporation\OptiX SDK 6.0.0\SDK\sutil

涉及文件

将其拷贝到与main.cpp同等目录下。将其加入到工程文件中,.h与.c文件都加入。

因为编译glew需要预定义一些宏,项目->属性->C/C++->预处理器定义:

_DEBUG;_CONSOLE;WIN32;GLEW_BUILD;GLUT_FOUND;GLUT_NO_LIB_PRAGMA;

【写主机端代码】

因为OptiX涉及到GPU端CUDA等操作,各种初始化操作等依赖较多,因此容易抛出异常,在main中首先将结构修改为try catch结构:

int main(int argc, char* argv[])
{try{}catch (const std::exception& e){std::cerr << "OptiX Error: '" << e.what() << "'\n";exit(1);}return 0;
}

本节的代码我们要使用OptiX渲染输出一个缓存,然后使用glut来展示这个缓存。因此这个输出缓存就是二者之间沟通的桥梁。而OptiX要 输出这个简单的缓存首先要弄清楚一些概念:

Context(上下文)

OptiX使用上下文来管理整个渲染场景,比如发射光线的种类,缓存的关联,场景的关联,以及光线生成shader的关联等等。可以理想Context是连接一切的基础,是一切的操作场所。通过Context可以获取程序中所有的配置也可以进行所有的配置。后面逐步介绍。首先申请全局变量Context:

#include <optix.h>
#include <optixu/optixpp_namespace.h>//申请场景上下文
optix::Context context;

Buffer(缓存)

缓存用来存放纹理、顶点以及其它任何的数值。此时我们创建一个缓存,用来输出OptiX渲染结果输送到glut当中。

//确定场景的长宽
int width = 800, height = 600;
//申请场景上下文
optix::Context context;
optix::Buffer output_buffer;

我们还定义了width和height用来确定场景的长和宽。

【光线产生模块(RayGenerationProgram)】

有了上下文和buffer之后,我们要开始介绍入口shader。在Optix中叫做RayGenerationProgram(光线产生模块),由该模块来产生光线并确定光线与场景相交的结果。这个模块是一个shader针对要渲染的结果的width和height每个像素产生一个调用,使用一个变量叫做rtLaunchIndex的来记录是哪个像素,也即rtLaunchIndex是个二维的整型内置变量,除了计算包围盒模块(BoundingBoxProgram)外,存在于整个渲染过程当中。也就是都可以访问到该变量。

光线产生模块使用shader定义,语法是C++的语法,语法简单,只是有一些内置变量和流程需要熟悉。随着本博客不断深入的介绍,用户会对每个内置变量和函数以及流程有着深切的认识。

我们边做边来解释,首先像新建main.cpp一样新建项,只是名字叫做draw_color.cu,内容如下:

#include <optix.h>
#include <optixu/optixu_math_namespace.h>using namespace optix;rtDeclareVariable(uint2, launch_index, rtLaunchIndex, );
rtBuffer<float4, 2> result_buffer;rtDeclareVariable(float3, draw_color, , );RT_PROGRAM void draw_solid_color()
{//rtPrintf("-%f-%f-%f", draw_color.x, draw_color.y, draw_color.z);//result_buffer[launch_index] = make_float4(draw_color, 0.f);float2 degree = make_float2(launch_index)*3.14/180.0f;result_buffer[launch_index] = make_float4(sin(degree.x), cos(degree.y), 0.0, 0.f);
}

在工程中要想把cu文件识别为C++文件,则需要一个设置,菜单->工具->选项->文本编辑器->文件扩展名,扩展名输入cu,编辑器选择Microsoft Visual C++。点击确定即可。

下面对draw_color.cu中的每句话做出解释:

包含文件就不说了,下面解释rtDeclareVariable(a, b, c, d),其共有四个参数,第一个参数是数据类型,第二个参数是变量名,第三个参数叫做名字空间,一来是为了防止重名,二来是为了说明该变量是在该阶段使用,第四参数是注释,使用字符串,可以随便写。

那么着重来说一下第三个参数:名字空间。OptiX目前共有5个名字空间,如下:

该图在随安装包的文档《Programming Guide》的第43页。这五个名字空间分别在不同的渲染阶段起作用代表不同的含义。就rtLaunchIndex来说比较特殊,在shader中只有对该变量读取的权限,rtDeclareVariable相当于定义了一个引用实例而已。其是二维的存放的是像素的行列编号,该编号也用于定位缓存中的位置。

rtBuffer<float4, 2> result_buffer 该行在shader中申明了一个buffer,也就是在GPU侧申请了一个buffer。且该buffer是float4类型,有二维。其实这个buffer就是输出buffer,需要向CPU端进行传递。在CPU端使用语句:rtContextDeclareVariable( context, "result_buffer", &output_buffer) 语句或者context["result_buffer"]->set( output_buffer);与CPU端的buffer进行相连。

rtDeclareVariable(float3, draw_color, , ); 就是申请了一个CPU与GPU端共同的变量draw_color,该变量只读,不可改变。若要改变可以创建一个临时变量拷贝后改变。这种申请的变量是全局变量,所有的shader的阶段都可读取。

RT_PROGRAM void draw_solid_color()
{result_buffer[launch_index] = make_float4(draw_color, 0.f);
}

每个光线发生模块需要有一个启动函数,在主机端使用

    Program ray_gen_program = context->createProgramFromPTXString( ptx, "draw_solid_color" );context->setRayGenerationProgram( 0, ray_gen_program );

来进行设置。该函数就是启动函数,该启动函数里只有一句话就是把输出buffer里对应像素位置设置成draw_color的值。整个光线跟踪流程就结束了。可以看到没有光线,其实即便有光线也是为了输出结果到result_buffer中,后面我们会逐渐的介绍各种shader,最终的计算都是为了填允result_buffer。因此这是一个大的框架。

【构建context和buffer】

有了上面的光线发生模块的shader,我们来初始化context和输出buffer:

        context = optix::Context::create();context->setRayTypeCount(1);//只有1种光线context->setEntryPointCount(1);//只有1个光线发生相机//申请Bufferoutput_buffer = context->createBuffer(RT_BUFFER_OUTPUT, RT_FORMAT_FLOAT4, width, height);context["result_buffer"]->set(output_buffer);//对shader进行编译,并取出编译结果const char* ptx = getPtxString("draw_color.cu");//从ptx字串中标识 "draw_solid_color"入口函数,并创建并返回光线发生器模块optix::Program ray_gen_program = context->createProgramFromPTXString(ptx, "draw_solid_color");//在setEntryPointCount设置只有一个入口,即指于此context->setRayGenerationProgram(0, ray_gen_program);//设置shader中变量draw_colorcontext["draw_color"]->setFloat(1.0f, 0.0f, 1.0f);  context->setPrintEnabled(true);context->setPrintBufferSize(4096);//内部检查有效性context->validate();//开启第0个入口context->launch(0, width, height); 

上面的代码完成了申请context与设置光线种类,比如有时候一个入口会发两种光线,一种用于渲染,一种专门用于计算阴影等。申请buffer的语句是使用context的createBuffer参数,可以看出这里申请的是一个output buffer,用于GPU向CPU侧输出的。可以通过‘|’符号设置如下标识:

typedef enum
{RT_BUFFER_INPUT                = 0x1,                               /*!< Input buffer for the GPU          */RT_BUFFER_OUTPUT               = 0x2,                               /*!< Output buffer for the GPU         */RT_BUFFER_INPUT_OUTPUT         = RT_BUFFER_INPUT | RT_BUFFER_OUTPUT,/*!< Ouput/Input buffer for the GPU    */RT_BUFFER_PROGRESSIVE_STREAM   = 0x10,                              /*!< Progressive stream buffer         */
} RTbuffertype;/*! Buffer flags */
typedef enum
{RT_BUFFER_GPU_LOCAL            = 0x4,  /*!< An @ref RT_BUFFER_INPUT_OUTPUT has separate copies on each device that are not synchronized                               */RT_BUFFER_COPY_ON_DIRTY        = 0x8,  /*!< A CUDA Interop buffer will only be synchronized across devices when dirtied by @ref rtBufferMap or @ref rtBufferMarkDirty */RT_BUFFER_DISCARD_HOST_MEMORY  = 0x20, /*!< An @ref RT_BUFFER_INPUT for which a synchronize is forced on unmapping from host and the host memory is freed */RT_BUFFER_LAYERED              = 0x200000, /*!< Depth specifies the number of layers, not the depth of a 3D array */RT_BUFFER_CUBEMAP              = 0x400000, /*!< Enables creation of cubemaps. If this flag is set, Width must be equal to Height, and Depth must be six. If the @ref RT_BUFFER_LAYERED flag is also set, then Depth must be a multiple of six */
} RTbufferflag;

可以看到这些宏以及注释都能看到其中的含义。

在shade中的变量可以通过

context["result_buffer"]->set(output_buffer);

context["draw_color"]->setFloat(1.0f, 0.0f, 1.0f);

的语句进行设置。当执行context->launch时,则整个shader就会启动。这里要特别说明一下

        context->setPrintEnabled(true);context->setPrintBufferSize(4096);

这里是允许shade端出打印,配合shade中的tfPrintf来使用,语法和C语言的printf是一样的。当上面两句设置时,shade中的打印才会输出。注意由于是并行,shade中的打印输出的前后顺序是不确定的。类似于多线程。

【展示buffer】

当我们完成了对OptiX的主要设置与渲染之后,就剩下将输出缓存output_buffer展示在OPENGL当中了,关于OPENGL我们使用freeglut来简化操作,freeglut初始化了一个小窗口,并且在上面贴一张纹理,这里无特别奇特之处,但是尤其把output_buffer中的内容输入到gl中的texture2d当中需要特别说明一下。

首先要注意颜色空间,有些从[0, 255]代表黑白,有些从[0,1]代表黑白。本例我们有浮点数,一旦有浮点数就可以认为不是整数,认为是[0, 1]代表黑白。这在opengl中是sRGB颜色空间,因此需要使用下面的语句启用sRGB颜色空间:

    //假如是浮点数,则证明在SRGB颜色空间,也即每个颜色[0,1],与之对应的是[0, 255]GLboolean use_SRGB = GL_FALSE;glGetBooleanv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &use_SRGB);if (use_SRGB)glEnable(GL_FRAMEBUFFER_SRGB_EXT);

其次将OptiX buffer中的内容传递到主机端(CPU)有个简便的方法

    GLvoid* imageData = 0;imageData = output_buffer->map(0, RT_BUFFER_MAP_READ);

使用map方法则代表将buffer中的内容传送到了imageData当中。而从主机端将内容传送到服务端则可以使用unmap,可以先把数据拷贝到buffer中再unmap,拷贝是使用 memcpy( vertex_buffer->map(), tet.vertices, sizeof( tet.vertices ) );类似即可。

当执行完map后,数据就到了imageData当中,因为是float类型的RGBA,用户可以自己打印查看数据是否正确。

【代码下载】

链接:https://pan.baidu.com/s/1LpFdVgvCtk1f7WMhdp35Lg 
提取码:6vod 
下载解压后使用VS2015打开工程,并确保把工程调整至Debug和X64平台再编译运行:


http://chatgpt.dhexx.cn/article/4apeNEP0.shtml

相关文章

OptiX-7入门教程

OptiX是英伟达专为光线追踪打造的SDK&#xff0c;但是他的官方案例都比较复杂&#xff0c;包含了大量初始化相关的代码&#xff0c;初学容易一头雾水。 本人跟着Github上的optiX7course一步步学习才算入门。这个课程是Siggraph 2019/2020上的OptiX课程&#xff0c;有源码&…

optix入门

射线追踪是embarrassingly parallel/perfectly parallel/pleasingly parallel的问题&#xff0c;就是说基本不用费劲就可以并行化。 射线追踪是指从某点发射射线&#xff0c;判断其与几何结构的交点&#xff0c;根据交点对图像进行渲染&#xff0c;或者计算。 nvidia optix是基…

jwt *

目录 一、jwt出现的原因及工作原理 1. JWT是什么 2. 为什么使用JWT 3. JWT的工作原理 4、jwt解决不需要登录就能直接访问的问题&#xff1a; 解决登录后树形菜单未出现的问题 &#xff1a; 二、jwt工具类介绍&#xff0c;三种场景 1、jwt工具类 2、三种场景 三、jwt…

JWT JWT

JWT&#xff08;JSON WEB TOKEN&#xff09; JWT的组成 header&#xff08;头部&#xff09;&#xff1a;中主要存储了两个字段 alg&#xff0c;typ。 alg表示加密的算法默认&#xff08;HMAC SHA256&#xff09;&#xff0c;typ表示这个令牌的类型默认为JWT。 payload&#…

JWT__

文章目录 JWT什么是JWT&#xff1f;JWT能做什么&#xff1f;认证流程JWT的结构是什么&#xff1f;使用代码要做一个JWT的例子引入pom依赖生成一个Token令牌验证令牌并从令牌中取出信息 JWT 什么是JWT&#xff1f; 官网地址:https://jwt.io/introduction/ 官方文档 JSON Web T…

JWT 和 JJWT 还傻傻的分不清吗

JWTs是JSON对象的编码表示。JSON对象由零或多个名称/值对组成&#xff0c;其中名称为字符串&#xff0c;值为任意JSON值。 JWT有助于在clear(例如在URL中)发送这样的信息&#xff0c;可以被信任为不可读(即加密的)、不可修改的(即签名)和URL - safe(即Base64编码的)。 JSON W…

【编码实战】2022年还在用jjwt操作jwt?,推荐你使用nimbus-jose-jwt,爽到飞起~

什么是nimbus-jose-jwt&#xff1f; nimbus-jose-jwt是基于Apache2.0开源协议的JWT开源库&#xff0c;支持所有的签名(JWS)和加密(JWE)算法。 对于JWT、JWS、JWE介绍 JWT是一种规范&#xff0c;它强调了两个组织之间传递安全的信息JWS是JWT的一种实现&#xff0c;包含三部分hea…

什么是JWT??

一、什么是JWT JWT(JSON WEB TOKEN)&#xff0c;通过数字签名的方式&#xff0c;以json对象为载体&#xff0c;在不同的服务终端之间安全的传输信息&#xff0c;用来解决传统session的弊端。 JWT在前后端分离系统&#xff0c;或跨平台系统中&#xff0c;通过JSON形式作为WEB应用…

JJWT实现令牌Token

登录实现方式 Session 详情&#xff1a; https://www.cnblogs.com/andy-zhou/p/5360107.html 会话的概念 会话就好比打电话&#xff0c;一次通话可以理解为一次会话。我们登录一个网站&#xff0c;在一个网站上不同的页面浏览&#xff0c;最后退出这个网站&#xff0c;也是…

3.JJWT

目录 1.JWT简介 2.JWT的结构 3.基于服务器的传统身份认证 4.基于token的身份认证 5. JWT的优势 6.Java中使用JJWT实现JWT 1.JWT简介 Json web token (JWT)&#xff0c; 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519)。该token被设计为紧凑且安全的…

JWT技术

JWT 一、 JWT 实现无状态 Web 服务 1、什么是有状态 有状态服务&#xff0c;即服务端需要记录每次会话的客户端信息&#xff0c;从而识别客户端身份&#xff0c;根据用户身份进行请求的处理&#xff0c;典型的设计如tomcat中的session。 例如登录&#xff1a;用户登录后&am…

token学习笔记(JWT、jjwt的使用及案例实现)

文章目录 1. 首先、了解什么是会话2. 会话跟踪的主要技术3. Token 令牌学习3.1 流程图3.2 token3.3 JWT(JSON web Tokens)Json web 令牌(规范)3.4 JWT结构3.5 JWT需要的依赖3.6 JWT的获取与验证流程3.7JWT的使用方式3.8 jjwt的使用&#xff08;创建JWT方式&#xff09;1. jjwt需…

JWT 进阶 -- JJWT

###jwt是什么? JWTs是JSON对象的编码表示。JSON对象由零或多个名称/值对组成&#xff0c;其中名称为字符串&#xff0c;值为任意JSON值。JWT有助于在clear(例如在URL中)发送这样的信息&#xff0c;可以被信任为不可读(即加密的)、不可修改的(即签名)和URL - safe(即Base64编码…

JWT详解和使用(jjwt)

JWT详解和使用 JWT是啥 JWT&#xff08;JSON Web Token&#xff09;是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 下列场景中使用…

JJWT 实现JWT

1什么是JJWT JJWT 是一个提供端到端的 JWT 创建和验证的 Java 库。永远免费和开源 (Apache License&#xff0c;版本2.0)&#xff0c;JJWT 很容易使用和理解。它被设计成一个以建筑为中心的流畅界面&#xff0c;隐藏了它的大部分复杂性。 2JJWT快速入门 2.1token的创建 2.1…

什么是JWT?

在HTTP接口调用的时候&#xff0c;服务端经常需要对调用方做认证&#xff0c;以保证安全性。一种常见的认证方式是使用JWT(Json Web Token)&#xff0c;采用这种方式时&#xff0c;经常在header传入一个authorization字段&#xff0c;值为对应的jwt_token&#xff0c;或者也有图…

JWT的学习和JJWT的使用

1.什么是JWT JWT&#xff08;JSON Web Token&#xff09;是一个开放的行业标准&#xff0c;它定义了一种简介的&#xff0c;自包含的协议格式&#xff0c;用于在通信双方传递json对象&#xff0c;传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或者使用RSA的公…

JWT详解

本文从本人博客搬运&#xff0c;原文格式更加美观&#xff0c;可以移步原文阅读&#xff1a;JWT详解 JWT简介 1.什么是JWT 在介绍JWT之前&#xff0c;我们先来回顾一下利用token进行用户身份验证的流程&#xff1a; 客户端使用用户名和密码请求登录服务端收到请求&#xff…

JWT详解、JJWT使用、token 令牌

前言 在正式讲解JWT之前&#xff0c;我们先重温一下用户身份认证相关的一些概念&#xff1a; 有状态登录&#xff08;session认证&#xff09; 服务器当中记录每一次的登录信息&#xff0c;从而根据客户端发送的数据来判断登录过来的用户是否合法。 缺点&#xff1a; 每个用…

Java的JJWT实现JWT

1 什么是 JJWT JJWT 是一个提供端到端的 JWT 创建和验证的 Java 库。永远免费和开源 (Apache License&#xff0c;版本2.0)&#xff0c;JJWT 很容易使用和理解。它被设计成一个以建筑为中心的流畅界面&#xff0c;隐藏了它的大部分复杂性。 2 token 的创建 2.1 引入依赖 &l…