渲染场景要做出的修改
CPU版本的深度贴图算法不能被用于GPU。这贴图会同时被OpenCL使用,而OpenCL能直接使用的OpenGL贴图格式有限。依据文档clCreateFromGLTexture2D其中提到的支持的图像通道格式,GL_DEPTH_COMPONENT32不是可以被OpenCL使用的图像格式,非常不幸,因为这个图像格式和我们想要使用的非常像,但是我们可以避开这个问题。
为从场景渲染步骤中获取深度纹理,第二个纹理对象将填充到帧缓冲区。切记只有单一深度纹理会附属于CPU版本。这个深度纹理仍然需要填充到深度缓冲区进行深度测试才能显示。不管怎样,另一个纹理会作为成颜色填充除非接收到相应颜色单元值,它将会接收深度值。下面的代码展示了如何创建纹理以及如何将它帧缓冲区对象。
// Skipped code to allocate depth texture...
// *** DIFFERENCE FROM CPU IMPLEMENTATION ***
// However, because OpenCL can't bind itself to depth textures, we also create
// a "normal" floating point texture that will also hold depths.
// This texture will be the input for our stereogram generation algorithm.
glGenTextures( 1 , &mColorTexture );
glBindTexture( GL_TEXTURE_2D , mColorTexture );
glTexImage2D(
GL_TEXTURE_2D ,
0 ,
GL_R32F ,
kSceneWidth ,
kSceneHeight ,
0 ,
GL_RED ,
GL_FLOAT ,
0
);
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE );
glBindTexture( GL_TEXTURE_2D , 0 );
// Create a framebuffer object to render directly to the depth texture.
glGenFramebuffers( 1 , &mDepthFramebufferObject );
// Attach the depth texture and the color texture (to which depths will be output)
glBindFramebuffer( GL_FRAMEBUFFER , mDepthFramebufferObject );
glFramebufferTexture2D(
GL_FRAMEBUFFER ,
GL_DEPTH_ATTACHMENT ,
GL_TEXTURE_2D ,
mDepthTexture ,
0
);
glFramebufferTexture2D(
GL_FRAMEBUFFER ,
GL_COLOR_ATTACHMENT0 ,
GL_TEXTURE_2D ,
mColorTexture ,
0
);
glBindFramebuffer( GL_FRAMEBUFFER , 0 );
片断着色器会使用深度值对颜色填充进行渲染。正如下面代码所示的那样简洁。
#version 150
out vec4 outColor;
void main( )
{
float lValue = gl_FragCoord.z;
outColor = vec4( lValue , lValue , lValue , 1.0 );
}
这些修改将使纹理适用于 withclCreateFromGLTexture2D(),以便于在OpenCL的上下文共享,正如下面的部分展示的那样。
创建OpenCL context通常执行如下步骤来创建一个OpenCL context:
List OpenCL platforms and choose one (usually the first one).
List OpenCL devices on this platform and choose one (usually the first one).
Create an OpenCL context on this device.
然而,对于多边形生成算法而言,必须注意合理分配OpenCL context,才能从现存的context中访问OpenCL资源。额外的参数将会被传递给OpenCL context创建例程来请求一个兼容的context。这意味着context创建可能会失败,例如 OpenGL context创建在一个我们试图分配一个OpenCL context的设备上。因此,创建步骤需要作适当修改以增强兼容性。
List OpenCL platforms and choose one (usually the first one).
List OpenCL devices on this platform
For each device:
Try to allocate a context
on this device
compatible with current OpenGL context
if context successfully created:
stop
注意到所有平台都可以遍历确保正确的context 被创建。下面的代码演示了OpenCL context 的创建。
cl_int lError = CL_SUCCESS;
std::string lBuffer;
//
// Generic OpenCL creation.
//
// Get platforms.
cl_uint lNbPlatformId = 0;
clGetPlatformIDs( 0 , 0 , &lNbPlatformId );
if ( lNbPlatformId == 0 )
{
std::cerr << "Unable to find an OpenCL platform." << std::endl;
return false;
}
// Choose the first platform.
std::vector< cl_platform_id > lPlatformIds( lNbPlatformId );
clGetPlatformIDs( lNbPlatformId , lPlatformIds.data() , 0 );
cl_platform_id lPlatformId = lPlatformIds[ 0 ];
// Get devices.
cl_uint lNbDeviceId = 0;
clGetDeviceIDs( lPlatformId , CL_DEVICE_TYPE_GPU , 0 , 0 , &lNbDeviceId );
if ( lNbDeviceId == 0 )
{
std::cerr << "Unable to find an OpenCL device." << std::endl;
return false;
}
std::vector< cl_device_id > lDeviceIds( lNbDeviceId );
clGetDeviceIDs( lPlatformId , CL_DEVICE_TYPE_GPU , lNbDeviceId , lDeviceIds.data() , 0 );
// Create the properties for this context.
cl_context_properties lContextProperties[] = {
// We need to add information about the OpenGL context with
// which we want to exchange information with the OpenCL context.
#if defined (WIN32)
// We should first check for cl_khr_gl_sharing extension.
CL_GL_CONTEXT_KHR , (cl_context_properties) wglGetCurrentContext() ,
CL_WGL_HDC_KHR , (cl_context_properties) wglGetCurrentDC() ,
#elif defined (__linux__)
// We should first check for cl_khr_gl_sharing extension.
CL_GL_CONTEXT_KHR , (cl_context_properties) glXGetCurrentContext() ,
CL_GLX_DISPLAY_KHR , (cl_context_properties) glXGetCurrentDisplay() ,
#elif defined (__APPLE__)
// We should first check for cl_APPLE_gl_sharing extension.
#if 0
// This doesn't work.
CL_GL_CONTEXT_KHR , (cl_context_properties) CGLGetCurrentContext() ,
CL_CGL_SHAREGROUP_KHR , (cl_context_properties) CGLGetShareGroup( CGLGetCurrentContext() ) ,
#else
CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE , (cl_context_properties) CGLGetShareGroup( CGLGetCurrentContext() ) ,
#endif
#endif
CL_CONTEXT_PLATFORM , (cl_context_properties) lPlatformId ,
0 , 0 ,
};
// Try to find the device with the compatible context.
cl_device_id lDeviceId = 0;
cl_context lContext = 0;
for ( size_t i = 0; i < lDeviceIds.size(); ++i )
{
cl_device_id lDeviceIdToTry = lDeviceIds[ i ];
cl_context lContextToTry = 0;
lContextToTry = clCreateContext(
lContextProperties ,
1 , &lDeviceIdToTry ,
0 , 0 ,
&lError
);
if ( lError == CL_SUCCESS )
{
// We found the context.
lDeviceId = lDeviceIdToTry;
lContext = lContextToTry;
break;
}
}
if ( lDeviceId == 0 )
{
std::cerr << "Unable to find a compatible OpenCL device." << std::endl;
return false;
}
// Create a command queue.
cl_command_queue lCommandQueue = clCreateCommandQueue( lContext , lDeviceId , 0 , &lError );
if ( !CheckForError( lError ) )
{
std::cerr << "Unable to create an OpenCL command queue." << std::endl;
return false;
}
OpenCL context创建之后,OpenCL缓冲对象( typecl_mem类型)才可以创建,用来表示OpenGL textures共享。这些缓冲区并不会立即被分配内存,他们仅会成为 OpenGL textures缓冲区的引用,允许OpenCL进行读写。
为了创建OpenGL textures的引用,可以如下调用clCreateFromGLTexture2D 函数。
// OpenCL 1.2 deprecates clCreateFromGLTexture2D to use
// clCreateFromGLTexture, but we keep it to work with OpenCL 1.1.
mDepthImage = clCreateFromGLTexture2D(
mContext ,
CL_MEM_READ_ONLY ,
GL_TEXTURE_2D ,
0 ,
pInputDepthTexture ,
&lError
);
if ( !CheckForError( lError ) )
return false;
mOffsetImage = clCreateFromGLTexture2D(
mContext ,
CL_MEM_WRITE_ONLY ,
GL_TEXTURE_2D ,
0 ,
pOutputOffsetTexture ,
&lError
);
if ( !CheckForError( lError ) )
return false;
注意到该函数在 OpenCL 1.2已经被clCreateFromGLTexture取代,但clCreateFromGLTexture2D 依然存在,以确保应用可以仅在OpenCL-1.1的系统中运行。
这些缓冲区可以像常规的 OpenCL缓冲区一样使用并被 OpenCL核心处理,这个核心将在下面的段落分析。