- 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>
Scene3D::Material::AlphaModeType::MASK,
true,
true,
+ true,
{
{
MaterialDefinition::ALBEDO,
Scene3D::Material::AlphaModeType::OPAQUE,
true,
false,
+ true,
{
{
MaterialDefinition::ALBEDO,
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);
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;
+}
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);
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);
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);
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);
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);
// 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;
}
if(renderer)
{
TextureSet textureSet = renderer.GetTextures();
- if(textureSet.GetTextureCount() == 9u)
+ if(textureSet.GetTextureCount() == 10u)
{
- texture = textureSet.GetTexture(7u);
+ texture = textureSet.GetTexture(8u);
}
}
}
if(renderer)
{
TextureSet textureSet = renderer.GetTextures();
- if(textureSet.GetTextureCount() == 9u)
+ if(textureSet.GetTextureCount() == 10u)
{
- texture = textureSet.GetTexture(8u);
+ texture = textureSet.GetTexture(9u);
}
}
}
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();
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);
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();
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;
* @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
}
}
+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)
UpdateShaderRecursively(modelNode, mShaderManager);
+ if(mShadowMapTexture)
+ {
+ UpdateShadowMapTextureRecursively(modelNode, mShadowMapTexture);
+ }
+
if(mIblDiffuseResourceReady && mIblSpecularResourceReady)
{
UpdateImageBasedLightTexture();
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)
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));
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;
EnvironmentMapLoadTaskPtr mIblDiffuseLoadTask;
EnvironmentMapLoadTaskPtr mIblSpecularLoadTask;
+ // Shadow
+ Dali::Texture mShadowMapTexture;
+
std::string mDiffuseIblUrl;
std::string mSpecularIblUrl;
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";
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()
{
bool enabled = mShaderManager->AddLight(light);
mLights.push_back(std::make_pair(light, enabled));
+
+ if(light.IsShadowEnabled())
+ {
+ SetShadow(light);
+ }
}
void SceneView::RemoveLight(Scene3D::Light light)
if(mLights.size() > mShaderManager->GetLightCount())
{
- for(auto && waitingLight : mLights)
+ for(auto&& waitingLight : mLights)
{
if(waitingLight.second)
{
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
return mShaderManager;
}
+void SceneView::UpdateShadowUniform(Scene3D::Light light)
+{
+ mShaderManager->UpdateShadowUniform(light);
+}
+
///////////////////////////////////////////////////////////
//
// Private methods
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);
taskList.RemoveTask(mRenderTask);
mRenderTask.Reset();
}
+ if(mShadowMapRenderTask)
+ {
+ RenderTaskList taskList = mSceneHolder.GetRenderTaskList();
+ taskList.RemoveTask(mShadowMapRenderTask);
+ mShadowMapRenderTask.Reset();
+ }
mSceneHolder.Reset();
}
mFrameBuffer.Reset();
+ mShadowFrameBuffer.Reset();
Control::OnSceneDisconnection();
}
}
mSelectedCamera = camera;
+ if(mShadowLight)
+ {
+ SetShadowLightConstraint(mSelectedCamera, GetImplementation(mShadowLight).GetCamera());
+ }
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();
}
}
+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
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;
*/
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.
*/
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;
// 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;
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;
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));
}
}
+ 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);
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);
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;
}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
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
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)
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();
{
GetImpl(sceneView).AddLight(Scene3D::Light::DownCast(Self()));
}
+ if(mIsShadowEnabled)
+ {
+ GetImpl(sceneView).SetShadow(Scene3D::Light::DownCast(Self()));
+ }
break;
}
parent = parent.GetParent();
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
*/
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.
*/
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
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
};
MaterialDefinition materialDefinition;
materialDefinition.mFlags |= MaterialDefinition::GLTF_CHANNELS;
+ materialDefinition.mShadowAvailable = true;
auto& pbr = material.mPbrMetallicRoughness;
if(material.mAlphaMode == gltf2::AlphaMode::BLEND)
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;
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;
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.
Actor self = Self();
GetImplementation(modelPrimitive).AddPrimitiveObserver(this);
+ if(mShadowMapTexture)
+ {
+ GetImplementation(modelPrimitive).SetShadowMapTexture(mShadowMapTexture);
+ }
+
if(mDiffuseTexture && mSpecularTexture)
{
GetImplementation(modelPrimitive).SetImageBasedLightTexture(mDiffuseTexture, mSpecularTexture, mIblScaleFactor, mSpecularMipmapLevels);
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;
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.
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};
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()
ApplyMaterialToRenderer();
}
}
+ UpdateShadowMapTexture();
UpdateImageBasedLightTexture();
}
}
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;
}
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;
}
}
+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)
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.
*/
void CreateRenderer();
+ void UpdateShadowMapTexture();
+
/**
* @brief Updates the image-based lighting texture.
*/
Scene3D::Loader::ShaderManagerPtr mShaderManager;
+ // For Shadow
+ Dali::Texture mShadowMapTexture;
+
// For IBL
Dali::Texture mSpecularTexture;
Dali::Texture mDiffuseTexture;
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)
{
*/
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
/**
++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()))
{
bool mIsOpaque = true;
bool mIsMask = false;
+ bool mShadowAvailable = false;
+
std::vector<TextureStage> mTextureStages;
Material mMaterial;
};
// 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>
}
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)
{
ApplyDefine(raw.mVertexShaderSource, definevar);
ApplyDefine(raw.mFragmentShaderSource, definevar);
+ ApplyDefine(raw.mShadowVertexShaderSource, definevar);
}
}
}
}
- 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);
{
std::string mVertexShaderSource;
std::string mFragmentShaderSource;
+ std::string mShadowVertexShaderSource;
+ std::string mShadowFragmentShaderSource;
};
/*
#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)
{
std::map<uint64_t, Index> mShaderMap;
std::vector<Dali::Shader> mShaders;
std::vector<Scene3D::Light> mLights;
+
+ Scene3D::Light mShadowLight;
};
ShaderManager::ShaderManager()
{
SetLightConstraintToShader(index, result);
}
+
+ result.RegisterProperty("uIsShadowEnabled", static_cast<int32_t>(!!mImpl->mShadowLight));
+ if(!!mImpl->mShadowLight)
+ {
+ SetShadowConstraintToShader(result);
+ SetShadowUniformToShader(result);
+ }
}
return result;
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)
}
}
+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
*/
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.
* @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;
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
*/
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