[Tizen] Shadow Mapping 14/298514/2 accepted/tizen/7.0/unified/20230915.095421
authorseungho baek <sbsh.baek@samsung.com>
Thu, 6 Jul 2023 13:00:35 +0000 (22:00 +0900)
committerseungho baek <sbsh.baek@samsung.com>
Tue, 12 Sep 2023 11:19:44 +0000 (20:19 +0900)
 - DALi will support shadow with a light.
 - PCF is supported to soften shadow edge

Change-Id: Iacba477230e315a73c95ef435d33a8deeac88a3e
Signed-off-by: seungho baek <sbsh.baek@samsung.com>
33 files changed:
automated-tests/src/dali-scene3d-internal/utc-Dali-Gltf2LoaderImpl.cpp
automated-tests/src/dali-scene3d/utc-Dali-Light.cpp
automated-tests/src/dali-scene3d/utc-Dali-Model.cpp
automated-tests/src/dali-scene3d/utc-Dali-SceneView.cpp
automated-tests/src/dali-toolkit/utc-Dali-Image.cpp
dali-scene3d/internal/common/light-observer.h
dali-scene3d/internal/controls/model/model-impl.cpp
dali-scene3d/internal/controls/model/model-impl.h
dali-scene3d/internal/controls/scene-view/scene-view-impl.cpp
dali-scene3d/internal/controls/scene-view/scene-view-impl.h
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.frag
dali-scene3d/internal/graphics/shaders/default-physically-based-shader.vert
dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag [new file with mode: 0644]
dali-scene3d/internal/graphics/shaders/shadow-map-shader.vert [new file with mode: 0644]
dali-scene3d/internal/light/light-impl.cpp
dali-scene3d/internal/light/light-impl.h
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/internal/model-components/material-impl.cpp
dali-scene3d/internal/model-components/material-impl.h
dali-scene3d/internal/model-components/model-node-impl.cpp
dali-scene3d/internal/model-components/model-node-impl.h
dali-scene3d/internal/model-components/model-primitive-impl.cpp
dali-scene3d/internal/model-components/model-primitive-impl.h
dali-scene3d/public-api/light/light.cpp
dali-scene3d/public-api/light/light.h
dali-scene3d/public-api/loader/material-definition.cpp
dali-scene3d/public-api/loader/material-definition.h
dali-scene3d/public-api/loader/shader-definition.cpp
dali-scene3d/public-api/loader/shader-definition.h
dali-scene3d/public-api/loader/shader-manager.cpp
dali-scene3d/public-api/loader/shader-manager.h
dali-toolkit/public-api/image-loader/image.cpp
dali-toolkit/public-api/image-loader/image.h

index 8f2f1f5..37c4914 100644 (file)
@@ -209,6 +209,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       Scene3D::Material::AlphaModeType::MASK,
       true,
       true,
+      true,
       {
         {
           MaterialDefinition::ALBEDO,
@@ -291,6 +292,7 @@ int UtcDaliGltfLoaderSuccess1(void)
       Scene3D::Material::AlphaModeType::OPAQUE,
       true,
       false,
+      true,
       {
         {
           MaterialDefinition::ALBEDO,
@@ -619,7 +621,7 @@ int UtcDaliGltfLoaderMRendererTest(void)
   DALI_TEST_EQUAL(child.GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
   DALI_TEST_EQUAL(child.GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
   DALI_TEST_EQUAL(child.GetRendererCount(), 1u);
-  DALI_TEST_EQUAL(child.GetRendererAt(0).GetTextures().GetTextureCount(), 4u);
+  DALI_TEST_EQUAL(child.GetRendererAt(0).GetTextures().GetTextureCount(), 5u);
 
   DALI_TEST_EQUAL(child.GetRendererCount(), 1u);
   DALI_TEST_EQUAL(child.GetRendererAt(0u).GetProperty<decltype(BlendMode::ON)>(Renderer::Property::BLEND_MODE), BlendMode::ON);
index 01bb4b0..cc5bc7f 100644 (file)
@@ -599,3 +599,450 @@ int UtcDaliLightModelAddAndRemove(void)
 
   END_TEST;
 }
+
+// Enable Shadow and add the light to SceneView.
+int UtcDaliLightEnableShadowOnScene01(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  auto shadowEnabledIndex = shader.GetPropertyIndex("uIsShadowEnabled");
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  sceneView.Add(light);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Add the light to SceneView and Enable Shadow.
+int UtcDaliLightEnableShadowOnScene02(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  auto shadowEnabledIndex = shader.GetPropertyIndex("uIsShadowEnabled");
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  sceneView.Add(light);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Add the light to SceneView and Add Model.
+int UtcDaliLightEnableShadowOnScene03(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.EnableShadow(true);
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  auto shadowEnabledIndex = shader.GetPropertyIndex("uIsShadowEnabled");
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Disable Shadow
+int UtcDaliLightDisableShadow01(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.EnableShadow(true);
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  auto shadowEnabledIndex = shader.GetPropertyIndex("uIsShadowEnabled");
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadow(false);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.Unparent();
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Disable Shadow
+int UtcDaliLightDisableShadow02(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.EnableShadow(true);
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  auto shadowEnabledIndex = shader.GetPropertyIndex("uIsShadowEnabled");
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadow(false);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  Scene3D::Light newLight = Scene3D::Light::New();
+  newLight.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(newLight, Vector3(1.0f, 0.0f, 0.0f));
+  newLight.EnableShadow(true);
+  sceneView.Add(newLight);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  // Disable light's shadow, the shadow of newLight is rendered
+  light.EnableShadow(false);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  // Enable light's shadow, but newLight's shadow is rendered.
+  light.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  // Disable newLight's shadow, light's shadow is rendered.
+  newLight.Unparent();
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  // Every shadow is disabled.
+  light.Unparent();
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Make a light EnableShadow that is not enabled on scene
+int UtcDaliLightEnableShadowOfNotEnabledLight(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  uint32_t maxLightCount = Scene3D::Light::GetMaximumEnabledLightCount();
+  std::vector<Scene3D::Light> lights;
+  for(uint32_t i = 0; i < maxLightCount; ++i)
+  {
+    Scene3D::Light light = Scene3D::Light::New();
+    light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+    Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+    sceneView.Add(light);
+    lights.push_back(light);
+  }
+
+  Scene3D::Light shadowLight = Scene3D::Light::New();
+  shadowLight.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(shadowLight, Vector3(1.0f, 0.0f, 0.0f));
+  sceneView.Add(shadowLight);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  auto shadowEnabledIndex = shader.GetPropertyIndex("uIsShadowEnabled");
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  shadowLight.EnableShadow(true);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  lights[0].Enable(false);
+
+  DALI_TEST_CHECK(shadowEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Set/Get Shadow Properties
+int UtcDaliLightSetGetProperty(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.EnableShadow(true);
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(0.001f, light.GetShadowBias(), TEST_LOCATION);
+  light.SetShadowBias(0.1f);
+  DALI_TEST_EQUALS(0.1f, light.GetShadowBias(), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(0.5f, light.GetShadowIntensity(), TEST_LOCATION);
+  light.SetShadowIntensity(0.1f);
+  DALI_TEST_EQUALS(0.1f, light.GetShadowIntensity(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+// Enable PCF for soft shadow edge.
+int UtcDaliLightShadowSoftFiltering(void)
+{
+  ToolkitTestApplication application;
+
+  Scene3D::SceneView sceneView = Scene3D::SceneView::New();
+  sceneView.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sceneView.SetProperty(Dali::Actor::Property::WIDTH_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  sceneView.SetProperty(Dali::Actor::Property::HEIGHT_RESIZE_POLICY, ResizePolicy::FILL_TO_PARENT);
+  application.GetScene().Add(sceneView);
+
+  Scene3D::Model model = Scene3D::Model::New(TEST_GLTF_FILE_NAME);
+  sceneView.Add(model);
+
+  Scene3D::Light light = Scene3D::Light::New();
+  light.SetProperty(Dali::Actor::Property::COLOR, Color::BLUE);
+  Dali::DevelActor::LookAt(light, Vector3(1.0f, 0.0f, 0.0f));
+  light.EnableShadow(true);
+  sceneView.Add(light);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+  application.SendNotification();
+  application.Render();
+
+  // Light is added on layer when on scene
+  DALI_TEST_EQUALS(true, model.GetProperty<bool>(Dali::Actor::Property::CONNECTED_TO_SCENE), TEST_LOCATION);
+
+  Renderer renderer = model.FindChildByName("node2").GetRendererAt(0u);
+  DALI_TEST_CHECK(renderer);
+  Shader shader = renderer.GetShader();
+  DALI_TEST_CHECK(shader);
+
+  DALI_TEST_EQUALS(false, light.IsShadowSoftFilteringEnabled(), TEST_LOCATION);
+  auto shadowFilteringEnabledIndex = shader.GetPropertyIndex("uEnableShadowSoftFiltering");
+  DALI_TEST_CHECK(shadowFilteringEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowFilteringEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadowSoftFiltering(true);
+  DALI_TEST_EQUALS(true, light.IsShadowSoftFilteringEnabled(), TEST_LOCATION);
+
+  DALI_TEST_CHECK(shadowFilteringEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(1, shader.GetProperty<int32_t>(shadowFilteringEnabledIndex), TEST_LOCATION);
+
+  light.EnableShadowSoftFiltering(false);
+  DALI_TEST_EQUALS(false, light.IsShadowSoftFilteringEnabled(), TEST_LOCATION);
+  DALI_TEST_CHECK(shadowFilteringEnabledIndex != DALI_KEY_INVALID);
+  DALI_TEST_EQUALS(0, shader.GetProperty<int32_t>(shadowFilteringEnabledIndex), TEST_LOCATION);
+
+  END_TEST;
+}
index 30b755d..1b898a0 100644 (file)
@@ -424,10 +424,10 @@ int UtcDaliModelSetImageBasedLightSource01(void)
   DALI_TEST_CHECK(renderer);
 
   TextureSet textureSet = renderer.GetTextures();
-  DALI_TEST_EQUALS(textureSet.GetTextureCount(), 9u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureSet.GetTextureCount(), 10u, TEST_LOCATION);
 
-  Texture diffuseTexture  = textureSet.GetTexture(7u);
-  Texture specularTexture = textureSet.GetTexture(8u);
+  Texture diffuseTexture  = textureSet.GetTexture(8u);
+  Texture specularTexture = textureSet.GetTexture(9u);
 
   gResourceReadyCalled = false;
   DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
@@ -443,8 +443,8 @@ int UtcDaliModelSetImageBasedLightSource01(void)
   DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
 
   TextureSet newTextureSet      = renderer.GetTextures();
-  Texture    newDiffuseTexture  = newTextureSet.GetTexture(7u);
-  Texture    newSpecularTexture = newTextureSet.GetTexture(8u);
+  Texture    newDiffuseTexture  = newTextureSet.GetTexture(8u);
+  Texture    newSpecularTexture = newTextureSet.GetTexture(9u);
 
   DALI_TEST_NOT_EQUALS(diffuseTexture, newDiffuseTexture, 0.0f, TEST_LOCATION);
   DALI_TEST_NOT_EQUALS(specularTexture, newSpecularTexture, 0.0f, TEST_LOCATION);
@@ -481,16 +481,16 @@ int UtcDaliModelSetImageBasedLightSource02(void)
   DALI_TEST_CHECK(renderer);
 
   TextureSet textureSet = renderer.GetTextures();
-  DALI_TEST_EQUALS(textureSet.GetTextureCount(), 9u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureSet.GetTextureCount(), 10u, TEST_LOCATION);
 
-  Texture diffuseTexture  = textureSet.GetTexture(7u);
-  Texture specularTexture = textureSet.GetTexture(8u);
+  Texture diffuseTexture  = textureSet.GetTexture(8u);
+  Texture specularTexture = textureSet.GetTexture(9u);
 
   // if url is empty, loading is not requested.
   model.SetImageBasedLightSource("", "");
 
-  Texture newDiffuseTexture  = textureSet.GetTexture(7u);
-  Texture newSpecularTexture = textureSet.GetTexture(8u);
+  Texture newDiffuseTexture  = textureSet.GetTexture(8u);
+  Texture newSpecularTexture = textureSet.GetTexture(9u);
 
   DALI_TEST_EQUALS(diffuseTexture, newDiffuseTexture, TEST_LOCATION);
   DALI_TEST_EQUALS(specularTexture, newSpecularTexture, TEST_LOCATION);
@@ -525,10 +525,10 @@ int UtcDaliModelSetImageBasedLightSource03(void)
   DALI_TEST_CHECK(renderer);
 
   TextureSet textureSet = renderer.GetTextures();
-  DALI_TEST_EQUALS(textureSet.GetTextureCount(), 9u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureSet.GetTextureCount(), 10u, TEST_LOCATION);
 
-  Texture diffuseTexture  = textureSet.GetTexture(7u);
-  Texture specularTexture = textureSet.GetTexture(8u);
+  Texture diffuseTexture  = textureSet.GetTexture(8u);
+  Texture specularTexture = textureSet.GetTexture(9u);
 
   gResourceReadyCalled = false;
   DALI_TEST_EQUALS(gResourceReadyCalled, false, TEST_LOCATION);
@@ -543,8 +543,8 @@ int UtcDaliModelSetImageBasedLightSource03(void)
 
   DALI_TEST_EQUALS(gResourceReadyCalled, true, TEST_LOCATION);
 
-  Texture newDiffuseTexture  = textureSet.GetTexture(7u);
-  Texture newSpecularTexture = textureSet.GetTexture(8u);
+  Texture newDiffuseTexture  = textureSet.GetTexture(8u);
+  Texture newSpecularTexture = textureSet.GetTexture(9u);
 
   DALI_TEST_EQUALS(diffuseTexture, newDiffuseTexture, TEST_LOCATION);
   DALI_TEST_EQUALS(specularTexture, newSpecularTexture, TEST_LOCATION);
@@ -1352,16 +1352,16 @@ int UtcDaliModelResourceCacheCheck(void)
   // but all the other textures are still the same
   TextureSet textureSet2 = renderer2.GetTextures();
   TextureSet textureSet3 = renderer3.GetTextures();
-  DALI_TEST_EQUALS(textureSet2.GetTextureCount(), 9u, TEST_LOCATION);
-  DALI_TEST_EQUALS(textureSet3.GetTextureCount(), 9u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureSet2.GetTextureCount(), 10u, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureSet3.GetTextureCount(), 10u, TEST_LOCATION);
 
   for(uint32_t i = 0; i < 7u; i++)
   {
     DALI_TEST_EQUALS(textureSet2.GetTexture(i), textureSet3.GetTexture(i), TEST_LOCATION);
   }
 
-  DALI_TEST_NOT_EQUALS(textureSet2.GetTexture(7u), textureSet3.GetTexture(7u), 0.0f, TEST_LOCATION);
-  DALI_TEST_NOT_EQUALS(textureSet2.GetTexture(8u), textureSet3.GetTexture(8u), 0.0f, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(textureSet2.GetTexture(8u), textureSet3.GetTexture(7u), 0.0f, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(textureSet2.GetTexture(9u), textureSet3.GetTexture(8u), 0.0f, TEST_LOCATION);
 
   END_TEST;
 }
index 53e7725..ca87d38 100644 (file)
@@ -88,9 +88,9 @@ Dali::Texture GetDiffuseTexture(Dali::Scene3D::Model model)
     if(renderer)
     {
       TextureSet textureSet = renderer.GetTextures();
-      if(textureSet.GetTextureCount() == 9u)
+      if(textureSet.GetTextureCount() == 10u)
       {
-        texture = textureSet.GetTexture(7u);
+        texture = textureSet.GetTexture(8u);
       }
     }
   }
@@ -109,9 +109,9 @@ Dali::Texture GetSpecularTexture(Dali::Scene3D::Model model)
     if(renderer)
     {
       TextureSet textureSet = renderer.GetTextures();
-      if(textureSet.GetTextureCount() == 9u)
+      if(textureSet.GetTextureCount() == 10u)
       {
-        texture = textureSet.GetTexture(8u);
+        texture = textureSet.GetTexture(9u);
       }
     }
   }
@@ -327,9 +327,9 @@ int UtcDaliSceneViewOnScene02(void)
   application.Render();
 
   renderTaskCount = application.GetScene().GetRenderTaskList().GetTaskCount();
-  DALI_TEST_EQUALS(2u, renderTaskCount, TEST_LOCATION);
+  DALI_TEST_EQUALS(3u, renderTaskCount, TEST_LOCATION);
 
-  RenderTask  renderTask = application.GetScene().GetRenderTaskList().GetTask(1u);
+  RenderTask  renderTask = application.GetScene().GetRenderTaskList().GetTask(2u);
   CameraActor camera     = renderTask.GetCameraActor();
 
   CameraActor defaultCamera = renderTask.GetCameraActor();
@@ -693,7 +693,7 @@ int UtcDaliSceneViewUseFramebuffer02(void)
   application.SendNotification();
   application.Render();
 
-  RenderTask renderTask = application.GetScene().GetRenderTaskList().GetTask(1u);
+  RenderTask renderTask = application.GetScene().GetRenderTaskList().GetTask(2u);
   DALI_TEST_CHECK(!renderTask.GetFrameBuffer());
 
   view.UseFramebuffer(true);
@@ -1005,7 +1005,7 @@ int UtcDaliSceneViewCreateAndRemoveRenderTask(void)
 
   application.GetScene().Add(view);
 
-  DALI_TEST_EQUALS(renderTaskCount + 1, application.GetScene().GetRenderTaskList().GetTaskCount(), TEST_LOCATION);
+  DALI_TEST_EQUALS(renderTaskCount + 2, application.GetScene().GetRenderTaskList().GetTaskCount(), TEST_LOCATION);
 
   view.Unparent();
 
index 4767977..e6451bc 100644 (file)
@@ -77,6 +77,23 @@ int UtcDaliImageConvertFrameBufferToUrl2(void)
   END_TEST;
 }
 
+int UtcDaliImageConvertDepthTextureFrameBufferToUrl(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcDaliImageConvertDepthTextureFrameBufferToUrl");
+
+  unsigned int width(64);
+  unsigned int height(64);
+  FrameBuffer  frameBuffer = FrameBuffer::New(width, height, FrameBuffer::Attachment::NONE);
+
+  Texture texture = Texture::New(TextureType::TEXTURE_2D, Pixel::DEPTH_UNSIGNED_INT, width, height);
+  DevelFrameBuffer::AttachDepthTexture(frameBuffer, texture);
+
+  DALI_TEST_CHECK(Dali::Toolkit::Image::GenerateDepthUrl(frameBuffer).GetUrl().size() > 0u);
+
+  END_TEST;
+}
+
 int UtcDaliImageConvertPixelDataToUrl01(void)
 {
   ToolkitTestApplication application;
index 608e100..eb98c90 100644 (file)
@@ -60,6 +60,13 @@ public:
    * @param[in] scaleFactor scale factor that controls light source intensity in [0.0f, 1.0f].
    */
   virtual void NotifyImageBasedLightScaleFactor(float scaleFactor) = 0;
+
+  /**
+   * @brief Notifies Shadow Map texture is changed by parent SceneView.
+   *
+   * @param[in] shadowMapTexture Shadow Map texture that will be used to compute shadow.
+   */
+  virtual void NotifyShadowMapTexture(Dali::Texture shadowMapTexture) = 0;
 };
 
 } // namespace Internal
index b3e3cc7..a7dde17 100644 (file)
@@ -213,6 +213,26 @@ void UpdateShaderRecursively(Scene3D::ModelNode node, Scene3D::Loader::ShaderMan
   }
 }
 
+void UpdateShadowMapTextureRecursively(Scene3D::ModelNode node, Dali::Texture shadowMapTexture)
+{
+  if(!node)
+  {
+    return;
+  }
+
+  GetImplementation(node).SetShadowMapTexture(shadowMapTexture);
+
+  uint32_t childrenCount = node.GetChildCount();
+  for(uint32_t i = 0; i < childrenCount; ++i)
+  {
+    Scene3D::ModelNode childNode = Scene3D::ModelNode::DownCast(node.GetChildAt(i));
+    if(childNode)
+    {
+      UpdateShadowMapTextureRecursively(childNode, shadowMapTexture);
+    }
+  }
+}
+
 } // anonymous namespace
 
 Model::Model(const std::string& modelUrl, const std::string& resourceDirectoryUrl)
@@ -280,6 +300,11 @@ void Model::AddModelNode(Scene3D::ModelNode modelNode)
 
   UpdateShaderRecursively(modelNode, mShaderManager);
 
+  if(mShadowMapTexture)
+  {
+    UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture);
+  }
+
   if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
   {
     UpdateImageBasedLightTexture();
@@ -1007,6 +1032,15 @@ void Model::ApplyCameraTransform(Dali::CameraActor camera) const
   camera.SetProperty(Actor::Property::SCALE, resultScale);
 }
 
+void Model::NotifyShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+  if(mShadowMapTexture != shadowMapTexture)
+  {
+    mShadowMapTexture = shadowMapTexture;
+    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+  }
+}
+
 void Model::NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels)
 {
   if(mSceneDiffuseTexture != diffuseTexture || mSceneSpecularTexture != specularTexture)
@@ -1062,6 +1096,10 @@ void Model::OnModelLoadComplete()
     mDefaultSpecularTexture = resources.mEnvironmentMaps.front().second.mSpecular;
   }
 
+  if(mShadowMapTexture)
+  {
+    UpdateShadowMapTextureRecursively(mModelRoot, mShadowMapTexture);
+  }
   UpdateImageBasedLightTexture();
   UpdateImageBasedLightScaleFactor();
   Self().SetProperty(Dali::Actor::Property::ANCHOR_POINT, Vector3(mModelPivot.x, 1.0f - mModelPivot.y, mModelPivot.z));
index 23337a4..b740da4 100644 (file)
@@ -272,6 +272,11 @@ private:
 
 public: // Overrides LightObserver Methods.
   /**
+   * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyShadowMapTexture()
+   */
+  void NotifyShadowMapTexture(Dali::Texture shadowMapTexture) override;
+
+  /**
    * @copydoc Dali::Scene3D::Internal::LightObserver::NotifyImageBasedLightTexture()
    */
   void NotifyImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float scaleFactor, uint32_t specularMipmapLevels) override;
@@ -361,6 +366,9 @@ private:
   EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
   EnvironmentMapLoadTaskPtr mIblSpecularLoadTask;
 
+  // Shadow
+  Dali::Texture mShadowMapTexture;
+
   std::string mDiffuseIblUrl;
   std::string mSpecularIblUrl;
 
index 65298a2..1273ac2 100644 (file)
@@ -62,9 +62,10 @@ BaseHandle Create()
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::SceneView, Toolkit::Control, Create);
 DALI_TYPE_REGISTRATION_END()
 
-Property::Index   RENDERING_BUFFER    = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
-constexpr int32_t DEFAULT_ORIENTATION = 0;
-constexpr int32_t INVALID_INDEX       = -1;
+Property::Index    RENDERING_BUFFER        = Dali::Toolkit::Control::CONTROL_PROPERTY_END_INDEX + 1;
+constexpr int32_t  DEFAULT_ORIENTATION     = 0;
+constexpr int32_t  INVALID_INDEX           = -1;
+constexpr uint32_t MAXIMUM_SIZE_SHADOW_MAP = 2048;
 
 static constexpr std::string_view SKYBOX_INTENSITY_STRING = "uIntensity";
 
@@ -148,6 +149,143 @@ Dali::Actor CreateSkybox()
   return skyboxActor;
 }
 
+void SetShadowLightConstraint(Dali::CameraActor selectedCamera, Dali::CameraActor shadowLightCamera)
+{
+  shadowLightCamera.SetProperty(Dali::CameraActor::Property::ASPECT_RATIO, 1.0f);
+
+  shadowLightCamera.SetProperty(Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE, 1.0f);
+  shadowLightCamera.SetProperty(Dali::CameraActor::Property::NEAR_PLANE_DISTANCE, 0.5f);
+  shadowLightCamera.SetProperty(Dali::CameraActor::Property::FAR_PLANE_DISTANCE, 3.5f);
+
+  //< Make constraint for above properties.
+  shadowLightCamera.RemoveConstraints();
+
+  // Compute View Matrix of ShadowLightCamera
+  // Input : ShadowLightCamera's world position, world orientation
+  auto       tempViewMatrixIndex  = shadowLightCamera.RegisterProperty("tempViewMatrix", Matrix::IDENTITY);
+  Constraint viewMatrixConstraint = Constraint::New<Matrix>(shadowLightCamera, tempViewMatrixIndex, [](Matrix& output, const PropertyInputContainer& inputs)
+                                                            {
+                                                              output = inputs[0]->GetMatrix();
+                                                              output.Invert(); });
+  viewMatrixConstraint.AddSource(Source{shadowLightCamera, Dali::Actor::Property::WORLD_MATRIX});
+  viewMatrixConstraint.ApplyPost();
+
+  // Compute Orthographic Size / Near / Far and store it to "TempCameraProperty" property that is a Vector3 property
+  auto       tempProjectionMatrixIndex  = shadowLightCamera.RegisterProperty("tempProjectionMatrix", Matrix::IDENTITY);
+  Constraint projectionMatrixConstraint = Constraint::New<Matrix>(shadowLightCamera, tempProjectionMatrixIndex, [](Matrix& output, const PropertyInputContainer& inputs)
+                                                                  {
+                                                                    Matrix worldMatrix = inputs[0]->GetMatrix();
+                                                                    float tangentFov_2 = tanf(inputs[4]->GetFloat());
+                                                                    float  nearDistance = inputs[5]->GetFloat();
+                                                                    float  farDistance  = inputs[6]->GetFloat();
+                                                                    float  aspectRatio  = inputs[7]->GetFloat();
+                                                                    float  nearY        = 0.0f;
+                                                                    float  nearX        = 0.0f;
+                                                                    float  farY         = 0.0f;
+                                                                    float  farX         = 0.0f;
+                                                                    if(inputs[1]->GetInteger() == Dali::Camera::ProjectionMode::PERSPECTIVE_PROJECTION)
+                                                                    {
+                                                                      if(inputs[2]->GetInteger() == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
+                                                                      {
+                                                                        nearY = tangentFov_2 * nearDistance;
+                                                                        nearX = nearY * aspectRatio;
+                                                                        farY  = tangentFov_2 * farDistance;
+                                                                        farX  = farY * aspectRatio;
+                                                                      }
+                                                                      else
+                                                                      {
+                                                                        nearX = tangentFov_2 * nearDistance;
+                                                                        nearY = nearX / aspectRatio;
+                                                                        farX  = tangentFov_2 * farDistance;
+                                                                        farY  = farX / aspectRatio;
+                                                                      }
+                                                                    }
+                                                                    else
+                                                                    {
+                                                                      if(inputs[2]->GetInteger() == Dali::DevelCameraActor::ProjectionDirection::VERTICAL)
+                                                                      {
+                                                                        nearY = inputs[3]->GetFloat();
+                                                                        nearX = nearY * aspectRatio;
+                                                                      }
+                                                                      else
+                                                                      {
+                                                                        nearX = inputs[3]->GetFloat();
+                                                                        nearY = nearX / aspectRatio;
+                                                                      }
+                                                                      farX = nearX;
+                                                                      farY = nearY;
+                                                                    }
+
+                                                                    std::vector<Vector4> points;
+                                                                    points.push_back(Vector4(nearX, nearY, nearDistance, 1.0f));
+                                                                    points.push_back(Vector4(-nearX, nearY, nearDistance, 1.0f));
+                                                                    points.push_back(Vector4(-nearX, -nearY, nearDistance, 1.0f));
+                                                                    points.push_back(Vector4(nearX, -nearY, nearDistance, 1.0f));
+                                                                    points.push_back(Vector4(farX, farY, farDistance, 1.0f));
+                                                                    points.push_back(Vector4(-farX, farY, farDistance, 1.0f));
+                                                                    points.push_back(Vector4(-farX, -farY, farDistance, 1.0f));
+                                                                    points.push_back(Vector4(farX, -farY, farDistance, 1.0f));
+
+                                                                    Vector3 areaMin = Vector3::ONE * MAXFLOAT, areaMax = Vector3::ONE * -MAXFLOAT;
+                                                                    for(auto&& point : points)
+                                                                    {
+                                                                      Vector4 pointW = worldMatrix * point;
+                                                                      Vector4 pointV = inputs[8]->GetMatrix() * pointW;
+                                                                      areaMin.x      = std::min(areaMin.x, pointV.x);
+                                                                      areaMin.y      = std::min(areaMin.y, pointV.y);
+                                                                      areaMin.z      = std::min(areaMin.z, pointV.z);
+                                                                      areaMax.x      = std::max(areaMax.x, pointV.x);
+                                                                      areaMax.y      = std::max(areaMax.y, pointV.y);
+                                                                      areaMax.z      = std::max(areaMax.z, pointV.z);
+                                                                    }
+
+                                                                    Vector2 center        = Vector2(areaMax + areaMin) / 2.0f;
+                                                                    float   delta         = std::max(std::abs(areaMax.x - areaMin.x), std::abs(areaMax.y - areaMin.y));
+                                                                    float   delta_2       = delta * 0.5f;
+                                                                    Vector2 squareAreaMin = center - Vector2::ONE * delta_2;
+                                                                    Vector2 squareAreaMax = center + Vector2::ONE * delta_2;
+                                                                    float   deltaZ        = areaMax.z - areaMin.z;
+
+                                                                    float right  = -squareAreaMin.x;
+                                                                    float left   = -squareAreaMax.x;
+                                                                    float top    = squareAreaMin.y;
+                                                                    float bottom = squareAreaMax.y;
+                                                                    float near   = areaMin.z;
+                                                                    float far    = areaMax.z;
+
+                                                                    float* projMatrix = output.AsFloat();
+
+                                                                    projMatrix[0] = -2.0f / delta;
+                                                                    projMatrix[1] = 0.0f;
+                                                                    projMatrix[2] = 0.0f;
+                                                                    projMatrix[3] = 0.0f;
+
+                                                                    projMatrix[4] = 0.0f;
+                                                                    projMatrix[5] = -2.0f / delta;
+                                                                    projMatrix[6] = 0.0f;
+                                                                    projMatrix[7] = 0.0f;
+
+                                                                    projMatrix[8]  = 0.0f;
+                                                                    projMatrix[9]  = 0.0f;
+                                                                    projMatrix[10] = 2.0f / deltaZ;
+                                                                    projMatrix[11] = 0.0f;
+
+                                                                    projMatrix[12] = -(right + left) / delta;
+                                                                    projMatrix[13] = -(top + bottom) / delta;
+                                                                    projMatrix[14] = -(near + far) / deltaZ;
+                                                                    projMatrix[15] = 1.0f; });
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::Actor::Property::WORLD_MATRIX});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::PROJECTION_MODE});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::DevelCameraActor::Property::PROJECTION_DIRECTION});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::DevelCameraActor::Property::ORTHOGRAPHIC_SIZE});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::FIELD_OF_VIEW});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::NEAR_PLANE_DISTANCE});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::FAR_PLANE_DISTANCE});
+  projectionMatrixConstraint.AddSource(Source{selectedCamera, Dali::CameraActor::Property::ASPECT_RATIO});
+  projectionMatrixConstraint.AddSource(Source{shadowLightCamera, tempViewMatrixIndex});
+  projectionMatrixConstraint.ApplyPost();
+}
+
 } // anonymous namespace
 
 SceneView::SceneView()
@@ -421,6 +559,11 @@ void SceneView::AddLight(Scene3D::Light light)
 {
   bool enabled = mShaderManager->AddLight(light);
   mLights.push_back(std::make_pair(light, enabled));
+
+  if(light.IsShadowEnabled())
+  {
+    SetShadow(light);
+  }
 }
 
 void SceneView::RemoveLight(Scene3D::Light light)
@@ -437,7 +580,7 @@ void SceneView::RemoveLight(Scene3D::Light light)
 
   if(mLights.size() > mShaderManager->GetLightCount())
   {
-    for(auto && waitingLight : mLights)
+    for(auto&& waitingLight : mLights)
     {
       if(waitingLight.second)
       {
@@ -448,6 +591,86 @@ void SceneView::RemoveLight(Scene3D::Light light)
       break;
     }
   }
+
+  if(light == mShadowLight)
+  {
+    RemoveShadow(light);
+  }
+
+  if(!mShadowLight)
+  {
+    for(auto&& lightEntity : mLights)
+    {
+      if(!lightEntity.second || !lightEntity.first.IsShadowEnabled())
+      {
+        continue;
+      }
+      SetShadow(lightEntity.first);
+      break;
+    }
+  }
+}
+
+void SceneView::SetShadow(Scene3D::Light light)
+{
+  if(!!mShadowLight)
+  {
+    return;
+  }
+
+  auto foundLight = std::find_if(mLights.begin(), mLights.end(), [light](std::pair<Scene3D::Light, bool> lightEntity) -> bool
+                                 { return (lightEntity.second && lightEntity.first == light); });
+
+  if(foundLight == mLights.end())
+  {
+    return;
+  }
+
+  mShadowLight = light;
+
+  // Directional Light setting.
+  CameraActor lightCamera    = GetImplementation(light).GetCamera();
+  CameraActor selectedCamera = GetSelectedCamera();
+  SetShadowLightConstraint(selectedCamera, lightCamera);
+
+  // make framebuffer for depth map and set it to render task.
+  Vector3  size               = Self().GetProperty<Vector3>(Dali::Actor::Property::SIZE);
+  uint32_t shadowMapBufferSize = std::min(static_cast<uint32_t>(std::max(size.width, size.height)), MAXIMUM_SIZE_SHADOW_MAP);
+  UpdateShadowMapBuffer(shadowMapBufferSize);
+
+  // use lightCamera as a camera of shadow render task.
+  mShadowMapRenderTask.SetCameraActor(lightCamera);
+
+  mShaderManager->SetShadow(light);
+}
+
+void SceneView::RemoveShadow(Scene3D::Light light)
+{
+  if(mShadowLight != light)
+  {
+    return;
+  }
+
+  // remove all constraint from light camera
+  CameraActor lightCamera = GetImplementation(mShadowLight).GetCamera();
+  lightCamera.RemoveConstraints();
+
+  // reset framebuffer and remove it from render task.
+  mShadowFrameBuffer.Reset();
+  mShaderManager->RemoveShadow();
+  mShadowMapRenderTask.SetCameraActor(CameraActor());
+
+  mShadowLight.Reset();
+
+  for(auto&& lightEntity : mLights)
+  {
+    if(!lightEntity.second || !lightEntity.first.IsShadowEnabled())
+    {
+      continue;
+    }
+    SetShadow(lightEntity.first);
+    break;
+  }
 }
 
 uint32_t SceneView::GetActivatedLightCount() const
@@ -550,6 +773,11 @@ Dali::Scene3D::Loader::ShaderManagerPtr SceneView::GetShaderManager() const
   return mShaderManager;
 }
 
+void SceneView::UpdateShadowUniform(Scene3D::Light light)
+{
+  mShaderManager->UpdateShadowUniform(light);
+}
+
 ///////////////////////////////////////////////////////////
 //
 // Private methods
@@ -583,7 +811,17 @@ void SceneView::OnSceneConnection(int depth)
   if(mSceneHolder)
   {
     RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
-    mRenderTask             = taskList.CreateTask();
+    mShadowMapRenderTask    = taskList.CreateTask();
+    mShadowMapRenderTask.SetSourceActor(mRootLayer);
+    mShadowMapRenderTask.SetExclusive(true);
+    mShadowMapRenderTask.SetInputEnabled(false);
+    mShadowMapRenderTask.SetCullMode(false);
+    mShadowMapRenderTask.SetClearEnabled(true);
+    mShadowMapRenderTask.SetClearColor(Color::WHITE);
+    mShadowMapRenderTask.SetRenderPassTag(10);
+    mShadowMapRenderTask.SetCameraActor(CameraActor());
+
+    mRenderTask = taskList.CreateTask();
     mRenderTask.SetSourceActor(mRootLayer);
     mRenderTask.SetExclusive(true);
     mRenderTask.SetInputEnabled(true);
@@ -615,9 +853,16 @@ void SceneView::OnSceneDisconnection()
       taskList.RemoveTask(mRenderTask);
       mRenderTask.Reset();
     }
+    if(mShadowMapRenderTask)
+    {
+      RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+      taskList.RemoveTask(mShadowMapRenderTask);
+      mShadowMapRenderTask.Reset();
+    }
     mSceneHolder.Reset();
   }
   mFrameBuffer.Reset();
+  mShadowFrameBuffer.Reset();
 
   Control::OnSceneDisconnection();
 }
@@ -694,6 +939,10 @@ void SceneView::UpdateCamera(CameraActor camera)
   }
 
   mSelectedCamera = camera;
+  if(mShadowLight)
+  {
+    SetShadowLightConstraint(mSelectedCamera, GetImplementation(mShadowLight).GetCamera());
+  }
   UpdateRenderTask();
 }
 
@@ -710,6 +959,9 @@ void SceneView::UpdateRenderTask()
     const float aspectRatio = size.width / size.height;
     mSelectedCamera.SetAspectRatio(aspectRatio);
 
+    uint32_t shadowMapBufferSize = std::min(static_cast<uint32_t>(std::max(size.width, size.height)), MAXIMUM_SIZE_SHADOW_MAP);
+    UpdateShadowMapBuffer(shadowMapBufferSize);
+
     if(mUseFrameBuffer)
     {
       Dali::FrameBuffer currentFrameBuffer = mRenderTask.GetFrameBuffer();
@@ -918,6 +1170,29 @@ void SceneView::NotifyImageBasedLightTextureChange()
   }
 }
 
+void SceneView::UpdateShadowMapBuffer(uint32_t shadowMapSize)
+{
+  Dali::FrameBuffer currentShadowFrameBuffer = mShadowMapRenderTask.GetFrameBuffer();
+  if(mShadowLight &&
+     (!currentShadowFrameBuffer ||
+      !Dali::Equals(DevelFrameBuffer::GetDepthTexture(currentShadowFrameBuffer).GetWidth(), shadowMapSize)))
+  {
+    mShadowFrameBuffer.Reset();
+    Dali::Texture shadowTexture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::DEPTH_UNSIGNED_INT, shadowMapSize, shadowMapSize);
+    mShadowFrameBuffer          = FrameBuffer::New(shadowMapSize, shadowMapSize, FrameBuffer::Attachment::NONE);
+    DevelFrameBuffer::AttachDepthTexture(mShadowFrameBuffer, shadowTexture);
+    mShadowMapRenderTask.SetFrameBuffer(mShadowFrameBuffer);
+
+    for(auto&& item : mItems)
+    {
+      if(item)
+      {
+        item->NotifyShadowMapTexture(shadowTexture);
+      }
+    }
+  }
+}
+
 } // namespace Internal
 } // namespace Scene3D
 } // namespace Dali
index 79f7d90..9a45eca 100644 (file)
@@ -150,6 +150,22 @@ public:
   void RemoveLight(Scene3D::Light light);
 
   /**
+   * @brief Set a shadow to this scene by input light.
+   * Currently, SceneView supports only one shadow.
+   *
+   * @param[in] light Light object to make shadow.
+   * @note The shadow will be drawn if the input light is turn on in current scene.
+   */
+  void SetShadow(Scene3D::Light light);
+
+  /**
+   * @brief Removes Shadow from this SceneView.
+   *
+   * @param[in] light Light object to be removed.
+   */
+  void RemoveShadow(Scene3D::Light light);
+
+  /**
    * @copydoc SceneView::GetActivatedLightCount()
    */
   uint32_t GetActivatedLightCount() const;
@@ -210,6 +226,12 @@ public:
    */
   Dali::Scene3D::Loader::ShaderManagerPtr GetShaderManager() const;
 
+  /**
+   * @brief Update shader uniforms about shadow.
+   * @param[in] light Light that makes shadow.
+   */
+  void UpdateShadowUniform(Scene3D::Light light);
+
 protected:
   /**
    * @brief Constructs a new SceneView.
@@ -322,6 +344,12 @@ private:
    */
   void NotifyImageBasedLightTextureChange();
 
+  /**
+   * @brief Update shadowMap framebuffer when the size should be changed.
+   * @param[in] shadowMapSize The size of shadowMap texture. The texture's width and hight is equal.
+   */
+  void UpdateShadowMapBuffer(uint32_t shadowMapSize);
+
 private:
   Toolkit::Visual::Base mVisual;
 
@@ -348,6 +376,9 @@ private:
 
   // Light
   std::vector<std::pair<Scene3D::Light, bool>> mLights; // Pair of Light object and flag that denotes the light is currently activated or not.
+  Dali::FrameBuffer                            mShadowFrameBuffer;
+  Dali::RenderTask                             mShadowMapRenderTask;
+  Scene3D::Light                               mShadowLight;
 
   // Asynchronous Loading.
   EnvironmentMapLoadTaskPtr mSkyboxLoadTask;
index de7b28d..db3c146 100644 (file)
@@ -78,6 +78,11 @@ uniform mediump int uLightCount;
 uniform mediump vec3 uLightDirection[MAX_LIGHTS];
 uniform mediump vec3 uLightColor[MAX_LIGHTS];
 
+// For Shadow Map
+uniform lowp int uIsShadowEnabled;
+uniform sampler2D sShadowMap;
+in highp vec3 positionFromLightView;
+
 //// For IBL
 uniform sampler2D sbrdfLUT;
 uniform samplerCube sDiffuseEnvSampler;
@@ -102,6 +107,21 @@ out vec4 FragColor;
 const float c_MinRoughness = 0.04;
 const float M_PI = 3.141592653589793;
 
+// These properties can be used for circular sampling for PCF
+
+// Percentage Closer Filtering to mitigate the banding artifacts.
+const int kPcfSampleCount = 9;
+
+const float kPi = 3.141592653589f;
+const float kInvSampleCount = 1.0 / float(kPcfSampleCount);
+const float kPcfTheta = 2.f * kPi * kInvSampleCount;
+const float kSinPcfTheta = sin(kPcfTheta);
+const float kCosPcfTheta = cos(kPcfTheta);
+
+uniform lowp int uEnableShadowSoftFiltering;
+uniform mediump float uShadowIntensity;
+uniform mediump float uShadowBias;
+
 vec3 linear(vec3 color)
 {
   return pow(color, vec3(2.2));
@@ -244,6 +264,31 @@ void main()
     }
   }
 
+  if(float(uIsShadowEnabled) * uShadowIntensity > 0.0)
+  {
+    mediump float exposureFactor = 0.0;
+    if(uEnableShadowSoftFiltering > 0)
+    {
+      ivec2 texSize = textureSize(sShadowMap, 0);
+      mediump vec2 texelSize = vec2(1.0) / vec2(texSize.x, texSize.y);
+      mediump vec2 pcfSample = vec2(1.f, 0.f);
+      for (int i = 0; i < kPcfSampleCount; ++i)
+      {
+        pcfSample = vec2(kCosPcfTheta * pcfSample.x - kSinPcfTheta * pcfSample.y,
+                         kSinPcfTheta * pcfSample.x + kCosPcfTheta * pcfSample.y);
+        lowp float depthValue = texture(sShadowMap, positionFromLightView.xy + pcfSample * texelSize).r;
+        exposureFactor += (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+      }
+      exposureFactor *= kInvSampleCount;
+    }
+    else
+    {
+      mediump float depthValue = texture(sShadowMap, positionFromLightView.xy).r;
+      exposureFactor           = (depthValue < positionFromLightView.z - uShadowBias) ? 0.0 : 1.0;
+    }
+    color *= (1.0 - (1.0 - exposureFactor) * uShadowIntensity);
+  }
+
 #ifdef OCCLUSION
   lowp float ao = texture(sOcclusion, vUV).r;
   color = mix(color, color * ao, uOcclusionStrength);
index 8be7c88..a0ffd0b 100644 (file)
@@ -58,6 +58,11 @@ uniform highp float uBlendShapeUnnormalizeFactor[MAX_BLEND_SHAPE_NUMBER]; ///< F
 uniform highp int uBlendShapeComponentSize;                               ///< The size in the texture of either the vertices, normals or tangents. Used to calculate the offset to address them.
 #endif
 
+// Shadow
+uniform lowp int uIsShadowEnabled;
+uniform highp mat4 uShadowLightViewProjectionMatrix;
+out highp vec3 positionFromLightView;
+
 void main()
 {
   highp vec4 position = vec4(aPosition, 1.0);
@@ -169,5 +174,12 @@ void main()
 
   vColor = aVertexColor;
 
+  positionFromLightView = vec3(1.0);
+  if(uIsShadowEnabled > 0)
+  {
+    highp vec4 positionInLightView = uShadowLightViewProjectionMatrix * positionW;
+    positionFromLightView = ((positionInLightView.xyz / positionInLightView.w) * 0.5) + vec3(0.5);
+  }
+
   gl_Position = uProjection * positionV;
 }
diff --git a/dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag b/dali-scene3d/internal/graphics/shaders/shadow-map-shader.frag
new file mode 100644 (file)
index 0000000..91dace8
--- /dev/null
@@ -0,0 +1,50 @@
+#version 300 es
+
+uniform lowp vec4 uColorFactor; // Color from material
+uniform lowp float uMask;
+uniform lowp float uAlphaThreshold;
+
+in mediump vec2 vUV;
+in lowp vec4 vColor;
+
+//in highp float depth;
+//out highp vec4 FragColor;
+
+#ifdef THREE_TEX
+#ifdef BASECOLOR_TEX
+uniform sampler2D sAlbedoAlpha;
+#endif // BASECOLOR_TEX
+#else // THREE_TEX
+uniform sampler2D sAlbedoMetal;
+#endif
+
+lowp vec3 linear(lowp vec3 color)
+{
+  return pow(color, vec3(2.2));
+}
+
+void main()
+{
+#ifdef THREE_TEX
+  // The albedo may be defined from a base texture or a flat color
+#ifdef BASECOLOR_TEX
+  lowp vec4 baseColor = texture(sAlbedoAlpha, vUV);
+  baseColor = vColor * vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
+#else // BASECOLOR_TEX
+  lowp vec4 baseColor = vColor * uColorFactor;
+#endif // BASECOLOR_TEX
+#else // THREE_TEX
+  lowp vec4 albedoMetal = texture(sAlbedoMetal, vUV);
+  lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * vColor * uColorFactor;
+#endif // THREE_TEX
+
+  // The value of uOpaque and uMask can be 0.0 or 1.0.
+  // If uMask is 1.0, a Pixel that has bigger alpha than uAlphaThreashold becomes fully opaque,
+  // and, a pixel that has smaller alpha than uAlphaThreashold becomes fully transparent.
+  // If uOpaque is 1.0, alpha value of final color is 1.0;
+  // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_material_alphamode
+  if(uMask > 0.5 && baseColor.a < uAlphaThreshold)
+  {
+    discard;
+  }
+}
\ No newline at end of file
diff --git a/dali-scene3d/internal/graphics/shaders/shadow-map-shader.vert b/dali-scene3d/internal/graphics/shaders/shadow-map-shader.vert
new file mode 100644 (file)
index 0000000..73bb37c
--- /dev/null
@@ -0,0 +1,110 @@
+#version 300 es
+
+precision highp float;
+
+in vec3 aPosition;
+in vec2 aTexCoord;
+in vec4 aVertexColor;
+
+out mediump vec2 vUV;
+out lowp vec4 vColor;
+
+uniform highp mat4 uViewMatrix;
+uniform highp mat4 uModelMatrix;
+uniform highp mat4 uProjection;
+
+#ifdef SKINNING
+  in vec4 aJoints;
+  in vec4 aWeights;
+  #define MAX_BONES 64
+  uniform mat4 uBone[MAX_BONES];
+  uniform mediump vec3 uYDirection;
+#endif
+
+#ifdef MORPH
+#define MAX_BLEND_SHAPE_NUMBER 128
+uniform int uNumberOfBlendShapes;                                         ///< Total number of blend shapes loaded.
+uniform highp float uBlendShapeWeight[MAX_BLEND_SHAPE_NUMBER];            ///< The weight of each blend shape.
+#ifdef MORPH_VERSION_2_0
+uniform highp float uBlendShapeUnnormalizeFactor;                         ///< Factor used to unnormalize the geometry of the blend shape.
+#else
+uniform highp float uBlendShapeUnnormalizeFactor[MAX_BLEND_SHAPE_NUMBER]; ///< Factor used to unnormalize the geometry of the blend shape.
+#endif
+uniform highp int uBlendShapeComponentSize;                               ///< The size in the texture of either the vertices, normals or tangents. Used to calculate the offset to address them.
+#endif
+
+uniform highp mat4 uShadowLightViewProjectionMatrix;
+
+void main()
+{
+  highp vec4 position = vec4(aPosition, 1.0);
+
+#ifdef MORPH
+  int width = textureSize( sBlendShapeGeometry, 0 ).x;
+
+  highp int blendShapeBufferOffset = 0;
+
+  for( int index = 0; index < uNumberOfBlendShapes; ++index )
+  {
+    highp vec3 diff = vec3(0.0);
+    highp int vertexId = 0;
+    highp int x = 0;
+    highp int y = 0;
+
+#ifdef MORPH_POSITION
+    // Calculate the index to retrieve the geometry from the texture.
+    vertexId = gl_VertexID + blendShapeBufferOffset;
+    x = vertexId % width;
+    y = vertexId / width;
+
+    // Retrieves the blend shape geometry from the texture, unnormalizes it and multiply by the weight.
+    if( 0.0 != uBlendShapeWeight[index] )
+    {
+#ifdef MORPH_VERSION_2_0
+       highp float unnormalizeFactor = uBlendShapeUnnormalizeFactor;
+#else
+       highp float unnormalizeFactor = uBlendShapeUnnormalizeFactor[index];
+#endif
+
+      diff = uBlendShapeWeight[index] * unnormalizeFactor * ( texelFetch( sBlendShapeGeometry, ivec2(x, y), 0 ).xyz - 0.5 );
+    }
+
+    position.xyz += diff;
+
+    blendShapeBufferOffset += uBlendShapeComponentSize;
+#endif
+
+#ifdef MORPH_NORMAL
+    blendShapeBufferOffset += uBlendShapeComponentSize;
+#endif
+
+#ifdef MORPH_TANGENT
+    blendShapeBufferOffset += uBlendShapeComponentSize;
+#endif
+  }
+
+#endif
+
+#ifdef SKINNING
+  highp mat4 bone = uBone[int(aJoints.x)] * aWeights.x +
+    uBone[int(aJoints.y)] * aWeights.y +
+    uBone[int(aJoints.z)] * aWeights.z +
+    uBone[int(aJoints.w)] * aWeights.w;
+  position = bone * position;
+
+  highp vec4 positionW = position;
+#else
+  highp vec4 positionW = uModelMatrix * position;
+#endif
+
+  // To synchronize View-Projection matrix with pbr shader
+  gl_Position = uShadowLightViewProjectionMatrix * positionW;
+
+#ifdef FLIP_V
+  vUV = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
+#else
+  vUV = aTexCoord;
+#endif
+
+  vColor = aVertexColor;
+}
index 00eb0f4..0a0a453 100644 (file)
@@ -38,6 +38,8 @@ static constexpr uint32_t         MAX_NUMBER_OF_LIGHT = 5;
 static constexpr std::string_view LIGHT_COUNT_STRING("uLightCount");
 static constexpr std::string_view LIGHT_DIRECTION_STRING("uLightDirection");
 static constexpr std::string_view LIGHT_COLOR_STRING("uLightColor");
+static constexpr std::string_view SHADOW_ENABLED_STRING("uIsShadowEnabled");
+static constexpr std::string_view SHADOW_VIEW_PROJECTION_MATRIX_STRING("uShadowLightViewProjectionMatrix");
 
 /**
  * Creates control through type registry
@@ -79,6 +81,13 @@ Light::~Light()
 void Light::Initialize()
 {
   Self().SetProperty(Dali::Actor::Property::COLOR, Color::WHITE);
+
+  // Directional Light setting
+  mLightSourceActor = Dali::CameraActor::New();
+  mLightSourceActor.SetProjectionMode(Dali::Camera::ORTHOGRAPHIC_PROJECTION);
+  mLightSourceActor.SetProperty(Dali::Actor::Property::POSITION, Vector3::ZERO);
+  mLightSourceActor.SetProperty(Dali::Actor::Property::ORIENTATION, Quaternion());
+  Self().Add(mLightSourceActor);
 }
 
 void Light::Enable(bool enable)
@@ -110,6 +119,73 @@ bool Light::IsEnabled() const
   return mIsEnabled;
 }
 
+void Light::EnableShadow(bool enable)
+{
+  if(enable == mIsShadowEnabled)
+  {
+    return;
+  }
+  mIsShadowEnabled = enable;
+
+  Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
+  if(!sceneView)
+  {
+    return;
+  }
+
+  if(mIsShadowEnabled)
+  {
+    GetImpl(sceneView).SetShadow(Scene3D::Light::DownCast(Self()));
+  }
+  else
+  {
+    GetImpl(sceneView).RemoveShadow(Scene3D::Light::DownCast(Self()));
+  }
+}
+
+bool Light::IsShadowEnabled() const
+{
+  return mIsShadowEnabled;
+}
+
+CameraActor Light::GetCamera() const
+{
+  return mLightSourceActor;
+}
+
+void Light::EnableShadowSoftFiltering(bool useSoftFiltering)
+{
+  mUseSoftFiltering = useSoftFiltering;
+  UpdateShadowUniforms();
+}
+
+bool Light::IsShadowSoftFilteringEnabled() const
+{
+  return mUseSoftFiltering;
+}
+
+void Light::SetShadowIntensity(float shadowIntensity)
+{
+  mShadowIntensity = shadowIntensity;
+  UpdateShadowUniforms();
+}
+
+float Light::GetShadowIntensity() const
+{
+  return mShadowIntensity;
+}
+
+void Light::SetShadowBias(float shadowBias)
+{
+  mShadowBias = shadowBias;
+  UpdateShadowUniforms();
+}
+
+float Light::GetShadowBias() const
+{
+  return mShadowBias;
+}
+
 void Light::OnSceneConnection(int depth)
 {
   Actor parent = Self().GetParent();
@@ -123,6 +199,10 @@ void Light::OnSceneConnection(int depth)
       {
         GetImpl(sceneView).AddLight(Scene3D::Light::DownCast(Self()));
       }
+      if(mIsShadowEnabled)
+      {
+        GetImpl(sceneView).SetShadow(Scene3D::Light::DownCast(Self()));
+      }
       break;
     }
     parent = parent.GetParent();
@@ -234,6 +314,30 @@ std::string_view Light::GetLightColorUniformName()
   return LIGHT_COLOR_STRING;
 }
 
+std::string_view Light::GetShadowEnabledUniformName()
+{
+  return SHADOW_ENABLED_STRING;
+}
+
+std::string_view Light::GetShadowViewProjectionMatrixUniformName()
+{
+  return SHADOW_VIEW_PROJECTION_MATRIX_STRING;
+}
+
+void Light::UpdateShadowUniforms()
+{
+  Scene3D::SceneView sceneView = mParentSceneView.GetHandle();
+  if(!sceneView)
+  {
+    return;
+  }
+
+  if(mIsShadowEnabled)
+  {
+    GetImpl(sceneView).UpdateShadowUniform(Scene3D::Light::DownCast(Self()));
+  }
+}
+
 } // namespace Internal
 
 } // namespace Scene3D
index 70b51f5..c691423 100644 (file)
@@ -67,6 +67,51 @@ public:
    */
   bool IsEnabled() const;
 
+  /**
+   * @copydoc Scene3D::Light::EnableShadow()
+   */
+  void EnableShadow(bool enable);
+
+  /**
+   * @copydoc Scene3D::Light::IsShadowEnabled()
+   */
+  bool IsShadowEnabled() const;
+
+  /**
+   * @copydoc Scene3D::Light::GetCamera()
+   */
+  CameraActor GetCamera() const;
+
+  /**
+   * @copydoc Scene3D::Light::EnableShadowSoftFiltering()
+   */
+  void EnableShadowSoftFiltering(bool useSoftFiltering);
+
+  /**
+   * @copydoc Scene3D::Light::IsShadowSoftFilteringEnabled()
+   */
+  bool IsShadowSoftFilteringEnabled() const;
+
+  /**
+   * @copydoc Scene3D::Light::SetShadowIntensity()
+   */
+  void SetShadowIntensity(float shadowIntensity);
+
+  /**
+   * @copydoc Scene3D::Light::GetShadowIntensity()
+   */
+  float GetShadowIntensity() const;
+
+  /**
+   * @copydoc Scene3D::Light::SetShadowBias()
+   */
+  void SetShadowBias(float shadowBias);
+
+  /**
+   * @copydoc Scene3D::Light::GetShadowBias()
+   */
+  float GetShadowBias() const;
+
 protected:
   /**
    * @brief Virtual destructor.
@@ -194,6 +239,21 @@ public: // Public Method
    */
   static std::string_view GetLightColorUniformName();
 
+  /**
+   * @brief Retrieves Uniform Name to define shadow is enabled or not.
+   * @return string_view for ShadowEnabledUniformName
+   */
+  static std::string_view GetShadowEnabledUniformName();
+
+  /**
+   * @brief Retrieves Uniform Name for View/Projection matrix of the Shadow.
+   * @return string_view for ShadowViewProjectionMatrixUniformName
+   */
+  static std::string_view GetShadowViewProjectionMatrixUniformName();
+
+private:
+  void UpdateShadowUniforms();
+
 private:
   /// @cond internal
 
@@ -204,8 +264,13 @@ private:
   DALI_INTERNAL Light& operator=(Light&&)      = delete; ///< Deleted move assignment operator.
 
 private:
+  Dali::CameraActor              mLightSourceActor;
   WeakHandle<Scene3D::SceneView> mParentSceneView;
-  bool mIsEnabled{true};
+  bool                           mIsEnabled{true};
+  bool                           mIsShadowEnabled{false};
+  bool                           mUseSoftFiltering{false};
+  float                          mShadowIntensity{0.5f};
+  float                          mShadowBias{0.001f};
   /// @endcond
 };
 
index 16a7bf5..2b80a70 100644 (file)
@@ -597,6 +597,7 @@ void ConvertMaterial(const gltf2::Material& material, const std::unordered_map<s
   MaterialDefinition materialDefinition;
 
   materialDefinition.mFlags |= MaterialDefinition::GLTF_CHANNELS;
+  materialDefinition.mShadowAvailable = true;
 
   auto& pbr = material.mPbrMetallicRoughness;
   if(material.mAlphaMode == gltf2::AlphaMode::BLEND)
index b7c16a7..69a1a53 100644 (file)
@@ -53,6 +53,7 @@ BaseHandle Create()
 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::Material, Dali::BaseHandle, Create);
 DALI_TYPE_REGISTRATION_END()
 
+static constexpr uint32_t         OFFSET_FOR_SHADOW_MAP_TEXTURE    = 4u;
 static constexpr uint32_t         OFFSET_FOR_DIFFUSE_CUBE_TEXTURE  = 2u;
 static constexpr uint32_t         OFFSET_FOR_SPECULAR_CUBE_TEXTURE = 1u;
 static constexpr uint32_t         INVALID_INDEX                    = 0u;
@@ -693,6 +694,11 @@ void Material::SetRendererUniform(Dali::Renderer renderer)
   Scene3D::Loader::RendererState::Apply(mRendererState, renderer);
 }
 
+uint32_t Material::GetShadowMapTextureOffset()
+{
+  return OFFSET_FOR_SHADOW_MAP_TEXTURE;
+}
+
 uint32_t Material::GetSpecularImageBasedLightTextureOffset()
 {
   return OFFSET_FOR_SPECULAR_CUBE_TEXTURE;
index 3b0c985..fe45dee 100644 (file)
@@ -197,6 +197,13 @@ public:
   void SetRendererUniform(Dali::Renderer renderer);
 
   /**
+   * @brief Retrieves shadow map texture offset.
+   *
+   * @return shadow map texture offset.
+   */
+  uint32_t GetShadowMapTextureOffset();
+
+  /**
    * @brief Retrieves specular image based light texture offset.
    *
    * @return Specular image based light texture offset.
index 9d8d4b6..f88b031 100644 (file)
@@ -187,6 +187,11 @@ void ModelNode::AddModelPrimitive(Dali::Scene3D::ModelPrimitive modelPrimitive)
 
   Actor self = Self();
   GetImplementation(modelPrimitive).AddPrimitiveObserver(this);
+  if(mShadowMapTexture)
+  {
+    GetImplementation(modelPrimitive).SetShadowMapTexture(mShadowMapTexture);
+  }
+
   if(mDiffuseTexture && mSpecularTexture)
   {
     GetImplementation(modelPrimitive).SetImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor, mSpecularMipmapLevels);
@@ -284,6 +289,15 @@ Loader::BlendShapes::Index ModelNode::GetBlendShapeIndexByName(std::string_view
   return Loader::BlendShapes::INVALID_INDEX;
 }
 
+void ModelNode::SetShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+  mShadowMapTexture       = shadowMapTexture;
+  for(auto&& primitive : mModelPrimitiveContainer)
+  {
+    GetImplementation(primitive).SetShadowMapTexture(mShadowMapTexture);
+  }
+}
+
 void ModelNode::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
 {
   mDiffuseTexture       = diffuseTexture;
index 2cc4f1a..ed17a45 100644 (file)
@@ -231,7 +231,14 @@ public: // Public Method
   Loader::BlendShapes::Index GetBlendShapeIndexByName(std::string_view blendShapeName) const;
 
   /**
-   * @brief Sets the diffuse and specular image-based lighting textures for a ModelPrimitive.
+   * @brief Sets the shadow map textures for a ModelNode.
+   *
+   * @param[in] shadowMapTexture The shadow map texture.
+   */
+  void SetShadowMapTexture(Dali::Texture shadowMapTexture);
+
+  /**
+   * @brief Sets the diffuse and specular image-based lighting textures for a ModelNode.
    *
    * @param[in] diffuseTexture The diffuse texture.
    * @param[in] specularTexture The specular texture.
@@ -300,6 +307,7 @@ private:
   ModelPrimitiveContainer           mModelPrimitiveContainer; ///< List of model primitives
   BoneDataContainer                 mBoneDataContainer;
   BlendShapeIndexMap                mBlendShapeIndexMap;      ///< Index of blend shape by name
+  Dali::Texture                     mShadowMapTexture;
   Dali::Texture                     mSpecularTexture;
   Dali::Texture                     mDiffuseTexture;
   float                             mIblScaleFactor{1.0f};
index 0aea879..edf3e86 100644 (file)
@@ -52,6 +52,15 @@ DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelPrimitive, Dali::BaseHandle, Create);
 DALI_TYPE_REGISTRATION_END()
 
 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
+
+Texture MakeEmptyTexture()
+{
+  PixelData pixelData = PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY);
+  Texture texture            = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight());
+  texture.Upload(pixelData, 0, 0, 0, 0, pixelData.GetWidth(), pixelData.GetHeight());
+
+  return texture;
+}
 } // unnamed namespace
 
 ModelPrimitivePtr ModelPrimitive::New()
@@ -138,6 +147,7 @@ void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material, bool updateRe
         ApplyMaterialToRenderer();
       }
     }
+    UpdateShadowMapTexture();
     UpdateImageBasedLightTexture();
   }
 }
@@ -157,6 +167,13 @@ void ModelPrimitive::RemovePrimitiveObserver(ModelPrimitiveModifyObserver* obser
   mObservers.erase(observer);
 }
 
+void ModelPrimitive::SetShadowMapTexture(Dali::Texture shadowMapTexture)
+{
+  mShadowMapTexture = shadowMapTexture;
+
+  UpdateShadowMapTexture();
+}
+
 void ModelPrimitive::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
 {
   mDiffuseTexture       = diffuseTexture;
@@ -291,7 +308,14 @@ void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag
     }
 
     uint32_t textureCount = mTextureSet.GetTextureCount();
-    Texture  brdfTexture  = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
+
+    if(!mShadowMapTexture)
+    {
+      mShadowMapTexture = MakeEmptyTexture();
+    }
+    mTextureSet.SetTexture(textureCount++, mShadowMapTexture);
+
+    Texture brdfTexture = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
     if(!mSpecularTexture || !mDiffuseTexture)
     {
       Scene3D::Loader::EnvironmentMapData environmentMapData;
@@ -357,6 +381,40 @@ void ModelPrimitive::CreateRenderer()
   }
 }
 
+void ModelPrimitive::UpdateShadowMapTexture()
+{
+  if(mRenderer && mMaterial)
+  {
+    Dali::TextureSet textures = mRenderer.GetTextures();
+    if(!textures)
+    {
+      return;
+    }
+
+    uint32_t textureCount = textures.GetTextureCount();
+    if(mShadowMapTexture &&
+       textureCount >= GetImplementation(mMaterial).GetShadowMapTextureOffset() &&
+       textures.GetTexture(textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset()) != mShadowMapTexture)
+    {
+      Dali::TextureSet newTextures = Dali::TextureSet::New();
+
+      for(uint32_t index = 0u; index < textureCount; ++index)
+      {
+        Dali::Texture texture = textures.GetTexture(index);
+        if(index == textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset())
+        {
+          texture = (!!mShadowMapTexture) ? mShadowMapTexture : MakeEmptyTexture();
+        }
+
+        newTextures.SetTexture(index, texture);
+        newTextures.SetSampler(index, textures.GetSampler(index));
+      }
+
+      mRenderer.SetTextures(newTextures);
+    }
+  }
+}
+
 void ModelPrimitive::UpdateImageBasedLightTexture()
 {
   if(mRenderer && mMaterial)
index 7334699..c6dbf37 100644 (file)
@@ -124,6 +124,13 @@ public:
   void RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer);
 
   /**
+   * @brief Sets shadow map texture for this model primitive.
+   *
+   * @param[in] shadowMapTexture The shadow map texture.
+   */
+  void SetShadowMapTexture(Dali::Texture shadowMapTexture);
+
+  /**
    * @brief Sets the image-based lighting texture for this model primitive.
    *
    * @param[in] diffuseTexture The diffuse texture.
@@ -200,6 +207,8 @@ private:
    */
   void CreateRenderer();
 
+  void UpdateShadowMapTexture();
+
   /**
    * @brief Updates the image-based lighting texture.
    */
@@ -224,6 +233,9 @@ private:
 
   Scene3D::Loader::ShaderManagerPtr mShaderManager;
 
+  // For Shadow
+  Dali::Texture mShadowMapTexture;
+
   // For IBL
   Dali::Texture mSpecularTexture;
   Dali::Texture mDiffuseTexture;
index 91d87c1..7008b37 100644 (file)
@@ -81,6 +81,46 @@ uint32_t Light::GetMaximumEnabledLightCount()
   return Internal::Light::GetMaximumEnabledLightCount();
 }
 
+void Light::EnableShadow(bool enable)
+{
+  Internal::GetImplementation(*this).EnableShadow(enable);
+}
+
+bool Light::IsShadowEnabled() const
+{
+  return Internal::GetImplementation(*this).IsShadowEnabled();
+}
+
+void Light::EnableShadowSoftFiltering(bool useSoftFiltering)
+{
+  Internal::GetImplementation(*this).EnableShadowSoftFiltering(useSoftFiltering);
+}
+
+bool Light::IsShadowSoftFilteringEnabled() const
+{
+  return Internal::GetImplementation(*this).IsShadowSoftFilteringEnabled();
+}
+
+void Light::SetShadowIntensity(float shadowIntensity)
+{
+  Internal::GetImplementation(*this).SetShadowIntensity(shadowIntensity);
+}
+
+float Light::GetShadowIntensity() const
+{
+  return Internal::GetImplementation(*this).GetShadowIntensity();
+}
+
+void Light::SetShadowBias(float shadowBias)
+{
+  Internal::GetImplementation(*this).SetShadowBias(shadowBias);
+}
+
+float Light::GetShadowBias() const
+{
+  return Internal::GetImplementation(*this).GetShadowBias();
+}
+
 Light::Light(Internal::Light& implementation)
 : CustomActor(implementation)
 {
index 1db757e..ac8e499 100644 (file)
@@ -159,6 +159,79 @@ public:
    */
   static uint32_t GetMaximumEnabledLightCount();
 
+  /**
+   * @brief Enables shadow for this light.
+   * Dali::Scene3D generates shadow by using shadow map.
+   * For the Directional Light, the shadow map is created to cover view frustum of current selected camera.
+   * This means that if the distance between the near and far planes is too large,
+   * the shadow map has to cover an unnecessarily large area. This results in lower shadow quality.
+   * @SINCE_2_2.43
+   * @note This light should be already turned on in the SceneView.
+   * @note (When enable is true) If there is previous light already enabled shadow in the SceneView, this function call is ignored.
+   * @note (When enable is false, and this light is currently used for shader)
+   * If there are other lights those are turned on and shadow enabled, one of the light will be used for shadow automatically.
+   * @param[in] enable True to make this Light's shadow enable.
+   */
+  void EnableShadow(bool enable);
+
+  /**
+   * @brief Checks whether the shadow of this light is enabled or not.
+   * @SINCE_2_2.43
+   * @return True if the shadow of this light is enabled.
+   */
+  bool IsShadowEnabled() const;
+
+  /**
+   * @brief Enables filtering to soften the edge of shadow.
+   * Basically the shadow is hard shadow that has sharp edge.
+   * This method enables soft filtering to make the sharp edge to smoothing.
+   * @SINCE_2_2.43
+   * @note This soft filtering requires expensive computation power.
+   * @param[in] useSoftFiltering True to soften the shadow edge.
+   */
+  void EnableShadowSoftFiltering(bool useSoftFiltering);
+
+  /**
+   * @brief Checks whether the shadow uses soft filtering.
+   * @SINCE_2_2.43
+   * @return True if the shadow edge is soften.
+   */
+  bool IsShadowSoftFilteringEnabled() const;
+
+  /**
+   * @brief Sets shadow intensity.
+   * If the intensity is larger, the shadow area will be darker.
+   * The intensity value is between [0, 1].
+   * Default value is 0.5
+   * @SINCE_2_2.43
+   * @param[in] shadowIntensity Shadow intensity value
+   */
+  void SetShadowIntensity(float shadowIntensity);
+
+  /**
+   * @brief Retrieve shadow intensity.
+   * @SINCE_2_2.43
+   * @return Current shadow intensity value.
+   */
+  float GetShadowIntensity() const;
+
+  /**
+   * @brief Sets shadow bias.
+   * Shadow bias is an offset value to remove shadow acne that is a visual artifact can be shown on the Shadow
+   * Default value is 0.001
+   * @SINCE_2_2.43
+   * @param[in] shadowBias bias value to remove shadow acne.
+   * @note If the shadow bias is too large, the object will appear detached from the shadow.
+   */
+  void SetShadowBias(float shadowBias);
+
+  /**
+   * @brief Retrieves shadow bias value.
+   * @SINCE_2_2.43
+   * @return Shadow bias value.
+   */
+  float GetShadowBias() const;
+
 public: // Not intended for application developers
   /// @cond internal
   /**
index eb87ae1..4fc6ac3 100644 (file)
@@ -345,6 +345,14 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
     ++n;
   }
 
+  if(mShadowAvailable)
+  {
+    PixelData shadowMapPixelData = PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY);
+    Texture   shadowMapTexture   = Texture::New(TextureType::TEXTURE_2D, shadowMapPixelData.GetPixelFormat(), shadowMapPixelData.GetWidth(), shadowMapPixelData.GetHeight());
+    shadowMapTexture.Upload(shadowMapPixelData, 0, 0, 0, 0, shadowMapPixelData.GetWidth(), shadowMapPixelData.GetHeight());
+    textureSet.SetTexture(n++, shadowMapTexture);
+  }
+
   // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
   if(mEnvironmentIdx < static_cast<Index>(environments.size()))
   {
index b8da495..43dabc1 100644 (file)
@@ -251,6 +251,8 @@ public: // DATA
   bool                             mIsOpaque      = true;
   bool                             mIsMask        = false;
 
+  bool mShadowAvailable = false;
+
   std::vector<TextureStage> mTextureStages;
   Material                  mMaterial;
 };
index 2796340..49ff885 100644 (file)
@@ -18,6 +18,9 @@
 // CLASS HEADER
 #include <dali-scene3d/public-api/loader/shader-definition.h>
 
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/property-array.h>
+
 // INTERNAL INCLUDES
 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
 #include <dali-scene3d/public-api/loader/utils.h>
@@ -100,8 +103,10 @@ ShaderDefinition::LoadRaw(const std::string& shadersPath) const
   }
   else
   {
-    raw.mVertexShaderSource   = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data();
-    raw.mFragmentShaderSource = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
+    raw.mVertexShaderSource         = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data();
+    raw.mFragmentShaderSource       = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
+    raw.mShadowVertexShaderSource   = SHADER_SHADOW_MAP_SHADER_VERT.data();
+    raw.mShadowFragmentShaderSource = SHADER_SHADOW_MAP_SHADER_FRAG.data();
   }
 
   if(!fail)
@@ -110,6 +115,7 @@ ShaderDefinition::LoadRaw(const std::string& shadersPath) const
     {
       ApplyDefine(raw.mVertexShaderSource, definevar);
       ApplyDefine(raw.mFragmentShaderSource, definevar);
+      ApplyDefine(raw.mShadowVertexShaderSource, definevar);
     }
   }
 
@@ -131,7 +137,21 @@ Shader ShaderDefinition::Load(RawData&& raw) const
     }
   }
 
-  Shader shader = Shader::New(raw.mVertexShaderSource, raw.mFragmentShaderSource, static_cast<Shader::Hint::Value>(hints));
+  Property::Map map[2];
+  map[0]["vertex"]        = raw.mVertexShaderSource;
+  map[0]["fragment"]      = raw.mFragmentShaderSource;
+  map[0]["renderPassTag"] = 0;
+  map[0]["hints"]         = static_cast<Shader::Hint::Value>(hints);
+
+  map[1]["vertex"]        = raw.mShadowVertexShaderSource;
+  map[1]["fragment"]      = raw.mShadowFragmentShaderSource;
+  map[1]["renderPassTag"] = 10;
+
+  Property::Array array;
+  array.PushBack(map[0]);
+  array.PushBack(map[1]);
+
+  Shader shader = Shader::New(array);
   for(Property::Map::SizeType i0 = 0, i1 = mUniforms.Count(); i0 != i1; ++i0)
   {
     auto pair = mUniforms.GetKeyValue(i0);
index 61f4a7c..0fe83d3 100644 (file)
@@ -40,6 +40,8 @@ struct DALI_SCENE3D_API ShaderDefinition
   {
     std::string mVertexShaderSource;
     std::string mFragmentShaderSource;
+    std::string mShadowVertexShaderSource;
+    std::string mShadowFragmentShaderSource;
   };
 
   /*
index 158598f..1648e97 100644 (file)
 #include <dali-scene3d/public-api/loader/blend-shape-details.h>
 #include <dali-scene3d/public-api/loader/node-definition.h>
 
+#include <dali/integration-api/debug.h>
+
 namespace Dali::Scene3D::Loader
 {
 namespace
 {
-static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
+static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG  = 10;
+static constexpr uint32_t INDEX_FOR_SHADOW_CONSTRAINT_TAG = 100;
 
 ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
 {
@@ -162,6 +165,8 @@ struct ShaderManager::Impl
   std::map<uint64_t, Index>   mShaderMap;
   std::vector<Dali::Shader>   mShaders;
   std::vector<Scene3D::Light> mLights;
+
+  Scene3D::Light mShadowLight;
 };
 
 ShaderManager::ShaderManager()
@@ -209,6 +214,13 @@ Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
     {
       SetLightConstraintToShader(index, result);
     }
+
+    result.RegisterProperty("uIsShadowEnabled", static_cast<int32_t>(!!mImpl->mShadowLight));
+    if(!!mImpl->mShadowLight)
+    {
+      SetShadowConstraintToShader(result);
+      SetShadowUniformToShader(result);
+    }
   }
 
   return result;
@@ -288,6 +300,42 @@ uint32_t ShaderManager::GetLightCount() const
   return mImpl->mLights.size();
 }
 
+void ShaderManager::SetShadow(Scene3D::Light light)
+{
+  mImpl->mShadowLight = light;
+  for(auto&& shader : mImpl->mShaders)
+  {
+    std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
+    shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(true));
+  }
+
+  SetShadowProperty();
+}
+
+void ShaderManager::RemoveShadow()
+{
+  for(auto&& shader : mImpl->mShaders)
+  {
+    std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
+    shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(false));
+    shader.RemoveConstraints(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
+  }
+  mImpl->mShadowLight.Reset();
+}
+
+void ShaderManager::UpdateShadowUniform(Scene3D::Light light)
+{
+  if(light != mImpl->mShadowLight)
+  {
+    return;
+  }
+
+  for(auto&& shader : mImpl->mShaders)
+  {
+    SetShadowUniformToShader(shader);
+  }
+}
+
 void ShaderManager::SetLightConstraint(uint32_t lightIndex)
 {
   for(auto&& shader : mImpl->mShaders)
@@ -325,4 +373,49 @@ void ShaderManager::RemoveLightConstraint(uint32_t lightIndex)
   }
 }
 
+void ShaderManager::SetShadowUniformToShader(Dali::Shader shader)
+{
+  shader.RegisterProperty("uShadowIntensity", mImpl->mShadowLight.GetShadowIntensity());
+  shader.RegisterProperty("uShadowBias", mImpl->mShadowLight.GetShadowBias());
+  shader.RegisterProperty("uEnableShadowSoftFiltering", static_cast<int>(mImpl->mShadowLight.IsShadowSoftFilteringEnabled()));
+}
+
+void ShaderManager::SetShadowProperty()
+{
+  for(auto&& shader : mImpl->mShaders)
+  {
+    SetShadowUniformToShader(shader);
+    SetShadowConstraintToShader(shader);
+  }
+}
+
+void ShaderManager::SetShadowConstraintToShader(Dali::Shader shader)
+{
+  // Constraint is applied before View/Projection Matrix is computed in update thread.
+  // So, it could show not plausible result if camera properties are changed discontinuesly.
+  // If we want to make it be synchronized, View/Projection matrix are needed to be conputed in below constraint.
+
+  std::string       shadowViewProjectionPropertyName(Scene3D::Internal::Light::GetShadowViewProjectionMatrixUniformName());
+  auto              shadowViewProjectionPropertyIndex = shader.RegisterProperty(shadowViewProjectionPropertyName, Matrix::IDENTITY);
+  Dali::CameraActor shadowLightCamera                 = Dali::Scene3D::Internal::GetImplementation(mImpl->mShadowLight).GetCamera();
+  auto              tempViewMatrixIndex               = shadowLightCamera.GetPropertyIndex("tempViewMatrix");
+  if(tempViewMatrixIndex != Dali::Property::INVALID_INDEX)
+  {
+    tempViewMatrixIndex = shadowLightCamera.RegisterProperty("tempViewMatrix", Matrix::IDENTITY);
+  }
+  auto tempProjectionMatrixIndex = shadowLightCamera.GetPropertyIndex("tempProjectionMatrix");
+  if(tempProjectionMatrixIndex != Dali::Property::INVALID_INDEX)
+  {
+    tempProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempProjectionMatrix", Matrix::IDENTITY);
+  }
+  Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs)
+                                                                                  {
+                                                                                    output = inputs[1]->GetMatrix() * inputs[0]->GetMatrix(); });
+
+  shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempViewMatrixIndex});
+  shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempProjectionMatrixIndex});
+  shadowViewProjectionConstraint.ApplyPost();
+  shadowViewProjectionConstraint.SetTag(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
+}
+
 } // namespace Dali::Scene3D::Loader
index 56adf38..4ca1384 100644 (file)
@@ -93,6 +93,24 @@ public:
    */
   uint32_t GetLightCount() const;
 
+  /**
+   * @brief Set a shadow to this scene by input light.
+   *
+   * @param[in] light Light object to make shadow.
+   */
+  void SetShadow(Scene3D::Light light);
+
+  /**
+   * @brief Removes Shadow from this SceneView.
+   */
+  void RemoveShadow();
+
+  /**
+   * @brief Update uniform properties of shadow for the input light.
+   * @param[in] light Light object to update shadow uniform.
+   */
+  void UpdateShadowUniform(Scene3D::Light light);
+
 private:
   /**
    * @brief Sets constraint to the shaders with light of light index.
@@ -112,6 +130,23 @@ private:
    * @param[in] lightIndex index of light that will be disconnected with shaders.
    */
   DALI_INTERNAL void RemoveLightConstraint(uint32_t lightIndex);
+
+  /**
+   * @brief Sets uniform about the shadow.
+   * @param[in] shader Shader that the constraint will be applied.
+   */
+  DALI_INTERNAL void SetShadowUniformToShader(Dali::Shader shader);
+
+  /**
+   * @brief Sets properties and constraint to the shaders.
+   */
+  DALI_INTERNAL void SetShadowProperty();
+
+  /**
+   * @brief Sets constraint to a shader about shadow
+   * @param[in] shader Shader that the constraint will be applied.
+   */
+  DALI_INTERNAL void SetShadowConstraintToShader(Dali::Shader shader);
 private:
   struct Impl;
   const std::unique_ptr<Impl> mImpl;
index 8ca77d2..dea398a 100644 (file)
@@ -67,6 +67,13 @@ Dali::Toolkit::ImageUrl GenerateUrl(const Dali::EncodedImageBuffer encodedImageB
   return Dali::Toolkit::ImageUrl::New(encodedImageBuffer);
 }
 
+Dali::Toolkit::ImageUrl GenerateDepthUrl(const Dali::FrameBuffer frameBuffer)
+{
+  Texture texture = Dali::DevelFrameBuffer::GetDepthTexture(frameBuffer);
+  Dali::Toolkit::ImageUrl imageUrl = Dali::Toolkit::ImageUrl::New(texture, false);
+  return imageUrl;
+}
+
 } // namespace Image
 
 } // namespace Toolkit
index f3675ef..bdb9996 100644 (file)
@@ -90,6 +90,15 @@ DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::NativeImageSour
  */
 DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateUrl(const Dali::EncodedImageBuffer encodedImageBuffer);
 
+/**
+ * @brief Generate a Url of depth texture from frame buffer.
+ * This Url can be used in visuals to render the frame buffer.
+ * This method does not check for duplicates, If same frame buffer is entered multiple times, a different URL is returned each time.
+ * @param[in] frameBuffer the frame buffer to converted to Url
+ * @return the ImageUrl representing this frame buffer
+ */
+DALI_TOOLKIT_API Dali::Toolkit::ImageUrl GenerateDepthUrl(const Dali::FrameBuffer frameBuffer);
+
 } // namespace Image
 
 } // namespace Toolkit