Cuda By Example - 7 (光线追踪)

第6章以实现简单的光线追踪为例子,引入了Constant Memory和性能测量方法。

Constant Memory

NVIDIA的硬件提供了64K的constant只读内存。定义constant内存的变量,使用关键字__constant__。从constant内存里读取出来的数据,可以缓存起来,后面代码读取相同地址的数据时,不用再从constant里读;另外一个线程读回的数据,会广播给其他的线程,其他线程不用再去读取。所有这些都由Cuda完成,不需要程序员去干预。

只是要记住:

1.用来存放只读数据

2.保证各个线程访问相同的地址,否则不仅不能提高性能,还会适得其反。

光线追踪

光线追踪是另一种将三维场景显示到二维图像的方法。对于图像的像素,我们可以看作是三维场景中物体发出光照射到这里产生的。循着光线的来路,反过来,找到光线与场景中物体的交点,以此物体在此点的颜色为基础,得出像素的颜色。书中的例子建立一个场景,场景由20个球体组成,假设图像垂直于Z轴,这样所有的光线就平行于Z轴,由像素(ox,oy)点出来的光线在整个空间,它的x,y轴的坐标都为(ox, oy)。设球心的坐标为(x, y, z),球的半径为r。光线会否跟球体相交,只需检查光线是否穿过球体垂直于Z轴,过球心的圆截面。计算在此截面上,(ox, oy)与圆心(x, y)的距离,小于球的半径,说明光线会穿过截面,跟球体相交。交点Z坐标计算,由球方程

(ox-x)^{2} + (oy-y)^{2}+(oz-z)^{2} = r^{2}

可以很容易得出。之所以要计算出交点Z轴坐标,是为了判断交点跟像素点的距离。这里再次体现了选择图像跟Z轴垂直带来的便利,交点Z轴坐标就可以用来判断它与图像平面的距离。显然除非刚好擦着边过去,否则如果相交,一束光线跟球的交点往往会有两个,但像素只取离图像平面近的那个交点。

球体建模

书中例子定义了结构Sphere。

#define INF     2e10fstruct Sphere {float   r,b,g;float   radius;float   x,y,z;__device__ float hit( float ox, float oy, float *n ) {float dx = ox - x;float dy = oy - y;if (dx*dx + dy*dy < radius*radius) {float dz = sqrtf( radius*radius - dx*dx - dy*dy );*n = dz / sqrtf( radius * radius );return dz + z;}return -INF;}
};

__device__ float hit( float ox, float oy, float *n) 的参数ox, oy为图像中像素点的坐标。参数n为一个浮点数指针,如果来自(ox,oy)的光线与球面相交,则n返回交点在Z方向与球心的距离与球半径的比值,返回的n将跟球体的颜色一起,参与像素点颜色的计算。函数的返回值为交点的z坐标(Z轴垂直于纸面往外,坐标值越大,距离图像越近)。若没有相交,则返回负无穷大。

场景生成

在main函数中,先在主机端生成20个球体,球体的位置,颜色和半径大小随机产生,然后传递到s指向的GPU内存,GPU使用光线追踪算法生成的图像则由_dev_bitmap保存。它们都是由cudaMalloc分配的GPU全局内存,还没有用到这一章引入的constant内存。

...
#define DIM 1024
#define rnd( x ) (x * rand() / RAND_MAX)
#define SPHERES 20...// globals needed by the update routine
struct DataBlock {unsigned char   *dev_bitmap;Sphere          *s;
};int main( void ) {DataBlock   data;CPUBitmap bitmap( DIM, DIM, &data );unsigned char   *dev_bitmap;Sphere          *s;// allocate memory on the GPU for the output bitmapHANDLE_ERROR( cudaMalloc( (void**)&dev_bitmap,bitmap.image_size() ) );// allocate memory for the Sphere datasetHANDLE_ERROR( cudaMalloc( (void**)&s,sizeof(Sphere) * SPHERES ) );// allocate temp memory, initialize it, copy to// memory on the GPU, then free our temp memorySphere *temp_s = (Sphere*)malloc( sizeof(Sphere) * SPHERES );for (int i=0; i<SPHERES; i++) {temp_s[i].r = rnd( 1.0f );temp_s[i].g = rnd( 1.0f );temp_s[i].b = rnd( 1.0f );temp_s[i].x = rnd( 1000.0f ) - 500;temp_s[i].y = rnd( 1000.0f ) - 500;temp_s[i].z = rnd( 1000.0f ) - 500;temp_s[i].radius = rnd( 100.0f ) + 20;}HANDLE_ERROR( cudaMemcpy( s, temp_s,sizeof(Sphere) * SPHERES,cudaMemcpyHostToDevice ) );free( temp_s );
...

内核函数

x,y和offset的计算,我们前面已经很熟悉了。ox,oy 由 x,y减去DIM/2是为了把Z轴移到图像的中心。

内核函数遍历场景中的20个球体,检查是否与线程负责的像素点的反向光线相交。在所有的交点中,只取离图像最近的交点的颜色。调用球体的hit函数,如果返回的值大于当前的maxz,则认为当前点更近,使用当前球体的颜色,重新计算像素点颜色。完成遍历后,将之存入ptr指向的内存。

__global__ void kernel( Sphere *s, unsigned char *ptr ) {// map from threadIdx/BlockIdx to pixel positionint x = threadIdx.x + blockIdx.x * blockDim.x;int y = threadIdx.y + blockIdx.y * blockDim.y;int offset = x + y * blockDim.x * gridDim.x;float   ox = (x - DIM/2);float   oy = (y - DIM/2);float   r=0, g=0, b=0;float   maxz = -INF;for(int i=0; i<SPHERES; i++) {float   n;float   t = s[i].hit( ox, oy, &n );if (t > maxz) {float fscale = n;r = s[i].r * fscale;g = s[i].g * fscale;b = s[i].b * fscale;maxz = t;}} ptr[offset*4 + 0] = (int)(r * 255);ptr[offset*4 + 1] = (int)(g * 255);ptr[offset*4 + 2] = (int)(b * 255);ptr[offset*4 + 3] = 255;
}

继续前面main剩下的部分

...   // generate a bitmap from our sphere datadim3    grids(DIM/16,DIM/16);dim3    threads(16,16);kernel<<<grids,threads>>>( s, dev_bitmap );// copy our bitmap back from the GPU for displayHANDLE_ERROR( cudaMemcpy( bitmap.get_ptr(), dev_bitmap,bitmap.image_size(),cudaMemcpyDeviceToHost ) );HANDLE_ERROR( cudaFree( dev_bitmap ) );HANDLE_ERROR( cudaFree( s ) );// displaybitmap.display_and_exit();
}

Global内存版完整代码

#include "../common/book.h"
#include "../common/cpu_bitmap.h"#define DIM 1024#define rnd( x ) (x * rand() / RAND_MAX)
#define INF     2e10fstruct Sphere {float   r,b,g;float   radius;float   x,y,z;__device__ float hit( float ox, float oy, float *n ) {float dx = ox - x;float dy = oy - y;if (dx*dx + dy*dy < radius*radius) {float dz = sqrtf( radius*radius - dx*dx - dy*dy );*n = dz / sqrtf( radius * radius );return dz + z;}return -INF;}
};
#define SPHERES 20__global__ void kernel( Sphere *s, unsigned char *ptr ) {// map from threadIdx/BlockIdx to pixel positionint x = threadIdx.x + blockIdx.x * blockDim.x;int y = threadIdx.y + blockIdx.y * blockDim.y;int offset = x + y * blockDim.x * gridDim.x;float   ox = (x - DIM/2);float   oy = (y - DIM/2);float   r=0, g=0, b=0;float   maxz = -INF;for(int i=0; i<SPHERES; i++) {float   n;float   t = s[i].hit( ox, oy, &n );if (t > maxz) {float fscale = n;r = s[i].r * fscale;g = s[i].g * fscale;b = s[i].b * fscale;maxz = t;}} ptr[offset*4 + 0] = (int)(r * 255);ptr[offset*4 + 1] = (int)(g * 255);ptr[offset*4 + 2] = (int)(b * 255);ptr[offset*4 + 3] = 255;
}// globals needed by the update routine
struct DataBlock {unsigned char   *dev_bitmap;Sphere          *s;
};int main( void ) {DataBlock   data;CPUBitmap bitmap( DIM, DIM, &data );unsigned char   *dev_bitmap;Sphere          *s;// allocate memory on the GPU for the output bitmapHANDLE_ERROR( cudaMalloc( (void**)&dev_bitmap,bitmap.image_size() ) );// allocate memory for the Sphere datasetHANDLE_ERROR( cudaMalloc( (void**)&s,sizeof(Sphere) * SPHERES ) );// allocate temp memory, initialize it, copy to// memory on the GPU, then free our temp memorySphere *temp_s = (Sphere*)malloc( sizeof(Sphere) * SPHERES );for (int i=0; i<SPHERES; i++) {temp_s[i].r = rnd( 1.0f );temp_s[i].g = rnd( 1.0f );temp_s[i].b = rnd( 1.0f );temp_s[i].x = rnd( 1000.0f ) - 500;temp_s[i].y = rnd( 1000.0f ) - 500;temp_s[i].z = rnd( 1000.0f ) - 500;temp_s[i].radius = rnd( 100.0f ) + 20;}HANDLE_ERROR( cudaMemcpy( s, temp_s,sizeof(Sphere) * SPHERES,cudaMemcpyHostToDevice ) );free( temp_s );// generate a bitmap from our sphere datadim3    grids(DIM/16,DIM/16);dim3    threads(16,16);kernel<<<grids,threads>>>( s, dev_bitmap );// copy our bitmap back from the GPU for displayHANDLE_ERROR( cudaMemcpy( bitmap.get_ptr(), dev_bitmap,bitmap.image_size(),cudaMemcpyDeviceToHost ) );HANDLE_ERROR( cudaFree( dev_bitmap ) );HANDLE_ERROR( cudaFree( s ) );// displaybitmap.display_and_exit();
}

运行所得的图片:

使用Constant内存

由constant内存特性,在光线追踪的例子中,适合将场景数据放入其中。各个球体数据在生成之后不会再变,而且各个线程都需要访问。

constant 内存变量不是通过cudaMalloc来分配的,它需要申明为一个静态全局变量,申明如下:

__constant__ Sphere s[SPHERES];

那么如何把CPU在main函数中生成的球体数据传递到constant内存呢。CUDA提供cudaMemcpyToSymbol函数。

    HANDLE_ERROR( cudaMemcpyToSymbol( s, temp_s, 
                                sizeof(Sphere) * SPHERES) );

而内核函数只保留了图像像素指针,GPU的constant内存变量s为全局变量,不许作为函数参数传入。

__global__ void kernel( unsigned char *ptr ) {// map from threadIdx/BlockIdx to pixel positionint x = threadIdx.x + blockIdx.x * blockDim.x;int y = threadIdx.y + blockIdx.y * blockDim.y;int offset = x + y * blockDim.x * gridDim.x;float   ox = (x - DIM/2);float   oy = (y - DIM/2);float   r=0, g=0, b=0;float   maxz = -INF;for(int i=0; i<SPHERES; i++) {float   n;float   t = s[i].hit( ox, oy, &n );if (t > maxz) {float fscale = n;r = s[i].r * fscale;g = s[i].g * fscale;b = s[i].b * fscale;maxz = t;}} ptr[offset*4 + 0] = (int)(r * 255);ptr[offset*4 + 1] = (int)(g * 255);ptr[offset*4 + 2] = (int)(b * 255);ptr[offset*4 + 3] = 255;
}

Constant内存完整代码

#include "../common/book.h"
#include "../common/cpu_bitmap.h"#define DIM 1024#define rnd( x ) (x * rand() / RAND_MAX)
#define INF     2e10fstruct Sphere {float   r,b,g;float   radius;float   x,y,z;__device__ float hit( float ox, float oy, float *n ) {float dx = ox - x;float dy = oy - y;if (dx*dx + dy*dy < radius*radius) {float dz = sqrtf( radius*radius - dx*dx - dy*dy );*n = dz / sqrtf( radius * radius );return dz + z;}return -INF;}
};
#define SPHERES 20__constant__ Sphere s[SPHERES];__global__ void kernel( unsigned char *ptr ) {// map from threadIdx/BlockIdx to pixel positionint x = threadIdx.x + blockIdx.x * blockDim.x;int y = threadIdx.y + blockIdx.y * blockDim.y;int offset = x + y * blockDim.x * gridDim.x;float   ox = (x - DIM/2);float   oy = (y - DIM/2);float   r=0, g=0, b=0;float   maxz = -INF;for(int i=0; i<SPHERES; i++) {float   n;float   t = s[i].hit( ox, oy, &n );if (t > maxz) {float fscale = n;r = s[i].r * fscale;g = s[i].g * fscale;b = s[i].b * fscale;maxz = t;}} ptr[offset*4 + 0] = (int)(r * 255);ptr[offset*4 + 1] = (int)(g * 255);ptr[offset*4 + 2] = (int)(b * 255);ptr[offset*4 + 3] = 255;
}// globals needed by the update routine
struct DataBlock {unsigned char   *dev_bitmap;
};int main( void ) {DataBlock   data;CPUBitmap bitmap( DIM, DIM, &data );unsigned char   *dev_bitmap;// allocate memory on the GPU for the output bitmapHANDLE_ERROR( cudaMalloc( (void**)&dev_bitmap,bitmap.image_size() ) );// allocate temp memory, initialize it, copy to constant// memory on the GPU, then free our temp memorySphere *temp_s = (Sphere*)malloc( sizeof(Sphere) * SPHERES );for (int i=0; i<SPHERES; i++) {temp_s[i].r = rnd( 1.0f );temp_s[i].g = rnd( 1.0f );temp_s[i].b = rnd( 1.0f );temp_s[i].x = rnd( 1000.0f ) - 500;temp_s[i].y = rnd( 1000.0f ) - 500;temp_s[i].z = rnd( 1000.0f ) - 500;temp_s[i].radius = rnd( 100.0f ) + 20;}HANDLE_ERROR( cudaMemcpyToSymbol( s, temp_s, sizeof(Sphere) * SPHERES) );free( temp_s );// generate a bitmap from our sphere datadim3    grids(DIM/16,DIM/16);dim3    threads(16,16);kernel<<<grids,threads>>>( dev_bitmap );// copy our bitmap back from the GPU for displayHANDLE_ERROR( cudaMemcpy( bitmap.get_ptr(), dev_bitmap,bitmap.image_size(),cudaMemcpyDeviceToHost ) );HANDLE_ERROR( cudaFree( dev_bitmap ) );// displaybitmap.display_and_exit();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/452373.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Ubuntu18.04命令行code打不开】可能的解决方法

目录 问题&#xff1a;命令行code打不开文件尝试① kimi是这么说的② sudo apt-get install apparmor apparmor_utils③ 在混沌的操作完以上一通后&#xff0c;sudo apt-get install snapd 我试了将近一个小时 : ( so depressed 我只是想用vscode打开个文件夹&#xff0c;我甚至…

Leetcode 1129. 颜色交替的最短路径

1.题目基本信息 1.1.题目描述 给定一个整数 n&#xff0c;即有向图中的节点数&#xff0c;其中节点标记为 0 到 n – 1。图中的每条边为红色或者蓝色&#xff0c;并且可能存在自环或平行边。 给定两个数组 redEdges 和 blueEdges&#xff0c;其中&#xff1a; redEdges[i] …

从零入门AI篡改图片检测(金融场景)#Datawhale十月组队学习

1.大赛背景 在全球人工智能发展和治理广受关注的大趋势下&#xff0c;由中国图象图形学学会、蚂蚁集团、云安全联盟CSA大中华区主办&#xff0c;广泛联合学界、机构共同组织发起全球AI攻防挑战赛。本次比赛包含攻防两大赛道&#xff0c;分别聚焦大模型自身安全和大模型生成内容…

安装好的 Nginx 增加 nginx-module-vts 模块

目录 1. nginx-module-vts 准备 2.查看已安装的的 nginx 编译参数 3. 重新编译 nginx 添加 nginx-module-vts 模块 4. 验证 1. nginx-module-vts 准备 # 解压 unzip nginx-module-vts-master.zip # 将解压包移动到/usr/local/目录 mv nginx-module-vts-master /usr/local/ …

jmeter响应断言放进csv文件遇到的问题

用Jmeter的json 断言去测试http请求响应结果&#xff0c;发现遇到中文时出现乱码&#xff0c;导致无法正常进行响应断言&#xff0c;很影响工作。于是&#xff0c;察看了其他测试人员的解决方案&#xff0c;发现是jmeter本身对编码格式的设置导致了这一问题。解决方案是在jmete…

轻松应对PDF编辑难题:四款免费pdf编辑器实测体验

作为一名办公室文员&#xff0c;每天处理各种文件是家常便饭。而PDF文档因其格式稳定、不易篡改的特性&#xff0c;在工作中扮演着重要角色。但编辑PDF文件却不像编辑Word文档那样简单&#xff0c;这就需要一款得心应手的PDF编辑器。今天&#xff0c;我就来分享一下我使用过的几…

如何利用解析器绕过访问控制

0x01 前言 每年blackhat总是会有一些新奇的攻击思路值得大家学习&#xff0c;在2024年blackhat的议题中发现一篇很有意思的文章&#xff0c;作者提出了一套基于邮箱的欺骗攻击思路&#xff0c;利用RFC标准中对SMTP协议中邮箱地址的特性&#xff0c;提供一系列绕过技巧&#xf…

揭秘Map与Set的键值奥秘与集合魅力,解锁高效数据魔法

文章目录 前言➰一、关联式容器1.1 关联式容器的概述1.2 关联式容器的工作原理1.3 关联式容器的核心特性 ➰二、键值对2.1 键值对的基本概念2.2 键值对在C中的实现 ➰三、树形结构的关联式容器3.1 树形结构的特点3.2 使用场景 ➰四、set的使用与定义4.1 set的基本特性4.2 set的…

Flutter UI组件库(JUI)

Flutter UI组件库 (JUI) 介绍 您是否正在寻找一种方法来简化Flutter开发过程&#xff0c;并创建美观、一致的用户界面&#xff1f;您的搜索到此为止&#xff01;我们的Flutter UI组件库&#xff08;JUI&#xff09;提供了广泛的预构建、可自定义组件&#xff0c;帮助您快速构建…

RHCE--ntp客户端,时间服务器服务端

NTP 是网络时间协议&#xff08; Network Time Protocol &#xff09;的简称&#xff0c;通过 udp 123 端口进行网络时钟同步。 Chrony 是一个开源自由的网络时间协议 NTP 的客户端和服务器软件。它能让计算机保持系统时钟与时钟服务器&#xff08; NTP &#xff09;同步&#…

计算机网络:数据链路层 —— 以太网(Ethernet)

文章目录 局域网局域网的主要特征 以太网以太网的发展100BASE-T 以太网物理层标准 吉比特以太网载波延伸物理层标准 10吉比特以太网汇聚层交换机物理层标准 40/100吉比特以太网传输媒体 局域网 局域网&#xff08;Local Area Network, LAN&#xff09;是一种计算机网络&#x…

基于SSM果蔬经营系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商品信息管理&#xff0c;类型管理&#xff0c;系统管理&#xff0c;订单管理 前台账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品信息&#xff0c;广告…

爬虫+数据保存

爬虫以及数据保存 这篇文章, 分享如何将爬虫爬到的数据, 保存到excel表格当中。 文章目录 1.安装保存数据的第三方库openpyxl并使用 2.爬虫加单表数据保存 3.爬虫加多表数据保存 4.实战 一、安装保存数据的第三方库openpyxl并使用 我们需要安装openpyxl的第三方库 安装…

Qt第十三天:网络编程:TCP和UDP的使用

我发现了有些人喜欢静静看博客不聊天呐&#xff0c; 但是ta会点赞。 这样的人呢帅气低调有内涵&#xff0c; 美丽大方很优雅。 说的就是你&#xff0c; 不用再怀疑哦 ❤️TCP&#xff1a; 一、创建项目&#xff0c;命名为Server&#xff0c;继承QWidget 二、添加Qt设计师…

CentOS7安装RabbitMQ-3.13.7、修改端口号

本文安装版本&#xff1a; Erlang&#xff1a;26.0 官网下载地址 Erlang RabbitMQ&#xff1a;3.13.7 官网下载地址 RabbitMQ RabbitMQ和Erlang对应关系查看&#xff1a;https://www.rabbitmq.com/which-erlang.html 注&#xff1a;安装erlang之前先安装下依赖文件&#xff0…

云黑系统全解无后门 +搭建教程

这套系统呢是玖逸之前南逸写的一套云黑系统&#xff0c;功能带有卡密生成和添加黑名单等&#xff0c;源码放在我的网盘里已经两年之久&#xff0c;由于玖逸现在已经跑路了所以现在发出来分享给大家&#xff0c;需要的可以自己拿去而开&#xff0c;反正功能也不是很多具体的自己…

Teledyne LeCroy:800G高速以太网一站式自动化测试解决方案(网络打流测试+物理层加压干扰+协议分析)

LinkExpert一站式测试解决方案 LinkExpert 是一款软件应用程序&#xff0c;可对Teledyne LeCroy的协议分析仪和训练器进行自动化硬件控制和管理。除了作为合规性、一致性和验证测试的便捷接口外&#xff0c;它还能轻松地将这些测试添加到自动回归测试流程中。 现在&#xff0c;…

WPF基础权限系统

一.开发环境 VisualStudio 2022NET SDK 8.0Prism 版本 8.1.97Sqlite 二. 功能介绍 WPF 基础权限系统&#xff0c;是一个支持前后端分离设计的 客户端(C/S)项目&#xff0c;该示例项目前端xaml使用UI库 &#xff0c;Material Design Themes UI 来构建用户界面&#xff0c;确保…

C# -- Abstract、Virtual、interface

一、Virtual方法&#xff08;虚方法&#xff09; 1&#xff09;virtual 关键字用于在基类&#xff08;父类&#xff09;中修饰方法 2&#xff09;基类中定义了virtual方法&#xff0c;派生类中使用override重写该方法 二、Abstract方法&#xff08;抽象方法&#xff09; 1&…

【ssh】Mac 使用 ssh 连接阿里云报错:Connection reset by 8.155.1.xxx port 22

Mac 使用 ssh 连接阿里云报错&#xff1a;Connection reset by 8.155.1.xxx port 22 问题描述解决办法 问题描述 Connection reset by 8.155.1.xxx port 22解决办法 关掉代理 VPN