//---------------------------------------------------------------------------// // Copyright (c) 2013-2014 Kyle Lutz // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // // See http://boostorg.github.com/compute for more information. //---------------------------------------------------------------------------// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace compute = boost::compute; // tesselates a sphere with radius, phi_slices, and theta_slices. returns // a shared opencl/opengl buffer containing the vertex data. compute::opengl_buffer tesselate_sphere(float radius, size_t phi_slices, size_t theta_slices, compute::command_queue &queue) { using compute::dim; const compute::context &context = queue.get_context(); const size_t vertex_count = phi_slices * theta_slices; // create opengl buffer GLuint vbo; vtkgl::GenBuffersARB(1, &vbo); vtkgl::BindBufferARB(vtkgl::ARRAY_BUFFER, vbo); vtkgl::BufferDataARB(vtkgl::ARRAY_BUFFER, sizeof(float) * 4 * vertex_count, NULL, vtkgl::STREAM_DRAW); vtkgl::BindBufferARB(vtkgl::ARRAY_BUFFER, 0); // create shared opengl/opencl buffer compute::opengl_buffer vertex_buffer(context, vbo); // tesselate_sphere kernel source const char source[] = BOOST_COMPUTE_STRINGIZE_SOURCE( __kernel void tesselate_sphere(float radius, uint phi_slices, uint theta_slices, __global float4 *vertex_buffer) { const uint phi_i = get_global_id(0); const uint theta_i = get_global_id(1); const float phi = phi_i * 2.f * M_PI_F / phi_slices; const float theta = theta_i * 2.f * M_PI_F / theta_slices; float4 v; v.x = radius * cos(theta) * cos(phi); v.y = radius * cos(theta) * sin(phi); v.z = radius * sin(theta); v.w = 1.f; vertex_buffer[phi_i*phi_slices+theta_i] = v; } ); // build tesselate_sphere program compute::program program = compute::program::create_with_source(source, context); program.build(); // setup tesselate_sphere kernel compute::kernel kernel(program, "tesselate_sphere"); kernel.set_arg(0, radius); kernel.set_arg(1, phi_slices); kernel.set_arg(2, theta_slices); kernel.set_arg(3, vertex_buffer); // acqurire buffer so that it is accessible to OpenCL compute::opengl_enqueue_acquire_buffer(vertex_buffer, queue); // execute tesselate_sphere kernel queue.enqueue_nd_range_kernel( kernel, dim(0, 0), dim(phi_slices, theta_slices), dim(1, 1) ); // release buffer so that it is accessible to OpenGL compute::opengl_enqueue_release_buffer(vertex_buffer, queue); return vertex_buffer; } // simple vtkMapper subclass to render the tesselated sphere on the gpu. class gpu_sphere_mapper : public vtkMapper { public: vtkTypeMacro(gpu_sphere_mapper, vtkMapper) static gpu_sphere_mapper* New() { return new gpu_sphere_mapper; } void Render(vtkRenderer *renderer, vtkActor *actor) { if(!m_initialized){ Initialize(renderer, actor); m_initialized = true; } if(!m_tesselated){ m_vertex_count = m_phi_slices * m_theta_slices; // tesselate sphere m_vertex_buffer = tesselate_sphere( m_radius, m_phi_slices, m_theta_slices, m_command_queue ); // ensure tesselation is finished (seems to be required on AMD) m_command_queue.finish(); // set tesselated flag to true m_tesselated = true; } // draw sphere glEnableClientState(GL_VERTEX_ARRAY); vtkgl::BindBufferARB(vtkgl::ARRAY_BUFFER, m_vertex_buffer.get_opengl_object()); glVertexPointer(4, GL_FLOAT, sizeof(float)*4, 0); glDrawArrays(GL_POINTS, 0, m_vertex_count); } void Initialize(vtkRenderer *renderer, vtkActor *actor) { // initialize opengl extensions vtkOpenGLExtensionManager *extensions = static_cast(renderer->GetRenderWindow()) ->GetExtensionManager(); extensions->LoadExtension("GL_ARB_vertex_buffer_object"); // initialize opencl/opengl shared context m_context = compute::opengl_create_shared_context(); compute::device device = m_context.get_device(); std::cout << "device: " << device.name() << std::endl; // create command queue for the gpu device m_command_queue = compute::command_queue(m_context, device); } double* GetBounds() { static double bounds[6]; bounds[0] = -m_radius; bounds[1] = m_radius; bounds[2] = -m_radius; bounds[3] = m_radius; bounds[4] = -m_radius; bounds[5] = m_radius; return bounds; } protected: gpu_sphere_mapper() { m_radius = 5.0f; m_phi_slices = 100; m_theta_slices = 100; m_initialized = false; m_tesselated = false; } private: float m_radius; int m_phi_slices; int m_theta_slices; int m_vertex_count; bool m_initialized; bool m_tesselated; compute::context m_context; compute::command_queue m_command_queue; compute::opengl_buffer m_vertex_buffer; }; int main(int argc, char *argv[]) { // create gpu sphere mapper vtkSmartPointer mapper = vtkSmartPointer::New(); // create actor for gpu sphere mapper vtkSmartPointer actor = vtkSmartPointer::New(); actor->GetProperty()->LightingOff(); actor->GetProperty()->SetInterpolationToFlat(); actor->SetMapper(mapper); // create render window vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->SetBackground(.1, .2, .31); vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->SetSize(800, 600); renderWindow->AddRenderer(renderer); vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); vtkInteractorStyleSwitch *interactorStyle = vtkInteractorStyleSwitch::SafeDownCast( renderWindowInteractor->GetInteractorStyle() ); interactorStyle->SetCurrentStyleToTrackballCamera(); renderWindowInteractor->SetRenderWindow(renderWindow); renderer->AddActor(actor); // render renderer->ResetCamera(); vtkCamera *camera = renderer->GetActiveCamera(); camera->Elevation(-90.0); renderWindowInteractor->Initialize(); renderWindow->Render(); renderWindowInteractor->Start(); return 0; }