Jump to content
  • Advertisement
Sign in to follow this  
Jemme

CubeMap Conversion

Recommended Posts

Posted (edited)

Hello,

Im trying to automatically convert a cubemap image into an array of textures for a dds file:

CubeLayout6Faces.png

I have detected which Cubemap type the files are in, however i need to copy the data of each individual square into an array, this would require performing (ImageWidth * 6) std::memcpy operations, so for a 1024x1024 individual cubemap size map your doing 6144 copy operations.

Is there anyway to copy a section from an image (think paint select and copy) in a more efficient way?

Thanks,

Edited by Jemme
Simplified the question

Share this post


Link to post
Share on other sites
Advertisement
Posted (edited)

You could try rendering the six sides of the cubemap one at a time to a framebuffer with a texture mounted to it. Extract the pixels from the texture and copy them to the correct portion of the 2D array of pixel values.

Unfortunately, you cannot use one large framebuffer that's 6144x1024 due to texture size limitations. Just use a 1024x1024 size texture for each individual face of the cubemap.

Edited by Solid_Spy

Share this post


Link to post
Share on other sites
On 8/5/2018 at 5:55 AM, Solid_Spy said:

You could try rendering the six sides of the cubemap one at a time to a framebuffer with a texture mounted to it. Extract the pixels from the texture and copy them to the correct portion of the 2D array of pixel values.

Unfortunately, you cannot use one large framebuffer that's 6144x1024 due to texture size limitations. Just use a 1024x1024 size texture for each individual face of the cubemap.

Hi, i meant a sky map you download off the internet for use as a skybox or IBL, they come in the various formats shown in the picture i posted. I originally wanted to convert from those into a texture array, it means extracting each individual image out of the original and adding its data onto the end of my binary file.

I eventually gave up because the copy operations seemed Over the top, so i settled for forcing separate image assignment and convert that way:

image.png.b9067807e8747a4eef3b135fef8773c0.png

When the user clicks build it converts each image into a  dds style format, by default it uses DXT5 with mipmapping enabled. It adds the bytes from each image 1 after another itno a huge buffer of data and sets arraysize and mips to what ever the image needs. Then i just load it up at run time using my Texture2D class.

If you know how to split the images automatically from the original map's without doing 6000+ std::memcpy operations then id be happy to hear your suggestion :)

Share this post


Link to post
Share on other sites
Posted (edited)

Hello,

Back in my old DX11 engine I wrote a little tool to do similar things because as you've probably also noticed, most cubemap that we can download/buy on internet come in a format that is not convenient for a GPU. There was also no efficient tools that I found who could do such conversion without painful limitations. For example most tools were old and would just crash as soon as you try to convert an image that is above a certain resolution.

 

All I did is write a simple compute shader (my engine already had everything needed to easily just write a shader and get going). The compute shader was compiled on the fly before launching a conversion with proper pre-compiler instructions to make it execute the right code path for the type of conversion I was about to do. Compute shaders are very efficient for this IMO.

 

For example the compute shader could take a 15000x8000 panoramic image as input and project it to the outputs as a 2048x2048 texture array consisting of the 6 faces of a cubemap using very simple maths. After that process, my converter staged the texture from the GPU to the CPU and saved it in DDS format using my DDS library in the format that I wanted.

 

Surprisingly even for such huge textures the compute shader was able to convert all that in about 10ms even on HDR images that had 16/32 bits per channel. The GPU also make it easy to convert from or to any format (8bpp / 32bpp / 64bpp / 128bpp) as you manipulate normalized pixel values. I can hardly think of a more efficient way to do such conversion. My source images weren't unwrapped cubemap like in your original post but rather panoramic/equilateral like this one :

 

pano.jpg

 

However the math should still be simple for your scenario.

Here's a quick sample of the panoramic -> 6 faces part of my old compute shader :

	//-----------------------------------------------------------------------
	//	Convert from equilateral to cubemap.
	//-----------------------------------------------------------------------
#if WARPING_TECHNIQUE == EQUILATERAL_TO_CUBEMAP

	float3 normal;
	// find the normal of the current output pixel in a cubemap.
	[branch]
	if (dispatchThreadID.z == 0) {
		normal = normalize(float3(0.5f, (1.0f - sampleCoordinates.y) - 0.5f, (1.0f - sampleCoordinates.x) - 0.5f));
	}
	else if (dispatchThreadID.z == 1) {
		normal = normalize(float3(-0.5f, (1.0f - sampleCoordinates.y) - 0.5f, sampleCoordinates.x - 0.5f));
	}
	else if (dispatchThreadID.z == 2) {
		normal = normalize(float3(sampleCoordinates.x - 0.5f, 0.5f, sampleCoordinates.y - 0.5f));
	}
	else if (dispatchThreadID.z == 3) {
		normal = normalize(float3(sampleCoordinates.x - 0.5f, -0.5f, (1.0f - sampleCoordinates.y) - 0.5f));
	}
	else if (dispatchThreadID.z == 4) {
		normal = normalize(float3(sampleCoordinates.x - 0.5f, (1.0f - sampleCoordinates.y) - 0.5f, 0.5f));
	}
	else if (dispatchThreadID.z == 5) {
		normal = normalize(float3((1.0f - sampleCoordinates.x) - 0.5f, (1.0f - sampleCoordinates.y) - 0.5f, -0.5f));
	}

	// calculate the latitude from the y value.
	float latitude = acos(normal.y);

	// create two 3d vector to find out the longitude.
	float3 reference = float3(1.0f, 0.0f, 0.0f);
	float3 longitudeNormal;

	// pointing directly up or down.
	if (normal.x == 0.0f && normal.z == 0.0f)
	{
		longitudeNormal = normalize(float3(1.0f, 0.0f, 0.0f));
	}
	else
	{
		longitudeNormal = normalize(float3(normal.x, 0.0f, normal.z));
	}

	// calculate the edge for the cosine law.
	float arcDistance = distance(longitudeNormal, reference);

	// calculate the longitude.
	float longitude = acos((2.0f - pow(arcDistance, 2.0f)) / 2.0f);
	if (normal.z < 0.0f) {
		longitude = PI2 - longitude;
	}

	// get the sampling coordinates from the gathered angles.
	sampleCoordinates.x = longitude / PI2;
	sampleCoordinates.y = latitude / PI;

#endif

 

Also, assuming that the texture we want to save as DDS has no mipmaps, it was only a single memcpy instruction for me to grab all the image data once each face were converted into their own individual texture. So it's one way to make more efficient and you would also be able to implement more type of cubemap conversion that involves complex warping in the future.

Edited by ChuckNovice

Share this post


Link to post
Share on other sites
Posted (edited)

Oooh a skymap.

Yeah for that I just copied each side in a 1024x1024 size buffer, then converted it to an OpenGL TextureCubeMap. ChuckNovice's method seems better though. There is just the problem of creating custom painted skyboxes, which would be difficult if panoramic i guess.

Edited by Solid_Spy

Share this post


Link to post
Share on other sites
Posted (edited)
On 8/2/2018 at 2:02 PM, Jemme said:

Im trying to automatically convert a cubemap image into an array of textures for a dds file:

Doesn't NVIDIA have a tool for this still? I am honestly asking because I can't find it with my links to share it here.

 

Can't find the NVIDIA one any more but there is a AMD one: https://gpuopen.com/archive/gamescgi/cubemapgen/

Edited by Scouting Ninja

Share this post


Link to post
Share on other sites
7 hours ago, Scouting Ninja said:

Doesn't NVIDIA have a tool for this still? I am honestly asking because I can't find it with my links to share it here.

 

Can't find the NVIDIA one any more but there is a AMD one: https://gpuopen.com/archive/gamescgi/cubemapgen/

Im not sure if they have a cubemap tool, the format i use isn't really dds its custom because its easier to load in my case so i have to build the tool myself.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!