2 * Copyright (c) 2020 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.
17 #include "dali/dali.h"
18 #include "dali/public-api/actors/actor.h"
19 #include "dali/public-api/rendering/renderer.h"
28 //=============================================================================
29 // Demonstrates deferred shading with multiple render targets (for color,
30 // position, and normal), a Phong lighting model and 32 point lights.
32 // Invoked with the --show-lights it will render a mesh at each light position.
33 //=============================================================================
35 #define QUOTE(x) DALI_COMPOSE_SHADER(x)
39 #define DEFINE_MAX_LIGHTS "const int kMaxLights = " QUOTE(MAX_LIGHTS) ";"
41 #define DEFINE(x) "#define " DALI_COMPOSE_SHADER(x) DALI_COMPOSE_SHADER(\n)
43 //=============================================================================
45 //=============================================================================
46 const char* const PREPASS_VSH = DALI_COMPOSE_SHADER(#version 300 es\n
47 precision mediump float;)
51 uniform mat4 uMvpMatrix;
52 uniform mat3 uNormalMatrix;
55 uniform vec3 uDepth_InvDepth_Near;\n)
56 DEFINE(DEPTH uDepth_InvDepth_Near.x)
57 DEFINE(INV_DEPTH uDepth_InvDepth_Near.y)
58 DEFINE(NEAR uDepth_InvDepth_Near.z)
67 vec4 Map(vec4 v) // projection space -> texture
69 return vec4(v.xyz / (2.f * v.w) + vec3(.5f), (v.w - NEAR) * INV_DEPTH);
74 vec4 position = uMvpMatrix * vec4(aPosition * uSize, 1.f);
75 vPosition = Map(position);
76 gl_Position = position;
78 vNormal = normalize(uNormalMatrix * aNormal);
81 //=============================================================================
82 const char* const PREPASS_FSH = DALI_COMPOSE_SHADER(#version 300 es\n
83 precision mediump float;
91 // These are our outputs.
92 layout(location = 0) out vec3 oNormal;
93 layout(location = 1) out vec4 oPosition;
94 layout(location = 2) out vec3 oColor;
99 oPosition = vPosition;
100 oNormal = normalize(vNormal) * .5f + .5f;
103 //=============================================================================
104 // MAIN (LIGHTING) PASS
105 //=============================================================================
106 const char* const MAINPASS_VSH = DALI_COMPOSE_SHADER(#version 300 es\n
107 precision mediump float;
110 uniform mat4 uMvpMatrix;
120 vec4 position = uMvpMatrix * vec4(aPosition * uSize, 1.f);
123 gl_Position = position;
126 //=============================================================================
127 const char* const MAINPASS_FSH = DALI_COMPOSE_SHADER(#version 300 es\n
128 precision mediump float;\n)
132 const float kAttenuationConst = .05f;
133 const float kAttenuationLinear = .1f;
134 const float kAttenuationQuadratic = .15f;
137 uniform sampler2D uTextureNormal;
138 uniform sampler2D uTexturePosition;
139 uniform sampler2D uTextureColor;
141 uniform mat4 uInvProjection;
143 uniform vec3 uDepth_InvDepth_Near;\n)
144 DEFINE(DEPTH uDepth_InvDepth_Near.x)
145 DEFINE(INV_DEPTH uDepth_InvDepth_Near.y)
146 DEFINE(NEAR uDepth_InvDepth_Near.z)
149 // Light source uniforms
152 vec3 position; // view space
157 uniform Light uLights[kMaxLights];
163 vec4 Unmap(vec4 m) // texture -> projection
165 m.w = m.w * DEPTH + NEAR;
166 m.xyz = (m.xyz - vec3(.5)) * (2.f * m.w);
170 vec3 CalculateLighting(vec3 pos, vec3 normal)
172 vec3 viewDir = normalize(pos);
173 vec3 viewDirRefl = -reflect(viewDir, normal);
175 vec3 light = vec3(0.04f); // fake ambient term
176 for (int i = 0; i < kMaxLights; ++i)
178 vec3 rel = pos - uLights[i].position;
179 float distance = length(rel);
182 float a = uLights[i].radius / (kAttenuationConst + kAttenuationLinear * distance +
183 kAttenuationQuadratic * distance * distance); // attenuation
185 float l = max(0.f, dot(normal, rel)); // lambertian
186 float s = pow(max(0.f, dot(viewDirRefl, rel)), 256.f); // specular
188 light += (uLights[i].color * (l + s)) * a;
196 vec3 normSample = texture(uTextureNormal, vUv).xyz;
197 if (dot(normSample, normSample) == 0.f)
199 discard; // if we didn't write this texel, don't bother lighting it.
202 vec3 normal = normalize(normSample - .5f);
204 vec4 posSample = texture(uTexturePosition, vUv);
205 vec3 pos = (uInvProjection * Unmap(posSample)).xyz;
207 vec3 color = texture(uTextureColor, vUv).rgb;
208 vec3 finalColor = color * CalculateLighting(pos, normal);
210 oColor = vec4(finalColor, 1.f);
213 //=============================================================================
217 std::random_device mDevice;
218 std::mt19937 mMersenneTwister;
219 std::uniform_real_distribution<float> mDistribution;
222 : mMersenneTwister(mDevice()),
223 mDistribution(0., 1.)
228 return mDistribution(mMersenneTwister);
232 //=============================================================================
233 float FastFloor(float x)
235 return static_cast<int>(x) - static_cast<int>(x < 0);
238 //=============================================================================
239 Vector3 FromHueSaturationLightness(Vector3 hsl)
242 if (hsl.y * hsl.y > 0.f)
250 int i = FastFloor(hsl.x);
251 float ff = hsl.x - i;
252 float p = hsl.z * (1.0 - hsl.y);
253 float q = hsl.z * (1.0 - (hsl.y * ff));
254 float t = hsl.z * (1.0 - (hsl.y * (1.f - ff)));
298 rgb = Vector3::ONE * hsl.z;
304 //=============================================================================
305 Geometry CreateTexturedQuadGeometry(bool flipV)
307 // Create geometry -- unit square with whole of the texture mapped to it.
314 Vertex vertexData[] = {
315 { Vector3(-.5f, .5f, .0f), Vector2(.0f, 1.0f) },
316 { Vector3(.5f, .5f, .0f), Vector2(1.0f, 1.0f) },
317 { Vector3(-.5f, -.5f, .0f), Vector2(.0f, .0f) },
318 { Vector3(.5f, -.5f, .0f), Vector2(1.0f, .0f) },
323 std::swap(vertexData[0].aTexCoord, vertexData[2].aTexCoord);
324 std::swap(vertexData[1].aTexCoord, vertexData[3].aTexCoord);
327 PropertyBuffer vertexBuffer = PropertyBuffer::New( Property::Map()
328 .Add( "aPosition", Property::VECTOR3 )
329 .Add( "aTexCoord", Property::VECTOR2 ) );
330 vertexBuffer.SetData( vertexData, std::extent<decltype(vertexData)>::value );
332 Geometry geometry = Geometry::New();
333 geometry.AddVertexBuffer( vertexBuffer );
334 geometry.SetType( Geometry::TRIANGLE_STRIP );
338 //=============================================================================
339 Geometry CreateOctahedron(bool invertNormals)
341 Vector3 positions[] = {
342 Vector3{ -1.f, 0.f, 0.f },
343 Vector3{ 1.f, 0.f, 0.f },
344 Vector3{ 0.f, -1.f, 0.f },
345 Vector3{ 0.f, 1.f, 0.f },
346 Vector3{ 0.f, 0.f, -1.f },
347 Vector3{ 0.f, 0.f, 1.f },
355 Vertex vertexData[] = {
390 for (uint32_t i = 0; i < std::extent<decltype(vertexData)>::value / 3; ++i)
392 uint32_t idx = i * 3;
394 Vector3 normal = (vertexData[idx + 2].position - vertexData[idx].position).
395 Cross(vertexData[idx + 1].position - vertexData[idx].position);
397 normal *= invertNormals * 2.f - 1.f;
399 vertexData[idx++].normal = normal;
400 vertexData[idx++].normal = normal;
401 vertexData[idx].normal = normal;
404 // Configure property buffers and create geometry.
405 PropertyBuffer vertexBuffer = PropertyBuffer::New(Property::Map()
406 .Add("aPosition", Property::VECTOR3)
407 .Add("aNormal", Property::VECTOR3));
408 vertexBuffer.SetData(vertexData, std::extent<decltype(vertexData)>::value);
410 Geometry geometry = Geometry::New();
411 geometry.AddVertexBuffer( vertexBuffer );
412 geometry.SetType( Geometry::TRIANGLES );
416 //=============================================================================
421 OPTION_DEPTH_TEST = 0x02,
422 OPTION_DEPTH_WRITE = 0x04
425 Renderer CreateRenderer(TextureSet textures, Geometry geometry, Shader shader, uint32_t options = OPTION_NONE)
427 Renderer renderer = Renderer::New(geometry, shader);
428 renderer.SetProperty(Renderer::Property::BLEND_MODE,
429 (options & OPTION_BLEND) ? BlendMode::ON : BlendMode::OFF);
430 renderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE,
431 (options & OPTION_DEPTH_TEST) ? DepthTestMode::ON : DepthTestMode::OFF);
432 renderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE,
433 (options & OPTION_DEPTH_WRITE) ? DepthWriteMode::ON : DepthWriteMode::OFF);
434 renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
438 textures = TextureSet::New();
441 renderer.SetTextures(textures);
445 //=============================================================================
446 void CenterActor(Actor actor)
448 actor.SetAnchorPoint( AnchorPoint::CENTER );
449 actor.SetParentOrigin( ParentOrigin::CENTER );
452 //=============================================================================
453 void RegisterDepthProperties(float depth, float near, Handle& h)
455 h.RegisterProperty("uDepth_InvDepth_Near", Vector3(depth, 1.f / depth, near));
460 //=============================================================================
461 class DeferredShadingExample : public ConnectionTracker
473 DeferredShadingExample(Application& app, uint32_t options = Options::NONE)
477 app.InitSignal().Connect( this, &DeferredShadingExample::Create );
478 app.TerminateSignal().Connect( this, &DeferredShadingExample::Destroy );
482 void Create(Application& app)
484 // Grab stage, configure layer
485 Stage stage = Stage::GetCurrent();
486 auto rootLayer = stage.GetRootLayer();
487 rootLayer.SetBehavior(Layer::LAYER_3D);
489 auto stageSize = stage.GetSize();
490 auto stageHalfSize = stageSize * .5f;
491 auto invStageHalfSize = Vector2::ONE / stageHalfSize;
493 float unit = stageSize.y / 24.f;
495 // Get camera - we'll be re-using the same old camera in the two passes.
496 RenderTaskList tasks = stage.GetRenderTaskList();
497 CameraActor camera = tasks.GetTask(0).GetCameraActor();
499 auto zCameraPos = camera.GetProperty(Actor::Property::POSITION_Z).Get<float>();
500 camera.SetFarClippingPlane(zCameraPos + stageSize.y * .5f);
501 camera.SetNearClippingPlane(zCameraPos - stageSize.y * .5f);
503 const float zNear = camera.GetNearClippingPlane();
504 const float zFar = camera.GetFarClippingPlane();
505 const float depth = zFar - zNear;
507 // Create root of scene that shall be rendered off-screen.
508 auto sceneRoot = Actor::New();
509 CenterActor(sceneRoot);
511 mSceneRoot = sceneRoot;
512 stage.Add(sceneRoot);
514 // Create an axis to spin our actors around.
515 auto axis = Actor::New();
520 // Create an octahedral mesh for our main actors and to visualise the light sources.
521 Geometry mesh = CreateOctahedron(false);
523 // Create main actors
524 Shader preShader = Shader::New(PREPASS_VSH, PREPASS_FSH);
525 TextureSet noTexturesThanks = TextureSet::New();
526 Renderer meshRenderer = CreateRenderer(noTexturesThanks, mesh, preShader,
527 OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
528 meshRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
529 meshRenderer.RegisterProperty("uInvStageHalfSize", invStageHalfSize);
530 RegisterDepthProperties(depth, zNear, meshRenderer);
533 Vector3{ -c, -c, -c },
534 Vector3{ c, -c, -c },
535 Vector3{ -c, c, -c },
537 Vector3{ -c, -c, c },
542 Vector3{ 0.f, -c, -c },
543 Vector3{ 0.f, c, -c },
544 Vector3{ 0.f, -c, c },
545 Vector3{ 0.f, c, c },
547 Vector3{ -c, 0.f, -c },
548 Vector3{ c, 0.f, -c },
549 Vector3{ -c, 0.f, c },
550 Vector3{ c, 0.f, c },
552 Vector3{ -c, -c, 0.f },
553 Vector3{ c, -c, 0.f },
554 Vector3{ -c, c, 0.f },
555 Vector3{ c, c, 0.f },
558 Actor a = Actor::New();
561 Vector3 position{ v * unit * 5.f };
562 a.SetPosition(position);
564 float scale = (c + ((v.x + v.y + v.z) + c * 3.f) * .5f) / (c * 4.f);
565 Vector3 size{ Vector3::ONE * scale * unit * 2.f };
568 a.SetColor(Color::WHITE * .25f +
569 (Color::RED * (v.x + c) / (c * 2.f) +
570 Color::GREEN * (v.y + c) / (c * 2.f) +
571 Color::BLUE * (v.z + c) / (c * 2.f)) * .015625f);
572 a.AddRenderer(meshRenderer);
577 // Create off-screen textures, fbo and render task.
578 uint32_t width = static_cast<uint32_t>(stageSize.x);
579 uint32_t height = static_cast<uint32_t>(stageSize.y);
581 Texture rttNormal = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
583 Texture rttPosition = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888,
585 Texture rttColor = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
587 FrameBuffer fbo = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH);
588 fbo.AttachColorTexture(rttNormal);
589 fbo.AttachColorTexture(rttPosition);
590 fbo.AttachColorTexture(rttColor);
592 RenderTask sceneRender = tasks.CreateTask();
593 sceneRender.SetViewportSize(stageSize);
594 sceneRender.SetFrameBuffer(fbo);
595 sceneRender.SetCameraActor(camera);
596 sceneRender.SetSourceActor(sceneRoot);
597 sceneRender.SetInputEnabled(false);
598 sceneRender.SetCullMode(false);
599 sceneRender.SetClearEnabled(true);
600 sceneRender.SetClearColor(Color::BLACK);
601 sceneRender.SetExclusive(true);
603 mSceneRender = sceneRender;
605 // Create final image for deferred shading
606 auto finalImage = Actor::New();
607 CenterActor(finalImage);
608 finalImage.SetSize(stageSize);
610 TextureSet finalImageTextures = TextureSet::New();
611 finalImageTextures.SetTexture(0, rttNormal);
612 finalImageTextures.SetTexture(1, rttPosition);
613 finalImageTextures.SetTexture(2, rttColor);
615 Sampler sampler = Sampler::New();
616 sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
617 finalImageTextures.SetSampler(0, sampler);
618 finalImageTextures.SetSampler(1, sampler);
619 finalImageTextures.SetSampler(2, sampler);
621 Shader shdMain = Shader::New(MAINPASS_VSH, MAINPASS_FSH);
622 Geometry finalImageGeom = CreateTexturedQuadGeometry(true);
623 Renderer finalImageRenderer = CreateRenderer(finalImageTextures, finalImageGeom, shdMain);
624 finalImageRenderer.RegisterProperty("uStageHalfSize", stageHalfSize);
625 RegisterDepthProperties(depth, zNear, finalImageRenderer);
627 auto propInvProjection = finalImageRenderer.RegisterProperty("uInvProjection", Matrix::IDENTITY);
628 Constraint cnstrInvProjection = Constraint::New<Matrix>(finalImageRenderer, propInvProjection,
629 [zCameraPos, zNear, depth](Matrix& output, const PropertyInputContainer& input) {
630 output = input[0]->GetMatrix();
631 DALI_ASSERT_ALWAYS(output.Invert() && "Failed to invert projection matrix.");
633 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::PROJECTION_MATRIX));
634 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
635 cnstrInvProjection.Apply();
637 finalImage.AddRenderer(finalImageRenderer);
639 mFinalImage = finalImage;
640 stage.Add(finalImage);
642 // Create a node for our lights
643 auto lights = Actor::New();
645 sceneRoot.Add(lights);
648 const bool showLights = mOptions & Options::SHOW_LIGHTS;
649 Renderer lightRenderer;
652 Geometry lightMesh = CreateOctahedron(true);
653 lightRenderer = CreateRenderer(noTexturesThanks, lightMesh, preShader,
654 OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
655 lightRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::FRONT);
658 Vector3 lightPos{ unit * 12.f, 0.f, 0.f };
659 float theta = M_PI * 2.f / MAX_LIGHTS;
660 float cosTheta = std::cos(theta);
661 float sinTheta = std::sin(theta);
662 for (int i = 0; i < MAX_LIGHTS; ++i)
664 Vector3 color = FromHueSaturationLightness(Vector3((360.f * i) / MAX_LIGHTS, .5f, 1.f));
666 Actor light = CreateLight(lightPos * (1 + (i % 8)) / 8.f, unit * 16.f, color, camera, finalImageRenderer);
668 float z = (((i & 1) << 1) - 1) * unit * 8.f;
669 lightPos = Vector3(cosTheta * lightPos.x - sinTheta * lightPos.y, sinTheta * lightPos.x + cosTheta * lightPos.y, z);
673 light.SetProperty(Actor::Property::SIZE, Vector3::ONE * unit / 8.f);
674 light.AddRenderer(lightRenderer);
680 // Take them for a spin.
681 Animation animLights = Animation::New(40.f);
682 animLights.SetLooping(true);
683 animLights.AnimateBy(Property(lights, Actor::Property::ORIENTATION), Quaternion(Radian(M_PI * 2.f), Vector3::YAXIS));
687 stage.KeyEventSignal().Connect(this, &DeferredShadingExample::OnKeyEvent);
689 mPanDetector = PanGestureDetector::New();
690 mPanDetector.DetectedSignal().Connect(this, &DeferredShadingExample::OnPan);
691 mPanDetector.Attach(stage.GetRootLayer());
694 void Destroy(Application& app)
696 Stage::GetCurrent().GetRenderTaskList().RemoveTask(mSceneRender);
697 mSceneRender.Reset();
699 UnparentAndReset(mSceneRoot);
700 UnparentAndReset(mFinalImage);
703 Actor CreateLight(Vector3 position, float radius, Vector3 color, CameraActor camera, Renderer renderer)
705 Actor light = Actor::New();
707 light.SetProperty(Actor::Property::COLOR, Color::WHITE);
708 light.SetProperty(Actor::Property::POSITION, position);
710 auto iPropRadius = light.RegisterProperty("radius", radius);
711 auto iPropLightColor = light.RegisterProperty("lightcolor", color);
713 // Create light source uniforms on lighting shader.
715 char* writep = buffer + sprintf(buffer, "uLights[%d].", mNumLights);
718 strcpy(writep, "position");
719 auto oPropLightPos = renderer.RegisterProperty(buffer, position);
721 strcpy(writep, "radius");
722 auto oPropLightRadius = renderer.RegisterProperty(buffer, radius);
724 strcpy(writep, "color");
725 auto oPropLightColor = renderer.RegisterProperty(buffer, color);
727 // Constrain the light position, radius and color to lighting shader uniforms.
728 // Convert light position to view space;
729 Constraint cLightPos = Constraint::New<Vector3>(renderer, oPropLightPos, [](Vector3& output, const PropertyInputContainer& input)
731 Vector4 worldPos(input[0]->GetVector3());
734 worldPos = input[1]->GetMatrix() * worldPos;
735 output = Vector3(worldPos);
737 cLightPos.AddSource(Source(light, Actor::Property::WORLD_POSITION));
738 cLightPos.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
741 Constraint cLightRadius = Constraint::New<float>(renderer, oPropLightRadius,
742 EqualToConstraint());
743 cLightRadius.AddSource(Source(light, iPropRadius));
744 cLightRadius.Apply();
746 Constraint cLightColor = Constraint::New<Vector3>(renderer, oPropLightColor,
747 EqualToConstraint());
748 cLightColor.AddSource(Source(light, iPropLightColor));
754 void OnPan(Actor, PanGesture const& gesture)
756 Quaternion q = mAxis.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
757 Quaternion qx(Radian(Degree(gesture.screenDisplacement.y) * -.5f), Vector3::XAXIS);
758 Quaternion qy(Radian(Degree(gesture.screenDisplacement.x) * .5f), Vector3::YAXIS);
759 mAxis.SetProperty(Actor::Property::ORIENTATION, qy * qx * q);
762 void OnKeyEvent(const KeyEvent& event)
764 if(event.state == KeyEvent::Down)
766 if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
779 RenderTask mSceneRender;
784 PanGestureDetector mPanDetector;
788 int main(int argc, char** argv)
790 const bool showLights = [](int argc, char** argv)
792 auto endArgs = argv + argc;
793 return std::find_if(argv, endArgs, [](const char* arg)
795 return strcmp(arg, "--show-lights") == 0;
799 Application app = Application::New(&argc, &argv);
800 DeferredShadingExample example(app, (showLights ? DeferredShadingExample::Options::SHOW_LIGHTS : 0));