2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include "dali/dali.h"
21 #include "dali/public-api/actors/actor.h"
22 #include "dali/public-api/rendering/renderer.h"
24 #include "generated/deferred-shading-mainpass-frag.h"
25 #include "generated/deferred-shading-mainpass-vert.h"
26 #include "generated/deferred-shading-prepass-frag.h"
27 #include "generated/deferred-shading-prepass-vert.h"
33 //=============================================================================
34 // Demonstrates deferred shading with multiple render targets (for color,
35 // position, and normal), a Phong lighting model and 32 point lights.
37 // Invoked with the --show-lights it will render a mesh at each light position.
38 //=============================================================================
40 #define QUOTE(x) DALI_COMPOSE_SHADER(x)
44 #define DEFINE_MAX_LIGHTS "const int kMaxLights = " QUOTE(MAX_LIGHTS) ";"
46 //=============================================================================
50 std::random_device mDevice;
51 std::mt19937 mMersenneTwister;
52 std::uniform_real_distribution<float> mDistribution;
55 : mMersenneTwister(mDevice()),
62 return mDistribution(mMersenneTwister);
66 //=============================================================================
67 float FastFloor(float x)
69 return static_cast<int>(x) - static_cast<int>(x < 0);
72 //=============================================================================
73 Vector3 FromHueSaturationLightness(Vector3 hsl)
76 if(hsl.y * hsl.y > 0.f)
84 int i = FastFloor(hsl.x);
86 float p = hsl.z * (1.0 - hsl.y);
87 float q = hsl.z * (1.0 - (hsl.y * ff));
88 float t = hsl.z * (1.0 - (hsl.y * (1.f - ff)));
132 rgb = Vector3::ONE * hsl.z;
138 //=============================================================================
139 Geometry CreateTexturedQuadGeometry(bool flipV)
141 // Create geometry -- unit square with whole of the texture mapped to it.
148 Vertex vertexData[] = {
149 {Vector3(-.5f, .5f, .0f), Vector2(.0f, 1.0f)},
150 {Vector3(.5f, .5f, .0f), Vector2(1.0f, 1.0f)},
151 {Vector3(-.5f, -.5f, .0f), Vector2(.0f, .0f)},
152 {Vector3(.5f, -.5f, .0f), Vector2(1.0f, .0f)},
157 std::swap(vertexData[0].aTexCoord, vertexData[2].aTexCoord);
158 std::swap(vertexData[1].aTexCoord, vertexData[3].aTexCoord);
161 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
162 .Add("aPosition", Property::VECTOR3)
163 .Add("aTexCoord", Property::VECTOR2));
164 vertexBuffer.SetData(vertexData, std::extent<decltype(vertexData)>::value);
166 Geometry geometry = Geometry::New();
167 geometry.AddVertexBuffer(vertexBuffer);
168 geometry.SetType(Geometry::TRIANGLE_STRIP);
172 //=============================================================================
173 Geometry CreateOctahedron(bool invertNormals)
175 Vector3 positions[] = {
176 Vector3{-1.f, 0.f, 0.f},
177 Vector3{1.f, 0.f, 0.f},
178 Vector3{0.f, -1.f, 0.f},
179 Vector3{0.f, 1.f, 0.f},
180 Vector3{0.f, 0.f, -1.f},
181 Vector3{0.f, 0.f, 1.f},
189 Vertex vertexData[] = {
224 for(uint32_t i = 0; i < std::extent<decltype(vertexData)>::value / 3; ++i)
226 uint32_t idx = i * 3;
228 Vector3 normal = (vertexData[idx + 2].position - vertexData[idx].position).Cross(vertexData[idx + 1].position - vertexData[idx].position);
230 normal *= invertNormals * 2.f - 1.f;
232 vertexData[idx++].normal = normal;
233 vertexData[idx++].normal = normal;
234 vertexData[idx].normal = normal;
237 // Configure property buffers and create geometry.
238 VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
239 .Add("aPosition", Property::VECTOR3)
240 .Add("aNormal", Property::VECTOR3));
241 vertexBuffer.SetData(vertexData, std::extent<decltype(vertexData)>::value);
243 Geometry geometry = Geometry::New();
244 geometry.AddVertexBuffer(vertexBuffer);
245 geometry.SetType(Geometry::TRIANGLES);
249 //=============================================================================
254 OPTION_DEPTH_TEST = 0x02,
255 OPTION_DEPTH_WRITE = 0x04
258 Renderer CreateRenderer(TextureSet textures, Geometry geometry, Shader shader, uint32_t options = OPTION_NONE)
260 Renderer renderer = Renderer::New(geometry, shader);
261 renderer.SetProperty(Renderer::Property::BLEND_MODE,
262 (options & OPTION_BLEND) ? BlendMode::ON : BlendMode::OFF);
263 renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE,
264 (options & OPTION_DEPTH_TEST) ? DepthTestMode::ON : DepthTestMode::OFF);
265 renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE,
266 (options & OPTION_DEPTH_WRITE) ? DepthWriteMode::ON : DepthWriteMode::OFF);
267 renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
271 textures = TextureSet::New();
274 renderer.SetTextures(textures);
278 //=============================================================================
279 void CenterActor(Actor actor)
281 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
282 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
285 //=============================================================================
286 void RegisterDepthProperties(float depth, float near, Handle& h)
288 h.RegisterProperty("uDepth_InvDepth_Near", Vector3(depth, 1.f / depth, near));
293 //=============================================================================
294 /// Create a String whose size can be evaluated at compile time
295 struct ConstantString
297 const char* const string;
300 template<uint16_t inputSize>
301 constexpr ConstantString(const char (&input)[inputSize])
308 constexpr ConstantString POSITION_STRING("position");
309 constexpr ConstantString RADIUS_STRING("radius");
310 constexpr ConstantString COLOR_STRING("color");
311 constexpr uint16_t LIGHT_SOURCE_BUFFER_SIZE(128u);
313 //=============================================================================
314 class DeferredShadingExample : public ConnectionTracker
326 DeferredShadingExample(Application& app, uint32_t options = Options::NONE)
330 app.InitSignal().Connect(this, &DeferredShadingExample::Create);
331 app.TerminateSignal().Connect(this, &DeferredShadingExample::Destroy);
335 void Create(Application& app)
337 // Grab window, configure layer
338 Window window = app.GetWindow();
339 auto rootLayer = window.GetRootLayer();
340 rootLayer.SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
342 Vector2 windowSize = window.GetSize();
344 float unit = windowSize.y / 24.f;
346 // Get camera - we'll be re-using the same old camera in the two passes.
347 RenderTaskList tasks = window.GetRenderTaskList();
348 CameraActor camera = tasks.GetTask(0).GetCameraActor();
350 auto zCameraPos = camera.GetProperty(Actor::Property::POSITION_Z).Get<float>();
351 camera.SetFarClippingPlane(zCameraPos + windowSize.y * .5f);
352 camera.SetNearClippingPlane(zCameraPos - windowSize.y * .5f);
354 const float zNear = camera.GetNearClippingPlane();
355 const float zFar = camera.GetFarClippingPlane();
356 const float depth = zFar - zNear;
358 // Create root of scene that shall be rendered off-screen.
359 auto sceneRoot = Actor::New();
360 CenterActor(sceneRoot);
362 mSceneRoot = sceneRoot;
363 window.Add(sceneRoot);
365 // Create an axis to spin our actors around.
366 auto axis = Actor::New();
371 // Create an octahedral mesh for our main actors and to visualise the light sources.
372 Geometry mesh = CreateOctahedron(false);
374 // Create main actors
375 Shader preShader = Shader::New(SHADER_DEFERRED_SHADING_PREPASS_VERT, SHADER_DEFERRED_SHADING_PREPASS_FRAG);
376 TextureSet noTexturesThanks = TextureSet::New();
377 Renderer meshRenderer = CreateRenderer(noTexturesThanks, mesh, preShader, OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
378 meshRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
379 RegisterDepthProperties(depth, zNear, meshRenderer);
391 Vector3{0.f, -c, -c},
396 Vector3{-c, 0.f, -c},
401 Vector3{-c, -c, 0.f},
407 Actor a = Actor::New();
410 Vector3 position{v * unit * 5.f};
411 a.SetProperty(Actor::Property::POSITION, position);
413 float scale = (c + ((v.x + v.y + v.z) + c * 3.f) * .5f) / (c * 4.f);
414 Vector3 size{Vector3::ONE * scale * unit * 2.f};
415 a.SetProperty(Actor::Property::SIZE, size);
417 a.SetProperty(Actor::Property::COLOR, Color::WHITE * .25f + (Color::RED * (v.x + c) / (c * 2.f) + Color::GREEN * (v.y + c) / (c * 2.f) + Color::BLUE * (v.z + c) / (c * 2.f)) * .015625f);
418 a.AddRenderer(meshRenderer);
423 // Create off-screen textures, fbo and render task.
424 uint32_t width = static_cast<uint32_t>(windowSize.x);
425 uint32_t height = static_cast<uint32_t>(windowSize.y);
427 Texture rttNormal = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888, width, height);
428 Texture rttPosition = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, width, height);
429 Texture rttColor = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888, width, height);
430 FrameBuffer fbo = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH);
431 fbo.AttachColorTexture(rttNormal);
432 fbo.AttachColorTexture(rttPosition);
433 fbo.AttachColorTexture(rttColor);
435 RenderTask sceneRender = tasks.CreateTask();
436 sceneRender.SetViewportSize(windowSize);
437 sceneRender.SetFrameBuffer(fbo);
438 sceneRender.SetCameraActor(camera);
439 sceneRender.SetSourceActor(sceneRoot);
440 sceneRender.SetInputEnabled(false);
441 sceneRender.SetCullMode(false);
442 sceneRender.SetClearEnabled(true);
443 sceneRender.SetClearColor(Color::BLACK);
444 sceneRender.SetExclusive(true);
446 mSceneRender = sceneRender;
448 // Create final image for deferred shading
449 auto finalImage = Actor::New();
450 CenterActor(finalImage);
451 finalImage.SetProperty(Actor::Property::SIZE, windowSize);
453 TextureSet finalImageTextures = TextureSet::New();
454 finalImageTextures.SetTexture(0, rttNormal);
455 finalImageTextures.SetTexture(1, rttPosition);
456 finalImageTextures.SetTexture(2, rttColor);
458 Sampler sampler = Sampler::New();
459 sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
460 finalImageTextures.SetSampler(0, sampler);
461 finalImageTextures.SetSampler(1, sampler);
462 finalImageTextures.SetSampler(2, sampler);
464 Shader shdMain = Shader::New(SHADER_DEFERRED_SHADING_MAINPASS_VERT, SHADER_DEFERRED_SHADING_MAINPASS_FRAG);
465 Geometry finalImageGeom = CreateTexturedQuadGeometry(true);
466 Renderer finalImageRenderer = CreateRenderer(finalImageTextures, finalImageGeom, shdMain);
467 RegisterDepthProperties(depth, zNear, finalImageRenderer);
469 auto propInvProjection = finalImageRenderer.RegisterProperty("uInvProjection", Matrix::IDENTITY);
470 Constraint cnstrInvProjection = Constraint::New<Matrix>(finalImageRenderer, propInvProjection, [zCameraPos, zNear, depth](Matrix& output, const PropertyInputContainer& input) {
471 output = input[0]->GetMatrix();
472 DALI_ASSERT_ALWAYS(output.Invert() && "Failed to invert projection matrix.");
474 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::PROJECTION_MATRIX));
475 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
476 cnstrInvProjection.Apply();
478 finalImage.AddRenderer(finalImageRenderer);
480 mFinalImage = finalImage;
481 window.Add(finalImage);
483 // Create a node for our lights
484 auto lights = Actor::New();
486 sceneRoot.Add(lights);
489 const bool showLights = mOptions & Options::SHOW_LIGHTS;
490 Renderer lightRenderer;
493 Geometry lightMesh = CreateOctahedron(true);
494 lightRenderer = CreateRenderer(noTexturesThanks, lightMesh, preShader, OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
495 lightRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::FRONT);
498 Vector3 lightPos{unit * 12.f, 0.f, 0.f};
499 float theta = M_PI * 2.f / MAX_LIGHTS;
500 float cosTheta = std::cos(theta);
501 float sinTheta = std::sin(theta);
502 for(int i = 0; i < MAX_LIGHTS; ++i)
504 Vector3 color = FromHueSaturationLightness(Vector3((360.f * i) / MAX_LIGHTS, .5f, 1.f));
506 Actor light = CreateLight(lightPos * (1 + (i % 8)) / 8.f, unit * 16.f, color, camera, finalImageRenderer);
508 float z = (((i & 1) << 1) - 1) * unit * 8.f;
509 lightPos = Vector3(cosTheta * lightPos.x - sinTheta * lightPos.y, sinTheta * lightPos.x + cosTheta * lightPos.y, z);
513 light.SetProperty(Actor::Property::SIZE, Vector3::ONE * unit / 8.f);
514 light.AddRenderer(lightRenderer);
520 // Take them for a spin.
521 Animation animLights = Animation::New(40.f);
522 animLights.SetLooping(true);
523 animLights.AnimateBy(Property(lights, Actor::Property::ORIENTATION), Quaternion(Radian(M_PI * 2.f), Vector3::YAXIS));
527 window.KeyEventSignal().Connect(this, &DeferredShadingExample::OnKeyEvent);
529 mPanDetector = PanGestureDetector::New();
530 mPanDetector.DetectedSignal().Connect(this, &DeferredShadingExample::OnPan);
531 mPanDetector.Attach(window.GetRootLayer());
534 void Destroy(Application& app)
536 app.GetWindow().GetRenderTaskList().RemoveTask(mSceneRender);
537 mSceneRender.Reset();
539 UnparentAndReset(mSceneRoot);
540 UnparentAndReset(mFinalImage);
543 Actor CreateLight(Vector3 position, float radius, Vector3 color, CameraActor camera, Renderer renderer)
545 Actor light = Actor::New();
547 light.SetProperty(Actor::Property::COLOR, Color::WHITE);
548 light.SetProperty(Actor::Property::POSITION, position);
550 auto iPropRadius = light.RegisterProperty("radius", radius);
551 auto iPropLightColor = light.RegisterProperty("lightcolor", color);
553 // Create light source uniforms on lighting shader.
554 char buffer[LIGHT_SOURCE_BUFFER_SIZE];
555 char* writep = buffer + snprintf(buffer, LIGHT_SOURCE_BUFFER_SIZE, "uLights[%d].", mNumLights);
558 strncpy(writep, POSITION_STRING.string, POSITION_STRING.size);
559 auto oPropLightPos = renderer.RegisterProperty(buffer, position);
561 strncpy(writep, RADIUS_STRING.string, RADIUS_STRING.size);
562 auto oPropLightRadius = renderer.RegisterProperty(buffer, radius);
564 strncpy(writep, COLOR_STRING.string, COLOR_STRING.size);
565 auto oPropLightColor = renderer.RegisterProperty(buffer, color);
567 // Constrain the light position, radius and color to lighting shader uniforms.
568 // Convert light position to view space;
569 Constraint cLightPos = Constraint::New<Vector3>(renderer, oPropLightPos, [](Vector3& output, const PropertyInputContainer& input) {
570 Vector4 worldPos(input[0]->GetVector3());
573 worldPos = input[1]->GetMatrix() * worldPos;
574 output = Vector3(worldPos);
576 cLightPos.AddSource(Source(light, Actor::Property::WORLD_POSITION));
577 cLightPos.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
580 Constraint cLightRadius = Constraint::New<float>(renderer, oPropLightRadius, EqualToConstraint());
581 cLightRadius.AddSource(Source(light, iPropRadius));
582 cLightRadius.Apply();
584 Constraint cLightColor = Constraint::New<Vector3>(renderer, oPropLightColor, EqualToConstraint());
585 cLightColor.AddSource(Source(light, iPropLightColor));
591 void OnPan(Actor, PanGesture const& gesture)
593 Quaternion q = mAxis.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
594 const Vector2& displacement = gesture.GetScreenDisplacement();
595 Quaternion qx(Radian(Degree(displacement.y) * -.5f), Vector3::XAXIS);
596 Quaternion qy(Radian(Degree(displacement.x) * .5f), Vector3::YAXIS);
597 mAxis.SetProperty(Actor::Property::ORIENTATION, qy * qx * q);
600 void OnKeyEvent(const KeyEvent& event)
602 if(event.GetState() == KeyEvent::DOWN)
604 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
617 RenderTask mSceneRender;
622 PanGestureDetector mPanDetector;
625 int DALI_EXPORT_API main(int argc, char** argv)
627 const bool showLights = [argc, argv]() {
628 auto endArgs = argv + argc;
629 return std::find_if(argv, endArgs, [](const char* arg) {
630 return strcmp(arg, "--show-lights") == 0;
634 Application app = Application::New(&argc, &argv);
635 DeferredShadingExample example(app, (showLights ? DeferredShadingExample::Options::SHOW_LIGHTS : 0));