kicad/3d-viewer/3d_rendering/3d_render_raytracing/c3d_render_raytracing.cpp

2072 lines
84 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file c3d_render_raytracing.cpp
* @brief
*/
#include <GL/glew.h>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
#include <thread>
#include "c3d_render_raytracing.h"
#include "mortoncodes.h"
#include "../ccolorrgb.h"
#include "3d_fastmath.h"
#include "3d_math.h"
#include "../common_ogl/ogl_utils.h"
#include <profile.h> // To use GetRunningMicroSecs or another profiling utility
// This should be used in future for the function
// convertLinearToSRGB
//#include <glm/gtc/color_space.hpp>
C3D_RENDER_RAYTRACING::C3D_RENDER_RAYTRACING( BOARD_ADAPTER& aAdapter, CCAMERA& aCamera ) :
C3D_RENDER_BASE( aAdapter, aCamera ),
m_postshader_ssao( aCamera )
{
wxLogTrace( m_logTrace, wxT( "C3D_RENDER_RAYTRACING::C3D_RENDER_RAYTRACING" ) );
m_opengl_support_vertex_buffer_object = false;
m_pboId = GL_NONE;
m_pboDataSize = 0;
m_accelerator = NULL;
m_stats_converted_dummy_to_plane = 0;
m_stats_converted_roundsegment2d_to_roundsegment = 0;
m_oldWindowsSize.x = 0;
m_oldWindowsSize.y = 0;
m_outlineBoard2dObjects = NULL;
m_firstHitinfo = NULL;
m_shaderBuffer = NULL;
m_camera_light = NULL;
m_xoffset = 0;
m_yoffset = 0;
m_isPreview = false;
m_rt_render_state = RT_RENDER_STATE_MAX; // Set to an initial invalid state
m_stats_start_rendering_time = 0;
m_nrBlocksRenderProgress = 0;
}
C3D_RENDER_RAYTRACING::~C3D_RENDER_RAYTRACING()
{
wxLogTrace( m_logTrace, wxT( "C3D_RENDER_RAYTRACING::~C3D_RENDER_RAYTRACING" ) );
delete m_accelerator;
m_accelerator = NULL;
delete m_outlineBoard2dObjects;
m_outlineBoard2dObjects = NULL;
delete[] m_shaderBuffer;
m_shaderBuffer = NULL;
opengl_delete_pbo();
}
int C3D_RENDER_RAYTRACING::GetWaitForEditingTimeOut()
{
return 1000; // ms
}
void C3D_RENDER_RAYTRACING::opengl_delete_pbo()
{
// Delete PBO if it was created
if( m_opengl_support_vertex_buffer_object )
{
if( glIsBufferARB( m_pboId ) )
glDeleteBuffers( 1, &m_pboId );
m_pboId = GL_NONE;
}
}
void C3D_RENDER_RAYTRACING::SetCurWindowSize( const wxSize &aSize )
{
if( m_windowSize != aSize )
{
m_windowSize = aSize;
glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
initializeNewWindowSize();
}
}
void C3D_RENDER_RAYTRACING::restart_render_state()
{
m_stats_start_rendering_time = GetRunningMicroSecs();
m_rt_render_state = RT_RENDER_STATE_TRACING;
m_nrBlocksRenderProgress = 0;
m_postshader_ssao.InitFrame();
m_blockPositionsWasProcessed.resize( m_blockPositions.size() );
// Mark the blocks not processed yet
std::fill( m_blockPositionsWasProcessed.begin(),
m_blockPositionsWasProcessed.end(),
0 );
}
static inline void SetPixel( GLubyte *p, const CCOLORRGB &v )
{
p[0] = v.c[0]; p[1] = v.c[1]; p[2] = v.c[2]; p[3] = 255;
}
bool C3D_RENDER_RAYTRACING::Redraw(
bool aIsMoving, REPORTER* aStatusReporter, REPORTER* aWarningReporter )
{
bool requestRedraw = false;
// Initialize openGL if need
// /////////////////////////////////////////////////////////////////////////
if( !m_is_opengl_initialized )
{
if( !initializeOpenGL() )
return false;
//aIsMoving = true;
requestRedraw = true;
// It will assign the first time the windows size, so it will now
// revert to preview mode the first time the Redraw is called
m_oldWindowsSize = m_windowSize;
initialize_block_positions();
}
std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
// Reload board if it was requested
// /////////////////////////////////////////////////////////////////////////
if( m_reloadRequested )
{
if( aStatusReporter )
aStatusReporter->Report( _( "Loading..." ) );
//aIsMoving = true;
requestRedraw = true;
reload( aStatusReporter, aWarningReporter );
}
// Recalculate constants if windows size was changed
// /////////////////////////////////////////////////////////////////////////
if( m_windowSize != m_oldWindowsSize )
{
m_oldWindowsSize = m_windowSize;
aIsMoving = true;
requestRedraw = true;
initialize_block_positions();
}
// Clear buffers
// /////////////////////////////////////////////////////////////////////////
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
glClearDepth( 1.0f );
glClearStencil( 0x00 );
glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
// 4-byte pixel alignment
glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
glDisable( GL_STENCIL_TEST );
glDisable( GL_LIGHTING );
glDisable( GL_COLOR_MATERIAL );
glDisable( GL_DEPTH_TEST );
glDisable( GL_TEXTURE_2D );
glDisable( GL_BLEND );
glDisable( GL_MULTISAMPLE );
const bool was_camera_changed = m_camera.ParametersChanged();
if( requestRedraw || aIsMoving || was_camera_changed )
m_rt_render_state = RT_RENDER_STATE_MAX; // Set to an invalid state,
// so it will restart again latter
// This will only render if need, otherwise it will redraw the PBO on the screen again
if( aIsMoving || was_camera_changed )
{
// Set head light (camera view light) with the oposite direction of the camera
if( m_camera_light )
m_camera_light->SetDirection( -m_camera.GetDir() );
OGL_DrawBackground( SFVEC3F( m_boardAdapter.m_BgColorTop),
SFVEC3F( m_boardAdapter.m_BgColorBot) );
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
// Get the PBO pixel pointer to write the data
GLubyte *ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB,
GL_WRITE_ONLY_ARB );
if( ptrPBO )
{
render_preview( ptrPBO );
// release pointer to mapping buffer, this initialize the coping to PBO
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
}
glWindowPos2i( m_xoffset, m_yoffset );
}
else
{
// Bind PBO
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
if( m_rt_render_state != RT_RENDER_STATE_FINISH )
{
// Get the PBO pixel pointer to write the data
GLubyte *ptrPBO = (GLubyte *)glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB,
GL_WRITE_ONLY_ARB );
if( ptrPBO )
{
render( ptrPBO, aStatusReporter );
if( m_rt_render_state != RT_RENDER_STATE_FINISH )
requestRedraw = true;
// release pointer to mapping buffer, this initialize the coping to PBO
glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
}
}
if( m_rt_render_state == RT_RENDER_STATE_FINISH )
{
glClear( GL_COLOR_BUFFER_BIT );
// Options if we want draw background instead
//OGL_DrawBackground( SFVEC3F(m_boardAdapter.m_BgColorTop),
// SFVEC3F(m_boardAdapter.m_BgColorBot) );
}
glWindowPos2i( m_xoffset, m_yoffset );
}
// This way it will blend the progress rendering with the last buffer. eg:
// if it was called after a openGL.
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnable( GL_ALPHA_TEST );
glDrawPixels( m_realBufferSize.x,
m_realBufferSize.y,
GL_RGBA,
GL_UNSIGNED_BYTE,
0 );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
return requestRedraw;
}
void C3D_RENDER_RAYTRACING::render( GLubyte* ptrPBO, REPORTER* aStatusReporter )
{
if( (m_rt_render_state == RT_RENDER_STATE_FINISH) ||
(m_rt_render_state >= RT_RENDER_STATE_MAX) )
{
restart_render_state();
if( m_camera_light )
m_camera_light->SetDirection( -m_camera.GetDir() );
if( m_boardAdapter.RenderEngineGet() == RENDER_ENGINE::OPENGL_LEGACY )
{
// Set all pixels of PBO transparent (Alpha to 0)
// This way it will draw the full buffer but only shows the updated (
// already calculated) squares
// /////////////////////////////////////////////////////////////////////
unsigned int nPixels = m_realBufferSize.x * m_realBufferSize.y;
GLubyte *tmp_ptrPBO = ptrPBO + 3; // PBO is RGBA
for( unsigned int i = 0; i < nPixels; ++i )
{
*tmp_ptrPBO = 0;
tmp_ptrPBO += 4; // PBO is RGBA
}
}
m_BgColorTop_LinearRGB = ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_BgColorTop );
m_BgColorBot_LinearRGB = ConvertSRGBToLinear( (SFVEC3F)m_boardAdapter.m_BgColorBot );
}
switch( m_rt_render_state )
{
case RT_RENDER_STATE_TRACING:
rt_render_tracing( ptrPBO, aStatusReporter );
break;
case RT_RENDER_STATE_POST_PROCESS_SHADE:
rt_render_post_process_shade( ptrPBO, aStatusReporter );
break;
case RT_RENDER_STATE_POST_PROCESS_BLUR_AND_FINISH:
rt_render_post_process_blur_finish( ptrPBO, aStatusReporter );
break;
default:
wxASSERT_MSG( false, "Invalid state on m_rt_render_state");
restart_render_state();
break;
}
if( aStatusReporter && ( m_rt_render_state == RT_RENDER_STATE_FINISH ) )
{
// Calculation time in seconds
const double calculation_time = (double)( GetRunningMicroSecs() -
m_stats_start_rendering_time ) / 1e6;
aStatusReporter->Report( wxString::Format( _( "Rendering time %.3f s" ),
calculation_time ) );
}
}
void C3D_RENDER_RAYTRACING::rt_render_tracing( GLubyte* ptrPBO ,
REPORTER* aStatusReporter )
{
m_isPreview = false;
auto startTime = std::chrono::steady_clock::now();
bool breakLoop = false;
std::atomic<size_t> numBlocksRendered( 0 );
std::atomic<size_t> currentBlock( 0 );
std::atomic<size_t> threadsFinished( 0 );
size_t parallelThreadCount = std::min<size_t>(
std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
m_blockPositions.size() );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [&]()
{
for( size_t iBlock = currentBlock.fetch_add( 1 );
iBlock < m_blockPositions.size() && !breakLoop;
iBlock = currentBlock.fetch_add( 1 ) )
{
if( !m_blockPositionsWasProcessed[iBlock] )
{
rt_render_trace_block( ptrPBO, iBlock );
numBlocksRendered++;
m_blockPositionsWasProcessed[iBlock] = 1;
// Check if it spend already some time render and request to exit
// to display the progress
if( std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - startTime ).count() > 150 )
breakLoop = true;
}
}
threadsFinished++;
} );
t.detach();
}
while( threadsFinished < parallelThreadCount )
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
m_nrBlocksRenderProgress += numBlocksRendered;
if( aStatusReporter )
aStatusReporter->Report( wxString::Format( _( "Rendering: %.0f %%" ),
(float)(m_nrBlocksRenderProgress * 100) /
(float)m_blockPositions.size() ) );
// Check if it finish the rendering and if should continue to a post processing
// or mark it as finished
if( m_nrBlocksRenderProgress >= m_blockPositions.size() )
{
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
m_rt_render_state = RT_RENDER_STATE_POST_PROCESS_SHADE;
else
m_rt_render_state = RT_RENDER_STATE_FINISH;
}
}
#ifdef USE_SRGB_SPACE
// This should be removed in future when the KiCad support a greater version of
// glm lib.
#define SRGB_GAMA 2.4f
// This function implements the conversion from linear RGB to sRGB
// https://github.com/g-truc/glm/blob/master/glm/gtc/color_space.inl#L12
static SFVEC3F convertLinearToSRGB( const SFVEC3F &aRGBcolor )
{
const float gammaCorrection = 1.0f / SRGB_GAMA;
const SFVEC3F clampedColor = glm::clamp( aRGBcolor, SFVEC3F(0.0f), SFVEC3F(1.0f) );
return glm::mix(
glm::pow( clampedColor, SFVEC3F(gammaCorrection) ) * 1.055f - 0.055f,
clampedColor * 12.92f,
glm::lessThan( clampedColor, SFVEC3F(0.0031308f) ) );
}
// This function implements the conversion from sRGB to linear RGB
// https://github.com/g-truc/glm/blob/master/glm/gtc/color_space.inl#L35
SFVEC3F ConvertSRGBToLinear( const SFVEC3F &aSRGBcolor )
{
const float gammaCorrection = SRGB_GAMA;
return glm::mix(
glm::pow( (aSRGBcolor + SFVEC3F(0.055f)) * SFVEC3F(0.94786729857819905213270142180095f),
SFVEC3F(gammaCorrection) ),
aSRGBcolor * SFVEC3F(0.07739938080495356037151702786378f),
glm::lessThanEqual( aSRGBcolor, SFVEC3F(0.04045f) ) );
}
#endif
void C3D_RENDER_RAYTRACING::rt_final_color( GLubyte *ptrPBO, const SFVEC3F &rgbColor,
bool applyColorSpaceConversion )
{
SFVEC3F color = rgbColor;
#ifdef USE_SRGB_SPACE
// This should be used in future when the KiCad support a greater version of
// glm lib.
// if( applyColorSpaceConversion )
// rgbColor = glm::convertLinearToSRGB( rgbColor );
if( applyColorSpaceConversion )
color = convertLinearToSRGB( rgbColor );
#endif
ptrPBO[0] = (unsigned int)glm::clamp( (int)(color.r * 255), 0, 255 );
ptrPBO[1] = (unsigned int)glm::clamp( (int)(color.g * 255), 0, 255 );
ptrPBO[2] = (unsigned int)glm::clamp( (int)(color.b * 255), 0, 255 );
ptrPBO[3] = 255;
}
static void HITINFO_PACKET_init( HITINFO_PACKET *aHitPacket )
{
// Initialize hitPacket with a "not hit" information
for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i )
{
aHitPacket[i].m_HitInfo.m_tHit = std::numeric_limits<float>::infinity();
aHitPacket[i].m_HitInfo.m_acc_node_info = 0;
aHitPacket[i].m_hitresult = false;
aHitPacket[i].m_HitInfo.m_HitNormal = SFVEC3F( 0.0f );
aHitPacket[i].m_HitInfo.m_ShadowFactor = 1.0f;
}
}
void C3D_RENDER_RAYTRACING::rt_shades_packet(const SFVEC3F *bgColorY,
const RAY *aRayPkt,
HITINFO_PACKET *aHitPacket,
bool is_testShadow,
SFVEC3F *aOutHitColor )
{
for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y )
{
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i )
{
if( aHitPacket[i].m_hitresult == true )
{
aOutHitColor[i] = shadeHit( bgColorY[y],
aRayPkt[i],
aHitPacket[i].m_HitInfo,
false,
0,
is_testShadow );
}
else
{
aOutHitColor[i] = bgColorY[y];
}
}
}
}
void C3D_RENDER_RAYTRACING::rt_trace_AA_packet( const SFVEC3F *aBgColorY,
const HITINFO_PACKET *aHitPck_X0Y0,
const HITINFO_PACKET *aHitPck_AA_X1Y1,
const RAY *aRayPck,
SFVEC3F *aOutHitColor )
{
const bool is_testShadow = m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_SHADOWS );
for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y )
{
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i )
{
const RAY &rayAA = aRayPck[i];
HITINFO hitAA;
hitAA.m_tHit = std::numeric_limits<float>::infinity();
hitAA.m_acc_node_info = 0;
bool hitted = false;
const unsigned int idx0y1 = ( x + 0 ) + RAYPACKET_DIM * ( y + 1 );
const unsigned int idx1y1 = ( x + 1 ) + RAYPACKET_DIM * ( y + 1 );
// Gets the node info from the hit.
const unsigned int nodex0y0 = aHitPck_X0Y0[ i ].m_HitInfo.m_acc_node_info;
const unsigned int node_AA_x0y0 = aHitPck_AA_X1Y1[ i ].m_HitInfo.m_acc_node_info;
unsigned int nodex1y0 = 0;
if( x < (RAYPACKET_DIM - 1) )
nodex1y0 = aHitPck_X0Y0[ i + 1 ].m_HitInfo.m_acc_node_info;
unsigned int nodex0y1 = 0;
if( y < (RAYPACKET_DIM - 1) )
nodex0y1 = aHitPck_X0Y0[ idx0y1 ].m_HitInfo.m_acc_node_info;
unsigned int nodex1y1 = 0;
if( ((x < (RAYPACKET_DIM - 1)) &&
(y < (RAYPACKET_DIM - 1))) )
nodex1y1 = aHitPck_X0Y0[ idx1y1 ].m_HitInfo.m_acc_node_info;
if( ((nodex0y0 == nodex1y0) || (nodex1y0 == 0)) && // If all notes are equal we assume there was no change on the object hits
((nodex0y0 == nodex0y1) || (nodex0y1 == 0)) &&
((nodex0y0 == nodex1y1) || (nodex1y1 == 0)) &&
(nodex0y0 == node_AA_x0y0) )
{
// Option 1
// This option will give a very good quality on reflections (slow)
/*
if( m_accelerator->Intersect( rayAA, hitAA, nodex0y0 ) )
{
aOutHitColor[i] += shadeHit( aBgColorY[y], rayAA, hitAA, false, 0 );
}
else
{
if( m_accelerator->Intersect( rayAA, hitAA ) )
aOutHitColor[i] += shadeHit( aBgColorY[y], rayAA, hitAA, false, 0 );
else
aOutHitColor[i] += hitColor[i];
}
*/
// Option 2
// Trace again with the same node,
// then if miss just give the same color as before
//if( m_accelerator->Intersect( rayAA, hitAA, nodex0y0 ) )
// aOutHitColor[i] += shadeHit( aBgColorY[y], rayAA, hitAA, false, 0 );
// Option 3
// Use same color
}
else
{
// Try to intersect the different nodes
// It tests the possible combination of hitted or not hitted points
// This will try to get the best hit for this ray
if( nodex0y0 != 0 )
hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex0y0 );
if( ( nodex1y0 != 0 ) &&
( nodex0y0 != nodex1y0 ) )
hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex1y0 );
if( ( nodex0y1 != 0 ) &&
( nodex0y0 != nodex0y1 ) &&
( nodex1y0 != nodex0y1 ) )
hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex0y1 );
if( (nodex1y1 != 0 ) &&
( nodex0y0 != nodex1y1 ) &&
( nodex0y1 != nodex1y1 ) &&
( nodex1y0 != nodex1y1 ) )
hitted |= m_accelerator->Intersect( rayAA, hitAA, nodex1y1 );
if( (node_AA_x0y0 != 0 ) &&
( nodex0y0 != node_AA_x0y0 ) &&
( nodex0y1 != node_AA_x0y0 ) &&
( nodex1y0 != node_AA_x0y0 ) &&
( nodex1y1 != node_AA_x0y0 ) )
hitted |= m_accelerator->Intersect( rayAA, hitAA, node_AA_x0y0 );
if( hitted )
{
// If we got any result, shade it
aOutHitColor[i] = shadeHit( aBgColorY[y], rayAA, hitAA, false, 0, is_testShadow );
}
else
{
// Note: There are very few cases that will end on this situation
// so it is not so expensive to trace a single ray from the beginning
// It was missed the 'last nodes' so, trace a ray from the beginning
if( m_accelerator->Intersect( rayAA, hitAA ) )
aOutHitColor[i] = shadeHit( aBgColorY[y], rayAA, hitAA, false, 0, is_testShadow );
}
}
}
}
}
#define DISP_FACTOR 0.075f
void C3D_RENDER_RAYTRACING::rt_render_trace_block( GLubyte *ptrPBO ,
signed int iBlock )
{
// Initialize ray packets
// /////////////////////////////////////////////////////////////////////////
const SFVEC2UI &blockPos = m_blockPositions[iBlock];
const SFVEC2I blockPosI = SFVEC2I( blockPos.x + m_xoffset,
blockPos.y + m_yoffset );
RAYPACKET blockPacket( m_camera, (SFVEC2F)blockPosI + SFVEC2F(DISP_FACTOR, DISP_FACTOR),
SFVEC2F(DISP_FACTOR, DISP_FACTOR) /* Displacement random factor */ );
HITINFO_PACKET hitPacket_X0Y0[RAYPACKET_RAYS_PER_PACKET];
HITINFO_PACKET_init( hitPacket_X0Y0 );
// Calculate background gradient color
// /////////////////////////////////////////////////////////////////////////
SFVEC3F bgColor[RAYPACKET_DIM];// Store a vertical gradient color
for( unsigned int y = 0; y < RAYPACKET_DIM; ++y )
{
const float posYfactor = (float)(blockPosI.y + y) / (float)m_windowSize.y;
bgColor[y] = m_BgColorTop_LinearRGB * SFVEC3F(posYfactor) +
m_BgColorBot_LinearRGB * ( SFVEC3F(1.0f) - SFVEC3F(posYfactor) );
}
// Intersect ray packets (calculate the intersection with rays and objects)
// /////////////////////////////////////////////////////////////////////////
if( !m_accelerator->Intersect( blockPacket, hitPacket_X0Y0 ) )
{
// If block is empty then set shades and continue
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
{
for( unsigned int y = 0; y < RAYPACKET_DIM; ++y )
{
const SFVEC3F &outColor = bgColor[y];
const unsigned int yBlockPos = blockPos.y + y;
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x )
{
m_postshader_ssao.SetPixelData( blockPos.x + x,
yBlockPos,
SFVEC3F( 0.0f ),
outColor,
SFVEC3F( 0.0f ),
0,
1.0f );
}
}
}
// This will set the output color to be displayed
// If post processing is enabled, it will not reflect the final result
// (as the final color will be computed on post processing)
// but it is used for report progress
const bool isFinalColor = !m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING );
for( unsigned int y = 0; y < RAYPACKET_DIM; ++y )
{
const SFVEC3F &outColor = bgColor[y];
const unsigned int yConst = blockPos.x + ( (y + blockPos.y) * m_realBufferSize.x);
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x )
{
GLubyte *ptr = &ptrPBO[ (yConst + x) * 4 ];
rt_final_color( ptr, outColor, isFinalColor );
}
}
// There is nothing more here to do.. there are no hits ..
// just background so continue
return;
}
SFVEC3F hitColor_X0Y0[RAYPACKET_RAYS_PER_PACKET];
// Shade original (0, 0) hits ("paint" the intersected objects)
// /////////////////////////////////////////////////////////////////////////
rt_shades_packet( bgColor,
blockPacket.m_ray,
hitPacket_X0Y0,
m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_SHADOWS ),
hitColor_X0Y0 );
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) )
{
SFVEC3F hitColor_AA_X1Y1[RAYPACKET_RAYS_PER_PACKET];
// Intersect one blockPosI + (0.5, 0.5) used for anti aliasing calculation
// /////////////////////////////////////////////////////////////////////////
HITINFO_PACKET hitPacket_AA_X1Y1[RAYPACKET_RAYS_PER_PACKET];
HITINFO_PACKET_init( hitPacket_AA_X1Y1 );
RAYPACKET blockPacket_AA_X1Y1( m_camera, (SFVEC2F)blockPosI + SFVEC2F(0.5f, 0.5f),
SFVEC2F(DISP_FACTOR, DISP_FACTOR) );
if( !m_accelerator->Intersect( blockPacket_AA_X1Y1, hitPacket_AA_X1Y1 ) )
{
// Missed all the package
for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y )
{
const SFVEC3F &outColor = bgColor[y];
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i )
{
hitColor_AA_X1Y1[i] = outColor;
}
}
}
else
{
rt_shades_packet( bgColor,
blockPacket_AA_X1Y1.m_ray,
hitPacket_AA_X1Y1,
m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_SHADOWS ),
hitColor_AA_X1Y1
);
}
SFVEC3F hitColor_AA_X1Y0[RAYPACKET_RAYS_PER_PACKET];
SFVEC3F hitColor_AA_X0Y1[RAYPACKET_RAYS_PER_PACKET];
SFVEC3F hitColor_AA_X0Y1_half[RAYPACKET_RAYS_PER_PACKET];
for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i )
{
const SFVEC3F color_average = ( hitColor_X0Y0[i] +
hitColor_AA_X1Y1[i] ) * SFVEC3F(0.5f);
hitColor_AA_X1Y0[i] = color_average;
hitColor_AA_X0Y1[i] = color_average;
hitColor_AA_X0Y1_half[i] = color_average;
}
RAY blockRayPck_AA_X1Y0[RAYPACKET_RAYS_PER_PACKET];
RAY blockRayPck_AA_X0Y1[RAYPACKET_RAYS_PER_PACKET];
RAY blockRayPck_AA_X1Y1_half[RAYPACKET_RAYS_PER_PACKET];
RAYPACKET_InitRays_with2DDisplacement( m_camera,
(SFVEC2F)blockPosI + SFVEC2F(0.5f - DISP_FACTOR, DISP_FACTOR),
SFVEC2F(DISP_FACTOR, DISP_FACTOR), // Displacement random factor
blockRayPck_AA_X1Y0 );
RAYPACKET_InitRays_with2DDisplacement( m_camera,
(SFVEC2F)blockPosI + SFVEC2F(DISP_FACTOR, 0.5f - DISP_FACTOR),
SFVEC2F(DISP_FACTOR, DISP_FACTOR), // Displacement random factor
blockRayPck_AA_X0Y1 );
RAYPACKET_InitRays_with2DDisplacement( m_camera,
(SFVEC2F)blockPosI + SFVEC2F(0.25f - DISP_FACTOR, 0.25f - DISP_FACTOR),
SFVEC2F(DISP_FACTOR, DISP_FACTOR), // Displacement random factor
blockRayPck_AA_X1Y1_half );
rt_trace_AA_packet( bgColor,
hitPacket_X0Y0, hitPacket_AA_X1Y1,
blockRayPck_AA_X1Y0,
hitColor_AA_X1Y0 );
rt_trace_AA_packet( bgColor,
hitPacket_X0Y0, hitPacket_AA_X1Y1,
blockRayPck_AA_X0Y1,
hitColor_AA_X0Y1 );
rt_trace_AA_packet( bgColor,
hitPacket_X0Y0, hitPacket_AA_X1Y1,
blockRayPck_AA_X1Y1_half,
hitColor_AA_X0Y1_half );
// Average the result
for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i )
{
hitColor_X0Y0[i] = ( hitColor_X0Y0[i] +
hitColor_AA_X1Y1[i] +
hitColor_AA_X1Y0[i] +
hitColor_AA_X0Y1[i] +
hitColor_AA_X0Y1_half[i]
) * SFVEC3F(1.0f / 5.0f);
}
}
// Copy results to the next stage
// /////////////////////////////////////////////////////////////////////
GLubyte *ptr = &ptrPBO[ ( blockPos.x +
(blockPos.y * m_realBufferSize.x) ) * 4 ];
const uint32_t ptrInc = (m_realBufferSize.x - RAYPACKET_DIM) * 4;
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
{
SFVEC2I bPos;
bPos.y = blockPos.y;
for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y )
{
bPos.x = blockPos.x;
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i )
{
const SFVEC3F &hColor = hitColor_X0Y0[i];
if( hitPacket_X0Y0[i].m_hitresult == true )
m_postshader_ssao.SetPixelData( bPos.x, bPos.y,
hitPacket_X0Y0[i].m_HitInfo.m_HitNormal,
hColor,
blockPacket.m_ray[i].at(
hitPacket_X0Y0[i].m_HitInfo.m_tHit ),
hitPacket_X0Y0[i].m_HitInfo.m_tHit,
hitPacket_X0Y0[i].m_HitInfo.m_ShadowFactor );
else
m_postshader_ssao.SetPixelData( bPos.x, bPos.y,
SFVEC3F( 0.0f ),
hColor,
SFVEC3F( 0.0f ),
0,
1.0f );
rt_final_color( ptr, hColor, false );
bPos.x++;
ptr += 4;
}
ptr += ptrInc;
bPos.y++;
}
}
else
{
for( unsigned int y = 0, i = 0; y < RAYPACKET_DIM; ++y )
{
for( unsigned int x = 0; x < RAYPACKET_DIM; ++x, ++i )
{
rt_final_color( ptr, hitColor_X0Y0[i], true );
ptr += 4;
}
ptr += ptrInc;
}
}
}
void C3D_RENDER_RAYTRACING::rt_render_post_process_shade( GLubyte* ptrPBO,
REPORTER* aStatusReporter )
{
(void)ptrPBO; // unused
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
{
if( aStatusReporter )
aStatusReporter->Report( _( "Rendering: Post processing shader" ) );
m_postshader_ssao.SetShadowsEnabled( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_SHADOWS ) );
std::atomic<size_t> nextBlock( 0 );
std::atomic<size_t> threadsFinished( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [&]()
{
for( size_t y = nextBlock.fetch_add( 1 );
y < m_realBufferSize.y;
y = nextBlock.fetch_add( 1 ) )
{
SFVEC3F *ptr = &m_shaderBuffer[ y * m_realBufferSize.x ];
for( signed int x = 0; x < (int)m_realBufferSize.x; ++x )
{
*ptr = m_postshader_ssao.Shade( SFVEC2I( x, y ) );
ptr++;
}
}
threadsFinished++;
} );
t.detach();
}
while( threadsFinished < parallelThreadCount )
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
m_postshader_ssao.SetShadedBuffer( m_shaderBuffer );
// Set next state
m_rt_render_state = RT_RENDER_STATE_POST_PROCESS_BLUR_AND_FINISH;
}
else
{
// As this was an invalid state, set to finish
m_rt_render_state = RT_RENDER_STATE_FINISH;
}
}
void C3D_RENDER_RAYTRACING::rt_render_post_process_blur_finish( GLubyte *ptrPBO,
REPORTER *aStatusReporter )
{
(void) aStatusReporter; //unused
if( m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
{
// Now blurs the shader result and compute the final color
std::atomic<size_t> nextBlock( 0 );
std::atomic<size_t> threadsFinished( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [&]()
{
for( size_t y = nextBlock.fetch_add( 1 );
y < m_realBufferSize.y;
y = nextBlock.fetch_add( 1 ) )
{
GLubyte *ptr = &ptrPBO[ y * m_realBufferSize.x * 4 ];
for( signed int x = 0; x < (int)m_realBufferSize.x; ++x )
{
const SFVEC3F bluredShadeColor = m_postshader_ssao.Blur( SFVEC2I( x, y ) );
#ifdef USE_SRGB_SPACE
const SFVEC3F originColor = convertLinearToSRGB( m_postshader_ssao.GetColorAtNotProtected( SFVEC2I( x,y ) ) );
#else
const SFVEC3F originColor = m_postshader_ssao.GetColorAtNotProtected( SFVEC2I( x,y ) );
#endif
const SFVEC3F shadedColor = m_postshader_ssao.ApplyShadeColor( SFVEC2I( x,y ), originColor, bluredShadeColor );
rt_final_color( ptr, shadedColor, false );
ptr += 4;
}
}
threadsFinished++;
} );
t.detach();
}
while( threadsFinished < parallelThreadCount )
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
// Debug code
//m_postshader_ssao.DebugBuffersOutputAsImages();
}
// End rendering
m_rt_render_state = RT_RENDER_STATE_FINISH;
}
void C3D_RENDER_RAYTRACING::render_preview( GLubyte *ptrPBO )
{
m_isPreview = true;
std::atomic<size_t> nextBlock( 0 );
std::atomic<size_t> threadsFinished( 0 );
size_t parallelThreadCount = std::min<size_t>(
std::max<size_t>( std::thread::hardware_concurrency(), 2 ),
m_blockPositions.size() );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [&]()
{
for( size_t iBlock = nextBlock.fetch_add( 1 );
iBlock < m_blockPositionsFast.size();
iBlock = nextBlock.fetch_add( 1 ) )
{
const SFVEC2UI &windowPosUI = m_blockPositionsFast[ iBlock ];
const SFVEC2I windowsPos = SFVEC2I( windowPosUI.x + m_xoffset,
windowPosUI.y + m_yoffset );
RAYPACKET blockPacket( m_camera, windowsPos, 4 );
HITINFO_PACKET hitPacket[RAYPACKET_RAYS_PER_PACKET];
// Initialize hitPacket with a "not hit" information
for( HITINFO_PACKET& packet : hitPacket )
{
packet.m_HitInfo.m_tHit = std::numeric_limits<float>::infinity();
packet.m_HitInfo.m_acc_node_info = 0;
packet.m_hitresult = false;
}
// Intersect packet block
m_accelerator->Intersect( blockPacket, hitPacket );
// Calculate background gradient color
// /////////////////////////////////////////////////////////////////////
SFVEC3F bgColor[RAYPACKET_DIM];
for( unsigned int y = 0; y < RAYPACKET_DIM; ++y )
{
const float posYfactor = (float)(windowsPos.y + y * 4.0f) / (float)m_windowSize.y;
bgColor[y] = (SFVEC3F)m_boardAdapter.m_BgColorTop * SFVEC3F( posYfactor) +
(SFVEC3F)m_boardAdapter.m_BgColorBot * ( SFVEC3F( 1.0f) - SFVEC3F( posYfactor) );
}
CCOLORRGB hitColorShading[RAYPACKET_RAYS_PER_PACKET];
for( unsigned int i = 0; i < RAYPACKET_RAYS_PER_PACKET; ++i )
{
const SFVEC3F bhColorY = bgColor[i / RAYPACKET_DIM];
if( hitPacket[i].m_hitresult == true )
{
const SFVEC3F hitColor = shadeHit( bhColorY,
blockPacket.m_ray[i],
hitPacket[i].m_HitInfo,
false,
0,
false );
hitColorShading[i] = CCOLORRGB( hitColor );
}
else
hitColorShading[i] = bhColorY;
}
CCOLORRGB cLRB_old[(RAYPACKET_DIM - 1)];
for( unsigned int y = 0; y < (RAYPACKET_DIM - 1); ++y )
{
const SFVEC3F bgColorY = bgColor[y];
const CCOLORRGB bgColorYRGB = CCOLORRGB( bgColorY );
// This stores cRTB from the last block to be reused next time in a cLTB pixel
CCOLORRGB cRTB_old;
//RAY cRTB_ray;
//HITINFO cRTB_hitInfo;
for( unsigned int x = 0; x < (RAYPACKET_DIM - 1); ++x )
{
// pxl 0 pxl 1 pxl 2 pxl 3 pxl 4
// x0 x1 ...
// .---------------------------.
// y0 | cLT | cxxx | cLRT | cxxx | cRT |
// | cxxx | cLTC | cxxx | cRTC | cxxx |
// | cLTB | cxxx | cC | cxxx | cRTB |
// | cxxx | cLBC | cxxx | cRBC | cxxx |
// '---------------------------'
// y1 | cLB | cxxx | cLRB | cxxx | cRB |
const unsigned int iLT = ((x + 0) + RAYPACKET_DIM * (y + 0));
const unsigned int iRT = ((x + 1) + RAYPACKET_DIM * (y + 0));
const unsigned int iLB = ((x + 0) + RAYPACKET_DIM * (y + 1));
const unsigned int iRB = ((x + 1) + RAYPACKET_DIM * (y + 1));
// !TODO: skip when there are no hits
const CCOLORRGB &cLT = hitColorShading[ iLT ];
const CCOLORRGB &cRT = hitColorShading[ iRT ];
const CCOLORRGB &cLB = hitColorShading[ iLB ];
const CCOLORRGB &cRB = hitColorShading[ iRB ];
// Trace and shade cC
// /////////////////////////////////////////////////////////////
CCOLORRGB cC = bgColorYRGB;
const SFVEC3F &oriLT = blockPacket.m_ray[ iLT ].m_Origin;
const SFVEC3F &oriRB = blockPacket.m_ray[ iRB ].m_Origin;
const SFVEC3F &dirLT = blockPacket.m_ray[ iLT ].m_Dir;
const SFVEC3F &dirRB = blockPacket.m_ray[ iRB ].m_Dir;
SFVEC3F oriC;
SFVEC3F dirC;
HITINFO centerHitInfo;
centerHitInfo.m_tHit = std::numeric_limits<float>::infinity();
bool hittedC = false;
if( (hitPacket[ iLT ].m_hitresult == true) ||
(hitPacket[ iRT ].m_hitresult == true) ||
(hitPacket[ iLB ].m_hitresult == true) ||
(hitPacket[ iRB ].m_hitresult == true) )
{
oriC = ( oriLT + oriRB ) * 0.5f;
dirC = glm::normalize( ( dirLT + dirRB ) * 0.5f );
// Trace the center ray
RAY centerRay;
centerRay.Init( oriC, dirC );
const unsigned int nodeLT = hitPacket[ iLT ].m_HitInfo.m_acc_node_info;
const unsigned int nodeRT = hitPacket[ iRT ].m_HitInfo.m_acc_node_info;
const unsigned int nodeLB = hitPacket[ iLB ].m_HitInfo.m_acc_node_info;
const unsigned int nodeRB = hitPacket[ iRB ].m_HitInfo.m_acc_node_info;
if( nodeLT != 0 )
hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeLT );
if( ( nodeRT != 0 ) &&
( nodeRT != nodeLT ) )
hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeRT );
if( ( nodeLB != 0 ) &&
( nodeLB != nodeLT ) &&
( nodeLB != nodeRT ) )
hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeLB );
if( ( nodeRB != 0 ) &&
( nodeRB != nodeLB ) &&
( nodeRB != nodeLT ) &&
( nodeRB != nodeRT ) )
hittedC |= m_accelerator->Intersect( centerRay, centerHitInfo, nodeRB );
if( hittedC )
cC = CCOLORRGB( shadeHit( bgColorY, centerRay, centerHitInfo, false, 0, false ) );
else
{
centerHitInfo.m_tHit = std::numeric_limits<float>::infinity();
hittedC = m_accelerator->Intersect( centerRay, centerHitInfo );
if( hittedC )
cC = CCOLORRGB( shadeHit( bgColorY,
centerRay,
centerHitInfo,
false,
0,
false ) );
}
}
// Trace and shade cLRT
// /////////////////////////////////////////////////////////////
CCOLORRGB cLRT = bgColorYRGB;
const SFVEC3F &oriRT = blockPacket.m_ray[ iRT ].m_Origin;
const SFVEC3F &dirRT = blockPacket.m_ray[ iRT ].m_Dir;
if( y == 0 )
{
// Trace the center ray
RAY rayLRT;
rayLRT.Init( ( oriLT + oriRT ) * 0.5f,
glm::normalize( ( dirLT + dirRT ) * 0.5f ) );
HITINFO hitInfoLRT;
hitInfoLRT.m_tHit = std::numeric_limits<float>::infinity();
if( hitPacket[ iLT ].m_hitresult &&
hitPacket[ iRT ].m_hitresult &&
(hitPacket[ iLT ].m_HitInfo.pHitObject == hitPacket[ iRT ].m_HitInfo.pHitObject) )
{
hitInfoLRT.pHitObject = hitPacket[ iLT ].m_HitInfo.pHitObject;
hitInfoLRT.m_tHit = ( hitPacket[ iLT ].m_HitInfo.m_tHit +
hitPacket[ iRT ].m_HitInfo.m_tHit ) * 0.5f;
hitInfoLRT.m_HitNormal =
glm::normalize( ( hitPacket[ iLT ].m_HitInfo.m_HitNormal +
hitPacket[ iRT ].m_HitInfo.m_HitNormal ) * 0.5f );
cLRT = CCOLORRGB( shadeHit( bgColorY, rayLRT, hitInfoLRT, false, 0, false ) );
cLRT = BlendColor( cLRT, BlendColor( cLT, cRT) );
}
else
{
if( hitPacket[ iLT ].m_hitresult ||
hitPacket[ iRT ].m_hitresult ) // If any hits
{
const unsigned int nodeLT = hitPacket[ iLT ].m_HitInfo.m_acc_node_info;
const unsigned int nodeRT = hitPacket[ iRT ].m_HitInfo.m_acc_node_info;
bool hittedLRT = false;
if( nodeLT != 0 )
hittedLRT |= m_accelerator->Intersect( rayLRT, hitInfoLRT, nodeLT );
if( ( nodeRT != 0 ) &&
( nodeRT != nodeLT ) )
hittedLRT |= m_accelerator->Intersect( rayLRT,
hitInfoLRT,
nodeRT );
if( hittedLRT )
cLRT = CCOLORRGB( shadeHit( bgColorY,
rayLRT,
hitInfoLRT,
false,
0,
false ) );
else
{
hitInfoLRT.m_tHit = std::numeric_limits<float>::infinity();
if( m_accelerator->Intersect( rayLRT,hitInfoLRT ) )
cLRT = CCOLORRGB( shadeHit( bgColorY,
rayLRT,
hitInfoLRT,
false,
0,
false ) );
}
}
}
}
else
cLRT = cLRB_old[x];
// Trace and shade cLTB
// /////////////////////////////////////////////////////////////
CCOLORRGB cLTB = bgColorYRGB;
if( x == 0 )
{
const SFVEC3F &oriLB = blockPacket.m_ray[ iLB ].m_Origin;
const SFVEC3F &dirLB = blockPacket.m_ray[ iLB ].m_Dir;
// Trace the center ray
RAY rayLTB;
rayLTB.Init( ( oriLT + oriLB ) * 0.5f,
glm::normalize( ( dirLT + dirLB ) * 0.5f ) );
HITINFO hitInfoLTB;
hitInfoLTB.m_tHit = std::numeric_limits<float>::infinity();
if( hitPacket[ iLT ].m_hitresult &&
hitPacket[ iLB ].m_hitresult &&
( hitPacket[ iLT ].m_HitInfo.pHitObject ==
hitPacket[ iLB ].m_HitInfo.pHitObject ) )
{
hitInfoLTB.pHitObject = hitPacket[ iLT ].m_HitInfo.pHitObject;
hitInfoLTB.m_tHit = ( hitPacket[ iLT ].m_HitInfo.m_tHit +
hitPacket[ iLB ].m_HitInfo.m_tHit ) * 0.5f;
hitInfoLTB.m_HitNormal =
glm::normalize( ( hitPacket[ iLT ].m_HitInfo.m_HitNormal +
hitPacket[ iLB ].m_HitInfo.m_HitNormal ) * 0.5f );
cLTB = CCOLORRGB( shadeHit( bgColorY, rayLTB, hitInfoLTB, false, 0, false ) );
cLTB = BlendColor( cLTB, BlendColor( cLT, cLB) );
}
else
{
if( hitPacket[ iLT ].m_hitresult ||
hitPacket[ iLB ].m_hitresult ) // If any hits
{
const unsigned int nodeLT = hitPacket[ iLT ].m_HitInfo.m_acc_node_info;
const unsigned int nodeLB = hitPacket[ iLB ].m_HitInfo.m_acc_node_info;
bool hittedLTB = false;
if( nodeLT != 0 )
hittedLTB |= m_accelerator->Intersect( rayLTB,
hitInfoLTB,
nodeLT );
if( ( nodeLB != 0 ) &&
( nodeLB != nodeLT ) )
hittedLTB |= m_accelerator->Intersect( rayLTB,
hitInfoLTB,
nodeLB );
if( hittedLTB )
cLTB = CCOLORRGB( shadeHit( bgColorY,
rayLTB,
hitInfoLTB,
false,
0,
false ) );
else
{
hitInfoLTB.m_tHit = std::numeric_limits<float>::infinity();
if( m_accelerator->Intersect( rayLTB, hitInfoLTB ) )
cLTB = CCOLORRGB( shadeHit( bgColorY,
rayLTB,
hitInfoLTB,
false,
0,
false ) );
}
}
}
}
else
cLTB = cRTB_old;
// Trace and shade cRTB
// /////////////////////////////////////////////////////////////
CCOLORRGB cRTB = bgColorYRGB;
// Trace the center ray
RAY rayRTB;
rayRTB.Init( ( oriRT + oriRB ) * 0.5f,
glm::normalize( ( dirRT + dirRB ) * 0.5f ) );
HITINFO hitInfoRTB;
hitInfoRTB.m_tHit = std::numeric_limits<float>::infinity();
if( hitPacket[ iRT ].m_hitresult &&
hitPacket[ iRB ].m_hitresult &&
( hitPacket[ iRT ].m_HitInfo.pHitObject ==
hitPacket[ iRB ].m_HitInfo.pHitObject ) )
{
hitInfoRTB.pHitObject = hitPacket[ iRT ].m_HitInfo.pHitObject;
hitInfoRTB.m_tHit = ( hitPacket[ iRT ].m_HitInfo.m_tHit +
hitPacket[ iRB ].m_HitInfo.m_tHit ) * 0.5f;
hitInfoRTB.m_HitNormal =
glm::normalize( ( hitPacket[ iRT ].m_HitInfo.m_HitNormal +
hitPacket[ iRB ].m_HitInfo.m_HitNormal ) * 0.5f );
cRTB = CCOLORRGB( shadeHit( bgColorY, rayRTB, hitInfoRTB, false, 0, false ) );
cRTB = BlendColor( cRTB, BlendColor( cRT, cRB) );
}
else
{
if( hitPacket[ iRT ].m_hitresult ||
hitPacket[ iRB ].m_hitresult ) // If any hits
{
const unsigned int nodeRT = hitPacket[ iRT ].m_HitInfo.m_acc_node_info;
const unsigned int nodeRB = hitPacket[ iRB ].m_HitInfo.m_acc_node_info;
bool hittedRTB = false;
if( nodeRT != 0 )
hittedRTB |= m_accelerator->Intersect( rayRTB, hitInfoRTB, nodeRT );
if( ( nodeRB != 0 ) &&
( nodeRB != nodeRT ) )
hittedRTB |= m_accelerator->Intersect( rayRTB, hitInfoRTB, nodeRB );
if( hittedRTB )
cRTB = CCOLORRGB( shadeHit( bgColorY,
rayRTB,
hitInfoRTB,
false,
0,
false) );
else
{
hitInfoRTB.m_tHit = std::numeric_limits<float>::infinity();
if( m_accelerator->Intersect( rayRTB, hitInfoRTB ) )
cRTB = CCOLORRGB( shadeHit( bgColorY,
rayRTB,
hitInfoRTB,
false,
0,
false ) );
}
}
}
cRTB_old = cRTB;
// Trace and shade cLRB
// /////////////////////////////////////////////////////////////
CCOLORRGB cLRB = bgColorYRGB;
const SFVEC3F &oriLB = blockPacket.m_ray[ iLB ].m_Origin;
const SFVEC3F &dirLB = blockPacket.m_ray[ iLB ].m_Dir;
// Trace the center ray
RAY rayLRB;
rayLRB.Init( ( oriLB + oriRB ) * 0.5f,
glm::normalize( ( dirLB + dirRB ) * 0.5f ) );
HITINFO hitInfoLRB;
hitInfoLRB.m_tHit = std::numeric_limits<float>::infinity();
if( hitPacket[ iLB ].m_hitresult &&
hitPacket[ iRB ].m_hitresult &&
( hitPacket[ iLB ].m_HitInfo.pHitObject ==
hitPacket[ iRB ].m_HitInfo.pHitObject ) )
{
hitInfoLRB.pHitObject = hitPacket[ iLB ].m_HitInfo.pHitObject;
hitInfoLRB.m_tHit = ( hitPacket[ iLB ].m_HitInfo.m_tHit +
hitPacket[ iRB ].m_HitInfo.m_tHit ) * 0.5f;
hitInfoLRB.m_HitNormal =
glm::normalize( ( hitPacket[ iLB ].m_HitInfo.m_HitNormal +
hitPacket[ iRB ].m_HitInfo.m_HitNormal ) * 0.5f );
cLRB = CCOLORRGB( shadeHit( bgColorY, rayLRB, hitInfoLRB, false, 0, false ) );
cLRB = BlendColor( cLRB, BlendColor( cLB, cRB) );
}
else
{
if( hitPacket[ iLB ].m_hitresult ||
hitPacket[ iRB ].m_hitresult ) // If any hits
{
const unsigned int nodeLB = hitPacket[ iLB ].m_HitInfo.m_acc_node_info;
const unsigned int nodeRB = hitPacket[ iRB ].m_HitInfo.m_acc_node_info;
bool hittedLRB = false;
if( nodeLB != 0 )
hittedLRB |= m_accelerator->Intersect( rayLRB, hitInfoLRB, nodeLB );
if( ( nodeRB != 0 ) &&
( nodeRB != nodeLB ) )
hittedLRB |= m_accelerator->Intersect( rayLRB, hitInfoLRB, nodeRB );
if( hittedLRB )
cLRB = CCOLORRGB( shadeHit( bgColorY, rayLRB, hitInfoLRB, false, 0, false ) );
else
{
hitInfoLRB.m_tHit = std::numeric_limits<float>::infinity();
if( m_accelerator->Intersect( rayLRB, hitInfoLRB ) )
cLRB = CCOLORRGB( shadeHit( bgColorY,
rayLRB,
hitInfoLRB,
false,
0,
false ) );
}
}
}
cLRB_old[x] = cLRB;
// Trace and shade cLTC
// /////////////////////////////////////////////////////////////
CCOLORRGB cLTC = BlendColor( cLT , cC );
if( hitPacket[ iLT ].m_hitresult || hittedC )
{
// Trace the center ray
RAY rayLTC;
rayLTC.Init( ( oriLT + oriC ) * 0.5f,
glm::normalize( ( dirLT + dirC ) * 0.5f ) );
HITINFO hitInfoLTC;
hitInfoLTC.m_tHit = std::numeric_limits<float>::infinity();
bool hitted = false;
if( hittedC )
hitted = centerHitInfo.pHitObject->Intersect( rayLTC, hitInfoLTC );
else
if( hitPacket[ iLT ].m_hitresult )
hitted = hitPacket[ iLT ].m_HitInfo.pHitObject->Intersect( rayLTC,
hitInfoLTC );
if( hitted )
cLTC = CCOLORRGB( shadeHit( bgColorY, rayLTC, hitInfoLTC, false, 0, false ) );
}
// Trace and shade cRTC
// /////////////////////////////////////////////////////////////
CCOLORRGB cRTC = BlendColor( cRT , cC );
if( hitPacket[ iRT ].m_hitresult || hittedC )
{
// Trace the center ray
RAY rayRTC;
rayRTC.Init( ( oriRT + oriC ) * 0.5f,
glm::normalize( ( dirRT + dirC ) * 0.5f ) );
HITINFO hitInfoRTC;
hitInfoRTC.m_tHit = std::numeric_limits<float>::infinity();
bool hitted = false;
if( hittedC )
hitted = centerHitInfo.pHitObject->Intersect( rayRTC, hitInfoRTC );
else
if( hitPacket[ iRT ].m_hitresult )
hitted = hitPacket[ iRT ].m_HitInfo.pHitObject->Intersect( rayRTC,
hitInfoRTC );
if( hitted )
cRTC = CCOLORRGB( shadeHit( bgColorY, rayRTC, hitInfoRTC, false, 0, false ) );
}
// Trace and shade cLBC
// /////////////////////////////////////////////////////////////
CCOLORRGB cLBC = BlendColor( cLB , cC );
if( hitPacket[ iLB ].m_hitresult || hittedC )
{
// Trace the center ray
RAY rayLBC;
rayLBC.Init( ( oriLB + oriC ) * 0.5f,
glm::normalize( ( dirLB + dirC ) * 0.5f ) );
HITINFO hitInfoLBC;
hitInfoLBC.m_tHit = std::numeric_limits<float>::infinity();
bool hitted = false;
if( hittedC )
hitted = centerHitInfo.pHitObject->Intersect( rayLBC, hitInfoLBC );
else
if( hitPacket[ iLB ].m_hitresult )
hitted = hitPacket[ iLB ].m_HitInfo.pHitObject->Intersect( rayLBC,
hitInfoLBC );
if( hitted )
cLBC = CCOLORRGB( shadeHit( bgColorY, rayLBC, hitInfoLBC, false, 0, false ) );
}
// Trace and shade cRBC
// /////////////////////////////////////////////////////////////
CCOLORRGB cRBC = BlendColor( cRB , cC );
if( hitPacket[ iRB ].m_hitresult || hittedC )
{
// Trace the center ray
RAY rayRBC;
rayRBC.Init( ( oriRB + oriC ) * 0.5f,
glm::normalize( ( dirRB + dirC ) * 0.5f ) );
HITINFO hitInfoRBC;
hitInfoRBC.m_tHit = std::numeric_limits<float>::infinity();
bool hitted = false;
if( hittedC )
hitted = centerHitInfo.pHitObject->Intersect( rayRBC, hitInfoRBC );
else
if( hitPacket[ iRB ].m_hitresult )
hitted = hitPacket[ iRB ].m_HitInfo.pHitObject->Intersect( rayRBC,
hitInfoRBC );
if( hitted )
cRBC = CCOLORRGB( shadeHit( bgColorY, rayRBC, hitInfoRBC, false, 0, false ) );
}
// Set pixel colors
// /////////////////////////////////////////////////////////////
GLubyte *ptr = &ptrPBO[ (4 * x + m_blockPositionsFast[iBlock].x +
m_realBufferSize.x *
(m_blockPositionsFast[iBlock].y + 4 * y)) * 4 ];
SetPixel( ptr + 0, cLT );
SetPixel( ptr + 4, BlendColor( cLT, cLRT, cLTC ) );
SetPixel( ptr + 8, cLRT );
SetPixel( ptr + 12, BlendColor( cLRT, cRT, cRTC ) );
ptr += m_realBufferSize.x * 4;
SetPixel( ptr + 0, BlendColor( cLT , cLTB, cLTC ) );
SetPixel( ptr + 4, BlendColor( cLTC, BlendColor( cLT , cC ) ) );
SetPixel( ptr + 8, BlendColor( cC, BlendColor( cLRT, cLTC, cRTC ) ) );
SetPixel( ptr + 12, BlendColor( cRTC, BlendColor( cRT , cC ) ) );
ptr += m_realBufferSize.x * 4;
SetPixel( ptr + 0, cLTB );
SetPixel( ptr + 4, BlendColor( cC, BlendColor( cLTB, cLTC, cLBC ) ) );
SetPixel( ptr + 8, cC );
SetPixel( ptr + 12, BlendColor( cC, BlendColor( cRTB, cRTC, cRBC ) ) );
ptr += m_realBufferSize.x * 4;
SetPixel( ptr + 0, BlendColor( cLB , cLTB, cLBC ) );
SetPixel( ptr + 4, BlendColor( cLBC, BlendColor( cLB , cC ) ) );
SetPixel( ptr + 8, BlendColor( cC, BlendColor( cLRB, cLBC, cRBC ) ) );
SetPixel( ptr + 12, BlendColor( cRBC, BlendColor( cRB , cC ) ) );
}
}
}
threadsFinished++;
} );
t.detach();
}
while( threadsFinished < parallelThreadCount )
std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
}
#define USE_EXPERIMENTAL_SOFT_SHADOWS 1
SFVEC3F C3D_RENDER_RAYTRACING::shadeHit( const SFVEC3F &aBgColor,
const RAY &aRay,
HITINFO &aHitInfo,
bool aIsInsideObject,
unsigned int aRecursiveLevel,
bool is_testShadow ) const
{
if( aRecursiveLevel > 2 )
return SFVEC3F( 0.0f );
SFVEC3F hitPoint = aHitInfo.m_HitPoint;
if( !m_isPreview )
hitPoint += aHitInfo.m_HitNormal * m_boardAdapter.GetNonCopperLayerThickness3DU() * 0.6f;
const CMATERIAL *objMaterial = aHitInfo.pHitObject->GetMaterial();
wxASSERT( objMaterial != NULL );
const SFVEC3F diffuseColorObj = aHitInfo.pHitObject->GetDiffuseColor( aHitInfo );
SFVEC3F outColor = objMaterial->GetEmissiveColor() + objMaterial->GetAmbientColor();
const LIST_LIGHT &lightList = m_lights.GetList();
#if USE_EXPERIMENTAL_SOFT_SHADOWS
const bool is_aa_enabled = m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_ANTI_ALIASING ) &&
(!m_isPreview);
#endif
float shadow_att_factor_sum = 0.0f;
unsigned int nr_lights_that_can_cast_shadows = 0;
for( LIST_LIGHT::const_iterator ii = lightList.begin();
ii != lightList.end();
++ii )
{
const CLIGHT *light = (CLIGHT *)*ii;
SFVEC3F vectorToLight;
SFVEC3F colorOfLight;
float distToLight;
light->GetLightParameters( hitPoint, vectorToLight, colorOfLight, distToLight );
if( m_isPreview )
colorOfLight = SFVEC3F( 1.0f );
/*
if( (!m_isPreview) &&
// Little hack to make randomness to the shading and shadows
m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
vectorToLight = glm::normalize( vectorToLight +
UniformRandomHemisphereDirection() * 0.1f );
*/
const float NdotL = glm::dot( aHitInfo.m_HitNormal, vectorToLight );
// Only calc shade if the normal is facing the direction of light,
// otherwise it is in the shadow
if( NdotL >= FLT_EPSILON )
{
float shadow_att_factor_light = 1.0f;
if( is_testShadow && light->GetCastShadows() )
{
nr_lights_that_can_cast_shadows++;
#if USE_EXPERIMENTAL_SOFT_SHADOWS
if( (!is_aa_enabled) ||
// For rays that are recursive, just calculate one hit shadow
(aRecursiveLevel > 0) ||
// Only use soft shadows if using post processing
(!m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_POST_PROCESSING ) )
)
{
#endif
RAY rayToLight;
rayToLight.Init( hitPoint, vectorToLight );
// Test if point is not in the shadow.
// Test for any hit from the point in the direction of light
if( m_accelerator->IntersectP( rayToLight, distToLight ) )
shadow_att_factor_light = 0.0f;
#if USE_EXPERIMENTAL_SOFT_SHADOWS
}
// Experimental softshadow calculation
else
{
const unsigned int shadow_number_of_samples = 3;
const float shadow_inc_factor = 1.0f / (float)(shadow_number_of_samples);
for( unsigned int i = 0; i < shadow_number_of_samples; ++i )
{
const SFVEC3F unifVector = UniformRandomHemisphereDirection();
const SFVEC3F disturbed_vector_to_light = glm::normalize( vectorToLight +
unifVector *
0.05f );
RAY rayToLight;
rayToLight.Init( hitPoint, disturbed_vector_to_light );
// !TODO: there are multiple ways that this tests can be
// optimized. Eg: by packing rays or to test against the
// latest hit object.
if( m_accelerator->IntersectP( rayToLight, distToLight ) )
{
shadow_att_factor_light -= shadow_inc_factor;
}
}
}
#endif
shadow_att_factor_sum += shadow_att_factor_light;
}
outColor += objMaterial->Shade( aRay,
aHitInfo,
NdotL,
diffuseColorObj,
vectorToLight,
colorOfLight,
shadow_att_factor_light );
}
// Only use the headlight for preview
if( m_isPreview )
break;
}
// Improvement: this is not taking in account the lightcolor
if( nr_lights_that_can_cast_shadows > 0 )
{
aHitInfo.m_ShadowFactor = glm::max( shadow_att_factor_sum /
(float)(nr_lights_that_can_cast_shadows * 1.0f), 0.0f );
}
else
{
aHitInfo.m_ShadowFactor = 1.0f;
}
// Clamp color to not be brighter than 1.0f
outColor = glm::min( outColor, SFVEC3F( 1.0f ) );
if( !m_isPreview )
{
// Reflections
// /////////////////////////////////////////////////////////////////////
if( !aIsInsideObject &&
(objMaterial->GetReflection() > 0.0f) &&
m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_REFLECTIONS ) )
{
const unsigned int reflection_number_of_samples = objMaterial->GetNrReflectionsSamples();
SFVEC3F sum_color = SFVEC3F(0.0f);
const SFVEC3F reflectVector = aRay.m_Dir -
2.0f * glm::dot( aRay.m_Dir, aHitInfo.m_HitNormal ) *
aHitInfo.m_HitNormal;
for( unsigned int i = 0; i < reflection_number_of_samples; ++i )
{
// Apply some randomize to the reflected vector
const SFVEC3F random_reflectVector =
glm::normalize( reflectVector +
UniformRandomHemisphereDirection() *
0.025f );
RAY reflectedRay;
reflectedRay.Init( hitPoint, random_reflectVector );
HITINFO reflectedHit;
reflectedHit.m_tHit = std::numeric_limits<float>::infinity();
if( m_accelerator->Intersect( reflectedRay, reflectedHit ) )
{
sum_color += ( diffuseColorObj + objMaterial->GetSpecularColor() ) *
shadeHit( aBgColor,
reflectedRay,
reflectedHit,
false,
aRecursiveLevel + 1,
is_testShadow ) *
SFVEC3F( objMaterial->GetReflection() *
// Falloff factor
(1.0f / ( 1.0f + 0.75f * reflectedHit.m_tHit *
reflectedHit.m_tHit) ) );
}
}
outColor += (sum_color / SFVEC3F( (float)reflection_number_of_samples) );
}
// Refractions
// /////////////////////////////////////////////////////////////////////
const float objTransparency = aHitInfo.pHitObject->GetModelTransparency();
if( ( objTransparency > 0.0f ) &&
m_boardAdapter.GetFlag( FL_RENDER_RAYTRACING_REFRACTIONS ) )
{
const float airIndex = 1.000293f;
const float glassIndex = 1.49f;
const float air_over_glass = airIndex / glassIndex;
const float glass_over_air = glassIndex / airIndex;
const float refractionRatio = aIsInsideObject?glass_over_air:air_over_glass;
SFVEC3F refractedVector;
if( Refract( aRay.m_Dir,
aHitInfo.m_HitNormal,
refractionRatio,
refractedVector ) )
{
// This increase the start point by a "fixed" factor so it will work the
// same for all distances
const SFVEC3F startPoint = aRay.at( NextFloatUp(
NextFloatUp(
NextFloatUp( aHitInfo.m_tHit ) ) ) );
const unsigned int refractions_number_of_samples = objMaterial->GetNrRefractionsSamples();
SFVEC3F sum_color = SFVEC3F(0.0f);
for( unsigned int i = 0; i < refractions_number_of_samples; ++i )
{
RAY refractedRay;
if( refractions_number_of_samples > 1 )
{
// apply some randomize to the refracted vector
const SFVEC3F randomizeRefractedVector = glm::normalize( refractedVector +
UniformRandomHemisphereDirection() *
0.15f *
(1.0f - objTransparency) );
refractedRay.Init( startPoint, randomizeRefractedVector );
}
else
{
refractedRay.Init( startPoint, refractedVector );
}
HITINFO refractedHit;
refractedHit.m_tHit = std::numeric_limits<float>::infinity();
SFVEC3F refractedColor = objMaterial->GetAmbientColor();
if( m_accelerator->Intersect( refractedRay, refractedHit ) )
{
refractedColor = shadeHit( aBgColor,
refractedRay,
refractedHit,
true,
aRecursiveLevel + 1,
false );
const SFVEC3F absorbance = ( SFVEC3F(1.0f) - diffuseColorObj ) *
(1.0f - objTransparency ) *
objMaterial->GetAbsorvance() * // Adjust falloff factor
-refractedHit.m_tHit;
const SFVEC3F transparency = SFVEC3F( expf( absorbance.r ),
expf( absorbance.g ),
expf( absorbance.b ) );
sum_color += refractedColor * transparency * objTransparency;
}
else
{
sum_color += refractedColor * objTransparency;
}
}
outColor = outColor * (1.0f - objTransparency) +
(sum_color / SFVEC3F( (float)refractions_number_of_samples) );
}
}
}
//outColor += glm::max( -glm::dot( aHitInfo.m_HitNormal, aRay.m_Dir ), 0.0f ) *
// objMaterial->GetAmbientColor();
return outColor;
}
void C3D_RENDER_RAYTRACING::initializeNewWindowSize()
{
opengl_init_pbo();
}
void C3D_RENDER_RAYTRACING::opengl_init_pbo()
{
if( GLEW_ARB_pixel_buffer_object )
{
m_opengl_support_vertex_buffer_object = true;
// Try to delete vbo if it was already initialized
opengl_delete_pbo();
// Learn about Pixel buffer objects at:
// http://www.songho.ca/opengl/gl_pbo.html
// http://web.eecs.umich.edu/~sugih/courses/eecs487/lectures/25-PBO+Mipmapping.pdf
// "create 2 pixel buffer objects, you need to delete them when program exits.
// glBufferDataARB with NULL pointer reserves only memory space."
// This sets the number of RGBA pixels
m_pboDataSize = m_realBufferSize.x * m_realBufferSize.y * 4;
glGenBuffersARB( 1, &m_pboId );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboId );
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_pboDataSize, 0, GL_STREAM_DRAW_ARB );
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
wxLogTrace( m_logTrace,
wxT( "C3D_RENDER_RAYTRACING:: GLEW_ARB_pixel_buffer_object is supported" ) );
}
}
bool C3D_RENDER_RAYTRACING::initializeOpenGL()
{
m_is_opengl_initialized = true;
return true;
}
static float distance( const SFVEC2UI& a, const SFVEC2UI& b )
{
const float dx = (float) a.x - (float) b.x;
const float dy = (float) a.y - (float) b.y;
return hypotf( dx, dy );
}
void C3D_RENDER_RAYTRACING::initialize_block_positions()
{
m_realBufferSize = SFVEC2UI( 0 );
// Calc block positions for fast preview mode
// /////////////////////////////////////////////////////////////////////
m_blockPositionsFast.clear();
unsigned int i = 0;
while(1)
{
const unsigned int mX = DecodeMorton2X(i);
const unsigned int mY = DecodeMorton2Y(i);
i++;
const SFVEC2UI blockPos( mX * 4 * RAYPACKET_DIM - mX * 4,
mY * 4 * RAYPACKET_DIM - mY * 4);
if( ( blockPos.x >= ( (unsigned int)m_windowSize.x - ( 4 * RAYPACKET_DIM + 4 ) ) ) &&
( blockPos.y >= ( (unsigned int)m_windowSize.y - ( 4 * RAYPACKET_DIM + 4 ) ) ) )
break;
if( ( blockPos.x < ( (unsigned int)m_windowSize.x - ( 4 * RAYPACKET_DIM + 4) ) ) &&
( blockPos.y < ( (unsigned int)m_windowSize.y - ( 4 * RAYPACKET_DIM + 4) ) ) )
{
m_blockPositionsFast.push_back( blockPos );
if( blockPos.x > m_realBufferSize.x )
m_realBufferSize.x = blockPos.x;
if( blockPos.y > m_realBufferSize.y )
m_realBufferSize.y = blockPos.y;
}
}
m_fastPreviewModeSize = m_realBufferSize;
m_realBufferSize.x = ((m_realBufferSize.x + RAYPACKET_DIM * 4) & RAYPACKET_INVMASK);
m_realBufferSize.y = ((m_realBufferSize.y + RAYPACKET_DIM * 4) & RAYPACKET_INVMASK);
m_xoffset = (m_windowSize.x - m_realBufferSize.x) / 2;
m_yoffset = (m_windowSize.y - m_realBufferSize.y) / 2;
m_postshader_ssao.UpdateSize( m_realBufferSize );
// Calc block positions for regular rendering. Choose an 'inside out'
// style of rendering
// /////////////////////////////////////////////////////////////////////
m_blockPositions.clear();
const int blocks_x = m_realBufferSize.x / RAYPACKET_DIM;
const int blocks_y = m_realBufferSize.y / RAYPACKET_DIM;
m_blockPositions.reserve( blocks_x * blocks_y );
for( int x = 0; x < blocks_x; ++x )
for( int y = 0; y < blocks_y; ++y )
m_blockPositions.emplace_back( x * RAYPACKET_DIM, y * RAYPACKET_DIM );
const SFVEC2UI center( m_realBufferSize.x / 2, m_realBufferSize.y / 2 );
std::sort( m_blockPositions.begin(), m_blockPositions.end(),
[&]( const SFVEC2UI& a, const SFVEC2UI& b ) {
// Sort order: inside out.
return distance( a, center ) < distance( b, center );
} );
// Create m_shader buffer
delete[] m_shaderBuffer;
m_shaderBuffer = new SFVEC3F[m_realBufferSize.x * m_realBufferSize.y];
opengl_init_pbo();
}