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.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
449 actor.SetProperty( Actor::Property::PARENT_ORIGIN, 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 window, configure layer
505 Window window = app.GetWindow();
506 auto rootLayer = window.GetRootLayer();
507 rootLayer.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_3D );
509 Vector2 windowSize = window.GetSize();
511 float unit = windowSize.y / 24.f;
513 // Get camera - we'll be re-using the same old camera in the two passes.
514 RenderTaskList tasks = window.GetRenderTaskList();
515 CameraActor camera = tasks.GetTask(0).GetCameraActor();
517 auto zCameraPos = camera.GetProperty(Actor::Property::POSITION_Z).Get<float>();
518 camera.SetFarClippingPlane(zCameraPos + windowSize.y * .5f);
519 camera.SetNearClippingPlane(zCameraPos - windowSize.y * .5f);
521 const float zNear = camera.GetNearClippingPlane();
522 const float zFar = camera.GetFarClippingPlane();
523 const float depth = zFar - zNear;
525 // Create root of scene that shall be rendered off-screen.
526 auto sceneRoot = Actor::New();
527 CenterActor(sceneRoot);
529 mSceneRoot = sceneRoot;
530 window.Add(sceneRoot);
532 // Create an axis to spin our actors around.
533 auto axis = Actor::New();
538 // Create an octahedral mesh for our main actors and to visualise the light sources.
539 Geometry mesh = CreateOctahedron(false);
541 // Create main actors
542 Shader preShader = Shader::New(PREPASS_VSH, PREPASS_FSH);
543 TextureSet noTexturesThanks = TextureSet::New();
544 Renderer meshRenderer = CreateRenderer(noTexturesThanks, mesh, preShader,
545 OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
546 meshRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
547 RegisterDepthProperties(depth, zNear, meshRenderer);
550 Vector3{ -c, -c, -c },
551 Vector3{ c, -c, -c },
552 Vector3{ -c, c, -c },
554 Vector3{ -c, -c, c },
559 Vector3{ 0.f, -c, -c },
560 Vector3{ 0.f, c, -c },
561 Vector3{ 0.f, -c, c },
562 Vector3{ 0.f, c, c },
564 Vector3{ -c, 0.f, -c },
565 Vector3{ c, 0.f, -c },
566 Vector3{ -c, 0.f, c },
567 Vector3{ c, 0.f, c },
569 Vector3{ -c, -c, 0.f },
570 Vector3{ c, -c, 0.f },
571 Vector3{ -c, c, 0.f },
572 Vector3{ c, c, 0.f },
575 Actor a = Actor::New();
578 Vector3 position{ v * unit * 5.f };
579 a.SetProperty( Actor::Property::POSITION, position );
581 float scale = (c + ((v.x + v.y + v.z) + c * 3.f) * .5f) / (c * 4.f);
582 Vector3 size{ Vector3::ONE * scale * unit * 2.f };
583 a.SetProperty( Actor::Property::SIZE, size);
585 a.SetProperty( Actor::Property::COLOR,Color::WHITE * .25f +
586 (Color::RED * (v.x + c) / (c * 2.f) +
587 Color::GREEN * (v.y + c) / (c * 2.f) +
588 Color::BLUE * (v.z + c) / (c * 2.f)) * .015625f);
589 a.AddRenderer(meshRenderer);
594 // Create off-screen textures, fbo and render task.
595 uint32_t width = static_cast<uint32_t>(windowSize.x);
596 uint32_t height = static_cast<uint32_t>(windowSize.y);
598 Texture rttNormal = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
600 Texture rttPosition = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888,
602 Texture rttColor = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGB888,
604 FrameBuffer fbo = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH);
605 fbo.AttachColorTexture(rttNormal);
606 fbo.AttachColorTexture(rttPosition);
607 fbo.AttachColorTexture(rttColor);
609 RenderTask sceneRender = tasks.CreateTask();
610 sceneRender.SetViewportSize(windowSize);
611 sceneRender.SetFrameBuffer(fbo);
612 sceneRender.SetCameraActor(camera);
613 sceneRender.SetSourceActor(sceneRoot);
614 sceneRender.SetInputEnabled(false);
615 sceneRender.SetCullMode(false);
616 sceneRender.SetClearEnabled(true);
617 sceneRender.SetClearColor(Color::BLACK);
618 sceneRender.SetExclusive(true);
620 mSceneRender = sceneRender;
622 // Create final image for deferred shading
623 auto finalImage = Actor::New();
624 CenterActor(finalImage);
625 finalImage.SetProperty( Actor::Property::SIZE, windowSize);
627 TextureSet finalImageTextures = TextureSet::New();
628 finalImageTextures.SetTexture(0, rttNormal);
629 finalImageTextures.SetTexture(1, rttPosition);
630 finalImageTextures.SetTexture(2, rttColor);
632 Sampler sampler = Sampler::New();
633 sampler.SetFilterMode(FilterMode::NEAREST, FilterMode::NEAREST);
634 finalImageTextures.SetSampler(0, sampler);
635 finalImageTextures.SetSampler(1, sampler);
636 finalImageTextures.SetSampler(2, sampler);
638 Shader shdMain = Shader::New(MAINPASS_VSH, MAINPASS_FSH);
639 Geometry finalImageGeom = CreateTexturedQuadGeometry(true);
640 Renderer finalImageRenderer = CreateRenderer(finalImageTextures, finalImageGeom, shdMain);
641 RegisterDepthProperties(depth, zNear, finalImageRenderer);
643 auto propInvProjection = finalImageRenderer.RegisterProperty("uInvProjection", Matrix::IDENTITY);
644 Constraint cnstrInvProjection = Constraint::New<Matrix>(finalImageRenderer, propInvProjection,
645 [zCameraPos, zNear, depth](Matrix& output, const PropertyInputContainer& input) {
646 output = input[0]->GetMatrix();
647 DALI_ASSERT_ALWAYS(output.Invert() && "Failed to invert projection matrix.");
649 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::PROJECTION_MATRIX));
650 cnstrInvProjection.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
651 cnstrInvProjection.Apply();
653 finalImage.AddRenderer(finalImageRenderer);
655 mFinalImage = finalImage;
656 window.Add(finalImage);
658 // Create a node for our lights
659 auto lights = Actor::New();
661 sceneRoot.Add(lights);
664 const bool showLights = mOptions & Options::SHOW_LIGHTS;
665 Renderer lightRenderer;
668 Geometry lightMesh = CreateOctahedron(true);
669 lightRenderer = CreateRenderer(noTexturesThanks, lightMesh, preShader,
670 OPTION_DEPTH_TEST | OPTION_DEPTH_WRITE);
671 lightRenderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::FRONT);
674 Vector3 lightPos{ unit * 12.f, 0.f, 0.f };
675 float theta = M_PI * 2.f / MAX_LIGHTS;
676 float cosTheta = std::cos(theta);
677 float sinTheta = std::sin(theta);
678 for (int i = 0; i < MAX_LIGHTS; ++i)
680 Vector3 color = FromHueSaturationLightness(Vector3((360.f * i) / MAX_LIGHTS, .5f, 1.f));
682 Actor light = CreateLight(lightPos * (1 + (i % 8)) / 8.f, unit * 16.f, color, camera, finalImageRenderer);
684 float z = (((i & 1) << 1) - 1) * unit * 8.f;
685 lightPos = Vector3(cosTheta * lightPos.x - sinTheta * lightPos.y, sinTheta * lightPos.x + cosTheta * lightPos.y, z);
689 light.SetProperty(Actor::Property::SIZE, Vector3::ONE * unit / 8.f);
690 light.AddRenderer(lightRenderer);
696 // Take them for a spin.
697 Animation animLights = Animation::New(40.f);
698 animLights.SetLooping(true);
699 animLights.AnimateBy(Property(lights, Actor::Property::ORIENTATION), Quaternion(Radian(M_PI * 2.f), Vector3::YAXIS));
703 window.KeyEventSignal().Connect(this, &DeferredShadingExample::OnKeyEvent);
705 mPanDetector = PanGestureDetector::New();
706 mPanDetector.DetectedSignal().Connect(this, &DeferredShadingExample::OnPan);
707 mPanDetector.Attach(window.GetRootLayer());
710 void Destroy(Application& app)
712 app.GetWindow().GetRenderTaskList().RemoveTask(mSceneRender);
713 mSceneRender.Reset();
715 UnparentAndReset(mSceneRoot);
716 UnparentAndReset(mFinalImage);
719 Actor CreateLight(Vector3 position, float radius, Vector3 color, CameraActor camera, Renderer renderer)
721 Actor light = Actor::New();
723 light.SetProperty(Actor::Property::COLOR, Color::WHITE);
724 light.SetProperty(Actor::Property::POSITION, position);
726 auto iPropRadius = light.RegisterProperty("radius", radius);
727 auto iPropLightColor = light.RegisterProperty("lightcolor", color);
729 // Create light source uniforms on lighting shader.
730 char buffer[LIGHT_SOURCE_BUFFER_SIZE];
731 char* writep = buffer + snprintf(buffer, LIGHT_SOURCE_BUFFER_SIZE, "uLights[%d].", mNumLights);
734 strncpy(writep, POSITION_STRING.string, POSITION_STRING.size);
735 auto oPropLightPos = renderer.RegisterProperty(buffer, position);
737 strncpy(writep, RADIUS_STRING.string, RADIUS_STRING.size);
738 auto oPropLightRadius = renderer.RegisterProperty(buffer, radius);
740 strncpy(writep, COLOR_STRING.string, COLOR_STRING.size);
741 auto oPropLightColor = renderer.RegisterProperty(buffer, color);
743 // Constrain the light position, radius and color to lighting shader uniforms.
744 // Convert light position to view space;
745 Constraint cLightPos = Constraint::New<Vector3>(renderer, oPropLightPos, [](Vector3& output, const PropertyInputContainer& input)
747 Vector4 worldPos(input[0]->GetVector3());
750 worldPos = input[1]->GetMatrix() * worldPos;
751 output = Vector3(worldPos);
753 cLightPos.AddSource(Source(light, Actor::Property::WORLD_POSITION));
754 cLightPos.AddSource(Source(camera, CameraActor::Property::VIEW_MATRIX));
757 Constraint cLightRadius = Constraint::New<float>(renderer, oPropLightRadius,
758 EqualToConstraint());
759 cLightRadius.AddSource(Source(light, iPropRadius));
760 cLightRadius.Apply();
762 Constraint cLightColor = Constraint::New<Vector3>(renderer, oPropLightColor,
763 EqualToConstraint());
764 cLightColor.AddSource(Source(light, iPropLightColor));
770 void OnPan(Actor, PanGesture const& gesture)
772 Quaternion q = mAxis.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
773 Quaternion qx(Radian(Degree(gesture.screenDisplacement.y) * -.5f), Vector3::XAXIS);
774 Quaternion qy(Radian(Degree(gesture.screenDisplacement.x) * .5f), Vector3::YAXIS);
775 mAxis.SetProperty(Actor::Property::ORIENTATION, qy * qx * q);
778 void OnKeyEvent(const KeyEvent& event)
780 if(event.state == KeyEvent::Down)
782 if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
795 RenderTask mSceneRender;
800 PanGestureDetector mPanDetector;
804 int main(int argc, char** argv)
806 const bool showLights = [](int argc, char** argv)
808 auto endArgs = argv + argc;
809 return std::find_if(argv, endArgs, [](const char* arg)
811 return strcmp(arg, "--show-lights") == 0;
815 Application app = Application::New(&argc, &argv);
816 DeferredShadingExample example(app, (showLights ? DeferredShadingExample::Options::SHOW_LIGHTS : 0));