Add bloom demo and release notes for #119.
This commit is contained in:
@@ -6,6 +6,8 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
# Release notes
|
||||
|
||||
- Introduced `RenderTarget` API that allows View to reference an offscreen render target.
|
||||
- Added `lucy_bloom` sample to demonstrate the new `RenderTarget` API.
|
||||
- Added Screen Space Ambient Occlusion support (SAO)
|
||||
- New blending modes: `multiply` and `screen`
|
||||
- Fixed an issue when sorting blended objects with different blending modes
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
Builder& material(size_t index, MaterialInstance const* materialInstance) noexcept;
|
||||
// The axis aligned bounding box of the Renderable. Mandatory unless culling is disabled.
|
||||
Builder& boundingBox(const Box& axisAlignedBoundingBox) noexcept;
|
||||
// See View::setVisibleLayers().
|
||||
Builder& layerMask(uint8_t select, uint8_t values) noexcept;
|
||||
// The priority is clamped to the range [0..7], defaults to 4; 7 is lowest priority
|
||||
Builder& priority(uint8_t priority) noexcept;
|
||||
@@ -130,7 +131,10 @@ public:
|
||||
void destroy(utils::Entity e) noexcept;
|
||||
|
||||
void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept;
|
||||
|
||||
// See View::setVisibleLayers
|
||||
void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept;
|
||||
|
||||
void setPriority(Instance instance, uint8_t priority) noexcept;
|
||||
void setCastShadows(Instance instance, bool enable) noexcept;
|
||||
void setReceiveShadows(Instance instance, bool enable) noexcept;
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
size_t getLevels() const noexcept { return mLevels; }
|
||||
Sampler getTarget() const noexcept { return mTarget; }
|
||||
InternalFormat getFormat() const noexcept { return mFormat; }
|
||||
Usage getUsage() const noexcept { return mUsage; }
|
||||
bool isRgbm() const noexcept { return mRgbm; }
|
||||
|
||||
void setImage(FEngine& engine, size_t level,
|
||||
|
||||
@@ -149,13 +149,18 @@ endif()
|
||||
add_library(suzanne-resources ${DUMMY_SRC} ${RESGEN_SOURCE})
|
||||
|
||||
# ==================================================================================================
|
||||
# Resources for gltf_viewer
|
||||
# Resources for the gltf_viewer and lucy demos
|
||||
# ==================================================================================================
|
||||
|
||||
get_resgen_vars(${RESOURCE_DIR} gltf)
|
||||
|
||||
set(GLTF_RESOURCE_FILES
|
||||
${ROOT_DIR}/third_party/models/DamagedHelmet/DamagedHelmet.glb
|
||||
${ROOT_DIR}/third_party/models/lucy/lucy.glb
|
||||
${ROOT_DIR}/third_party/textures/Blue_tiles_01/Blue_tiles_01_AO.png
|
||||
${ROOT_DIR}/third_party/textures/Blue_tiles_01/Blue_tiles_01_Color.png
|
||||
${ROOT_DIR}/third_party/textures/Blue_tiles_01/Blue_tiles_01_Normal.png
|
||||
${ROOT_DIR}/third_party/textures/Blue_tiles_01/Blue_tiles_01_Roughness.png
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
@@ -263,6 +268,10 @@ if (NOT ANDROID)
|
||||
target_link_libraries(suzanne PRIVATE suzanne-resources)
|
||||
target_link_libraries(gltf_viewer PRIVATE gltf-resources gltfio)
|
||||
target_link_libraries(gltf_baker PRIVATE gltf-resources gltfio imageio)
|
||||
|
||||
add_executable(lucy_bloom lucy_bloom.cpp lucy_utils.h lucy_utils.cpp)
|
||||
target_compile_options(lucy_bloom PRIVATE ${COMPILER_FLAGS})
|
||||
target_link_libraries(lucy_bloom PRIVATE ${APP_LIBS} gltf-resources gltfio)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -27,6 +27,7 @@ struct Config {
|
||||
float scale = 1.0f;
|
||||
bool splitView = false;
|
||||
filament::Engine::Backend backend = filament::Engine::Backend::OPENGL;
|
||||
bool resizeable = true;
|
||||
};
|
||||
|
||||
#endif // TNT_FILAMENT_SAMPLE_CONFIG_H
|
||||
|
||||
@@ -371,6 +371,9 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
}
|
||||
|
||||
if (renderer->beginFrame(window->getSwapChain())) {
|
||||
for (filament::View* offscreenView : mOffscreenViews) {
|
||||
renderer->render(offscreenView);
|
||||
}
|
||||
for (auto const& view : window->mViews) {
|
||||
renderer->render(view->getView());
|
||||
}
|
||||
@@ -442,7 +445,10 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
|
||||
: mFilamentApp(filamentApp) {
|
||||
const int x = SDL_WINDOWPOS_CENTERED;
|
||||
const int y = SDL_WINDOWPOS_CENTERED;
|
||||
const uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
if (config.resizeable) {
|
||||
windowFlags |= SDL_WINDOW_RESIZABLE;
|
||||
}
|
||||
mWindow = SDL_CreateWindow(title.c_str(), x, y, (int) w, (int) h, windowFlags);
|
||||
|
||||
// Create the Engine after the window in case this happens to be a single-threaded platform.
|
||||
|
||||
@@ -82,6 +82,8 @@ public:
|
||||
void setSidebarWidth(int width) { mSidebarWidth = width; }
|
||||
void setWindowTitle(const char* title) { mWindowTitle = title; }
|
||||
|
||||
void addOffscreenView(filament::View* view) { mOffscreenViews.push_back(view); }
|
||||
|
||||
size_t getSkippedFrameCount() const { return mSkippedFrames; }
|
||||
|
||||
FilamentApp(const FilamentApp& rhs) = delete;
|
||||
@@ -214,6 +216,7 @@ private:
|
||||
int mSidebarWidth = 0;
|
||||
size_t mSkippedFrames = 0;
|
||||
std::string mWindowTitle;
|
||||
std::vector<filament::View*> mOffscreenViews;
|
||||
};
|
||||
|
||||
#endif // TNT_FILAMENT_SAMPLE_FILAMENTAPP_H
|
||||
|
||||
@@ -253,7 +253,7 @@ static void createQuadRenderable(BakerApp& app) {
|
||||
static OverlayVertex kVertices[4] = {
|
||||
{{0, 0}, {0, 0}}, {{ 1000, 0}, {1, 0}}, {{0, 1000}, {0, 1}}, {{ 1000, 1000}, {1, 1}}
|
||||
};
|
||||
static constexpr uint16_t kInidices[6] = { 0, 1, 2, 3, 2, 1 };
|
||||
static constexpr uint16_t kIndices[6] = { 0, 1, 2, 3, 2, 1 };
|
||||
|
||||
if (!app.overlayQuad.entity) {
|
||||
app.overlayQuad.vb = VertexBuffer::Builder()
|
||||
@@ -267,7 +267,7 @@ static void createQuadRenderable(BakerApp& app) {
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(engine);
|
||||
app.overlayQuad.ib->setBuffer(engine,
|
||||
IndexBuffer::BufferDescriptor(kInidices, 12, nullptr));
|
||||
IndexBuffer::BufferDescriptor(kIndices, 12, nullptr));
|
||||
auto mat = Material::Builder()
|
||||
.package(RESOURCES_AOPREVIEW_DATA, RESOURCES_AOPREVIEW_SIZE)
|
||||
.build(engine);
|
||||
|
||||
404
samples/lucy_bloom.cpp
Normal file
404
samples/lucy_bloom.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "app/Config.h"
|
||||
#include "app/FilamentApp.h"
|
||||
#include "app/IBL.h"
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
#include <filament/LightManager.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/RenderTarget.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Skybox.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lucy_utils.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace gltfio;
|
||||
using namespace utils;
|
||||
|
||||
static constexpr uint32_t FBO_WIDTH = 1024;
|
||||
static constexpr uint32_t FBO_HEIGHT = 1024;
|
||||
static const uint32_t WINDOW_WIDTH = 800;
|
||||
static const uint32_t WINDOW_HEIGHT = 800;
|
||||
|
||||
struct Framebuffer {
|
||||
Camera* camera = nullptr;
|
||||
View* view = nullptr;
|
||||
Scene* scene = nullptr;
|
||||
Texture* color = nullptr;
|
||||
Texture* depth = nullptr;
|
||||
RenderTarget* target = nullptr;
|
||||
};
|
||||
|
||||
struct LucyApp {
|
||||
Config config;
|
||||
bool showQuads;
|
||||
bool showImgui;
|
||||
float iblIntensity = 40000;
|
||||
float iblRotation = M_PI * 1.5f;
|
||||
AssetLoader* loader;
|
||||
FilamentAsset* asset;
|
||||
MaterialProvider* materials;
|
||||
Entity rotationRoot;
|
||||
Entity hudQuads[4];
|
||||
Entity finalQuad;
|
||||
Entity hblurQuad;
|
||||
Entity vblurQuad;
|
||||
Framebuffer reflection;
|
||||
Framebuffer primary;
|
||||
Framebuffer hblur;
|
||||
Framebuffer vblur;
|
||||
Camera* finalCamera;
|
||||
};
|
||||
|
||||
static void setup(LucyApp& app, Engine* engine, View* finalView, Scene* finalScene) {
|
||||
|
||||
app.reflection.color = Texture::Builder()
|
||||
.width(FBO_WIDTH).height(FBO_HEIGHT).levels(1)
|
||||
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
|
||||
.format(Texture::InternalFormat::RGBA8).build(*engine);
|
||||
app.reflection.depth = Texture::Builder()
|
||||
.width(FBO_WIDTH).height(FBO_HEIGHT).levels(1)
|
||||
.usage(Texture::Usage::DEPTH_ATTACHMENT)
|
||||
.format(Texture::InternalFormat::DEPTH24).build(*engine);
|
||||
app.reflection.target = RenderTarget::Builder()
|
||||
.color(app.reflection.color) .depth(app.reflection.depth) .build(*engine);
|
||||
|
||||
app.primary.color = Texture::Builder()
|
||||
.width(FBO_WIDTH).height(FBO_HEIGHT).levels(1)
|
||||
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
|
||||
.format(Texture::InternalFormat::RGBA16F).build(*engine);
|
||||
app.primary.depth = Texture::Builder()
|
||||
.width(FBO_WIDTH).height(FBO_HEIGHT).levels(1)
|
||||
.usage(Texture::Usage::DEPTH_ATTACHMENT)
|
||||
.format(Texture::InternalFormat::DEPTH24).build(*engine);
|
||||
app.primary.target = RenderTarget::Builder()
|
||||
.color(app.primary.color) .depth(app.primary.depth) .build(*engine);
|
||||
|
||||
app.hblur.color = Texture::Builder()
|
||||
.width(FBO_WIDTH).height(FBO_HEIGHT).levels(1)
|
||||
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
|
||||
.format(Texture::InternalFormat::RGBA16F).build(*engine);
|
||||
app.hblur.target = RenderTarget::Builder().color(app.hblur.color).build(*engine);
|
||||
|
||||
app.vblur.color = Texture::Builder()
|
||||
.width(FBO_WIDTH).height(FBO_HEIGHT).levels(1)
|
||||
.usage(Texture::Usage::COLOR_ATTACHMENT | Texture::Usage::SAMPLEABLE)
|
||||
.format(Texture::InternalFormat::RGBA16F).build(*engine);
|
||||
app.vblur.target = RenderTarget::Builder().color(app.vblur.color).build(*engine);
|
||||
|
||||
// Create lights.
|
||||
auto* ibl = FilamentApp::get().getIBL();
|
||||
ibl->getIndirectLight()->setIntensity(app.iblIntensity);
|
||||
ibl->getIndirectLight()->setRotation(mat3f::rotation(app.iblRotation, float3{ 0, 1, 0 }));
|
||||
ibl->getSkybox()->setLayerMask(0xff, 0xff);
|
||||
|
||||
auto sunlight = EntityManager::get().create();
|
||||
LightManager::Builder(LightManager::Type::SUN)
|
||||
.color(Color::toLinear<ACCURATE>({0.98, 0.92, 0.89}))
|
||||
.intensity(50000)
|
||||
.direction(normalize(float3 {0.6, -1.0, -0.8}))
|
||||
.castShadows(true)
|
||||
.sunAngularRadius(1.9)
|
||||
.sunHaloSize(10.0)
|
||||
.sunHaloFalloff(80.0)
|
||||
.build(*engine, sunlight);
|
||||
|
||||
// Create filament entities for the embedded glTF model.
|
||||
app.materials = createMaterialGenerator(engine);
|
||||
app.loader = AssetLoader::create({engine, app.materials });
|
||||
app.asset = app.loader->createAssetFromBinary(RESOURCE_ARGS(LUCY));
|
||||
gltfio::ResourceLoader({ .engine = engine }).loadResources(app.asset);
|
||||
app.asset->releaseSourceData();
|
||||
|
||||
// Tweak the model materials.
|
||||
auto begin = app.asset->getMaterialInstances();
|
||||
auto end = begin + app.asset->getMaterialInstanceCount();
|
||||
for (auto iter = begin; iter != end; ++iter) {
|
||||
(*iter)->setParameter("roughnessFactor", 0.25f);
|
||||
(*iter)->setParameter("baseColorFactor", float4 {1.0, 0.6, 0.5, 1.0});
|
||||
}
|
||||
|
||||
// Transform the model to fit into the unit cube, then parent it under a rotation node.
|
||||
auto& tcm = engine->getTransformManager();
|
||||
auto root = tcm.getInstance(app.asset->getRoot());
|
||||
mat4f transform = LucyUtils::fitIntoUnitCube(app.asset->getBoundingBox());
|
||||
tcm.setTransform(root, transform);
|
||||
app.rotationRoot = EntityManager::get().create();
|
||||
tcm.create(app.rotationRoot);
|
||||
tcm.setParent(root, tcm.getInstance(app.rotationRoot));
|
||||
|
||||
// Create the podium.
|
||||
Entity disk = LucyUtils::createDisk(engine, app.reflection.color);
|
||||
mat4f diskTransform = mat4f::scaling(float3 {1.5f, 1.5, 1.0f});
|
||||
diskTransform = mat4f::rotation(-M_PI / 2, float3 {1, 0, 0}) * diskTransform;
|
||||
diskTransform = mat4f::translation(float3 {0, -1, 0}) * diskTransform;
|
||||
tcm.create(disk, tcm.getInstance(app.rotationRoot), diskTransform);
|
||||
|
||||
// Create full-screen quads.
|
||||
app.finalQuad = LucyUtils::createQuad(engine, app.primary.color, LucyUtils::MIX,
|
||||
app.vblur.color);
|
||||
app.hblurQuad = LucyUtils::createQuad(engine, app.primary.color, LucyUtils::HBLUR);
|
||||
app.vblurQuad = LucyUtils::createQuad(engine, app.hblur.color, LucyUtils::VBLUR);
|
||||
|
||||
mat4f quadScale = mat4f::scaling(float3 {FBO_WIDTH, FBO_HEIGHT, 1});
|
||||
tcm.setTransform(tcm.getInstance(app.hblurQuad), quadScale);
|
||||
tcm.setTransform(tcm.getInstance(app.vblurQuad), quadScale);
|
||||
|
||||
app.reflection.scene = engine->createScene();
|
||||
app.reflection.scene->setIndirectLight(ibl->getIndirectLight());
|
||||
app.reflection.scene->addEntities(app.asset->getEntities(), app.asset->getEntityCount());
|
||||
app.reflection.scene->addEntity(sunlight);
|
||||
|
||||
app.primary.scene = engine->createScene();
|
||||
app.primary.scene->addEntity(disk);
|
||||
app.primary.scene->addEntities(app.asset->getEntities(), app.asset->getEntityCount());
|
||||
app.primary.scene->addEntity(sunlight);
|
||||
app.primary.scene->setSkybox(ibl->getSkybox());
|
||||
app.primary.scene->setIndirectLight(ibl->getIndirectLight());
|
||||
|
||||
app.hblur.scene = engine->createScene();
|
||||
app.hblur.scene->addEntity(app.hblurQuad);
|
||||
app.vblur.scene = engine->createScene();
|
||||
app.vblur.scene->addEntity(app.vblurQuad);
|
||||
|
||||
app.reflection.camera = engine->createCamera();
|
||||
app.reflection.view = engine->createView();
|
||||
app.reflection.view->setName("reflection");
|
||||
app.reflection.view->setRenderTarget(app.reflection.target);
|
||||
app.reflection.view->setClearColor({0, 0, 0, 1});
|
||||
app.reflection.view->setClearTargets(true, true, false);
|
||||
app.reflection.view->setViewport(Viewport(0, 0, FBO_WIDTH, FBO_HEIGHT));
|
||||
app.reflection.view->setScene(app.reflection.scene);
|
||||
app.reflection.view->setCamera(app.reflection.camera);
|
||||
app.reflection.view->setToneMapping(View::ToneMapping::LINEAR);
|
||||
FilamentApp::get().addOffscreenView(app.reflection.view);
|
||||
|
||||
app.primary.camera = engine->createCamera();
|
||||
app.primary.view = engine->createView();
|
||||
app.primary.view->setName("primary");
|
||||
app.primary.view->setRenderTarget(app.primary.target);
|
||||
app.primary.view->setViewport(Viewport(0, 0, FBO_WIDTH, FBO_HEIGHT));
|
||||
app.primary.view->setScene(app.primary.scene);
|
||||
app.primary.view->setCamera(app.primary.camera);
|
||||
app.primary.view->setToneMapping(View::ToneMapping::LINEAR);
|
||||
FilamentApp::get().addOffscreenView(app.primary.view);
|
||||
|
||||
app.hblur.camera = engine->createCamera();
|
||||
app.hblur.view = engine->createView();
|
||||
app.hblur.view->setName("hblur");
|
||||
app.hblur.view->setRenderTarget(app.hblur.target);
|
||||
app.hblur.view->setViewport(Viewport(0, 0, FBO_WIDTH, FBO_HEIGHT));
|
||||
app.hblur.view->setScene(app.hblur.scene);
|
||||
app.hblur.view->setCamera(app.hblur.camera);
|
||||
app.hblur.view->setPostProcessingEnabled(false);
|
||||
FilamentApp::get().addOffscreenView(app.hblur.view);
|
||||
app.hblur.camera->setProjection(Camera::Projection::ORTHO,
|
||||
0.0, FBO_WIDTH, FBO_HEIGHT, 0.0, 0.0, 1.0);
|
||||
|
||||
app.vblur.camera = engine->createCamera();
|
||||
app.vblur.view = engine->createView();
|
||||
app.vblur.view->setName("vblur");
|
||||
app.vblur.view->setRenderTarget(app.vblur.target);
|
||||
app.vblur.view->setViewport(Viewport(0, 0, FBO_WIDTH, FBO_HEIGHT));
|
||||
app.vblur.view->setScene(app.vblur.scene);
|
||||
app.vblur.view->setCamera(app.vblur.camera);
|
||||
app.vblur.view->setPostProcessingEnabled(false);
|
||||
FilamentApp::get().addOffscreenView(app.vblur.view);
|
||||
app.vblur.camera->setProjection(Camera::Projection::ORTHO,
|
||||
0.0, FBO_WIDTH, FBO_HEIGHT, 0.0, 0.0, 1.0);
|
||||
|
||||
app.finalCamera = engine->createCamera();
|
||||
finalScene->addEntity(app.finalQuad);
|
||||
finalScene->setSkybox(nullptr);
|
||||
finalScene->setIndirectLight(nullptr);
|
||||
finalView->setSampleCount(1);
|
||||
finalView->setCamera(app.finalCamera);
|
||||
};
|
||||
|
||||
static float3 reflect(float3 i, float3 n) { return i - 2.0f * dot(n, i) * n; };
|
||||
|
||||
static void animate(LucyApp& app, Engine* engine, View* finalView, double now) {
|
||||
// Rotate the embedded mesh. (use now = -0.5 for a nice screenshot)
|
||||
const mat4f xform = mat4f::rotation(now, float3 {0, 1, 0});
|
||||
auto& tcm = engine->getTransformManager();
|
||||
tcm.setTransform(tcm.getInstance(app.rotationRoot), xform);
|
||||
|
||||
// Adjust the projection in case the window has been resized.
|
||||
const auto viewport = finalView->getViewport();
|
||||
const float aspect = float(viewport.width) / viewport.height;
|
||||
app.primary.camera->setProjection(30, aspect, 1.0, 10.0);
|
||||
app.reflection.camera->setProjection(30, aspect, 1.0, 10.0);
|
||||
app.finalCamera->setProjection(Camera::Projection::ORTHO,
|
||||
0.0, viewport.width, viewport.height, 0.0, 0.0, 1.0);
|
||||
|
||||
// Adjust the size of the final quad in case the window has been resized.
|
||||
mat4f finalQuadSize = mat4f::scaling(float3 {viewport.width, viewport.height, 1});
|
||||
tcm.setTransform(tcm.getInstance(app.finalQuad), finalQuadSize);
|
||||
|
||||
// Primary camera. The target point will be moved along the gazing direction to aid reflection.
|
||||
const float3 eye = {0.0f, 1.5f, 4.0f};
|
||||
float3 target = {0, 0, 0};
|
||||
|
||||
// Find where the central camera ray intersects the podium.
|
||||
const float3 gaze = normalize(target - eye);
|
||||
const float planeD = -1;
|
||||
const float3 planeN = {0, 1, 0};
|
||||
const float t = dot(planeN * planeD - eye, planeN) / dot(gaze, planeN);
|
||||
target = eye + t * gaze;
|
||||
|
||||
// Set up the primary camera.
|
||||
const float3 up = cross(gaze, float3 {-1, 0, 0});
|
||||
app.primary.camera->lookAt(eye, target, up);
|
||||
|
||||
// Find the location of the reflected camera.
|
||||
const float eyedist = length(target - eye);
|
||||
const float3 newgaze = reflect(gaze, planeN);
|
||||
const float3 neweye = target - eyedist * newgaze;
|
||||
const float3 newup = cross(newgaze, float3 {-1, 0, 0});
|
||||
app.reflection.camera->lookAt(neweye, neweye + newgaze, newup);
|
||||
};
|
||||
|
||||
static void drawHud(LucyApp& app, Engine* engine, View* hudView) {
|
||||
Scene* hudScene = hudView->getScene();
|
||||
if (!app.hudQuads[0] && app.showQuads) {
|
||||
auto& tcm = engine->getTransformManager();
|
||||
app.hudQuads[0] = LucyUtils::createQuad(engine, app.reflection.color, LucyUtils::BLIT);
|
||||
app.hudQuads[1] = LucyUtils::createQuad(engine, app.primary.color, LucyUtils::BLIT);
|
||||
app.hudQuads[2] = LucyUtils::createQuad(engine, app.hblur.color, LucyUtils::BLIT);
|
||||
app.hudQuads[3] = LucyUtils::createQuad(engine, app.vblur.color, LucyUtils::BLIT);
|
||||
hudScene->addEntity(app.hudQuads[0]);
|
||||
hudScene->addEntity(app.hudQuads[1]);
|
||||
hudScene->addEntity(app.hudQuads[2]);
|
||||
hudScene->addEntity(app.hudQuads[3]);
|
||||
mat4f scale = mat4f::scaling(float3 {100, 100, 1});
|
||||
tcm.create(app.hudQuads[0], {}, mat4f::translation(float3 {10, 10, 0}) * scale);
|
||||
tcm.create(app.hudQuads[1], {}, mat4f::translation(float3 {10, 120, 0}) * scale);
|
||||
tcm.create(app.hudQuads[2], {}, mat4f::translation(float3 {10, 230, 0}) * scale);
|
||||
tcm.create(app.hudQuads[3], {}, mat4f::translation(float3 {10, 340, 0}) * scale);
|
||||
}
|
||||
if (app.showImgui) {
|
||||
ImGui::SetNextWindowSize(ImVec2(305, 90));
|
||||
ImGui::Begin("Parameters");
|
||||
ImGui::SliderFloat("ibl", &app.iblIntensity, 0.0f, 50000.0f);
|
||||
ImGui::SliderAngle("ibl rotation", &app.iblRotation);
|
||||
ImGui::End();
|
||||
auto* ibl = FilamentApp::get().getIBL();
|
||||
ibl->getIndirectLight()->setIntensity(app.iblIntensity);
|
||||
ibl->getIndirectLight()->setRotation(mat3f::rotation(app.iblRotation, float3{ 0, 1, 0 }));
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup(LucyApp& app, Engine* engine) {
|
||||
engine->destroy(app.hudQuads[0]);
|
||||
engine->destroy(app.hudQuads[1]);
|
||||
engine->destroy(app.hudQuads[2]);
|
||||
engine->destroy(app.hudQuads[3]);
|
||||
|
||||
engine->destroy(app.hblurQuad);
|
||||
engine->destroy(app.finalQuad);
|
||||
|
||||
engine->destroy(app.hblur.camera);
|
||||
engine->destroy(app.hblur.scene);
|
||||
engine->destroy(app.hblur.view);
|
||||
engine->destroy(app.hblur.color);
|
||||
engine->destroy(app.hblur.target);
|
||||
|
||||
engine->destroy(app.vblur.camera);
|
||||
engine->destroy(app.vblur.scene);
|
||||
engine->destroy(app.vblur.view);
|
||||
engine->destroy(app.vblur.color);
|
||||
engine->destroy(app.vblur.target);
|
||||
|
||||
engine->destroy(app.reflection.camera);
|
||||
engine->destroy(app.reflection.scene);
|
||||
engine->destroy(app.reflection.view);
|
||||
engine->destroy(app.reflection.color);
|
||||
engine->destroy(app.reflection.depth);
|
||||
engine->destroy(app.reflection.target);
|
||||
|
||||
engine->destroy(app.primary.camera);
|
||||
engine->destroy(app.primary.scene);
|
||||
engine->destroy(app.primary.view);
|
||||
engine->destroy(app.primary.color);
|
||||
engine->destroy(app.primary.depth);
|
||||
engine->destroy(app.primary.target);
|
||||
|
||||
engine->destroy(app.finalCamera);
|
||||
|
||||
Fence::waitAndDestroy(engine->createFence());
|
||||
app.loader->destroyAsset(app.asset);
|
||||
app.materials->destroyMaterials();
|
||||
delete app.materials;
|
||||
AssetLoader::destroy(&app.loader);
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
static const char* DEFAULT_IBL = "envs/pillars";
|
||||
|
||||
LucyApp app;
|
||||
app.config.title = "Lucy";
|
||||
app.config.iblDirectory = FilamentApp::getRootPath() + DEFAULT_IBL;
|
||||
app.config.resizeable = false;
|
||||
app.showQuads = true;
|
||||
app.showImgui = true;
|
||||
|
||||
// Handle arguments.
|
||||
static constexpr const char* OPTSTR = "i:";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "ibl", required_argument, nullptr, 'i' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
int opt, option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
switch (opt) {
|
||||
case 'i': app.config.iblDirectory = arg; break;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the animation callback.
|
||||
FilamentApp::get().animate(
|
||||
[&app](Engine* engine, View* view, double now) { animate(app, engine, view, now); });
|
||||
|
||||
// Start the app.
|
||||
FilamentApp::get().run(app.config,
|
||||
[&app](Engine* engine, View* view, Scene* scene) { setup(app, engine, view, scene); },
|
||||
[&app](Engine* engine, View*, Scene*) { cleanup(app, engine); },
|
||||
[&app](Engine* engine, View* view) { drawHud(app, engine, view); },
|
||||
FilamentApp::PreRenderCallback(),
|
||||
FilamentApp::PostRenderCallback(),
|
||||
WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
}
|
||||
382
samples/lucy_utils.cpp
Normal file
382
samples/lucy_utils.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "lucy_utils.h"
|
||||
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
|
||||
#include <geometry/SurfaceOrientation.h>
|
||||
|
||||
#include <stb_image.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
|
||||
using filament::geometry::SurfaceOrientation;
|
||||
|
||||
#define FILTER_SIZE 9
|
||||
|
||||
namespace LucyUtils {
|
||||
|
||||
constexpr float M_PIf = float(M_PI);
|
||||
|
||||
Entity createQuad(Engine* engine, Texture* tex, ImageOp op, Texture* secondary) {
|
||||
|
||||
static VertexBuffer* vb = [](Engine& engine) {
|
||||
struct OverlayVertex {
|
||||
float2 position;
|
||||
float2 uv;
|
||||
quath tangent;
|
||||
};
|
||||
static OverlayVertex verts[4] = {
|
||||
{{0, 0}, {0, 0}, {0, 0, 0, 1}},
|
||||
{{1, 0}, {1, 0}, {0, 0, 0, 1}},
|
||||
{{0, 1}, {0, 1}, {0, 0, 0, 1}},
|
||||
{{1, 1}, {1, 1}, {0, 0, 0, 1}}
|
||||
};
|
||||
static float3 normals[4] = {
|
||||
{0, 0, 1},
|
||||
{0, 0, 1},
|
||||
{0, 0, 1},
|
||||
{0, 0, 1},
|
||||
};
|
||||
SurfaceOrientation::Builder()
|
||||
.vertexCount(4)
|
||||
.normals(normals)
|
||||
.build()
|
||||
.getQuats(2 + (quath*) verts, 4, 24);
|
||||
auto vb = VertexBuffer::Builder()
|
||||
.vertexCount(4)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 24)
|
||||
.attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::FLOAT2, 8, 24)
|
||||
.attribute(VertexAttribute::TANGENTS, 0, VertexBuffer::AttributeType::HALF4, 16, 24)
|
||||
.build(engine);
|
||||
vb->setBufferAt(engine, 0, VertexBuffer::BufferDescriptor(verts, sizeof(verts), nullptr));
|
||||
return vb;
|
||||
}(*engine);
|
||||
|
||||
static IndexBuffer* ib = [](Engine& engine) {
|
||||
static constexpr uint16_t indices[6] = { 2, 1, 0, 1, 2, 3 };
|
||||
auto ib = IndexBuffer::Builder()
|
||||
.indexCount(6)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(engine);
|
||||
ib->setBuffer(engine, IndexBuffer::BufferDescriptor(indices, sizeof(indices), nullptr));
|
||||
return ib;
|
||||
}(*engine);
|
||||
|
||||
static Material* blit = [](Engine& engine) {
|
||||
filamat::Package pkg = filamat::MaterialBuilder()
|
||||
.name("blit")
|
||||
.material(R"SHADER(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_color, getUV0());
|
||||
}
|
||||
)SHADER")
|
||||
.require(VertexAttribute::UV0)
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "color")
|
||||
.targetApi(filamat::MaterialBuilder::TargetApi::OPENGL)
|
||||
.shading(Shading::UNLIT)
|
||||
.depthWrite(false)
|
||||
.depthCulling(false)
|
||||
.build();
|
||||
return Material::Builder().package(pkg.getData(), pkg.getSize()).build(engine);
|
||||
}(*engine);
|
||||
|
||||
static Material* mix = [](Engine& engine) {
|
||||
filamat::Package pkg = filamat::MaterialBuilder()
|
||||
.name("mix")
|
||||
.material(R"SHADER(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
vec4 primary = texture(materialParams_color, getUV0());
|
||||
vec4 blurred = texture(materialParams_secondary, getUV0());
|
||||
// HACK: this is a crude bloom effect
|
||||
float L = max(0.0, max(blurred.r, max(blurred.g, blurred.b)) - 1.0);
|
||||
material.baseColor = mix(primary, blurred, min(1.0, L));
|
||||
}
|
||||
)SHADER")
|
||||
.require(VertexAttribute::UV0)
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "color")
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "secondary")
|
||||
.targetApi(filamat::MaterialBuilder::TargetApi::OPENGL)
|
||||
.shading(Shading::UNLIT)
|
||||
.depthWrite(false)
|
||||
.depthCulling(false)
|
||||
.build();
|
||||
return Material::Builder().package(pkg.getData(), pkg.getSize()).build(engine);
|
||||
}(*engine);
|
||||
|
||||
static const float4* weights = []() {
|
||||
static float4 weights[FILTER_SIZE * 2];
|
||||
float4* hweights = weights;
|
||||
float4* vweights = weights + FILTER_SIZE;
|
||||
static const float radius = 2;
|
||||
const auto filter = [](float t) {
|
||||
t /= 2.0;
|
||||
if (t >= 1.0) return 0.0f;
|
||||
const float scale = 1.0f / std::sqrt(0.5f * M_PIf);
|
||||
return std::exp(-2.0f * t * t) * scale;
|
||||
};
|
||||
constexpr float pixelWidth = 2.0f / float(FILTER_SIZE);
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < FILTER_SIZE; i++) {
|
||||
float x = -1.0f + pixelWidth / 2.0f + pixelWidth * i;
|
||||
hweights[i].x = vweights[i].x = filter(std::abs(x));
|
||||
hweights[i].y = vweights[i].z = radius * (i - (FILTER_SIZE - 1) / 2);
|
||||
hweights[i].z = vweights[i].y = 0.0f;
|
||||
sum += weights[i].x;
|
||||
}
|
||||
for (int i = 0; i < FILTER_SIZE; i++) {
|
||||
hweights[i].x /= sum;
|
||||
vweights[i].x /= sum;
|
||||
}
|
||||
return weights;
|
||||
}();
|
||||
static const float4* hweights = weights;
|
||||
static const float4* vweights = weights + FILTER_SIZE;
|
||||
|
||||
static Material* blur = [](Engine& engine) {
|
||||
std::string txt = R"SHADER(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
float2 uv = gl_FragCoord.xy;
|
||||
vec4 c = vec4(0);
|
||||
for (int i = 0; i < FILTER_SIZE; i++) {
|
||||
float2 st = (uv + materialParams.weights[i].yz) * getResolution().zw;
|
||||
c += texture(materialParams_color, st) * materialParams.weights[i].x;
|
||||
}
|
||||
material.baseColor = c;
|
||||
}
|
||||
)SHADER";
|
||||
const std::string from("FILTER_SIZE");
|
||||
const std::string to = std::to_string(FILTER_SIZE);
|
||||
size_t pos = txt.find(from);
|
||||
txt.replace(pos, from.length(), to.c_str());
|
||||
filamat::Package pkg = filamat::MaterialBuilder()
|
||||
.name("hblur")
|
||||
.material(txt.c_str())
|
||||
.require(VertexAttribute::UV0)
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "color")
|
||||
.parameter(filamat::MaterialBuilder::UniformType::FLOAT4, FILTER_SIZE, "weights")
|
||||
.targetApi(filamat::MaterialBuilder::TargetApi::OPENGL)
|
||||
.shading(Shading::UNLIT)
|
||||
.depthWrite(false)
|
||||
.depthCulling(false)
|
||||
.build();
|
||||
return Material::Builder().package(pkg.getData(), pkg.getSize()).build(engine);
|
||||
}(*engine);
|
||||
|
||||
TextureSampler::MinFilter minfilt;
|
||||
TextureSampler::MagFilter magfilt;
|
||||
MaterialInstance* matinstance;
|
||||
switch (op) {
|
||||
case BLIT:
|
||||
minfilt = TextureSampler::MinFilter::LINEAR;
|
||||
magfilt = TextureSampler::MagFilter::LINEAR;
|
||||
matinstance = blit->createInstance();
|
||||
break;
|
||||
case HBLUR:
|
||||
minfilt = TextureSampler::MinFilter::NEAREST;
|
||||
magfilt = TextureSampler::MagFilter::NEAREST;
|
||||
matinstance = blur->createInstance();
|
||||
matinstance->setParameter("weights", hweights, FILTER_SIZE);
|
||||
break;
|
||||
case VBLUR:
|
||||
minfilt = TextureSampler::MinFilter::NEAREST;
|
||||
magfilt = TextureSampler::MagFilter::NEAREST;
|
||||
matinstance = blur->createInstance();
|
||||
matinstance->setParameter("weights", vweights, FILTER_SIZE);
|
||||
break;
|
||||
case MIX:
|
||||
minfilt = TextureSampler::MinFilter::LINEAR;
|
||||
magfilt = TextureSampler::MagFilter::LINEAR;
|
||||
matinstance = mix->createInstance();
|
||||
matinstance->setParameter("secondary", secondary, TextureSampler(minfilt, magfilt));
|
||||
break;
|
||||
}
|
||||
matinstance->setParameter("color", tex, TextureSampler(minfilt, magfilt));
|
||||
|
||||
Entity entity = EntityManager::get().create();
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ 0, 0, 0 }, { 9000, 9000, 9000 }})
|
||||
.material(0, matinstance)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib)
|
||||
.build(*engine, entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity createDisk(Engine* engine, Texture* reflection) {
|
||||
constexpr int nslices = 64;
|
||||
constexpr int nverts = nslices + 2;
|
||||
|
||||
static float4 verts[nverts];
|
||||
int i = 0;
|
||||
while (i < nslices + 1) {
|
||||
float theta = 2.0f * M_PI * i / nslices;
|
||||
verts[i++] = float4 {
|
||||
std::cos(theta), std::sin(theta),
|
||||
0.5 + 0.5 * std::cos(theta), 0.5 + 0.5 * std::sin(theta)
|
||||
};
|
||||
}
|
||||
verts[i] = {0.0f, 0.0f, 0.5f, 0.5f};
|
||||
|
||||
static uint16_t indices[nslices * 3];
|
||||
for (int i = 0, j = 0; i < nslices; ++i) {
|
||||
indices[j++] = nverts - 1;
|
||||
indices[j++] = i;
|
||||
indices[j++] = (i + 1) % (nslices + 1);
|
||||
}
|
||||
|
||||
VertexBuffer* vb = VertexBuffer::Builder()
|
||||
.vertexCount(nverts)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 16)
|
||||
.attribute(VertexAttribute::UV0, 0, VertexBuffer::AttributeType::FLOAT2, 8, 16)
|
||||
.build(*engine);
|
||||
|
||||
vb->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(verts, sizeof(verts), nullptr));
|
||||
|
||||
IndexBuffer* ib = IndexBuffer::Builder()
|
||||
.indexCount(nslices * 3)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*engine);
|
||||
|
||||
ib->setBuffer(*engine, IndexBuffer::BufferDescriptor(indices, sizeof(indices), nullptr));
|
||||
|
||||
filamat::Package pkg = filamat::MaterialBuilder()
|
||||
.name("podium")
|
||||
.material(R"SHADER(
|
||||
void material(inout MaterialInputs material) {
|
||||
vec3 n = texture(materialParams_normal, getUV0()).xyz * 2.0 - 1.0;
|
||||
|
||||
// The blue tiles normal map is very harsh, so we soften the normal vector.
|
||||
material.normal = normalize(n + vec3(0, 0, 5));
|
||||
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_color, getUV0());
|
||||
material.roughness = texture(materialParams_roughness, getUV0()).r;
|
||||
material.ambientOcclusion = texture(materialParams_ao, getUV0()).r;
|
||||
|
||||
float2 uv = gl_FragCoord.xy * getResolution().zw; uv.y = 1.0 - uv.y;
|
||||
vec3 reflectance = texture(materialParams_reflection, uv).xyz;
|
||||
|
||||
// HACK: blend the reflection with the baseColor.
|
||||
material.baseColor.rgb = mix(reflectance, material.baseColor.rgb, 0.75);
|
||||
}
|
||||
)SHADER")
|
||||
.require(VertexAttribute::UV0)
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "normal")
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "color")
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "roughness")
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "ao")
|
||||
.parameter(filamat::MaterialBuilder::SamplerType::SAMPLER_2D, "reflection")
|
||||
.targetApi(filamat::MaterialBuilder::TargetApi::OPENGL)
|
||||
.specularAntiAliasing(true)
|
||||
.shading(Shading::LIT)
|
||||
.build();
|
||||
|
||||
Material* mat = Material::Builder().package(pkg.getData(), pkg.getSize()).build(*engine);
|
||||
MaterialInstance* matinstance = mat->createInstance();
|
||||
|
||||
auto createTexture4 = [engine](const uint8_t* data, int size, bool srgb) {
|
||||
int width, height, nchan;
|
||||
auto texels = stbi_load_from_memory(data, size, &width, &height, &nchan, 4);
|
||||
Texture::PixelBufferDescriptor buffer(texels, size_t(width * height * 4),
|
||||
Texture::Format::RGBA, Texture::Type::UBYTE,
|
||||
(Texture::PixelBufferDescriptor::Callback) &stbi_image_free);
|
||||
auto tex = Texture::Builder()
|
||||
.width(uint32_t(width)).height(uint32_t(height)).levels(1)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||
.format(srgb ? Texture::InternalFormat::SRGB8_A8 : Texture::InternalFormat::RGBA8)
|
||||
.build(*engine);
|
||||
tex->setImage(*engine, 0, std::move(buffer));
|
||||
tex->generateMipmaps(*engine);
|
||||
return tex;
|
||||
};
|
||||
|
||||
auto createTexture1 = [engine](const uint8_t* data, int size) {
|
||||
int width, height, nchan;
|
||||
auto texels = stbi_load_from_memory(data, size, &width, &height, &nchan, 1);
|
||||
Texture::PixelBufferDescriptor buffer(texels, size_t(width * height),
|
||||
Texture::Format::R, Texture::Type::UBYTE,
|
||||
(Texture::PixelBufferDescriptor::Callback) &stbi_image_free);
|
||||
auto tex = Texture::Builder()
|
||||
.width(uint32_t(width)).height(uint32_t(height)).levels(1)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||
.format(Texture::InternalFormat::R8)
|
||||
.build(*engine);
|
||||
tex->setImage(*engine, 0, std::move(buffer));
|
||||
tex->generateMipmaps(*engine);
|
||||
return tex;
|
||||
};
|
||||
|
||||
auto normal = createTexture4(RESOURCE_ARGS(BLUE_TILES_01_NORMAL), false);
|
||||
auto color = createTexture4(RESOURCE_ARGS(BLUE_TILES_01_COLOR), true);
|
||||
auto roughness = createTexture1(RESOURCE_ARGS(BLUE_TILES_01_ROUGHNESS));
|
||||
auto ao = createTexture1(RESOURCE_ARGS(BLUE_TILES_01_AO));
|
||||
|
||||
TextureSampler sampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR,
|
||||
TextureSampler::MagFilter::LINEAR);
|
||||
|
||||
matinstance->setParameter("normal", normal, sampler);
|
||||
matinstance->setParameter("color", color, sampler);
|
||||
matinstance->setParameter("roughness", roughness, sampler);
|
||||
matinstance->setParameter("ao", ao, sampler);
|
||||
matinstance->setParameter("reflection", reflection,
|
||||
TextureSampler(TextureSampler::MagFilter::LINEAR));
|
||||
|
||||
auto entity = EntityManager::get().create();
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ -1, -1, 1 }, { 1, 1, 1 }})
|
||||
.material(0, matinstance)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib)
|
||||
.culling(false)
|
||||
.receiveShadows(true)
|
||||
.castShadows(false)
|
||||
.build(*engine, entity);
|
||||
|
||||
auto& tcm = engine->getTransformManager();
|
||||
tcm.create(entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
mat4f fitIntoUnitCube(const Aabb& bounds) {
|
||||
auto minpt = bounds.min;
|
||||
auto maxpt = bounds.max;
|
||||
float maxExtent = 0;
|
||||
maxExtent = std::max(maxpt.x - minpt.x, maxpt.y - minpt.y);
|
||||
maxExtent = std::max(maxExtent, maxpt.z - minpt.z);
|
||||
float scaleFactor = 2.0f / maxExtent;
|
||||
float3 center = (minpt + maxpt) / 2.0f;
|
||||
return mat4f::scaling(float3(scaleFactor)) * mat4f::translation(-center);
|
||||
}
|
||||
|
||||
} // namespace LucyUtils
|
||||
38
samples/lucy_utils.h
Normal file
38
samples/lucy_utils.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_SAMPLES_LUCY_UTILS_H
|
||||
#define TNT_FILAMENT_SAMPLES_LUCY_UTILS_H
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/Box.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include "generated/resources/gltf.h"
|
||||
|
||||
#define RESOURCE_ARGS(name) GLTF_##name##_DATA, GLTF_##name##_SIZE
|
||||
|
||||
namespace LucyUtils {
|
||||
enum ImageOp { BLIT, HBLUR, VBLUR, MIX };
|
||||
utils::Entity createQuad(filament::Engine* engine, filament::Texture* tex, ImageOp op,
|
||||
filament::Texture* secondary = nullptr);
|
||||
utils::Entity createDisk(filament::Engine* engine, filament::Texture* reflection);
|
||||
filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds);
|
||||
}
|
||||
|
||||
#endif // #define TNT_FILAMENT_SAMPLES_LUCY_UTILS_H
|
||||
BIN
third_party/models/lucy/lucy.glb
vendored
Normal file
BIN
third_party/models/lucy/lucy.glb
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user