2019.1 converting cubemap to equirect problem

Hello,

I am trying to create an equirectangular image from a cubemap.

I have almost succeeded with this, but the equirectangular image has seams, and doesn’t line up properly.

This is the cubemap:

Imgur

Which seems to look ok.

This is the equirectangular image generated from it:

Imgur

Which doesn’t look ok, and doesn’t work properly. It has odd seaming issues, which I am guessing are caused by those center pieces of the image, that seem to be out of alignment.

This is my code:

               int sqr = 4096;

                Camera cam = virtuCamera.GetComponent<Camera>();
                cam.aspect = 1.0f;

                cam.targetTexture = rtex;
                cam.RenderToCubemap(rtex,63, Camera.MonoOrStereoscopicEye.Mono);

                rtex.ConvertToEquirect(stex, Camera.MonoOrStereoscopicEye.Mono);

                RenderTexture.active = stex;
                Texture2D virtualPhoto = new Texture2D(sqr, sqr, TextureFormat.RGB24, false);
                virtualPhoto.ReadPixels(new Rect(0, 0, sqr, sqr), 0, 0);

                RenderTexture.active = null;
                cam.targetTexture = null;
                virtualPhoto.Apply();

virtuCamera is just a regular camera set to “perspective” projection.
“rtex” is a 4096x4096 rendertexture, set to “cube” dimension.
“stex” is a 4096x4096 rendertexture, set to “2D” (I have also tried 4096x2048 here)

I believe the issue here has something to do with how the cubemap has been generated.

There seem to be seams and lines on the cubemap as well, so the issue could be there, and not in the equirectangular conversion.

I have tried various ways of generating cubemaps and equirectangular images, but all of them have produced the same results.

Is there some issue with how I am using RenderToCubemap?

Did you ever resolve this? Seeing the same issue.

No, unfortunately not. It’s not too noticeable in my project, so I decided to leave it for the time being, but there’s still an issue. Let me know if you figure it out!

Thanks for the reply, I’ll let you know. My issue is very noticeable and must be resolved.

Are there seams in the cubemap itself prior to conversion?

If there are, then I suspect it’s some effects using view facing instead of camera facing. View facing billboards change when the camera turns, so they’re not safe to render into a cubemap, you have to use camera facing.

If there aren’t seams in the cubemap, it’s relatively trivial to write a shader that would produce an equirectangular image from a cubemap if for some reason Unity’s implementation is broken. But looking at the built-in shader it doesn’t look wrong.

Hi, thanks for your reply. It’s been a while since I posted this, but I uploaded my cubemap image above (the first link) and it seems fine, there don’t seem to be any issues with it.

The issues seem to occur when converting the cubemap to an equirectangular image.

Is there some other shader I could try for comparisions sake? I’m pretty sure I tried several techniques before I posted this, so it could be, as you said, a camera facing issue.

I am using billboards in the scene. I will take a look at this tomorrow, and see if I can verify the exact cause.

I mean, that could be a view of a cubemap, or maybe one face of a cubemap. It’s not an entire cubemap itself. A cubemap as a texture is going to look like one of these layouts:
https://docs.unity3d.com/Manual/class-Cubemap.html

Looking at the equirectangular example you posted, it almost looks like it’s that one texture above on as 6 faces of the cubemap?

Yes, you’re right, that image that I posted was the output of the cubemap that I got in unity. I basically just did a “rendertocubemap” and posted the output. Is there another way of doing this, that would get a more conventional output?

As I said, the seams only appear when I convert to equirect.

This is my code to generate the cubemap (I am rendering to a texture here, not a rendertexture):

                int sqr = 4096;
                Camera cam = virtuCamera.GetComponent<Camera>();
                cam.aspect = 1.0f;         
                RenderTexture tempRT = new RenderTexture(sqr, sqr/2, 24);
                            
                cam.targetTexture = tempRT;
                cam.Render();

                RenderTexture.active = tempRT;
                Texture2D virtualPhoto = new Texture2D(sqr, sqr/2, TextureFormat.RGB24, false);
               
                virtualPhoto.ReadPixels(new Rect(0, 0, sqr, sqr/2), 0, 0); // you get the center section

                RenderTexture.active = null; // "just in case"
                cam.targetTexture = null;

                virtualPhoto.Apply();
                    
                byte[] bytes;
                bytes = virtualPhoto.EncodeToPNG();
                System.IO.File.WriteAllBytes(OurTempSquareImageLocation(), bytes);

So the original snippet of code from the first post was fine. The post you have in this latest one is just rendering the camera normally and then getting a copy of that one view. A cube map is 6 views in the 6 axial directions, hence the cube part of cubemap.

I could go into how to copy a cube map into a texture that you can save to disk in the form of a cross or stripe by copying each individually face to different parts of a larger texture … but that won’t fix the starting problem.

Try putting a reflection probe in your scene and bake it. If there are seams visible when looking at the sphere it renders when you have the probe selected, the problem is what you’re rendering, not the copy to the equirectangular texture.

edit: If you’re trying to bake particle systems, this won’t work since those never show in a reflection probe.

Hi, thanks for your reply!

The reflection probe shows up as just black (background colour). I suspect this is because my skybox is just black, I am drawing my nebulae, etc, using the spacegraphicstoolkit.

It’s creating stars, nebulae, etc, using it’s own custom rendering system. Could this be the cause of the issue? I don’t think it uses particle effects, but it could? It there some other way to test the cubemap generation?

Okay. Cheapo test setup.

Put camera in scene, set to 90 degrees. Change Game view to show at 512x512. Zero out camera rotation and position. Take screen grab (just basic windows / Mac screen grab). Rotate camera 90 degrees on y axis, screen grab again. See if they line up or if there’s a seam. If seam, then the content is a problem. If no seam, rotate another 90 degrees and check again. Repeat. If no seam still, then there’s a bug with Unity’s cubemap rendering or the equirectangular generation… but I’m at 99% sure you’ll see a seam.

I personally have no experience with the space graphics kit, so I don’t know how it works. But it is very likely the cause of the problem yes. It is a little odd though since I think some of the features of that asset work by rendering itself to a cubemap…

Unfortunately, it seems that you’re right!

I followed your instructions, and it seems like the images don’t line up. The Space Graphics toolkit does seem to use some kind of billboarding system, and as I rotated the camera, I could actually see the billboards rotating slightly.

This is likely the issue.

I’m guessing there is no workaround for this, other than, obviously, replacing the Space Graphics Background?

Thanks for your help in this by the way, at least I know what the issue is now.

It would be interesting to see if jeremedia is using the SGT too, or a similar system.

Well, you could look to modify the space toolkit’s code, or ask the author about a solution. They appear to still be active.

I have done that, thank you. However it seems that I am using an old version of the SGT, and I would need to update to fix the issue, which would take ages.

Quick question, would it be feasible to use six individual cameras to render the cubemap at the same time from the six cardinal directions? Or would that cause the same thing to happen?

I am doing this:

           ecam1.gameObject.SetActive(true);
            ecam2.gameObject.SetActive(true);
            ecam3.gameObject.SetActive(true);
            ecam4.gameObject.SetActive(true);
            ecam5.gameObject.SetActive(true);
            ecam6.gameObject.SetActive(true);

            int sqr = 2048;

            ecam1.aspect = 1.0f;
            ecam2.aspect = 1.0f;
            ecam3.aspect = 1.0f;
            ecam4.aspect = 1.0f;
            ecam5.aspect = 1.0f;
            ecam6.aspect = 1.0f;

            float brightness = 5;//2;
            setbackgroundbrightness(brightness);

            ecam1.RenderToCubemap(rtex, 1, Camera.MonoOrStereoscopicEye.Mono);
            ecam2.RenderToCubemap(rtex, 2, Camera.MonoOrStereoscopicEye.Mono);
            ecam3.RenderToCubemap(rtex, 3, Camera.MonoOrStereoscopicEye.Mono);
            ecam4.RenderToCubemap(rtex, 4, Camera.MonoOrStereoscopicEye.Mono);
            ecam5.RenderToCubemap(rtex, 5, Camera.MonoOrStereoscopicEye.Mono);
            ecam6.RenderToCubemap(rtex, 6, Camera.MonoOrStereoscopicEye.Mono);
   
            rtex.ConvertToEquirect(stex, Camera.MonoOrStereoscopicEye.Mono);

            RenderTexture.active = stex;
            Texture2D virtualPhoto = new Texture2D(sqr, sqr / 2, TextureFormat.RGB24, false);
            virtualPhoto.ReadPixels(new Rect(0, 0, sqr, sqr / 2), 0, 0); // you get the center section

            RenderTexture.active = null;        
            ecam1.targetTexture = null;
            ecam2.targetTexture = null;
            ecam3.targetTexture = null;
            ecam4.targetTexture = null;
            ecam5.targetTexture = null;
            ecam6.targetTexture = null;

            virtualPhoto.Apply();

            asm.starFieldTexture = virtualPhoto;
            asm.starNoiseTexture = null;

            asm.skyMaterial.EnableKeyword("_ALPHABLEND_ON (“Fade” Transparency Rendering Mode)");
     
            ecam1.gameObject.SetActive(false);
            ecam2.gameObject.SetActive(false);
            ecam3.gameObject.SetActive(false);
            ecam4.gameObject.SetActive(false);
            ecam5.gameObject.SetActive(false);
            ecam6.gameObject.SetActive(false);

So far, it seems to generate seams at the edge of the screen, but I don’t know if thats a fixable problem or my approach is flawed.

That’s already exactly what happens when you call cam.RenderToCubemap(cubeMap, 63); and is the cause of the problem.

Yeah, that’s what I thought.

Thanks anyway.