Equirectangular projection support for Skybox 98/285498/3
authorRichard <r.huang@samsung.com>
Tue, 13 Dec 2022 17:09:21 +0000 (17:09 +0000)
committerRichard <r.huang@samsung.com>
Tue, 13 Dec 2022 17:27:23 +0000 (17:27 +0000)
Change-Id: Idc94667e91f62508531745ed3c037561da4130b8

examples/rendering-skybox/rendering-skybox.cpp
examples/rendering-skybox/shaders/rendering-skybox-equirectangular.frag [new file with mode: 0644]
resources/images/veste_oberhaus_spherical_panoramic.jpg [new file with mode: 0644]

index c0083b9..d6bf994 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 
 #include "generated/rendering-skybox-cube-frag.h"
 #include "generated/rendering-skybox-cube-vert.h"
+#include "generated/rendering-skybox-equirectangular-frag.h"
 #include "generated/rendering-skybox-frag.h"
 #include "generated/rendering-skybox-vert.h"
 #include "look-camera.h"
@@ -40,6 +41,15 @@ const unsigned int SKYBOX_FACE_COUNT  = 6;
 const unsigned int SKYBOX_FACE_WIDTH  = 2048;
 const unsigned int SKYBOX_FACE_HEIGHT = 2048;
 
+/**
+ * @brief The skybox types supported by the application.
+ */
+enum class SkyboxType
+{
+  CUBEMAP,        ///< Skybox in cubemap
+  EQUIRECTANGULAR ///< Skybox in equirectangular projection
+};
+
 /*
  * Credit to Joey do Vries for the following cubemap images
  * Take from git://github.com/JoeyDeVries/LearnOpenGL.git
@@ -55,6 +65,13 @@ const char* SKYBOX_FACES[SKYBOX_FACE_COUNT] =
     DEMO_IMAGE_DIR "lake_back.jpg",
     DEMO_IMAGE_DIR "lake_front.jpg"};
 
+/*
+ * Take from Wikimedia Commons, the free media repository
+ * https://commons.wikimedia.org
+ * The image is licensed under the terms of the CC BY-SA 4.0 license:
+ * https://creativecommons.org/licenses/by-sa/4.0
+ */
+const char* EQUIRECTANGULAR_TEXTURE_URL = DEMO_IMAGE_DIR "veste_oberhaus_spherical_panoramic.jpg";
 } // namespace
 
 // This example shows how to create a skybox
@@ -65,7 +82,8 @@ class TexturedCubeController : public ConnectionTracker
 {
 public:
   TexturedCubeController(Application& application)
-  : mApplication(application)
+  : mApplication(application),
+    mCurrentSkyboxType(SkyboxType::CUBEMAP)
   {
     // Connect to the Application's Init signal
     mApplication.InitSignal().Connect(this, &TexturedCubeController::Create);
@@ -101,13 +119,18 @@ public:
     //         The depth test is enabled, the shader sets 1.0, which is the maximum depth and
     //         the depth function is set to LESS or EQUAL so the fragment shader will run only
     //         in those pixels that any other object has written on them.
-    DisplaySkybox();
+    DisplaySkybox(mCurrentSkyboxType);
 
     // Step 6. Play animation to rotate the cube
     PlayAnimation();
 
     // Respond to key events
     window.KeyEventSignal().Connect(this, &TexturedCubeController::OnKeyEvent);
+
+    // Create a tap gesture detector to detect double tap
+    mDoubleTapGesture = TapGestureDetector::New(2);
+    mDoubleTapGesture.Attach(window.GetRootLayer());
+    mDoubleTapGesture.DetectedSignal().Connect(this, &TexturedCubeController::OnDoubleTap);
   }
 
   /**
@@ -127,6 +150,20 @@ public:
     }
   }
 
+  void OnDoubleTap(Actor /*actor*/, const TapGesture& /*gesture*/)
+  {
+    if(mCurrentSkyboxType == SkyboxType::CUBEMAP)
+    {
+      mCurrentSkyboxType = SkyboxType::EQUIRECTANGULAR;
+    }
+    else
+    {
+      mCurrentSkyboxType = SkyboxType::CUBEMAP;
+    }
+
+    DisplaySkybox(mCurrentSkyboxType);
+  }
+
   /**
    * @brief Setup a perspective camera pointing in the negative Z direction
    */
@@ -148,8 +185,9 @@ public:
    */
   void CreateShaders()
   {
-    mShaderCube   = Shader::New(SHADER_RENDERING_SKYBOX_CUBE_VERT, SHADER_RENDERING_SKYBOX_CUBE_FRAG);
-    mShaderSkybox = Shader::New(SHADER_RENDERING_SKYBOX_VERT, SHADER_RENDERING_SKYBOX_FRAG);
+    mShaderCube                  = Shader::New(SHADER_RENDERING_SKYBOX_CUBE_VERT, SHADER_RENDERING_SKYBOX_CUBE_FRAG);
+    mShaderSkybox                = Shader::New(SHADER_RENDERING_SKYBOX_VERT, SHADER_RENDERING_SKYBOX_FRAG);
+    mShaderSkyboxEquirectangular = Shader::New(SHADER_RENDERING_SKYBOX_VERT, SHADER_RENDERING_SKYBOX_EQUIRECTANGULAR_FRAG);
   }
 
   /**
@@ -330,21 +368,41 @@ public:
   /**
    * Display a skybox surrounding the camera
    */
-  void DisplaySkybox()
+  void DisplaySkybox(SkyboxType type)
   {
-    // Load skybox faces from file
-    Texture texture = Texture::New(TextureType::TEXTURE_CUBE, Pixel::RGBA8888, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
-    for(unsigned int i = 0; i < SKYBOX_FACE_COUNT; i++)
+    mSkyboxTextures.Reset();
+    mSkyboxRenderer.Reset();
+    mSkyboxActor.Reset();
+
+    Texture texture;
+
+    if(type == SkyboxType::CUBEMAP)
+    {
+      // Load skybox faces from file
+      texture = Texture::New(TextureType::TEXTURE_CUBE, Pixel::RGBA8888, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
+      for(unsigned int i = 0; i < SKYBOX_FACE_COUNT; i++)
+      {
+        PixelData pixels = SyncImageLoader::Load(SKYBOX_FACES[i]);
+        texture.Upload(pixels, CubeMapLayer::POSITIVE_X + i, 0, 0, 0, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
+      }
+
+      mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
+    }
+    else
     {
-      PixelData pixels = SyncImageLoader::Load(SKYBOX_FACES[i]);
-      texture.Upload(pixels, CubeMapLayer::POSITIVE_X + i, 0, 0, 0, SKYBOX_FACE_WIDTH, SKYBOX_FACE_HEIGHT);
+      // Load equirectangular image from file
+      PixelData pixels = SyncImageLoader::Load(EQUIRECTANGULAR_TEXTURE_URL);
+
+      texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
+      texture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
+
+      mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkyboxEquirectangular);
     }
 
     // create TextureSet
     mSkyboxTextures = TextureSet::New();
     mSkyboxTextures.SetTexture(0, texture);
 
-    mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
     mSkyboxRenderer.SetTextures(mSkyboxTextures);
     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
 
@@ -385,6 +443,7 @@ private:
 
   Shader mShaderCube;
   Shader mShaderSkybox;
+  Shader mShaderSkyboxEquirectangular;
 
   Geometry   mGeometry;
   TextureSet mTextureSet;
@@ -396,6 +455,9 @@ private:
   TextureSet mSkyboxTextures;
   Renderer   mSkyboxRenderer;
   Actor      mSkyboxActor;
+
+  TapGestureDetector mDoubleTapGesture;
+  SkyboxType         mCurrentSkyboxType;
 };
 
 int DALI_EXPORT_API main(int argc, char** argv)
diff --git a/examples/rendering-skybox/shaders/rendering-skybox-equirectangular.frag b/examples/rendering-skybox/shaders/rendering-skybox-equirectangular.frag
new file mode 100644 (file)
index 0000000..e38d7e8
--- /dev/null
@@ -0,0 +1,35 @@
+// Fragment shader for a skybox in equirectangular projection
+precision mediump float;
+
+uniform sampler2D uSkyBoxEquirectangularTexture;
+
+varying vec3 vTexCoord;
+
+// Take the sample direction as interpolated from the cube's local position,
+// and use this direction vector and the spherical to cartesian coordinate
+// conversion to sample the equirectangular map as if it's a cube map.
+
+const float M_1_PI = 0.3183; // The reciprocal of pi in radians
+const float M_1_2PI = 0.1591; // The reciprocal of 2*pi in radians
+
+const vec2 inverseAtan = vec2(M_1_2PI, M_1_PI);
+
+vec2 SampleEquirectangularMapAsCubeMap(vec3 v)
+{
+    vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
+    uv *= inverseAtan;
+    uv += 0.5;
+    return uv;
+}
+
+void main()
+{
+  // Project the equirectangular map to a cube's faces
+  vec2 uv = SampleEquirectangularMapAsCubeMap(normalize(vTexCoord));
+
+  // Flip the texture UVs vertically
+  vec2 uvFlippped = vec2(uv.x, 1.0 - uv.y);
+
+  vec4 texColor = texture2D( uSkyBoxEquirectangularTexture, uvFlippped );
+  gl_FragColor = texColor;
+}
diff --git a/resources/images/veste_oberhaus_spherical_panoramic.jpg b/resources/images/veste_oberhaus_spherical_panoramic.jpg
new file mode 100644 (file)
index 0000000..b0781b5
Binary files /dev/null and b/resources/images/veste_oberhaus_spherical_panoramic.jpg differ