笔者测试环境VS2019。

基本介绍

原书作者引入Julia Sets意在使用GPU加速图形的绘制。Julia Set 是指满足下式迭代收敛的复数集合

\[Z_{n+1}=Z_{n}^2+C
\]

环境配置

跑这个例子的主要困难应该在于配置环境。这个程序依赖于openGL中的glut库。由于VS2019的整个软件架构发生了很大变化,一些链接库和头文件的位置都发生了改变,因此一些文章中的配置方法失效了。

首先我们需要获取glut库的头文件以及动态链接库。

点击这里cg-toolkit获取。安装成功之后,找到C:\Program Files (x86)\NVIDIA Corporation\Cg。注意勾选安装选项的x64相关应用。

将其中的lib文件夹中的_glut32.lib_复制到C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\ucrt\x86

将其中的lib.x64文件夹中的glut32.lib复制到C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\ucrt\x64并且重命名其为glut64.lib

笔者运行的是64位系统,就将bin.x64中的_glut32.dll_复制到C:\Windows\System32下

在这里下载头文件。下载完成之后,将头文件拷贝到C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt。并建立文件夹GL把它们包括起来。

提示,核心是找到C:\Program Files (x86)\Windows Kits\10,不要在Microsoft Visual Studio文件夹里浪费时间。

后面的10.0.18362.0根据版本不同可能不一致,具体问题具体分析

这个代码还需要一些别的头文件。如gl_helper.h, book.h, cpu_bitmap.h 等 在这里下载后复制到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\include

主要代码

CPU Julia Set

RGBA模式中,每一个像素会保存以下数据:R值(红色分量)、G值(绿色分量)、B值(蓝色分量)和A值(alpha分量)。其中红、绿、蓝三种颜色相组合,就可以得到我们所需要的各种颜色,而alpha不直接影响颜色,它的含义是透明度。1

下面是纯粹CPU中的代码,基本的注释在代码中

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "device_functions.h"
#include "device_atomic_functions.h"
#include <cuda.h>
#include "book.h"
#include <cpu_bitmap.h>
#include <stdio.h> #define DIM 1000 //图像的像素边长大小 struct cuComplex
{
float r;
float i;
cuComplex(float a, float b) : r(a), i(b) {}
float magnitude2() { return r * r + i * i; } //计算复数的模值
cuComplex operator* (const cuComplex& a)
{
return cuComplex(r * a.r - i * a.i, i * a.r + r * a.i);
}
cuComplex operator+ (const cuComplex& a)
{
return cuComplex(r + a.r, i + a.i);
}
}; int julia(int x, int y)
{
const float scale = 1.5; //放大倍率
float jx = scale * (float)(DIM / 2 - x) / (DIM / 2); //坐标变换,投影到-1~1scale
float jy = scale * (float)(DIM / 2 - y) / (DIM / 2);
cuComplex c(-0.8, 0.156); //基数
cuComplex a(jx, jy);
int i = 0;
for (i = 0; i < 200; i++) //迭代
{
a = a * a + c;
if (a.magnitude2() > 1000)
return 0;
}
return 1;
} void kernel(unsigned char* ptr)
{
for (int y = 0; y < DIM; y++) //遍历整个bitmap
{
for (int x = 0; x < DIM; x++)
{
int offset = x + y * DIM;
int juliaValue = julia(x, y);
//注意openGL这里的颜色格式是RGBA,000为黑色
ptr[offset * 4 + 0] = 255 * juliaValue;
ptr[offset * 4 + 1] = 0;
ptr[offset * 4 + 2] = 0;
ptr[offset * 4 + 3] = 255;
}
}
} int main()
{
CPUBitmap bitmap(DIM, DIM);
unsigned char* ptr = bitmap.get_ptr();
kernel(ptr); //运行渲染
bitmap.display_and_exit();
}

GPU Julia Set

注意由于内核函数是global的,要在GPU上运行需要将其调用的julia函数加上device。又因为,device函数只能由device函数或者global函数调用,所以最好把结构体中的所有函数都加上device。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "device_functions.h"
#include "device_atomic_functions.h"
#include <cuda.h>
#include "book.h"
#include <cpu_bitmap.h>
#include <stdio.h> //小于65536
#define DIM 1000 //图像的像素边长大小 struct cuComplex
{
float r;
float i;
__device__ cuComplex(float a, float b) : r(a), i(b) {}
__device__ float magnitude2() { return r * r + i * i; } //计算复数的模值
__device__ cuComplex operator* (const cuComplex& a)
{
return cuComplex(r * a.r - i * a.i, i * a.r + r * a.i);
}
__device__ cuComplex operator+ (const cuComplex& a)
{
return cuComplex(r + a.r, i + a.i);
}
}; __device__ int julia(int x, int y)
{
const float scale = 1.5; //放大倍率
float jx = scale * (float)(DIM / 2 - x) / (DIM / 2); //坐标变换,投影到-1~1scale
float jy = scale * (float)(DIM / 2 - y) / (DIM / 2);
cuComplex c(-0.8, 0.156); //基数
cuComplex a(jx, jy);
int i = 0;
for (i = 0; i < 200; i++) //迭代
{
a = a * a + c;
if (a.magnitude2() > 1000)
return 0;
}
return 1;
} __global__ void kernel(unsigned char* ptr)
{
int x = blockIdx.x; //纵向线程索引(x方向朝右,是行)
int y = blockIdx.y; //纵向线程索引(y方向朝下,是列)
int offset = x + y * gridDim.x;
int juliaValue = julia(x, y);
ptr[offset * 4 + 0] = 255 * juliaValue;
ptr[offset * 4 + 1] = 0;
ptr[offset * 4 + 2] = 0;
ptr[offset * 4 + 3] = 255;
} int main()
{
CPUBitmap bitmap(DIM, DIM);
unsigned char* dev_bitmap;
//在GPU中分配空间
HANDLE_ERROR(cudaMalloc((void**)&dev_bitmap, bitmap.image_size()));
dim3 grid(DIM, DIM); //dim3结构体
kernel <<<grid, 1 >>> (dev_bitmap); //一个线程块中的线程网络1000x1000
HANDLE_ERROR(cudaMemcpy(bitmap.get_ptr(), dev_bitmap, bitmap.image_size(), cudaMemcpyDeviceToHost)); //将dev_bitmap中的内容从device拷贝到cpu中
bitmap.display_and_exit();
HANDLE_ERROR(cudaFree(dev_bitmap));
}


参考资料

详解 CUDA By Example 中的 Julia Set 绘制GPU优化的更多相关文章

  1. Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  2. Scala 深入浅出实战经典 第60讲:Scala中隐式参数实战详解以及在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  3. 详解OJ(Online Judge)中PHP代码的提交方法及要点【举例:ZOJ 1001 (A + B Problem)】

    详解OJ(Online Judge)中PHP代码的提交方法及要点 Introduction of How to submit PHP code to Online Judge Systems  Int ...

  4. 详解Linux下iptables中的DNAT与SNAT设置(转)

    详解Linux下iptables中的DNAT与SNAT设置 这篇文章主要介绍了Linux下iptables中的DNAT与SNAT设置,是Linux网络配置中的基础知识,需要的朋友可以参考下   原文连 ...

  5. Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制

    Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...

  6. 详解在Linux系统中安装Tomcat

    本文以在CentOS 7.6中安装Tomcat8.5为例进行安装,其他系统和版本都是大同小异的. 安装JDK 安装Tomcat之前,需要先安装JDK,可以参看之前的文章详解在Linux系统中安装JDK ...

  7. 详解如何在CentOS7中使用Nginx和PHP7-FPM安装Nextcloud

    转载地址:https://www.jb51.net/article/109382.htm 这篇文章主要介绍了详解如何在CentOS7中使用Nginx和PHP7-FPM安装Nextcloud,会通过 N ...

  8. 详解如何在Laravel中增加自定义全局函数

    http://www.php.cn/php-weizijiaocheng-383928.html 如何在Laravel中增加自定义全局函数?在我们的应用里经常会有一些全局都可能会用的函数,我们应该怎么 ...

  9. 第7.18节 案例详解:Python类中装饰器@staticmethod定义的静态方法

    第7.18节 案例详解:Python类中装饰器@staticmethod定义的静态方法 上节介绍了Python中类的静态方法,本节将结合案例详细说明相关内容. 一.    案例说明 本节定义了类Sta ...

随机推荐

  1. 使用canvas制作五子棋游戏

    要制作JS五子棋的话我们可以一开始来理清一下思路,这样对我们后来的编程是有好处的 1.棋盘使用canvas制作.canvas用来做这种不用太过复杂的图形的时候是很有用处的,下图是我制作的一个五子棋棋盘 ...

  2. 详解c++中对二维数组下标[][]的重载

    首先定义一个矩阵类,我用一个二维数组存储矩阵中的数据,矩阵详细定义如下 class Matrix { public: Matrix(int rows, int cols) { _rows = rows ...

  3. [Effective Java 读书笔记] 第二章 创建和销毁对象 第一条

    第二章  创建和销毁对象 第一条 使用静态工厂方法替代构造器,原因: 静态工厂方法可以有不同的名字,也就是说,构造器只能通过参数的不同来区分不同的目的,静态工厂在名字上就能表达不同的目的 静态工厂方法 ...

  4. windows10 CTCP

    windows上除了普通tcp之外,有一个CTCP. 据说可以提升长延时情况下的吞吐量. win7可以方便的通过netsh int tcp set global congestionprovider= ...

  5. [WPF 自定义控件]在MenuItem上使用RadioButton

    1. 需求 上图这种包含多选(CheckBox)和单选(RadioButton)的菜单十分常见,可是在WPF中只提供了多选的MenuItem.顺便一提,要使MenuItem可以多选,只需要将MenuI ...

  6. 【算法总结】图论/dp-动态规划 大总结

    写于一只蹲在角落的蒟蒻-Z__X... 2020.2.7,图论和 \(dp\) 终于告一段落.蓦然回首,好似已走过许多...不曾细细品味,太多太多又绵延不断地向我涌来... 谨以此纪念 逝去 的图论和 ...

  7. 4.【Spring Cloud Alibaba】服务容错-sentinel

    雪崩效应 常见容错方案 超时 限流 仓壁模式 断路器模式 断路器三态转换 使用Sentinel实现容错 什么是Sentinel https://github.com/alibaba/Sentinel ...

  8. C语言四

    c语言循环结构 为什么用循环? C语言中的循环主要分为三种:while循环,do...while循环 for循环 while循环:先判断条件是否成立,如果条件成立将执行其下的循环体,如果条件不成立一次 ...

  9. Javascript 基础学习(七)常用内置对象

    Array 定义 数组也是一个对象,是一个用来存储数据的对象.和Object类似,但是它的存储效率比普通对象要高.数组中保存的内容我们称为元素.数组使用索引(index)来操作元素.索引指由0开始的整 ...

  10. Cesium案例解析(三)——Camera相机

    目录 1. 概述 2. 实例 2.1. Camera.html 2.2. Camera.js 2.2.1. 飞行至某一点 2.2.2. 飞行至某区域 2.2.3. 两地之间飞行 2.2.4. 设置视图 ...