在GPU上实现光线跟踪

来源:互联网 发布:origin加速mac 编辑:程序博客网 时间:2024/05/16 15:14

#include "cuda.h"
#include "book.h"
#include "cpu_bitmap.h"

#define DIM 1024   //生成图像的大小,DIM*DIM
#define SPHERES 20   //生成的图像中球体的个数

#define rnd( x ) (x * rand() / RAND_MAX)    //产生随机数
#define INF     2e10f    //定义的无穷大数
//球体的定义
struct 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;
  //x*x+sy*sy+z*z=r*r,等价于z*z=r*r-x*xs-y*y
        if (dx*dx + dy*dy < radius*radius) {
            float dz = sqrtf( radius*radius - dx*dx - dy*dy );
            *n = dz /radius;   //n代表计算的Z分量与半径的比值,作用比值决定灰度的大小,比值大,灰度大
            return dz + z;
        }
        return -INF;
    }
};

 

__global__ void kernel( Sphere *s, unsigned char *ptr ) {
    // 从线程块中的线程标识到像素位置的映射
    int 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;   //深度距投影面距离为无穷远,不用显示该点的颜色
 //绘制SPHERES指定个数的球体
    for(int i=0; i<SPHERES; i++) {
        float   n;
        float   t = s[i].hit( ox, oy, &n );
  //如果t不是无穷远的话,就根据n的值计算该像素点的颜色
        if (t > maxz) {
            float fscale = n;
            r = s[i].r * fscale;
            g = s[i].g * fscale;
            b = s[i].b * fscale;
            maxz = t;
        }
    }
//将颜色变换到0~255之间,并写回输出图像中
    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;
}


// 图像块的定义
struct DataBlock {
    unsigned char   *dev_bitmap;
    Sphere          *s;
};

int main( void ) {
    DataBlock   data;   //图像数据块变量的定义
    //定义记录起始时间的事件对象
    cudaEvent_t     start, stop;
    HANDLE_ERROR( cudaEventCreate( &start ) );
    HANDLE_ERROR( cudaEventCreate( &stop ) );
    HANDLE_ERROR( cudaEventRecord( start, 0 ) );

    CPUBitmap bitmap( DIM, DIM, &data );
    unsigned char   *dev_bitmap;
    Sphere   *s;


    // 在GPU上分配内存,以输出计算所得的位图
    HANDLE_ERROR( cudaMalloc( (void**)&dev_bitmap,bitmap.image_size() ) );
    // 为球体数据集分配内存,SPHERES个球体的内存空间
    HANDLE_ERROR( cudaMalloc( (void**)&s,sizeof(Sphere) * SPHERES ) );

    // 分配临时主机内存,对初始化球体的初始数据并赋值到GPU上的内存,然后释放临时内存
    Sphere *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 );

    // 建立与球体对应的的位图显存空间所需要的线程组织结构
    dim3    grids(DIM/16,DIM/16);
    dim3    threads(16,16);
    kernel<<<grids,threads>>>( s, dev_bitmap );

    //从GPU将数据拷回到主机内存以显示
    HANDLE_ERROR( cudaMemcpy( bitmap.get_ptr(), dev_bitmap,
                              bitmap.image_size(),
                              cudaMemcpyDeviceToHost ) );

    // 获取终止时间,以计算图像生成运行的时间
    HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
    HANDLE_ERROR( cudaEventSynchronize( stop ) );
    float   elapsedTime;
    HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime,
                                        start, stop ) );
    printf( "Time to generate:  %3.1f ms\n", elapsedTime );
     HANDLE_ERROR( cudaEventDestroy( start ) );
    HANDLE_ERROR( cudaEventDestroy( stop ) );
//释放显存空间
    HANDLE_ERROR( cudaFree( dev_bitmap ) );
    HANDLE_ERROR( cudaFree( s ) );

    // 显示图像
    bitmap.display_and_exit();
}

原创粉丝点击