terrain_simulator.cpp
// |
// File: terrain_simulator.cpp |
// |
// Abstract: This example shows how OpenCL can be used to create a procedural field of |
// grass on a generated terrain model which is then rendered with OpenGL. |
// Because OpenGL buffers are shared with OpenCL, the data can remain on the |
// graphics card, thus eliminating the API overhead of creating and submitting |
// the vertices from the host. |
// |
// All geometry is generated on the compute device, and outputted into |
// a shared OpenGL buffer. The terrain gets generated only within the |
// visible arc covering the camera's view frustum to avoid the need for |
// culling. A page of grass is computed on the surface of the terrain as |
// bezier patches, and flow noise is applied to the angle of the blades |
// to simulate wind. Multiple instances of grass are rendered at jittered |
// offsets to add more grass coverage without having to compute new pages. |
// Finally, a physically based sky shader (via OpenGL) is applied to |
// the background to provide an environment for the grass. |
// |
// Version: <1.0> |
// |
// Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") |
// in consideration of your agreement to the following terms, and your use, |
// installation, modification or redistribution of this Apple software |
// constitutes acceptance of these terms. If you do not agree with these |
// terms, please do not use, install, modify or redistribute this Apple |
// software. |
// |
// In consideration of your agreement to abide by the following terms, and |
// subject to these terms, Apple grants you a personal, non - exclusive |
// license, under Apple's copyrights in this original Apple software ( the |
// "Apple Software" ), to use, reproduce, modify and redistribute the Apple |
// Software, with or without modifications, in source and / or binary forms; |
// provided that if you redistribute the Apple Software in its entirety and |
// without modifications, you must retain this notice and the following text |
// and disclaimers in all such redistributions of the Apple Software. Neither |
// the name, trademarks, service marks or logos of Apple Inc. may be used to |
// endorse or promote products derived from the Apple Software without specific |
// prior written permission from Apple. Except as expressly stated in this |
// notice, no other rights or licenses, express or implied, are granted by |
// Apple herein, including but not limited to any patent rights that may be |
// infringed by your derivative works or by other works in which the Apple |
// Software may be incorporated. |
// |
// The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
// WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
// WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A |
// PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION |
// ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
// |
// IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
// CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
// INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION |
// AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER |
// UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR |
// OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// |
// Copyright ( C ) 2008 Apple Inc. All Rights Reserved. |
// |
//////////////////////////////////////////////////////////////////////////////////////////////////// |
#include "terrain_simulator.h" |
#include "compute_math.h" |
#include <math.h> |
#include <assert.h> |
///////////////////////////////////////////////////////////////////////////// |
TerrainSimulator::TerrainSimulator() : |
m_bInitialized(0), |
m_uiKernelArgCount(0), |
m_apvKernelArgValues(0), |
m_atKernelArgSizes(0), |
m_uiSizeX(128), |
m_uiSizeY(128), |
m_fJitterAmount(0), |
m_fCameraFov(0), |
m_fNoiseAmplitude(0), |
m_afVertexData(0), |
m_uiVertexBytes(0), |
m_uiVertexCount(0), |
m_uiVertexComponents(4), |
m_uiVertexBufferId(0), |
m_bCopyVertexData(false), |
m_afNormalData(0), |
m_uiNormalBytes(0), |
m_uiNormalCount(0), |
m_uiNormalComponents(4), |
m_uiNormalBufferId(0), |
m_bCopyNormalData(false), |
m_afTexCoordData(0), |
m_uiTexCoordBytes(0), |
m_uiTexCoordCount(0), |
m_uiTexCoordComponents(4), |
m_uiTexCoordBufferId(0), |
m_bCopyTexCoordData(false), |
m_fFalloff(0) |
{ |
m_auiGlobalDim[0] = m_auiGlobalDim[1] = 0; |
} |
TerrainSimulator::~TerrainSimulator() |
{ |
if(m_apvKernelArgValues) |
delete [] m_apvKernelArgValues; |
m_apvKernelArgValues = 0; |
if(m_atKernelArgSizes) |
delete [] m_atKernelArgSizes; |
m_atKernelArgSizes = 0; |
m_uiKernelArgCount = 0; |
destroy(); |
} |
void |
TerrainSimulator::destroy() |
{ |
if(m_afVertexData) |
delete [] m_afVertexData; |
m_afVertexData = 0; |
m_uiVertexBytes = 0; |
m_uiVertexCount = 0; |
if(m_afNormalData) |
delete [] m_afNormalData; |
m_afNormalData = 0; |
m_uiNormalBytes = 0; |
m_uiNormalCount = 0; |
if(m_afTexCoordData) |
delete [] m_afTexCoordData; |
m_afTexCoordData = 0; |
m_uiTexCoordBytes = 0; |
m_uiTexCoordCount = 0; |
} |
bool |
TerrainSimulator::allocate(uint uiSizeX, uint uiSizeY) |
{ |
destroy(); |
m_uiVertexCount = uiSizeX * uiSizeY; |
if(!m_uiVertexBufferId || (m_uiVertexBufferId && m_bCopyVertexData)) |
{ |
m_afVertexData = new float[m_uiVertexCount * m_uiVertexComponents]; |
m_uiVertexBytes = m_uiVertexCount * sizeof(float) * m_uiVertexComponents; |
for(long long i = 0; i < m_uiVertexCount * m_uiVertexComponents; i++) |
{ |
m_afVertexData[i] = 0.0f; |
} |
} |
m_uiNormalCount = uiSizeX * uiSizeY; |
if(!m_uiNormalBufferId || (m_uiNormalBufferId && m_bCopyNormalData)) |
{ |
m_afNormalData = new float[m_uiNormalCount * m_uiNormalComponents]; |
m_uiNormalBytes = m_uiNormalCount * sizeof(float) * m_uiNormalComponents; |
for(long long i = 0; i < m_uiNormalCount * m_uiNormalComponents; i++) |
{ |
m_afNormalData[i] = 0.0f; |
} |
} |
m_uiTexCoordCount = uiSizeX * uiSizeY; |
if(!m_uiTexCoordBufferId || (m_uiTexCoordBufferId && m_bCopyTexCoordData)) |
{ |
m_afTexCoordData = new float[m_uiTexCoordCount * m_uiTexCoordComponents]; |
m_uiTexCoordBytes = m_uiTexCoordCount * sizeof(float) * m_uiTexCoordComponents; |
for(long long i = 0; i < m_uiTexCoordCount * m_uiTexCoordComponents; i++) |
{ |
m_afTexCoordData[i] = 0.0f; |
} |
} |
return true; |
} |
uint |
TerrainSimulator::getRequiredVertexBufferSize( |
uint uiSizeX, uint uiSizeY) |
{ |
uint uiVertexCount = uiSizeX * uiSizeY; |
uint uiVertexBytes = uiVertexCount * sizeof(float) * m_uiVertexComponents; |
return uiVertexBytes; |
} |
uint |
TerrainSimulator::getRequiredNormalBufferSize( |
uint uiSizeX, uint uiSizeY) |
{ |
uint uiNormalCount = uiSizeX * uiSizeY; |
uint uiNormalBytes = uiNormalCount * sizeof(float) * m_uiNormalComponents; |
return uiNormalBytes; |
} |
uint |
TerrainSimulator::getRequiredTexCoordBufferSize( |
uint uiSizeX, uint uiSizeY) |
{ |
uint uiTexCoordCount = uiSizeX * uiSizeY; |
uint uiTexCoordBytes = uiTexCoordCount * sizeof(float) * m_uiTexCoordComponents; |
return uiTexCoordBytes; |
} |
void |
TerrainSimulator::setProjectedCorners( |
const float4 afCorners[4]) |
{ |
for(uint i = 0; i < 4; i++) |
m_akProjectedCorners[i] = afCorners[i]; |
} |
bool |
TerrainSimulator::reset( |
ComputeEngine &rkCompute) |
{ |
ComputeEngine::MemFlags eMemFlags = ComputeEngine::MEM_READ_WRITE; |
bool bOk = true; |
if(m_uiVertexBufferId && !m_bCopyVertexData) |
bOk |= rkCompute.createGLBufferReference("terrain_vertices", eMemFlags, m_uiVertexBufferId); |
else |
bOk |= rkCompute.createBuffer("terrain_vertices", eMemFlags, m_uiVertexComponents * sizeof(float) * m_uiVertexCount); |
if(m_uiNormalBufferId && !m_bCopyNormalData) |
bOk |= rkCompute.createGLBufferReference("terrain_normals", eMemFlags, m_uiNormalBufferId); |
else |
bOk |= rkCompute.createBuffer("terrain_normals", eMemFlags, m_uiNormalComponents * sizeof(float) * m_uiNormalCount); |
if(m_uiTexCoordBufferId && !m_bCopyTexCoordData) |
bOk |= rkCompute.createGLBufferReference("terrain_texcoords", eMemFlags, m_uiTexCoordBufferId); |
else |
bOk |= rkCompute.createBuffer("terrain_texcoords", eMemFlags, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount); |
if(!bOk) |
{ |
printf("Terrain Simulator: Device memory allocation failed!\n"); |
return false; |
} |
m_bInitialized = true; |
return true; |
} |
bool |
TerrainSimulator::setup( |
ComputeEngine &rkCompute, |
uint uiSizeX, uint uiSizeY) |
{ |
if(!allocate(uiSizeX, uiSizeY)) |
return false; |
m_uiSizeX = uiSizeX; |
m_uiSizeY = uiSizeY; |
printf("Terrain Simulator: Vertices[%d] Count[%d, %d]\n", |
m_uiVertexCount, uiSizeX, uiSizeY); |
bool bOk = true; |
bOk = bOk && rkCompute.createProgramFromFile("tk", "terrain_kernels.cl"); |
bOk = bOk && rkCompute.createKernel("tk", "ComputeTerrainKernel"); |
if(!bOk) |
{ |
printf("Terrain Simulator: Failed to create device kernel!\n"); |
return false; |
} |
return reset(rkCompute); |
} |
bool |
TerrainSimulator::update( |
ComputeEngine &rkCompute, |
uint uiIteration) |
{ |
if(rkCompute.isConnected() == false || !m_bInitialized) |
return false; |
return compute(rkCompute, uiIteration); |
} |
bool |
TerrainSimulator::compute( |
ComputeEngine &rkCompute, |
uint uiIteration) |
{ |
if(rkCompute.isConnected() == false || !m_bInitialized) |
return false; |
const char *acKernelName = "ComputeTerrainKernel"; |
uint uiWorkItems = rkCompute.getEstimatedWorkGroupSize(acKernelName); |
uiWorkItems = round(m_uiSizeX * m_uiSizeY * uiWorkItems) / uiWorkItems; |
uiWorkItems = 1; |
int aiGridResolution[] = { m_uiSizeX, m_uiSizeY }; |
size_t auiGlobalDim[2] = { m_uiSizeX * m_uiSizeY }; |
size_t auiLocalDim[2] = { uiWorkItems }; |
/* |
float fFrequency = 0.0025f; |
float fAmplitude = 50.00f; |
float fPhase = 1.0f; |
float fLacunarity = 2.0345f; |
float fIncrement = 1.0f; |
float fOctaves = 1.0f; |
float fRoughness = 1.00f; |
*/ |
cl_mem kVertices = rkCompute.getMemObject("terrain_vertices"); |
cl_mem kNormals = rkCompute.getMemObject("terrain_normals"); |
cl_mem kTexCoords = rkCompute.getMemObject("terrain_texcoords"); |
attachMemory(rkCompute); |
uint uiArgIndex = 0; |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, aiGridResolution, sizeof(int) * 2); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &m_kCameraPosition, sizeof(float) * 4); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &m_kCameraRotation, sizeof(float) * 4); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &m_kCameraView, sizeof(float) * 4); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &m_kCameraLeft, sizeof(float) * 4); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &m_fCameraFov, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fFrequency, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fAmplitude, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fPhase, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fLacunarity, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fIncrement, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fOctaves, sizeof(float) * 1); |
// rkCompute.setKernelArg(acKernelName, uiArgIndex++, &fRoughness, sizeof(float) * 1); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &m_uiVertexCount, sizeof(uint) * 1); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &kVertices, sizeof(cl_mem)); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &kNormals, sizeof(cl_mem)); |
rkCompute.setKernelArg(acKernelName, uiArgIndex++, &kTexCoords, sizeof(cl_mem)); |
assert(uiArgIndex == rkCompute.getKernelArgCount(acKernelName)); |
bool bSuccess = rkCompute.executeKernel(acKernelName, 0, auiGlobalDim, auiLocalDim, 1); |
updateOutputs(rkCompute); |
detachMemory(rkCompute); |
if(!bSuccess) |
return false; |
return true; |
} |
void |
TerrainSimulator::attachMemory( |
ComputeEngine &rkCompute) |
{ |
if(m_uiVertexBufferId && !m_bCopyVertexData) |
rkCompute.attachGLBuffer("terrain_vertices");//, m_uiVertexBufferId); |
if(m_uiNormalBufferId && !m_bCopyNormalData) |
rkCompute.attachGLBuffer("terrain_normals");// m_uiNormalBufferId); |
if(m_uiTexCoordBufferId && !m_bCopyTexCoordData) |
rkCompute.attachGLBuffer("terrain_texcoords");//m_uiTexCoordBufferId); |
} |
void |
TerrainSimulator::detachMemory( |
ComputeEngine &rkCompute) |
{ |
if(m_uiVertexBufferId && !m_bCopyVertexData) |
rkCompute.detachGLBuffer("terrain_vertices"); |
if(m_uiNormalBufferId && !m_bCopyNormalData) |
rkCompute.detachGLBuffer("terrain_normals"); |
if(m_uiTexCoordBufferId && !m_bCopyTexCoordData) |
rkCompute.detachGLBuffer("terrain_texcoords"); |
rkCompute.barrier(); |
} |
void |
TerrainSimulator::updateOutputs( |
ComputeEngine &rkCompute) |
{ |
if(m_afVertexData && m_bCopyVertexData) |
{ |
rkCompute.readBuffer("terrain_vertices", 0, 0, m_uiVertexComponents * sizeof(float) * m_uiVertexCount, m_afVertexData); |
if(m_uiVertexBufferId) |
{ |
glBindBuffer(GL_ARRAY_BUFFER_ARB, m_uiVertexBufferId); |
glBufferSubData(GL_ARRAY_BUFFER_ARB, 0, m_uiVertexComponents * sizeof(float) * m_uiVertexCount, m_afVertexData); |
glVertexPointer(m_uiVertexComponents, GL_FLOAT, 0, 0); |
glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); |
} |
} |
if(m_afNormalData && m_bCopyNormalData) |
{ |
rkCompute.readBuffer("terrain_normals", 0, 0, m_uiNormalComponents * sizeof(float) * m_uiNormalCount, m_afNormalData); |
if(m_uiNormalBufferId) |
{ |
glBindBuffer(GL_ARRAY_BUFFER_ARB, m_uiNormalBufferId); |
glBufferSubData(GL_ARRAY_BUFFER_ARB, 0, m_uiNormalComponents * sizeof(float) * m_uiNormalCount, m_afNormalData); |
glNormalPointer(GL_FLOAT, m_uiNormalComponents * sizeof(float), 0); |
glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); |
} |
} |
if(m_afTexCoordData && m_bCopyTexCoordData) |
{ |
rkCompute.readBuffer("terrain_texcoords", 0, 0, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount, m_afTexCoordData); |
if(m_uiTexCoordBufferId) |
{ |
glBindBuffer(GL_ARRAY_BUFFER_ARB, m_uiTexCoordBufferId); |
glBufferSubData(GL_ARRAY_BUFFER_ARB, 0, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount, m_afTexCoordData); |
glTexCoordPointer(m_uiTexCoordComponents, GL_FLOAT, 0, 0); |
glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); |
} |
} |
} |
void |
TerrainSimulator::clearMemory( |
ComputeEngine &rkCompute) |
{ |
if(m_afVertexData) |
{ |
for(long long i = 0; i < m_uiVertexCount * m_uiVertexComponents; i++) |
m_afVertexData[i] = 0.0f; |
rkCompute.writeBuffer("terrain_vertices", 0, 0, m_uiVertexComponents * sizeof(float) * m_uiVertexCount, m_afVertexData); |
rkCompute.readBuffer("terrain_vertices", 0, 0, m_uiVertexComponents * sizeof(float) * m_uiVertexCount, m_afVertexData); |
if(m_uiVertexBufferId && m_bCopyVertexData) |
{ |
glBindBuffer(GL_ARRAY_BUFFER_ARB, m_uiVertexBufferId); |
glBufferSubData(GL_ARRAY_BUFFER_ARB, 0, m_uiVertexComponents * sizeof(float) * m_uiVertexCount, m_afVertexData); |
glVertexPointer(m_uiVertexComponents, GL_FLOAT, 0, 0); |
glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); |
} |
} |
else if(m_uiVertexBufferId && !m_bCopyVertexData) |
{ |
rkCompute.attachGLBuffer("terrain_vertices") ; //, m_uiVertexBufferId); |
rkCompute.clearMemory("terrain_vertices", 0, m_uiVertexComponents * sizeof(float) * m_uiVertexCount); |
rkCompute.detachGLBuffer("terrain_vertices"); |
} |
if(m_afNormalData) |
{ |
for(long long i = 0; i < m_uiNormalCount * m_uiNormalComponents; i++) |
m_afNormalData[i] = 0.0f; |
rkCompute.writeBuffer("terrain_normals", 0, 0, m_uiNormalComponents * sizeof(float) * m_uiNormalCount, m_afNormalData); |
rkCompute.readBuffer("terrain_normals", 0, 0, m_uiNormalComponents * sizeof(float) * m_uiNormalCount, m_afNormalData); |
if(m_uiNormalBufferId && m_bCopyNormalData) |
{ |
glBindBuffer(GL_ARRAY_BUFFER_ARB, m_uiNormalBufferId); |
glBufferSubData(GL_ARRAY_BUFFER_ARB, 0, m_uiNormalComponents * sizeof(float) * m_uiNormalCount, m_afNormalData); |
glNormalPointer(GL_FLOAT, m_uiNormalComponents * sizeof(float), 0); |
glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); |
} |
} |
else if(m_uiNormalBufferId && !m_bCopyNormalData) |
{ |
rkCompute.attachGLBuffer("terrain_normals"); //, m_uiNormalBufferId); |
rkCompute.clearMemory("terrain_normals", 0, m_uiNormalComponents * sizeof(float) * m_uiNormalCount); |
rkCompute.detachGLBuffer("terrain_normals"); |
} |
if(m_afTexCoordData) |
{ |
for(long long i = 0; i < m_uiTexCoordCount * m_uiTexCoordComponents; i++) |
m_afTexCoordData[i] = 0.0f; |
rkCompute.writeBuffer("terrain_texcoords", 0, 0, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount, m_afTexCoordData); |
rkCompute.readBuffer("terrain_texcoords", 0, 0, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount, m_afTexCoordData); |
if(m_uiTexCoordBufferId && m_bCopyTexCoordData) |
{ |
glBindBuffer(GL_ARRAY_BUFFER_ARB, m_uiTexCoordBufferId); |
glBufferSubData(GL_ARRAY_BUFFER_ARB, 0, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount, m_afTexCoordData); |
glTexCoordPointer(m_uiTexCoordComponents, GL_FLOAT, 0, 0); |
glBindBuffer(GL_ARRAY_BUFFER_ARB, 0); |
} |
} |
else if(m_uiTexCoordBufferId && !m_bCopyTexCoordData) |
{ |
rkCompute.attachGLBuffer("terrain_texcoords"); // , m_uiTexCoordBufferId); |
rkCompute.clearMemory("terrain_texcoords", 0, m_uiTexCoordComponents * sizeof(float) * m_uiTexCoordCount); |
rkCompute.detachGLBuffer("terrain_texcoords"); |
} |
} |
Copyright © 2011 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2011-01-12