From 6b3a4233336af0f8b1949246ddc45bc3f8219e4f Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 14 Dec 2022 15:37:13 +0000 Subject: [PATCH] Equirectangular projection support for Skybox in SceneView Change-Id: Ife95e49a7e5a59c2e44f874ccea1bc330ab88ef1 --- .../controls/scene-view/scene-view-impl.cpp | 30 +++++++++++++---- .../internal/controls/scene-view/scene-view-impl.h | 2 +- .../shaders/skybox-equirectangular-shader.frag | 38 ++++++++++++++++++++++ .../public-api/controls/scene-view/scene-view.cpp | 4 +-- .../public-api/controls/scene-view/scene-view.h | 15 +++++++-- 5 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 dali-scene3d/internal/graphics/shaders/skybox-equirectangular-shader.frag diff --git a/dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp b/dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp index e8fb48e..f4d5498 100644 --- a/dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp +++ b/dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +66,7 @@ constexpr uint8_t DEFAULT_FRAME_BUFFER_MULTI_SAMPLING_LEVEL = 4u; static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity"; -Dali::Actor CreateSkybox(const std::string& skyboxUrl) +Dali::Actor CreateSkybox(const std::string& skyboxUrl, Scene3D::SceneView::SkyboxType skyboxType) { struct Vertex { @@ -121,7 +122,6 @@ Dali::Actor CreateSkybox(const std::string& skyboxUrl) {Vector3(-1.0f, -1.0f, 1.0f)}, {Vector3(1.0f, -1.0f, 1.0f)}}; - Dali::Shader shaderSkybox = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data()); Dali::VertexBuffer vertexBuffer = Dali::VertexBuffer::New(Property::Map().Add("aPosition", Property::VECTOR3)); vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex)); @@ -129,11 +129,29 @@ Dali::Actor CreateSkybox(const std::string& skyboxUrl) skyboxGeometry.AddVertexBuffer(vertexBuffer); skyboxGeometry.SetType(Geometry::TRIANGLES); - Dali::Texture skyboxTexture = Dali::Scene3D::Loader::LoadCubeMap(skyboxUrl); + Dali::Texture skyboxTexture; + Dali::Shader shaderSkybox; + Dali::Renderer skyboxRenderer; + + if(skyboxType == Scene3D::SceneView::SkyboxType::CUBEMAP) + { + skyboxTexture = Dali::Scene3D::Loader::LoadCubeMap(skyboxUrl); + shaderSkybox = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_SHADER_FRAG.data()); + } + else // Scene3D::SceneView::SkyboxType::EQUIRECTANGULAR + { + // Load image from file + PixelData pixels = Dali::Toolkit::SyncImageLoader::Load(skyboxUrl); + + skyboxTexture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight()); + skyboxTexture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight()); + shaderSkybox = Shader::New(SHADER_SKYBOX_SHADER_VERT.data(), SHADER_SKYBOX_EQUIRECTANGULAR_SHADER_FRAG.data()); + } + Dali::TextureSet skyboxTextures = TextureSet::New(); skyboxTextures.SetTexture(0, skyboxTexture); - Dali::Renderer skyboxRenderer = Renderer::New(skyboxGeometry, shaderSkybox); + skyboxRenderer = Renderer::New(skyboxGeometry, shaderSkybox); skyboxRenderer.SetTextures(skyboxTextures); skyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f); // Enables the depth test. @@ -338,7 +356,7 @@ bool SceneView::IsUsingFramebuffer() const return mUseFrameBuffer; } -void SceneView::SetSkybox(const std::string& skyboxUrl) +void SceneView::SetSkybox(const std::string& skyboxUrl, Scene3D::SceneView::SkyboxType skyboxType) { mSkyboxResourceReady = false; if(mSkybox) @@ -346,7 +364,7 @@ void SceneView::SetSkybox(const std::string& skyboxUrl) mSkybox.Unparent(); mSkybox.Reset(); } - mSkybox = CreateSkybox(skyboxUrl); + mSkybox = CreateSkybox(skyboxUrl, skyboxType); SetSkyboxIntensity(mSkyboxIntensity); SetSkyboxOrientation(mSkyboxOrientation); if(mRootLayer) diff --git a/dali-scene3d/internal/controls/scene-view/scene-view-impl.h b/dali-scene3d/internal/controls/scene-view/scene-view-impl.h index f5e4f82..def2b83 100644 --- a/dali-scene3d/internal/controls/scene-view/scene-view-impl.h +++ b/dali-scene3d/internal/controls/scene-view/scene-view-impl.h @@ -140,7 +140,7 @@ public: /** * @copydoc SceneView::SetSkybox() */ - void SetSkybox(const std::string& skyboxUrl); + void SetSkybox(const std::string& skyboxUrl, Scene3D::SceneView::SkyboxType skyboxType); /** * @copydoc SceneView::SetSkyboxIntensity() diff --git a/dali-scene3d/internal/graphics/shaders/skybox-equirectangular-shader.frag b/dali-scene3d/internal/graphics/shaders/skybox-equirectangular-shader.frag new file mode 100644 index 0000000..c57c608 --- /dev/null +++ b/dali-scene3d/internal/graphics/shaders/skybox-equirectangular-shader.frag @@ -0,0 +1,38 @@ +// Fragment shader for a skybox in equirectangular projection +precision mediump float; + +uniform sampler2D uSkyBoxEquirectangularTexture; + +uniform vec4 uColor; +uniform float uIntensity; + +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 ) * uIntensity; + gl_FragColor = texColor * uColor; +} diff --git a/dali-scene3d/public-api/controls/scene-view/scene-view.cpp b/dali-scene3d/public-api/controls/scene-view/scene-view.cpp index bc41050..732c1d6 100644 --- a/dali-scene3d/public-api/controls/scene-view/scene-view.cpp +++ b/dali-scene3d/public-api/controls/scene-view/scene-view.cpp @@ -127,9 +127,9 @@ bool SceneView::IsUsingFramebuffer() const return GetImpl(*this).IsUsingFramebuffer(); } -void SceneView::SetSkybox(const std::string& skyboxUrl) +void SceneView::SetSkybox(const std::string& skyboxUrl, SkyboxType skyboxType) { - GetImpl(*this).SetSkybox(skyboxUrl); + GetImpl(*this).SetSkybox(skyboxUrl, skyboxType); } void SceneView::SetSkyboxIntensity(float intensity) diff --git a/dali-scene3d/public-api/controls/scene-view/scene-view.h b/dali-scene3d/public-api/controls/scene-view/scene-view.h index 9c72d9c..7c4f67b 100644 --- a/dali-scene3d/public-api/controls/scene-view/scene-view.h +++ b/dali-scene3d/public-api/controls/scene-view/scene-view.h @@ -104,6 +104,16 @@ class DALI_SCENE3D_API SceneView : public Dali::Toolkit::Control { public: /** + * @brief The skybox types + * @SINCE_2_2.6 + */ + enum class SkyboxType + { + CUBEMAP, ///< Skybox in cubemap + EQUIRECTANGULAR ///< Skybox in equirectangular projection + }; + + /** * @brief Create an initialized SceneView. * * @SINCE_2_1.38 @@ -320,9 +330,10 @@ public: * Skybox texture is asynchronously loaded. When loading is finished, ResourceReady is emitted. * * @SINCE_2_2.0 - * @param[in] skyboxUrl Cube map image url for skybox. + * @param[in] skyboxUrl image url for skybox. + * @param[in] skyboxType The skybox type (by default it is cubemap). */ - void SetSkybox(const std::string& skyboxUrl); + void SetSkybox(const std::string& skyboxUrl, SkyboxType skyboxType = SkyboxType::CUBEMAP); /** * @brief Sets Skybox intensity. -- 2.7.4