Skip to content

Instantly share code, notes, and snippets.

@porst17
Last active July 29, 2016 07:56
Show Gist options
  • Save porst17/15318057662359c9a383b44d1235c439 to your computer and use it in GitHub Desktop.
Save porst17/15318057662359c9a383b44d1235c439 to your computer and use it in GitHub Desktop.
Combine 4 orthographic renderings of a sphere into a equirectangular map of the sphere
/*
* Copyright 2016 Christian Stussak
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/***********************************************************************
* Combine 4 orthographic renderings of a sphere into a equirectangular
* map of the sphere.
*
* Compile with
* $ g++ op2erp.cpp -l freeimage -o op2erp
***********************************************************************/
#include <FreeImage.h>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <functional>
#define WIDTH 1024
#define HEIGHT 512
#define NUM_SAMPLES_SQRT 3
#define NUM_SAMPLES ( NUM_SAMPLES_SQRT * NUM_SAMPLES_SQRT )
#define THRESHOLD 0.25
#define EPSILON 0.000001 // to avoid sampling problems along the boundaries
FIBITMAP *front, *left, *back, *right, *top, *bottom;
FIBITMAP* FreeImage_Load32Bits( char *filename )
{
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(filename,0);
FIBITMAP* image = FreeImage_Load(format, filename);
if( !image )
std::cout << "unable to load " << filename << std::endl;
FIBITMAP* result = FreeImage_ConvertTo32Bits(image);
if( !image )
std::cout << "unable to convert " << filename << " to 32bit RGBA" << std::endl;
FreeImage_Unload(image);
return result;
}
template<typename T>
T clamp(const T &val, const T &min, const T &max)
{
return std::max(min, std::min(max, val));
}
RGBQUAD sampler( double phi, double theta )
{
RGBQUAD result;
if( theta > ( 1.0 - THRESHOLD ) * M_PI )
{
// sample bottom image
int w = FreeImage_GetWidth(bottom);
int h = FreeImage_GetHeight(bottom);
phi += 0;
int px = clamp( (int) ( ( ( ( std::sin( theta ) * std::sin( phi ) ) * w / 2.0 ) + w / 2 ) ), 0, w );
int py = clamp( (int) ( ( ( std::sin( theta ) * std::cos( phi ) ) * h / 2.0 ) + h / 2.0 ), 0, h );
FreeImage_GetPixelColor( bottom, px, py, &result );
}
else if ( theta < THRESHOLD * M_PI )
{
// sample top image
int w = FreeImage_GetWidth(top);
int h = FreeImage_GetHeight(top);
phi = M_PI - phi;
int px = clamp( (int) ( ( ( ( std::sin( theta ) * std::sin( phi ) ) * w / 2.0 ) + w / 2 ) ), 0, w );
int py = clamp( (int) ( ( ( std::sin( theta ) * std::cos( phi ) ) * h / 2.0 ) + h / 2.0 ), 0, h );
FreeImage_GetPixelColor( top, px, py, &result );
}
else
{
FIBITMAP *image;
if( phi < 0.125 * 2.0 * M_PI || phi >= 0.875 * 2.0 * M_PI )
{
// sample front image
image = front;
phi += 1.5 * M_PI;
}
else if( phi < 0.375 * 2.0 * M_PI )
{
// sample right image
image = right;
phi += 1.0 * M_PI;
}
else if( phi < 0.625 * 2.0 * M_PI )
{
// sample back image
image = back;
phi += 0.5 * M_PI;
}
else
{
// sample left image
image = left;
}
theta += M_PI;
int w = FreeImage_GetWidth(image);
int h = FreeImage_GetHeight(image);
int px = ( w - 1 ) - clamp( (int) ( ( ( ( std::sin( theta ) * std::cos( phi ) ) * w / 2.0 ) + w / 2 ) ), 0, w );
int py = ( h - 1 ) - clamp( (int) ( ( std::cos( theta ) * h / 2.0 ) + h / 2.0 ), 0, h );
FreeImage_GetPixelColor( image, px, py, &result );
}
return result;
}
RGBQUAD sample( double phi_min, double phi_max, double theta_min, double theta_max )
{
int R = 0, G = 0, B = 0;
RGBQUAD temp;
for( int i = 0; i < NUM_SAMPLES_SQRT; ++i )
{
for( int j = 0; j < NUM_SAMPLES_SQRT; ++j )
{
double phi = phi_min + ( phi_max - phi_min ) * ( i / NUM_SAMPLES_SQRT );
double theta = theta_min + ( theta_max - theta_min ) * ( j / NUM_SAMPLES_SQRT );
temp = sampler( phi, theta );
R += temp.rgbRed;
G += temp.rgbGreen;
B += temp.rgbBlue;
}
}
temp.rgbRed = R / NUM_SAMPLES;
temp.rgbGreen = G / NUM_SAMPLES;
temp.rgbBlue = B / NUM_SAMPLES;
return temp;
}
int main( int argc, char* argv[] )
{
if(argc != 8)
{
std::cout << "Usage: op2erp front.png left.png back.png right.png top.png bottom.png merged_result.png" << std::endl;
std::exit(-1);
}
FreeImage_Initialise(false);
front = FreeImage_Load32Bits( argv[ 1 ] );
left = FreeImage_Load32Bits( argv[ 2 ] );
back = FreeImage_Load32Bits( argv[ 3 ] );
right = FreeImage_Load32Bits( argv[ 4 ] );
top = FreeImage_Load32Bits( argv[ 5 ] );
bottom = FreeImage_Load32Bits( argv[ 6 ] );
FIBITMAP *output = FreeImage_Allocate( WIDTH, HEIGHT, 32);
for( int x = 0; x < WIDTH; ++x )
{
for( int y = 0; y < HEIGHT; ++y )
{
double theta_min = clamp( M_PI * ( 1.0 - y / ( double ) HEIGHT ), EPSILON, M_PI - EPSILON );
double theta_max = clamp( M_PI * ( 1.0 - ( y + 1 ) / ( double ) HEIGHT ), EPSILON, M_PI - EPSILON );
double phi_min = 2.0 * M_PI * ( x / ( double ) WIDTH );
double phi_max = 2.0 * M_PI * ( ( x + 1 ) / ( double ) WIDTH );
RGBQUAD color = sample( phi_min, phi_max, theta_min, theta_max );
FreeImage_SetPixelColor( output, x, y, &color );
}
}
std::cout << "done filling pixel data" << std::endl;
if (FreeImage_Save(FIF_PNG, output, argv[ 7 ], 0))
{
std::cout << "bitmap successfully saved!" << std::endl;
}
FreeImage_DeInitialise();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment