SkyBox from code to SkySphere Tesselation example.

Mr valentine suggested i make a example post on programatically creating a skybox and tesselating it to a sphere which i was doing. and so here it is. I somewhat prefer to post this for posterity, on the site. Also so it can be found more readily in a google search by anyone who maybe looking.

This will be a three part code post that covers a single Game1 example class.

Though this is in a single Game1 class Its heavily commented and not optimized at all. It maybe considered a working prototype and full example.

The two skybox algorithms will go into the second post
The sub-division method itself will be in the third part
Both of which belong to the SkyBoxTesselator class.
At the very bottom is the custom vertex structure used.

The Game1 class is here to serve as a example of its usage and generated the below gif.

This can be run with the console on to see quite a lot of data output as well by setting the bool at the top of the tesselator class to true. Google how to turn on console output in vs on a windows app if you don’t know how and wish to see it.

How to run the example ?

The code shown can all be copy pasted from top to bottom into a single Game1 class.
You change the namespace to your projects namespace then add the images.
It runs stand alone in a MonoGame project.

External requirements besides MonoGame

This example requires only that you load two skybox images.

Hopefully this will be found useful by some. lol because it nearly gave me a migraine to write it.
MonoGame is a great api and this is a little attempt to give back.

The keyboard controls can be found in the update method primarily wasd t r f g

Here are the two images i used for the example shown in the above gif.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace SkyCubeToSphereTesselationExample
{
    /// <summary>
    /// Much of this in game1 is not really optimal.
    /// However i squeezed it down because... 
    /// The skybox and sphere algorithms are large.
    /// </summary>
    public class Game1 : Game
    {
        #region This region defines some constants we might like to have on hand

        public const float PI = (float)(Math.PI);
        public const float PI2 = (float)(Math.PI * 2.0d);
        public const float TODEGREES = 360.0f / PI2;
        public const float TORADIANS = PI2 / 360.0f;

        # endregion

        #region This_region defines some useful states

        RasterizerState rs_regular = new RasterizerState()
        {
            FillMode = FillMode.Solid,
        };
        RasterizerState rs_solid_ccw = new RasterizerState()
        {
            FillMode = FillMode.Solid,
            CullMode = CullMode.CullCounterClockwiseFace
        };
        RasterizerState rs_wireframe_cullnone = new RasterizerState()
        {
            FillMode = FillMode.WireFrame,
            CullMode = CullMode.None
        };
        DepthStencilState ds_depthtest_lessthanequals = new DepthStencilState()
        {
            DepthBufferEnable = true,
            DepthBufferFunction = CompareFunction.LessEqual
        };
        DepthStencilState ds_depthtest_disabled = new DepthStencilState()
        {
            DepthBufferEnable = false,
        };

        #endregion

        #region These will be our commonly used variables graphics textures ect

        public static GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont font;

        // our example class
        SkyBoxTesselator sbt = new SkyBoxTesselator();

        private Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDeviceManager.DefaultBackBufferWidth / GraphicsDeviceManager.DefaultBackBufferHeight, .5f, 2000);
        private Matrix view = Matrix.Identity;
        private Matrix camera = Matrix.Identity;
        private Matrix worldSkybox = Matrix.Identity;
        private Matrix worldGlobe = Matrix.Identity;
        private float globeSpinAngle = 0f;

        BasicEffect beffect;
        private Texture2D currentSkyBoxTexture;
        private Texture2D skyBoxImageA;
        private Texture2D skyBoxImageB;

        /// <summary>
        /// This is a custom vertex struct defined at the bottom of the class
        /// It allows for position normals colors and u v texture coordinates.
        /// </summary>
        private VertexPositionNormalColorUv[] vertices_skybox;
        private int[] indices_skybox;

        int tesselations = 0;

        float timenow = 0f;
        float pausetimeset = 0f;

        #endregion


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            Window.AllowUserResizing = true;
            this.IsFixedTimeStep = true;
            this.TargetElapsedTime = TimeSpan.FromSeconds(1d / 60);
            this.IsMouseVisible = true;
            this.Window.ClientSizeChanged += HeyCallMeWhenTheUserResizesTheWindow;
        }
        public void HeyCallMeWhenTheUserResizesTheWindow(object sender, EventArgs e) { /* we are here when the windows being resized */ }

        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 900;
            graphics.PreferredBackBufferHeight = 600;
            graphics.ApplyChanges();
            base.Initialize();
        }

        protected override void UnloadContent() { }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //font = Content.Load<SpriteFont>("YourMonoGameCreatedFont");

            skyBoxImageB = Content.Load<Texture2D>("skyBoxCrossImage_Example");
            skyBoxImageA = Content.Load<Texture2D>("skyboxCrossImage_Desert");
            currentSkyBoxTexture = skyBoxImageA;

            beffect = new BasicEffect(GraphicsDevice);
            beffect.TextureEnabled = true;
            beffect.Texture = currentSkyBoxTexture;
            beffect.Projection = projection;

            // slightly raise our camera position
            camera.Translation = new Vector3(0f, 3f, 0f);

            CreateOrSetupSkyBoxOrSphere(out vertices_skybox, out indices_skybox, true, 0, 50f, true, true);
        }

        /// <summary>
        /// This is a sample setup method for illustration.
        /// The direct versions specifys also the facing order from 0 to 5 for front back right left ect...
        /// Standard order is 0,1,2,3,4,5  for each parameter in sequence.
        ///
        /// Texture order is not standardize for skyboxes so ive added the ability to swap order... 
        /// With many cross shaped skybox images the order is 0,1,2,3,4,6,5
        /// With square skyboxs they may have many orders.
        /// for example i think blender outputs 0,1,5,4,3,2 for its square images
        /// </summary>
        public void CreateOrSetupSkyBoxOrSphere(out VertexPositionNormalColorUv[] vertices_sborss, out int[] indices_sborss, bool createSphere, int numberOfTesselations, float scaleDistance, bool skyBoxUseImageCrossType, bool skyBoxCrossImageLeft)
        {
            if (skyBoxUseImageCrossType)
            {
                sbt.PrivateCreateSkyboxFromCrossImage(out vertices_sborss, out indices_sborss, scaleDistance, false, skyBoxCrossImageLeft, 0, 1, 2, 3, 5, 4);
            }
            else
            {
                sbt.PrivateCreateSkyboxFromSixBlockImage(out vertices_sborss, out indices_sborss, scaleDistance, false, 0, 1, 5, 4, 3, 2);
            }
            // this particular sphere type needs a preexisting cube type to be created like a skybox
            // because its basically a... (single texture), seperate quad faced cube subdivision method
            if (createSphere && numberOfTesselations > 0)
            {
                sbt.SkyBoxToSphere(vertices_sborss, indices_sborss, out vertices_sborss, out indices_sborss, numberOfTesselations, scaleDistance);
            }
        }

        protected override void Update(GameTime gameTime)
        {
            KeyboardState ks = Keyboard.GetState();
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || ks.IsKeyDown(Keys.Escape))
                Exit();

            // keypress pause that only applys to some keys you don't want to fire multiple times
            timenow = (float)gameTime.TotalGameTime.TotalSeconds;
            if (pausetimeset < timenow)
            {
                // recreate a cube
                if (ks.IsKeyDown(Keys.K)) { CreateOrSetupSkyBoxOrSphere(out vertices_skybox, out indices_skybox, false, 0, 2f, true, true); pausetimeset = timenow + .7f; }
                // tesselate
                if (ks.IsKeyDown(Keys.T)) 
                { 
                    sbt.Subdivide(vertices_skybox, indices_skybox, Vector3.Zero, 100f, 1, out vertices_skybox, out indices_skybox); pausetimeset = timenow + .7f; 
                    tesselations += 1; 
                }
                if (ks.IsKeyDown(Keys.R)) 
                { 
                    sbt.Subdivide(vertices_skybox, indices_skybox, Vector3.Zero, 100f, -1, out vertices_skybox, out indices_skybox); pausetimeset = timenow + .7f; 
                    tesselations -= 1; 
                }

                // flip to wirefram or solid
                if (ks.IsKeyDown(Keys.F)) { GraphicsDevice.RasterizerState = rs_wireframe_cullnone; pausetimeset = timenow + .7f; }
                if (ks.IsKeyDown(Keys.G)) { GraphicsDevice.RasterizerState = rs_regular; pausetimeset = timenow + .7f; }
            }

            // This is just a quick hack-in input camera slash view matrix.
            if (ks.IsKeyDown(Keys.W)) { camera = camera * Matrix.CreateFromAxisAngle(camera.Right, +.0123f); }
            if (ks.IsKeyDown(Keys.S)) { camera = camera * Matrix.CreateFromAxisAngle(camera.Right, -.0123f); }
            if (ks.IsKeyDown(Keys.A)) { camera = camera * Matrix.CreateFromAxisAngle(camera.Up, +.0123f); }
            if (ks.IsKeyDown(Keys.D)) { camera = camera * Matrix.CreateFromAxisAngle(camera.Up, -.0123f); } 
            var up = ((new Vector3(0f,.99f,0f) - camera.Up) *.05f) + camera.Up;
            camera = Matrix.CreateWorld(Vector3.Zero, camera.Forward, up);
            view = Matrix.CreateLookAt(Vector3.Zero, camera.Forward, camera.Up);
            
            // set the cameras view to the basic effect
            beffect.View = view;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            currentSkyBoxTexture = skyBoxImageA;
            DrawCustomVerticesWithBasicEffect(currentSkyBoxTexture, worldSkybox, vertices_skybox, indices_skybox);

            globeSpinAngle += .01f;
            worldGlobe = Matrix.Identity * Matrix.CreateScale(.03f);
            worldGlobe *= Matrix.CreateRotationY(globeSpinAngle);
            worldGlobe.Translation = new Vector3(0, 0, -10f);
            currentSkyBoxTexture = skyBoxImageB;
            DrawCustomVerticesWithBasicEffect(currentSkyBoxTexture, worldGlobe, vertices_skybox, indices_skybox);

            base.Draw(gameTime);
        }

        public void DrawCustomVerticesWithBasicEffect(Texture2D texture, Matrix world, VertexPositionNormalColorUv[] verts, int[] indexs)
        {
            beffect.Texture = texture;
            beffect.World = world;
            foreach (EffectPass pass in beffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, verts, 0, 2, indexs, 0, indexs.Length / 3, VertexPositionNormalColorUv.VertexDeclaration);
            }
        }

continued below…

1 Like

This begins the skybox class itself and the first two skybox methods these generate the vertices and indices required as well as offer constructors that allow for textures that are not oriented in the same way to have there parts swapped around.

        public class SkyBoxTesselator
        {
            // shows console output 
            // this can get large very large
            bool showConsoleOutput = false;

            #region well put our skybox creation methods here

            /// <summary>
            /// Creates a skybox with the scale and the facing order in the skybox image 
            /// Front, Back, Right, Left, Top, Bottom 
            /// is defined as ... 
            /// 0 1 2  from the upper part of the image across
            /// 3 4 5  from the lower part of the image across
            /// You may change the order to handle different skyboxes orders
            /// </summary>
            public void PrivateCreateSkyboxFromSixBlockImage
                (
                out VertexPositionNormalColorUv[] vertices,
                out int[] indices,
                float scale,
                bool skyBoxFlipCullingDirection,
                int Front, int Back, int Right, int Left, int Top, int Bottom
                )
            {
                VertexPositionNormalColorUv[] vertices_sborss = new VertexPositionNormalColorUv[24];
                int[] indices_sborss = new int[36];

                var front = 4 * Front;
                var back = 4 * Back;
                var right = 4 * Right;
                var left = 4 * Left;
                var top = 4 * Top;
                var bottom = 4 * Bottom;

                bool flipuUv_V = true;

                Vector3 LT_f = new Vector3(-1, -1, -1) * scale;
                Vector3 LB_f = new Vector3(-1, 1, -1) * scale;
                Vector3 RT_f = new Vector3(1, -1, -1) * scale;
                Vector3 RB_f = new Vector3(1, 1, -1) * scale;

                Vector3 LT_b = new Vector3(-1, -1, 1) * scale;
                Vector3 LB_b = new Vector3(-1, 1, 1) * scale;
                Vector3 RT_b = new Vector3(1, -1, 1) * scale;
                Vector3 RB_b = new Vector3(1, 1, 1) * scale;

                Vector3[] p = new Vector3[]
            {
                LT_f, LB_f, RT_f, RB_f,
                LT_b, LB_b, RT_b, RB_b
            };

                int i = 0;
                // front;
                vertices_sborss[i].Position = p[0]; i++; //left
                vertices_sborss[i].Position = p[1]; i++;
                vertices_sborss[i].Position = p[2]; i++; //right
                vertices_sborss[i].Position = p[3]; i++;
                // back 
                vertices_sborss[i].Position = p[6]; i++;
                vertices_sborss[i].Position = p[7]; i++;
                vertices_sborss[i].Position = p[4]; i++;
                vertices_sborss[i].Position = p[5]; i++;
                // right
                vertices_sborss[i].Position = p[2]; i++;
                vertices_sborss[i].Position = p[3]; i++;
                vertices_sborss[i].Position = p[6]; i++;
                vertices_sborss[i].Position = p[7]; i++;
                // left
                vertices_sborss[i].Position = p[4]; i++;
                vertices_sborss[i].Position = p[5]; i++;
                vertices_sborss[i].Position = p[0]; i++;
                vertices_sborss[i].Position = p[1]; i++;
                // top
                vertices_sborss[i].Position = p[4]; i++;
                vertices_sborss[i].Position = p[0]; i++;
                vertices_sborss[i].Position = p[6]; i++;
                vertices_sborss[i].Position = p[2]; i++;
                // bottom mirrored
                vertices_sborss[i].Position = p[1]; i++;
                vertices_sborss[i].Position = p[5]; i++;
                vertices_sborss[i].Position = p[3]; i++;
                vertices_sborss[i].Position = p[7]; i++;

                Vector2 tupeBuvwh = new Vector2(.250000f, .333333f); // i might just delete this one
                Vector2 tupeAuvwh = new Vector2(.333333f, .500000f);
                Vector2 currentuvWH = tupeAuvwh;
                Vector2 uvStart = Vector2.Zero;
                Vector2 uvEnd = Vector2.Zero;

                for (int j = 0; j < 6; j++)
                {
                    // face represents which set of 4 vertices we assign a set of texture coordinates to
                    // we can move them by simply moving face number
                    int face = 0;
                    if (j == 0)
                    {
                        face = front;
                        uvStart = new Vector2(currentuvWH.X * 0f, currentuvWH.Y * 0f);
                        uvEnd = uvStart + currentuvWH;
                    }
                    if (j == 1)
                    {
                        face = back;
                        uvStart = new Vector2(currentuvWH.X * 1f, currentuvWH.Y * 0f);
                        uvEnd = uvStart + currentuvWH;
                    }
                    if (j == 2)
                    {
                        face = right;
                        uvStart = new Vector2(currentuvWH.X * 2f, currentuvWH.Y * 0f);
                        uvEnd = uvStart + currentuvWH;
                    }
                    if (j == 3)
                    {
                        face = left;
                        uvStart = new Vector2(currentuvWH.X * 0f, currentuvWH.Y * 1f);
                        uvEnd = uvStart + currentuvWH;
                    }
                    if (j == 4)
                    {
                        face = top;
                        uvStart = new Vector2(currentuvWH.X * 1f, currentuvWH.Y * 1f);
                        uvEnd = uvStart + currentuvWH;
                    }
                    if (j == 5)
                    {
                        face = bottom;
                        uvStart = new Vector2(currentuvWH.X * 2f, currentuvWH.Y * 1f);
                        uvEnd = uvStart + currentuvWH;
                    }
                    if (flipuUv_V) { float y = uvStart.Y; uvStart.Y = uvEnd.Y; uvEnd.Y = y; }
                    vertices_sborss[face + 0].TextureCoordinateA = new Vector2(uvStart.X, uvStart.Y);
                    vertices_sborss[face + 1].TextureCoordinateA = new Vector2(uvStart.X, uvEnd.Y);
                    vertices_sborss[face + 2].TextureCoordinateA = new Vector2(uvEnd.X, uvStart.Y);
                    vertices_sborss[face + 3].TextureCoordinateA = new Vector2(uvEnd.X, uvEnd.Y);
                }

                Vector3 center = new Vector3(0, 0, 0);
                for (int j = 0; j < 24; j++)
                {
                    if (skyBoxFlipCullingDirection)
                    {
                        vertices_sborss[j].Normal = -Vector3.Normalize(vertices_sborss[j].Position - center);
                    }
                    else
                    {
                        vertices_sborss[j].Normal = Vector3.Normalize(vertices_sborss[j].Position - center);
                    }
                    vertices_sborss[j].Color = Color.White;
                }

                //
                i = 0;
                int k = 0; // front
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 4; // back
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 8; // right
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 12; // left
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 16; // top
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 20; // bottom
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                vertices = vertices_sborss;
                indices = indices_sborss;
            }

            /// <summary>
            /// Creates a skybox with the scale and the facing order in the skybox image 
            /// Front, Back, Right, Left, Top, Bottom 
            /// is defined as ... 
            /// 0 1 2  from the upper part of the image across
            /// 3 4 5  from the lower part of the image across
            /// You may change the order to handle different skyboxes orders
            /// </summary>
            public void PrivateCreateSkyboxFromCrossImage(
                out VertexPositionNormalColorUv[] vertices,
                out int[] indices,
                float scale,
                bool skyBoxFlipCullingDirection,
                bool skyBoxCrossImageLeft,
                int Front, int Back, int Right, int Left, int Top, int Bottom)
            {
                VertexPositionNormalColorUv[] vertices_sborss = new VertexPositionNormalColorUv[24];
                int[] indices_sborss = new int[36];

                var front = 4 * Front;
                var back = 4 * Back;
                var right = 4 * Right;
                var left = 4 * Left;
                var top = 4 * Top;
                var bottom = 4 * Bottom;

                bool flipuUv_V = true;

                Vector3 LT_f = new Vector3(-1f, -1f, -1f) * scale;
                Vector3 LB_f = new Vector3(-1f, 1f, -1f) * scale;
                Vector3 RT_f = new Vector3(1f, -1f, -1f) * scale;
                Vector3 RB_f = new Vector3(1f, 1f, -1f) * scale;

                Vector3 LT_b = new Vector3(-1f, -1f, 1f) * scale;
                Vector3 LB_b = new Vector3(-1f, 1f, 1f) * scale;
                Vector3 RT_b = new Vector3(1f, -1f, 1f) * scale;
                Vector3 RB_b = new Vector3(1f, 1f, 1f) * scale;

                // floating point face error corrections by 1 % at more then 99 pixels
                Vector3 PlotArtifactCorrectionX = new Vector3(.99f, 1f, 1f);
                var xc = PlotArtifactCorrectionX;
                Vector3 PlotArtifactCorrectionY = new Vector3(1f, .99f, 1f);
                var yc = PlotArtifactCorrectionY;
                Vector3 PlotArtifactCorrectionZ = new Vector3(1f, 1f, .99f);
                var zc = PlotArtifactCorrectionZ;

                Vector3[] p = new Vector3[]
            {
                LT_f, LB_f, RT_f, RB_f,
                LT_b, LB_b, RT_b, RB_b
            };

                int i = 0;
                // front;
                vertices_sborss[i].Position = p[0] * zc; i++; //left
                vertices_sborss[i].Position = p[1] * zc; i++;
                vertices_sborss[i].Position = p[2] * zc; i++; //right
                vertices_sborss[i].Position = p[3] * zc; i++;
                // back is mirrored front
                vertices_sborss[i].Position = p[6] * zc; i++; // mirrored
                vertices_sborss[i].Position = p[7] * zc; i++;
                vertices_sborss[i].Position = p[4] * zc; i++; // mirrored
                vertices_sborss[i].Position = p[5] * zc; i++;
                // right
                vertices_sborss[i].Position = p[2] * xc; i++;
                vertices_sborss[i].Position = p[3] * xc; i++;
                vertices_sborss[i].Position = p[6] * xc; i++;
                vertices_sborss[i].Position = p[7] * xc; i++;
                // left
                vertices_sborss[i].Position = p[4] * xc; i++;
                vertices_sborss[i].Position = p[5] * xc; i++;
                vertices_sborss[i].Position = p[0] * xc; i++;
                vertices_sborss[i].Position = p[1] * xc; i++;
                // top
                vertices_sborss[i].Position = p[4] * yc; i++;
                vertices_sborss[i].Position = p[0] * yc; i++;
                vertices_sborss[i].Position = p[6] * yc; i++;
                vertices_sborss[i].Position = p[2] * yc; i++;
                // bottom mirrored
                vertices_sborss[i].Position = p[1] * yc; i++;
                vertices_sborss[i].Position = p[5] * yc; i++;
                vertices_sborss[i].Position = p[3] * yc; i++;
                vertices_sborss[i].Position = p[7] * yc; i++;

                Vector2 tupeBuvwh = new Vector2(.25000000f, .33333333f); // i might just delete this one
                Vector2 tupeAuvwh = new Vector2(.33333333f, .50000000f);
                Vector2 currentuvWH = tupeBuvwh;
                Vector2 uvStart = Vector2.Zero;
                Vector2 uvEnd = Vector2.Zero;

                // left cross
                if (skyBoxCrossImageLeft)
                {
                    for (int j = 0; j < 6; j++)
                    {
                        // face represents which set of 4 vertices we assign a set of texture coordinates to
                        // we can move them by simply moving face number
                        int face = 0;
                        if (j == 0)
                        {
                            face = front; //front
                            uvStart = new Vector2(currentuvWH.X * 1f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 1)
                        {
                            face = back; // back
                            uvStart = new Vector2(currentuvWH.X * 3f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 2)
                        {
                            face = right; // right
                            uvStart = new Vector2(currentuvWH.X * 2f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 3)
                        {
                            face = left; // left
                            uvStart = new Vector2(currentuvWH.X * 0f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 4)
                        {
                            face = top; // top
                            uvStart = new Vector2(currentuvWH.X * 1f, currentuvWH.Y * 0f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 5)
                        {
                            face = bottom; // bottom
                            uvStart = new Vector2(currentuvWH.X * 1f, currentuvWH.Y * 2f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (flipuUv_V) { float y = uvStart.Y; uvStart.Y = uvEnd.Y; uvEnd.Y = y; }
                        vertices_sborss[face + 0].TextureCoordinateA = new Vector2(uvStart.X, uvStart.Y);
                        vertices_sborss[face + 1].TextureCoordinateA = new Vector2(uvStart.X, uvEnd.Y);
                        vertices_sborss[face + 2].TextureCoordinateA = new Vector2(uvEnd.X, uvStart.Y);
                        vertices_sborss[face + 3].TextureCoordinateA = new Vector2(uvEnd.X, uvEnd.Y);
                    }
                }
                else
                {
                    // right cross
                    for (int j = 0; j < 6; j++)
                    {
                        // face represents which set of 4 vertices we assign a set of texture coordinates to
                        // we can move them by simply moving face number
                        int face = 0;
                        if (j == 0)
                        {
                            face = front; //front
                            uvStart = new Vector2(currentuvWH.X * 2f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 1)
                        {
                            face = back; // back
                            uvStart = new Vector2(currentuvWH.X * 0f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 2)
                        {
                            face = right; // right
                            uvStart = new Vector2(currentuvWH.X * 3f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 3)
                        {
                            face = left; // left
                            uvStart = new Vector2(currentuvWH.X * 1f, currentuvWH.Y * 1f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 4)
                        {
                            face = top; // top
                            uvStart = new Vector2(currentuvWH.X * 2f, currentuvWH.Y * 0f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (j == 5)
                        {
                            face = bottom; // bottom
                            uvStart = new Vector2(currentuvWH.X * 2f, currentuvWH.Y * 2f);
                            uvEnd = uvStart + currentuvWH;
                        }
                        if (flipuUv_V) { float y = uvStart.Y; uvStart.Y = uvEnd.Y; uvEnd.Y = y; }
                        vertices_sborss[face + 0].TextureCoordinateA = new Vector2(uvStart.X, uvStart.Y);
                        vertices_sborss[face + 1].TextureCoordinateA = new Vector2(uvStart.X, uvEnd.Y);
                        vertices_sborss[face + 2].TextureCoordinateA = new Vector2(uvEnd.X, uvStart.Y);
                        vertices_sborss[face + 3].TextureCoordinateA = new Vector2(uvEnd.X, uvEnd.Y);
                    }
                }

                Vector3 center = new Vector3(0, 0, 0);
                for (int j = 0; j < 24; j++)
                {
                    if (skyBoxFlipCullingDirection)
                    {
                        vertices_sborss[j].Normal = -Vector3.Normalize(vertices_sborss[j].Position - center);
                    }
                    else
                    {
                        vertices_sborss[j].Normal = Vector3.Normalize(vertices_sborss[j].Position - center);
                    }
                    vertices_sborss[j].Color = Color.White;
                }

                //
                i = 0;
                int k = 0; // front
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 4; // back
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 8; // right
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 12; // left
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 16; // top
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                k = 20; // bottom
                indices_sborss[i] = k + 0; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 2; i++;
                indices_sborss[i] = k + 1; i++;
                indices_sborss[i] = k + 3; i++;

                vertices = vertices_sborss;
                indices = indices_sborss;
            }

continued below is the skybox to sphere tesselator algorithm.
It is still part of the skybox class above.

the third part is the actual subdivision algorithm

                // Tesselate a skycube into a sphere based on distance and normals. 
                // All created points should be based on the distance * the normal.
                //    
                //    A skybox it turns out on the first prototype has some special considerations.
                //    This forced me to scrap my simpler design due to each face having its own disjointed uv areas.
                //    In so doing i decided to expound on the quads treat them as meshes and expand them in place.
                //    This is primarily done by simple remapping however that required quite a bit of thought.
                //
                //    We will tesselate a face by simply expanding it.
                //    Well start off by precalculating all the variables we might need.
                //
                //    The first major step is we will remap edges to edges.
                //
                //    //
                //    // such that this
                //    //
                //    //    0          2 
                //    //   LT ------ RT
                //    //   |          |  
                //    //   |1         |3 
                //    //   LB ------ RB
                //
                //    //
                //    // is mapped to the below when it is tesselated 1 time.
                //    //
                //    //    0          3          6
                //    //   LT ----- LTcRT ----- RT
                //    //   |          |          |
                //    //   |1         |4         |7
                //    //   LTcLB -- cent ------ RBcRT
                //    //   |          |          |
                //    //   |2         |5         |8
                //    //   LB ----- LBcRB ----- RB
                //    //
                //
                //    If we do it this way we simply expand on each face and remmap it. 
                //    As if each quad face is a mesh. 
                //    Since we are using quads we can subdivide them in this manner.
                //
                //    Recalculating the mesh... this wont be actual local (vertice) tesselation.
                //    It's more like remapping a a mesh... i would of liked to but it would be impractical in this case
                //    Though i believe the proper term is (quad subdivision) or quad tesselation.
                //    
                //    For a skybox with such a six side image... 
                //    To map that to a sphere we must keep seperate faces seperate.
                //    This way is pretty much a must in this case. 
                //    We however wish for unobvious reasons to keep the entire skybox vertices and indice sets each in a single array of their own.
                //    In the same two arrays and we can treat it as a single vertice set.
                //

                public void SkyBoxToSphere
                    (
                    VertexPositionNormalColorUv[] source_vertices,
                    int[] source_indices,
                    out VertexPositionNormalColorUv[] dest_vertices,
                    out int[] dest_indices,
                    int numberOfSubdivisions,
                    float distance
                    )
                {
                    Subdivide(source_vertices, source_indices, Vector3.Zero, distance, numberOfSubdivisions, out dest_vertices, out dest_indices);
                }

                // provided this is based on a skybox it can be subdivided again
                public void Subdivide
                (
                   VertexPositionNormalColorUv[] source_vertices,
                   int[] source_indices,
                   Vector3 origin,
                   float distance,
                   int numberOfSubdivisions,
                   out VertexPositionNormalColorUv[] dest_vertices,
                   out int[] dest_indices
                )
                {
                    string ret = "\n";

                    VertexPositionNormalColorUv[] sv = source_vertices;
                    int[] si = source_indices;

                    int Faces = 6;

                    int s_quads = si.Length / 6;
                    int s_quadsPerFace = s_quads / 6;
                    int s_indicesPerFace = s_quadsPerFace * 6;
                    int s_totalIndices = s_indicesPerFace * Faces;
                    int s_totalVertices = sv.Length;
                    int s_verticesPerFace = sv.Length / Faces;
                    int s_verticesPerDirection = (int)Math.Sqrt((double)s_verticesPerFace);
                    int sw = s_verticesPerDirection;
                    int sh = s_verticesPerDirection;

                    // well be moving the edges outwards in this case as we recalculate vertices
                    // inside this just means im retesselating a mesh not really tesselating vertices
                    // though ultimately it gives the same result 
                    // vertices are normally simpler but here we have a non contigous texture with 6 parts
                    // each part has its own uv set thus its own mesh that we must tesselate individually
                    // as the uv coordinates cannot interpolate across texels directly in the non contiguous skybox image
                    int d_verticesPerDirection = s_verticesPerDirection + numberOfSubdivisions;
                    int d_verticesPerFace = d_verticesPerDirection * d_verticesPerDirection;
                    int d_totalVertices = d_verticesPerFace * Faces;
                    int d_quadsPerFace = (d_verticesPerDirection - 1) * (d_verticesPerDirection - 1);
                    int d_quads = d_quadsPerFace * 6; // coincidwntally 6 faces 
                    int d_indicesPerFace = d_quadsPerFace * 6; // 6 indices per quad
                    int d_totalIndices = d_indicesPerFace * Faces;

                    VertexPositionNormalColorUv[] dv = new VertexPositionNormalColorUv[d_totalVertices];
                    int[] di = new int[d_totalIndices];
                    int dw = d_verticesPerDirection;
                    int dh = d_verticesPerDirection;

                    string[] verticeMsg = new string[d_totalVertices];

                    if (showConsoleOutput)
                    {
                        Console.Write
                        (
                            ret + "Faces " + Faces +
                            ret + " " +
                            ret + "quads                     " + s_quads +
                            ret + "quadsPerFace              " + s_quadsPerFace +
                            ret + "totalVertices             " + s_totalVertices +
                            ret + "verticesPerFace           " + s_verticesPerFace +
                            ret + "verticesPerDirection      " + s_verticesPerDirection +
                            ret + "s_indicesPerFace          " + s_indicesPerFace +
                            ret + "s_totalIndices            " + s_totalIndices +
                            ret + " " +
                            ret + "new_quads                 " + d_quads +
                            ret + "new_quadsPerFace          " + d_quadsPerFace +
                            ret + "new_totalVertices         " + d_totalVertices +
                            ret + "new_verticesPerFace       " + d_verticesPerFace +
                            ret + "new_verticesPerDirection  " + d_verticesPerDirection +
                            ret + "new_indicesPerFace        " + d_indicesPerFace +
                            ret + "new_totalIndices          " + d_totalIndices +
                            ret + ret + " "
                        );
                    }

                    // Maping to the face surface or quad edge vertices
                    //
                    // because quads are vertically oriented this means our loops will be x y not y x ordered
                    // so later on ill have to either change the order of the cube or keep in mind
                    // that this particular sphere mesh is x y ordered
                    // i could however make seperate function to swap order i probably should have already
                    // but not in this class as input is expected in the x y order a swaped order would 
                    // screw this all up and so whatever order our quad draw is in this function must follow
                    // in order to properly tesselate the faces
                    // y + x * height; were height is verticesPerDirection
                    int s_lt_Edge = 0;
                    int s_lb_Edge = s_verticesPerDirection - 1;
                    int s_rt_Edge = (s_verticesPerDirection - 1) * s_verticesPerDirection;
                    int s_rb_Edge = s_verticesPerFace - 1;

                    int d_lt_Edge = 0;
                    int d_lb_Edge = d_verticesPerDirection - 1;
                    int d_rt_Edge = (d_verticesPerDirection - 1) * d_verticesPerDirection;
                    int d_rb_Edge = d_verticesPerFace - 1;

                    // now here we have the decision to make do we loop the source or the destination
                    // i have been to this point only considered expanding the mesh;
                    // however later and for other things i may want to deflate it
                    // in either case looping destination or source we must calculate the opposite.
                    // Its the sides however that matters

                    for (int f = 0; f < Faces; f++)
                    {
                        // the index that denotes the top left of the entire face
                        int s_startVertice = s_verticesPerFace * f;
                        int s_startIndice = s_indicesPerFace * f;
                        int d_startVertice = d_verticesPerFace * f;
                        int d_startIndice = d_indicesPerFace * f;

                        int s = s_startVertice;
                        int d = d_startVertice;

                        // calculate were the corners are on the source face and destination face
                        s_lt_Edge = s + 0;
                        s_lb_Edge = s + s_verticesPerDirection - 1;
                        s_rt_Edge = s + (s_verticesPerDirection - 1) * s_verticesPerDirection;
                        s_rb_Edge = s + s_verticesPerFace - 1;

                        d_lt_Edge = d + 0;
                        d_lb_Edge = d + d_verticesPerDirection - 1;
                        d_rt_Edge = d + (d_verticesPerDirection - 1) * d_verticesPerDirection;
                        d_rb_Edge = d + d_verticesPerFace - 1;

                        // normalize and redistance the source corner positions before setting them
                        sv[s_lt_Edge].Position = Vector3.Normalize(sv[s_lt_Edge].Position) * distance;
                        sv[s_lb_Edge].Position = Vector3.Normalize(sv[s_lb_Edge].Position) * distance;
                        sv[s_rt_Edge].Position = Vector3.Normalize(sv[s_rt_Edge].Position) * distance;
                        sv[s_rb_Edge].Position = Vector3.Normalize(sv[s_rb_Edge].Position) * distance;

                        // sets the corners destination vertices to recalculate the entire quad mesh
                        dv[d_lt_Edge] = sv[s_lt_Edge];
                        dv[d_lb_Edge] = sv[s_lb_Edge];

                        dv[d_rt_Edge] = sv[s_rt_Edge];
                        dv[d_rb_Edge] = sv[s_rb_Edge];

                        if (showConsoleOutput)
                        {
                            // just a msg to track all this crap
                            verticeMsg[d_lt_Edge] += " < Corner_Placed               ";
                            verticeMsg[d_lb_Edge] += " < Corner_Placed               ";
                            verticeMsg[d_rt_Edge] += " < Corner_Placed               ";
                            verticeMsg[d_rb_Edge] += " < Corner_Placed               ";

                            Console.Write
                            (
                            ret + "Face" + f +
                            ret + " " +
                            ret + "s_lt_Edge " + s_lt_Edge + " " + sv[s_lt_Edge].Position +
                            ret + "s_lb_Edge " + s_lb_Edge + " " + sv[s_lb_Edge].Position +
                            ret + "s_rt_Edge " + s_rt_Edge + " " + sv[s_rt_Edge].Position +
                            ret + "s_rb_Edge " + s_rb_Edge + " " + sv[s_rb_Edge].Position +
                            ret + " " +
                            ret + "d_lt_Edge " + d_lt_Edge + " " + dv[d_lt_Edge].Position +
                            ret + "d_lb_Edge " + d_lb_Edge + " " + dv[d_lb_Edge].Position +
                            ret + "d_rt_Edge " + d_rt_Edge + " " + dv[d_rt_Edge].Position +
                            ret + "d_rb_Edge " + d_rb_Edge + " " + dv[d_rb_Edge].Position +
                            ret + ""
                            );
                        }

                        // the left lines empty vertices are lerped down, thru the face edges between top bottom known endpoints
                        for (int j = 1; j < d_verticesPerDirection - 1; j++)
                        {
                            int dindex = d_lt_Edge + j;
                            float lerptime = (float)(j) / (float)(d_verticesPerDirection - 1); // - d_lt_Edge
                            dv[dindex] = LerpSubDivisionVeritces(dv[d_lt_Edge], dv[d_lb_Edge], lerptime, distance, origin);
                            if (showConsoleOutput)
                            {
                                verticeMsg[dindex] += " | LeftEdge_Calculated         ";
                            }
                        }
                        // the right lines empty vertices are lerped down, thru the face edges between top bottom known endpoints
                        for (int j = 1; j < d_verticesPerDirection - 1; j++)
                        {
                            int dindex = d_rt_Edge + j;
                            float lerptime = (float)(j) / (float)(d_verticesPerDirection - 1);//j / (d_verticesPerDirection - d_rt_Edge);
                            dv[dindex] = LerpSubDivisionVeritces(dv[d_rt_Edge], dv[d_rb_Edge], lerptime, distance, origin);
                            if (showConsoleOutput)
                            {
                                verticeMsg[dindex] += " | RightEdge_Calculated        ";
                            }
                        }
                        // we fill across
                        for (int down = 0; down < d_verticesPerDirection; down++)
                        {
                            int fromLeft = d_lt_Edge + down;
                            int toRight = d_rt_Edge + down;

                            for (int across = 1; across < d_verticesPerDirection - 1; across++)
                            {
                                int creationindex = fromLeft + across * d_verticesPerDirection;
                                float lerptime = (float)(across) / (float)(d_verticesPerDirection - 1);
                                dv[creationindex] = LerpSubDivisionVeritces(dv[fromLeft], dv[toRight], lerptime, distance, origin);
                                if (showConsoleOutput)
                                {
                                    verticeMsg[creationindex] += " - Across_LtoR_LastCalculated    ";
                                }
                            }
                        }
                    }


                    // We have are vertices setup now we must set the indices to them as triangles
                    int dIndice_Index = 0;
                    for (int f = 0; f < Faces; f++)
                    {
                        int d_startFaceOffset = d_verticesPerFace * f;
                        // we move across
                        for (int across = 0; across < d_verticesPerDirection - 1; across++)
                        {
                            // we move down
                            for (int down = 0; down < d_verticesPerDirection - 1; down++)
                            {

                                // we now must find the 4 vertices of each quad in this mesh
                                // our loop procedes in the order of down then left to right
                                // basically this
                                // int index0 = x * stride + y + startOffset;
                                //
                                int vindex0 = ((across + 0) * d_verticesPerDirection) + (down + 0) + d_startFaceOffset;
                                int vindex1 = ((across + 0) * d_verticesPerDirection) + (down + 1) + d_startFaceOffset;
                                int vindex2 = ((across + 1) * d_verticesPerDirection) + (down + 0) + d_startFaceOffset;
                                int vindex3 = ((across + 1) * d_verticesPerDirection) + (down + 1) + d_startFaceOffset;

                                // set the indices
                                di[dIndice_Index] = vindex0; dIndice_Index++;
                                di[dIndice_Index] = vindex1; dIndice_Index++;
                                di[dIndice_Index] = vindex2; dIndice_Index++;

                                di[dIndice_Index] = vindex2; dIndice_Index++;
                                di[dIndice_Index] = vindex1; dIndice_Index++;
                                di[dIndice_Index] = vindex3; dIndice_Index++;
                            }
                        }
                    }

                    if (showConsoleOutput)
                    {
                        Console.WriteLine();
                        for (int j = 0; j < dv.Length; j++)
                        {
                            if (j % d_verticesPerFace == 0 && j != 0) { Console.WriteLine(" Face "); }
                            Console.WriteLine(" vertice[" + j + "] " + verticeMsg[j] + dv[j].Position);
                        }
                        Console.WriteLine();
                        for (int j = 0; j < di.Length; j += 6)
                        {
                            if (j % d_indicesPerFace == 0 && j != 0) { Console.WriteLine(" Face "); }
                            var j0 = j + 0;
                            var j1 = j + 1;
                            var j2 = j + 2;
                            var j3 = j + 3;
                            var j4 = j + 4;
                            var j5 = j + 5;
                            Console.WriteLine(" Quad indice[ " + j0 + " ] + 0 to 5 triangles  T0: " + di[j0] + "," + di[j1] + "," + di[j2] + "  T1: " + di[j3] + "," + di[j4] + "," + di[j5] + " ");
                        }
                    }

                    // well repeat this process when generating a new face surface
                    // to set the originals to the edges.
                    dest_vertices = dv;
                    dest_indices = di;
                }

                /// <summary>
                /// This method lerps between two custom vertices by the specified percentage. 
                /// The position is found by the normal * the distance from the origin specified.
                /// </summary>
                public VertexPositionNormalColorUv LerpSubDivisionVeritces
                    (
                    VertexPositionNormalColorUv from,
                    VertexPositionNormalColorUv to,
                    float lerpPercentage,
                    float distance,
                    Vector3 origin
                    )
                {
                    VertexPositionNormalColorUv B = new VertexPositionNormalColorUv();
                    B.Color = new Color(
                        (byte)(from.Color.R + (to.Color.R - from.Color.R) * lerpPercentage),
                        (byte)(from.Color.G + (to.Color.G - from.Color.G) * lerpPercentage),
                        (byte)(from.Color.B + (to.Color.B - from.Color.B) * lerpPercentage),
                        (byte)(from.Color.A + (to.Color.A - from.Color.A) * lerpPercentage)
                        );
                    Vector3 d = (from.Normal + ((to.Normal - from.Normal) * lerpPercentage));
                    B.Normal = Vector3.Normalize(d);
                    B.Position = origin + B.Normal * distance;
                    B.TextureCoordinateA = from.TextureCoordinateA + ((to.TextureCoordinateA - from.TextureCoordinateA) * lerpPercentage);
                    return B;
                }

                #endregion

            }

            // Ill just be placing these as inner classes for now 
            // So i can proof test this fast and not mess up my working project
            // i still have more to do on it.
        }
        public struct VertexPositionNormalColorUv : IVertexType
        {
            public Vector3 Position;
            public Color Color;
            public Vector3 Normal;
            public Vector2 TextureCoordinateA;

            public static VertexDeclaration VertexDeclaration = new VertexDeclaration
            (
                  new VertexElement(VertexElementByteOffset.PositionStartOffset(), VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
                  new VertexElement(VertexElementByteOffset.OffsetColor(), VertexElementFormat.Color, VertexElementUsage.Color, 0),
                  new VertexElement(VertexElementByteOffset.OffsetVector3(), VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
                  new VertexElement(VertexElementByteOffset.OffsetVector2(), VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
            );
            VertexDeclaration IVertexType.VertexDeclaration { get { return VertexDeclaration; } }
        }
        /// <summary>
        /// This is actually a helper struct
        /// </summary>
        public struct VertexElementByteOffset
        {
            public static int currentByteSize = 0;
            public static int PositionStartOffset() { currentByteSize = 0; var s = sizeof(float) * 3; currentByteSize += s; return currentByteSize - s; }
            public static int Offset(float n) { var s = sizeof(float); currentByteSize += s; return currentByteSize - s; }
            public static int Offset(Vector2 n) { var s = sizeof(float) * 2; currentByteSize += s; return currentByteSize - s; }
            public static int Offset(Color n) { var s = sizeof(int); currentByteSize += s; return currentByteSize - s; }
            public static int Offset(Vector3 n) { var s = sizeof(float) * 3; currentByteSize += s; return currentByteSize - s; }
            public static int Offset(Vector4 n) { var s = sizeof(float) * 4; currentByteSize += s; return currentByteSize - s; }

            public static int OffsetFloat() { var s = sizeof(float); currentByteSize += s; return currentByteSize - s; }
            public static int OffsetColor() { var s = sizeof(int); currentByteSize += s; return currentByteSize - s; }
            public static int OffsetVector2() { var s = sizeof(float) * 2; currentByteSize += s; return currentByteSize - s; }
            public static int OffsetVector3() { var s = sizeof(float) * 3; currentByteSize += s; return currentByteSize - s; }
            public static int OffsetVector4() { var s = sizeof(float) * 4; currentByteSize += s; return currentByteSize - s; }
        }

    }

This ends the namespace.

1 Like

what is the purpose of this?

Your Sky Box can double as Sky Sphere or a planet.

You have a regular SkyBox image that can map directly to a sphere.
You don’t need a premade model.
You have some control on the lod for a sphere on load. It maps to different resolution levels.
You don’t have to mess around in like blender to get it onto a sphere or remap it to a hirdef texture because it already maps to a sphere.

It has many possible purposes.

You can directly use it for planets at distances.
I suppose something might be done with render target reflections for a 6 faced sphere.

Skyboxes are favored for the low vertice count skyspheres for other attributes.
You get both in one.

I suppose i should probably have gone into this a bit more.
But there are tons of topics on the uses of both.
https://www.gamedev.net/topic/647584-skybox-vs-skysphere/
It also demonstrates mesh subdivision if that helps anyone at all.

What are the benefits of using UVs when you already have normals? The cubemap is accessed by normals anyways, right?

Sorry i don’t follow im a bit tired.

This has all the values mapped to each normal uniformly.
The color the normal the texture uv and position.

So the question is - why does the sky sphere need to have UVs?

It needs to read from a cubemap, so UV’s aren’t all that useful, i would think, in that case.

Sorry the vertex structure is essentially Position Normal Color Texture

No shader this is just using the most basic parts of basic effect.

exactly! Why would you need them when you have normals

EDIT:

to make it clear, I presume this is the basic pixel shader for a skybox

float4 DrawSkybox_PixelShader(DrawBasic_VSIn input) : COLOR
{
	float3 normal = normalize(input.Normal);
	return SkyboxTexture.Sample(CubeMapSampler, normal.xzy);
}

and no UVs are used

EDIT2: I think i understand now. Instead of using a cubemap a 2d cross texture is used and applied with a basic effect.

EDIT3: Do you not run into interpolation problems at the edges where the texture is black? It can’t really interpolate between the correct pixels then, can it

I cheated my way around it on the skybox.
(you’ll find 3 error corrections in it i labeled them that, which really is a hack job)
The sphere doesn’t seem to get them at all so i didn’t bother but it inherited the hack so.

You can run it yourself tell me if you find any artifacts.

It really is just all copy paste right into a new projects game 1

max edits i edit too much.

Sorry i can’t edit anymore lol
I wanted to elaborate though now that its been brought up that.

  1. This really is just a prototype i just wrote it over the last few days and finished it yesterday. Ill probably improve it over time if anyone is bored they are free to as well.

  2. It really is only using the image u,v’s by that i mean to emphasize ‘ONLY’
    You fully control it.

So i wanted to remark on what else is possible with it.
It has normals for lighting colors for blending or shading and you can warp the vertices in the vertex shader. So you can do effects on the sphere or cube cheap.

For instance.

You could put the sun in the sky with a specular reflection shader, by blasting the normals against the light direction.

You could straight shade against all the colors to on the vertices to make it turn red with a ambient color.

You could put in a inner sphere with the clouds separately and alpha blend it so they move.

You can switch the texture just like you would in a spritebatch draw.

You could one up the whole algorithm and add multi-texturing and really do some cool stuff.

You could “animate” the entire skysphere.

You could reflect the entire world onto the inside of the sky sphere or sky cube.

Im sure there are other things.

Maybe you can do all that with a regular one im not really sure
But …i know you can with this.

With a skysphere (or an hemisphere) you can make the sky change according to the hour of the day easier than a skycube. :slight_smile:
I have some code somewhere, I did a skysphere during my XNA3.1 times, Iif I foind it I’ll share it.