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 /// Create a String whose size can be evaluated at compile time
462 struct ConstantString
464 const char * const string;
467 template<uint16_t inputSize>
468 constexpr ConstantString(const char (&input)[inputSize])
475 constexpr ConstantString POSITION_STRING("position");
476 constexpr ConstantString RADIUS_STRING("radius");
477 constexpr ConstantString COLOR_STRING("color");
478 constexpr uint16_t LIGHT_SOURCE_BUFFER_SIZE(128u);
480 //=============================================================================
481 class DeferredShadingExample : public ConnectionTracker
493 DeferredShadingExample(Application& app, uint32_t options = Options::NONE)
497 app.InitSignal().Connect( this, &DeferredShadingExample::Create );
498 app.TerminateSignal().Connect( this, &DeferredShadingExample::Destroy );
502 void Create(Application& app)
504 // Grab stage, configure layer
505 Stage stage = Stage::GetCurrent();
506 auto rootLayer = stage.GetRootLayer();
507 rootLayer.SetBehavior(Layer::LAYER_3D);
509 auto stageSize = stage.GetSize();
510 auto stageHalfSize = stageSize * .5f;
511 auto invStageHalfSize = Vector2::ONE / stageHalfSize;
513 float unit = stageSize.y / 24.f;
515 // Get camera - we'll be re-using the same old camera in the two passes.
516 RenderTaskList tasks = stage.GetRenderTaskList();
517 CameraActor camera = tasks.GetTask(0).GetCameraActor();
519 auto zCameraPos = camera.GetProperty(Actor::Property::POSITION_Z).Get<float>();
520 camera.SetFarClippingPlane(zCameraPos + stageSize.y * .5f);
521 camera.SetNearClippingPlane(zCameraPos - stageSize.y * .5f);
523 const float zNear = camera.GetNearClippingPlane();
524 const float zFar = camera.GetFarClippingPlane();
525 const float depth = zFar - zNear;
527 // Create root of scene that shall be rendered off-screen.
528 auto sceneRoot = Actor::New();
529 CenterActor(sceneRoot);
531 mSceneRoot = sceneRoot;
532 stage.Add(sceneRoot);
534 // Create an axis to spin our actors around.
535 auto axis = Actor::New();
540 // Create an octahedral mesh for our main actors and to visualise the light sources.
541 Geometry mesh = CreateOctahedron(false);
543 // Create main actors
544 Shader preShader = Shader::New(PREPASS_VSH, PREPASS_FSH);
545 TextureSet noTexturesThanks = TextureSet::New();
546 Renderer meshRenderer = CreateRenderer(noTexturesThanks, mesh, preShader,
547 OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
548 meshRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
549 meshRenderer.RegisterProperty("uInvStageHalfSize", invStageHalfSize);
550 RegisterDepthProperties(depth, zNear, meshRenderer);
553 Vector3{ -c, -c, -c },
554 Vector3{ c, -c, -c },
555 Vector3{ -c, c, -c },
557 Vector3{ -c, -c, c },
562 Vector3{ 0.f, -c, -c },
563 Vector3{ 0.f, c, -c },
564 Vector3{ 0.f, -c, c },
565 Vector3{ 0.f, c, c },
567 Vector3{ -c, 0.f, -c },
568 Vector3{ c, 0.f, -c },
569 Vector3{ -c, 0.f, c },
570 Vector3{ c, 0.f, c },
572 Vector3{ -c, -c, 0.f },
573 Vector3{ c, -c, 0.f },
574 Vector3{ -c, c, 0.f },
575 Vector3{ c, c, 0.f },
578 Actor a = Actor::New();
581 Vector3 position{ v * unit * 5.f };
582 a.SetPosition(position);
584 float scale = (c + ((v.x + v.y + v.z) + c * 3.f) * .5f) / (c * 4.f);
585 Vector3 size{ Vector3::ONE * scale * unit * 2.f };
588 a.SetColor(Color::WHITE * .25f +
589 (Color::RED * (v.x + c) / (c * 2.f) +
590 Color::GREEN * (v.y + c) / (c * 2.f) +
591 Color::BLUE * (v.z + c) / (c * 2.f)) * .015625f);
592 a.AddRenderer(meshRenderer);
597 // Create off-screen textures, fbo and render task.
598 uint32_t width = static_cast<uint32_t>(stageSize.x);
599 uint32_t height = static_cast<uint32_t>(stageSize.y);
601 Texture rttNormal = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
603 Texture rttPosition = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888,
605 Texture rttColor = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
607 FrameBuffer fbo = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH);
608 fbo.AttachColorTexture(rttNormal);
609 fbo.AttachColorTexture(rttPosition);
610 fbo.AttachColorTexture(rttColor);
612 RenderTask sceneRender = tasks.CreateTask();
613 sceneRender.SetViewportSize(stageSize);
614 sceneRender.SetFrameBuffer(fbo);
615 sceneRender.SetCameraActor(camera);
616 sceneRender.SetSourceActor(sceneRoot);
617 sceneRender.SetInputEnabled(false);
618 sceneRender.SetCullMode(false);
619 sceneRender.SetClearEnabled(true);
620 sceneRender.SetClearColor(Color::BLACK);
621 sceneRender.SetExclusive(true);
623 mSceneRender = sceneRender;
625 // Create final image for deferred shading
626 auto finalImage = Actor::New();
627 CenterActor(finalImage);
628 finalImage.SetSize(stageSize);
630 TextureSet finalImageTextures = TextureSet::New();
631 finalImageTextures.SetTexture(0, rttNormal);
632 finalImageTextures.SetTexture(1, rttPosition);
633 finalImageTextures.SetTexture(2, rttColor);
635 Sampler sampler = Sampler::New();
636 sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
637 finalImageTextures.SetSampler(0, sampler);
638 finalImageTextures.SetSampler(1, sampler);
639 finalImageTextures.SetSampler(2, sampler);
641 Shader shdMain = Shader::New(MAINPASS_VSH, MAINPASS_FSH);
642 Geometry finalImageGeom = CreateTexturedQuadGeometry(true);
643 Renderer finalImageRenderer = CreateRenderer(finalImageTextures, finalImageGeom, shdMain);
644 finalImageRenderer.RegisterProperty("uStageHalfSize", stageHalfSize);
645 RegisterDepthProperties(depth, zNear, finalImageRenderer);
647 auto propInvProjection = finalImageRenderer.RegisterProperty("uInvProjection", Matrix::IDENTITY);
648 Constraint cnstrInvProjection = Constraint::New<Matrix>(finalImageRenderer, propInvProjection,
649 [zCameraPos, zNear, depth](Matrix& output, const PropertyInputContainer& input) {
650 output = input[0]->GetMatrix();
651 DALI_ASSERT_ALWAYS(output.Invert() && "Failed to invert projection matrix.");
653 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::PROJECTION_MATRIX));
654 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
655 cnstrInvProjection.Apply();
657 finalImage.AddRenderer(finalImageRenderer);
659 mFinalImage = finalImage;
660 stage.Add(finalImage);
662 // Create a node for our lights
663 auto lights = Actor::New();
665 sceneRoot.Add(lights);
668 const bool showLights = mOptions & Options::SHOW_LIGHTS;
669 Renderer lightRenderer;
672 Geometry lightMesh = CreateOctahedron(true);
673 lightRenderer = CreateRenderer(noTexturesThanks, lightMesh, preShader,
674 OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
675 lightRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::FRONT);
678 Vector3 lightPos{ unit * 12.f, 0.f, 0.f };
679 float theta = M_PI * 2.f / MAX_LIGHTS;
680 float cosTheta = std::cos(theta);
681 float sinTheta = std::sin(theta);
682 for (int i = 0; i < MAX_LIGHTS; ++i)
684 Vector3 color = FromHueSaturationLightness(Vector3((360.f * i) / MAX_LIGHTS, .5f, 1.f));
686 Actor light = CreateLight(lightPos * (1 + (i % 8)) / 8.f, unit * 16.f, color, camera, finalImageRenderer);
688 float z = (((i & 1) << 1) - 1) * unit * 8.f;
689 lightPos = Vector3(cosTheta * lightPos.x - sinTheta * lightPos.y, sinTheta * lightPos.x + cosTheta * lightPos.y, z);
693 light.SetProperty(Actor::Property::SIZE, Vector3::ONE * unit / 8.f);
694 light.AddRenderer(lightRenderer);
700 // Take them for a spin.
701 Animation animLights = Animation::New(40.f);
702 animLights.SetLooping(true);
703 animLights.AnimateBy(Property(lights, Actor::Property::ORIENTATION), Quaternion(Radian(M_PI * 2.f), Vector3::YAXIS));
707 stage.KeyEventSignal().Connect(this, &DeferredShadingExample::OnKeyEvent);
709 mPanDetector = PanGestureDetector::New();
710 mPanDetector.DetectedSignal().Connect(this, &DeferredShadingExample::OnPan);
711 mPanDetector.Attach(stage.GetRootLayer());
714 void Destroy(Application& app)
716 Stage::GetCurrent().GetRenderTaskList().RemoveTask(mSceneRender);
717 mSceneRender.Reset();
719 UnparentAndReset(mSceneRoot);
720 UnparentAndReset(mFinalImage);
723 Actor CreateLight(Vector3 position, float radius, Vector3 color, CameraActor camera, Renderer renderer)
725 Actor light = Actor::New();
727 light.SetProperty(Actor::Property::COLOR, Color::WHITE);
728 light.SetProperty(Actor::Property::POSITION, position);
730 auto iPropRadius = light.RegisterProperty("radius", radius);
731 auto iPropLightColor = light.RegisterProperty("lightcolor", color);
733 // Create light source uniforms on lighting shader.
734 char buffer[LIGHT_SOURCE_BUFFER_SIZE];
735 char* writep = buffer + snprintf(buffer, LIGHT_SOURCE_BUFFER_SIZE, "uLights[%d].", mNumLights);
738 strncpy(writep, POSITION_STRING.string, POSITION_STRING.size);
739 auto oPropLightPos = renderer.RegisterProperty(buffer, position);
741 strncpy(writep, RADIUS_STRING.string, RADIUS_STRING.size);
742 auto oPropLightRadius = renderer.RegisterProperty(buffer, radius);
744 strncpy(writep, COLOR_STRING.string, COLOR_STRING.size);
745 auto oPropLightColor = renderer.RegisterProperty(buffer, color);
747 // Constrain the light position, radius and color to lighting shader uniforms.
748 // Convert light position to view space;
749 Constraint cLightPos = Constraint::New<Vector3>(renderer, oPropLightPos, [](Vector3& output, const PropertyInputContainer& input)
751 Vector4 worldPos(input[0]->GetVector3());
754 worldPos = input[1]->GetMatrix() * worldPos;
755 output = Vector3(worldPos);
757 cLightPos.AddSource(Source(light, Actor::Property::WORLD_POSITION));
758 cLightPos.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
761 Constraint cLightRadius = Constraint::New<float>(renderer, oPropLightRadius,
762 EqualToConstraint());
763 cLightRadius.AddSource(Source(light, iPropRadius));
764 cLightRadius.Apply();
766 Constraint cLightColor = Constraint::New<Vector3>(renderer, oPropLightColor,
767 EqualToConstraint());
768 cLightColor.AddSource(Source(light, iPropLightColor));
774 void OnPan(Actor, PanGesture const& gesture)
776 Quaternion q = mAxis.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
777 Quaternion qx(Radian(Degree(gesture.screenDisplacement.y) * -.5f), Vector3::XAXIS);
778 Quaternion qy(Radian(Degree(gesture.screenDisplacement.x) * .5f), Vector3::YAXIS);
779 mAxis.SetProperty(Actor::Property::ORIENTATION, qy * qx * q);
782 void OnKeyEvent(const KeyEvent& event)
784 if(event.state == KeyEvent::Down)
786 if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
799 RenderTask mSceneRender;
804 PanGestureDetector mPanDetector;
808 int main(int argc, char** argv)
810 const bool showLights = [](int argc, char** argv)
812 auto endArgs = argv + argc;
813 return std::find_if(argv, endArgs, [](const char* arg)
815 return strcmp(arg, "--show-lights") == 0;
819 Application app = Application::New(&argc, &argv);
820 DeferredShadingExample example(app, (showLights ? DeferredShadingExample::Options::SHOW_LIGHTS : 0));