bokeh.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052
  1. /*
  2. * Copyright 2021 elven cache. All rights reserved.
  3. * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
  4. */
  5. /*
  6. * Implement bokeh depth of field as described in the blog post here:
  7. * https://web.archive.org/web/20201215123940/https://blog.tuxedolabs.com/2018/05/04/bokeh-depth-of-field-in-single-pass.html
  8. *
  9. * Additionally, implement the optimizations discussed in the closing paragraph.
  10. * Apply the effect in multiple passes. Calculate the circle of confusion and store
  11. * in the alpha channel while downsampling the image. Then compute depth of field
  12. * at this lower res, storing sample size in alpha. Then composite the blurred image,
  13. * based on the sample size. Compositing the lower res like this can lead to blocky
  14. * edges where there's a depth discontinuity and the blur is just enough. May be
  15. * an area to improve on.
  16. *
  17. * Provide an alternate means of determining radius of current sample when blurring.
  18. * I find the blog post's sample pattern to be difficult to directly reason about. It
  19. * is not obvious, given the parameters, how many samples will be taken. And it can
  20. * be very many samples. Though the results are good. The 'sqrt' pattern chosen here
  21. * looks alright and allows for the number of samples to be set directly. If you are
  22. * going to use this in a project, may be worth exploring additional sample patterns.
  23. * And certainly update the shader to remove the pattern choice from inside the
  24. * sample loop.
  25. */
  26. #include <common.h>
  27. #include <camera.h>
  28. #include <bgfx_utils.h>
  29. #include <imgui/imgui.h>
  30. #include <bx/rng.h>
  31. #include <bx/os.h>
  32. namespace {
  33. #define FRAMEBUFFER_RT_COLOR 0
  34. #define FRAMEBUFFER_RT_DEPTH 1
  35. #define FRAMEBUFFER_RENDER_TARGETS 2
  36. enum Meshes
  37. {
  38. MeshCube = 0,
  39. MeshHollowCube
  40. };
  41. static const char * s_meshPaths[] =
  42. {
  43. "meshes/cube.bin",
  44. "meshes/hollowcube.bin"
  45. };
  46. static const float s_meshScale[] =
  47. {
  48. 0.45f,
  49. 0.30f
  50. };
  51. // Vertex decl for our screen space quad (used in deferred rendering)
  52. struct PosTexCoord0Vertex
  53. {
  54. float m_x;
  55. float m_y;
  56. float m_z;
  57. float m_u;
  58. float m_v;
  59. static void init()
  60. {
  61. ms_layout
  62. .begin()
  63. .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
  64. .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
  65. .end();
  66. }
  67. static bgfx::VertexLayout ms_layout;
  68. };
  69. bgfx::VertexLayout PosTexCoord0Vertex::ms_layout;
  70. struct PassUniforms
  71. {
  72. enum { NumVec4 = 4 };
  73. void init() {
  74. u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4, NumVec4);
  75. };
  76. void submit() const {
  77. bgfx::setUniform(u_params, m_params, NumVec4);
  78. }
  79. void destroy() {
  80. bgfx::destroy(u_params);
  81. }
  82. union
  83. {
  84. struct
  85. {
  86. /* 0 */ struct { float m_depthUnpackConsts[2]; float m_frameIdx; float m_lobeRotation; };
  87. /* 1 */ struct { float m_ndcToViewMul[2]; float m_ndcToViewAdd[2]; };
  88. /* 2 */ struct { float m_blurSteps; float m_lobeCount; float m_lobeRadiusMin; float m_lobeRadiusDelta2x; };
  89. /* 3 */ struct { float m_maxBlurSize; float m_focusPoint; float m_focusScale; float m_radiusScale; };
  90. };
  91. float m_params[NumVec4 * 4];
  92. };
  93. bgfx::UniformHandle u_params;
  94. };
  95. struct ModelUniforms
  96. {
  97. enum { NumVec4 = 2 };
  98. void init() {
  99. u_params = bgfx::createUniform("u_modelParams", bgfx::UniformType::Vec4, NumVec4);
  100. };
  101. void submit() const {
  102. bgfx::setUniform(u_params, m_params, NumVec4);
  103. };
  104. void destroy() {
  105. bgfx::destroy(u_params);
  106. }
  107. union
  108. {
  109. struct
  110. {
  111. /* 0 */ struct { float m_color[3]; float m_unused0; };
  112. /* 1 */ struct { float m_lightPosition[3]; float m_unused1; };
  113. };
  114. float m_params[NumVec4 * 4];
  115. };
  116. bgfx::UniformHandle u_params;
  117. };
  118. struct RenderTarget
  119. {
  120. void init(uint32_t _width, uint32_t _height, bgfx::TextureFormat::Enum _format, uint64_t _flags)
  121. {
  122. m_texture = bgfx::createTexture2D(uint16_t(_width), uint16_t(_height), false, 1, _format, _flags);
  123. const bool destroyTextures = true;
  124. m_buffer = bgfx::createFrameBuffer(1, &m_texture, destroyTextures);
  125. }
  126. void destroy()
  127. {
  128. // also responsible for destroying texture
  129. bgfx::destroy(m_buffer);
  130. }
  131. bgfx::TextureHandle m_texture;
  132. bgfx::FrameBufferHandle m_buffer;
  133. };
  134. void screenSpaceQuad(float _textureWidth, float _textureHeight, float _texelHalf, bool _originBottomLeft, float _width = 1.0f, float _height = 1.0f)
  135. {
  136. if (3 == bgfx::getAvailTransientVertexBuffer(3, PosTexCoord0Vertex::ms_layout))
  137. {
  138. bgfx::TransientVertexBuffer vb;
  139. bgfx::allocTransientVertexBuffer(&vb, 3, PosTexCoord0Vertex::ms_layout);
  140. PosTexCoord0Vertex* vertex = (PosTexCoord0Vertex*)vb.data;
  141. const float minx = -_width;
  142. const float maxx = _width;
  143. const float miny = 0.0f;
  144. const float maxy = _height * 2.0f;
  145. const float texelHalfW = _texelHalf / _textureWidth;
  146. const float texelHalfH = _texelHalf / _textureHeight;
  147. const float minu = -1.0f + texelHalfW;
  148. const float maxu = 1.0f + texelHalfW;
  149. const float zz = 0.0f;
  150. float minv = texelHalfH;
  151. float maxv = 2.0f + texelHalfH;
  152. if (_originBottomLeft)
  153. {
  154. float temp = minv;
  155. minv = maxv;
  156. maxv = temp;
  157. minv -= 1.0f;
  158. maxv -= 1.0f;
  159. }
  160. vertex[0].m_x = minx;
  161. vertex[0].m_y = miny;
  162. vertex[0].m_z = zz;
  163. vertex[0].m_u = minu;
  164. vertex[0].m_v = minv;
  165. vertex[1].m_x = maxx;
  166. vertex[1].m_y = miny;
  167. vertex[1].m_z = zz;
  168. vertex[1].m_u = maxu;
  169. vertex[1].m_v = minv;
  170. vertex[2].m_x = maxx;
  171. vertex[2].m_y = maxy;
  172. vertex[2].m_z = zz;
  173. vertex[2].m_u = maxu;
  174. vertex[2].m_v = maxv;
  175. bgfx::setVertexBuffer(0, &vb);
  176. }
  177. }
  178. void vec2Set(float* _v, float _x, float _y)
  179. {
  180. _v[0] = _x;
  181. _v[1] = _y;
  182. }
  183. class ExampleBokeh : public entry::AppI
  184. {
  185. public:
  186. ExampleBokeh(const char* _name, const char* _description)
  187. : entry::AppI(_name, _description)
  188. , m_currFrame(UINT32_MAX)
  189. , m_texelHalf(0.0f)
  190. {
  191. }
  192. void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
  193. {
  194. Args args(_argc, _argv);
  195. m_width = _width;
  196. m_height = _height;
  197. m_debug = BGFX_DEBUG_NONE;
  198. m_reset = BGFX_RESET_VSYNC;
  199. bgfx::Init init;
  200. init.type = args.m_type;
  201. init.vendorId = args.m_pciId;
  202. init.platformData.nwh = entry::getNativeWindowHandle(entry::kDefaultWindowHandle);
  203. init.platformData.ndt = entry::getNativeDisplayHandle();
  204. init.platformData.type = entry::getNativeWindowHandleType(entry::kDefaultWindowHandle);
  205. init.resolution.width = m_width;
  206. init.resolution.height = m_height;
  207. init.resolution.reset = m_reset;
  208. bgfx::init(init);
  209. // Enable debug text.
  210. bgfx::setDebug(m_debug);
  211. // Create uniforms for screen passes and models
  212. m_uniforms.init();
  213. m_modelUniforms.init();
  214. // Create texture sampler uniforms (used when we bind textures)
  215. s_albedo = bgfx::createUniform("s_albedo", bgfx::UniformType::Sampler);
  216. s_color = bgfx::createUniform("s_color", bgfx::UniformType::Sampler);
  217. s_normal = bgfx::createUniform("s_normal", bgfx::UniformType::Sampler);
  218. s_depth = bgfx::createUniform("s_depth", bgfx::UniformType::Sampler);
  219. s_blurredColor = bgfx::createUniform("s_blurredColor", bgfx::UniformType::Sampler);
  220. // Create program from shaders.
  221. m_forwardProgram = loadProgram("vs_bokeh_forward", "fs_bokeh_forward");
  222. m_gridProgram = loadProgram("vs_bokeh_forward", "fs_bokeh_forward_grid");
  223. m_copyProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_copy");
  224. m_copyLinearToGammaProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_copy_linear_to_gamma");
  225. m_linearDepthProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_linear_depth");
  226. m_dofSinglePassProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_dof_single_pass");
  227. m_dofDownsampleProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_dof_downsample");
  228. m_dofQuarterProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_dof_second_pass");
  229. m_dofCombineProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_dof_combine");
  230. m_dofDebugProgram = loadProgram("vs_bokeh_screenquad", "fs_bokeh_dof_debug");
  231. // Load some meshes
  232. for (uint32_t ii = 0; ii < BX_COUNTOF(s_meshPaths); ++ii)
  233. {
  234. m_meshes[ii] = meshLoad(s_meshPaths[ii]);
  235. }
  236. m_groundTexture = loadTexture("textures/fieldstone-rgba.dds");
  237. m_normalTexture = loadTexture("textures/fieldstone-n.dds");
  238. m_recreateFrameBuffers = false;
  239. createFramebuffers();
  240. // Vertex decl
  241. PosTexCoord0Vertex::init();
  242. // Init camera
  243. cameraCreate();
  244. cameraSetPosition({ 0.0f, 2.5f, -20.0f });
  245. cameraSetVerticalAngle(-0.3f);
  246. m_fovY = 60.0f;
  247. // Init "prev" matrices, will be same for first frame
  248. cameraGetViewMtx(m_view);
  249. bx::mtxProj(m_proj, m_fovY, float(m_size[0]) / float(m_size[1]), 0.01f, 100.0f, bgfx::getCaps()->homogeneousDepth);
  250. // Get renderer capabilities info.
  251. const bgfx::RendererType::Enum renderer = bgfx::getRendererType();
  252. m_texelHalf = bgfx::RendererType::Direct3D9 == renderer ? 0.5f : 0.0f;
  253. m_bokehTexture.idx = bgfx::kInvalidHandle;
  254. updateDisplayBokehTexture(m_radiusScale, m_maxBlurSize, m_lobeCount, (1.0f-m_lobePinch), 1.0f, m_lobeRotation);
  255. imguiCreate();
  256. }
  257. int32_t shutdown() override
  258. {
  259. for (uint32_t ii = 0; ii < BX_COUNTOF(s_meshPaths); ++ii)
  260. {
  261. meshUnload(m_meshes[ii]);
  262. }
  263. bgfx::destroy(m_normalTexture);
  264. bgfx::destroy(m_groundTexture);
  265. bgfx::destroy(m_bokehTexture);
  266. bgfx::destroy(m_forwardProgram);
  267. bgfx::destroy(m_gridProgram);
  268. bgfx::destroy(m_copyProgram);
  269. bgfx::destroy(m_copyLinearToGammaProgram);
  270. bgfx::destroy(m_linearDepthProgram);
  271. bgfx::destroy(m_dofSinglePassProgram);
  272. bgfx::destroy(m_dofDownsampleProgram);
  273. bgfx::destroy(m_dofQuarterProgram);
  274. bgfx::destroy(m_dofCombineProgram);
  275. bgfx::destroy(m_dofDebugProgram);
  276. m_uniforms.destroy();
  277. m_modelUniforms.destroy();
  278. bgfx::destroy(s_albedo);
  279. bgfx::destroy(s_color);
  280. bgfx::destroy(s_normal);
  281. bgfx::destroy(s_depth);
  282. bgfx::destroy(s_blurredColor);
  283. destroyFramebuffers();
  284. cameraDestroy();
  285. imguiDestroy();
  286. bgfx::shutdown();
  287. return 0;
  288. }
  289. bool update() override
  290. {
  291. if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState))
  292. {
  293. // skip processing when minimized, otherwise crashing
  294. if (0 == m_width || 0 == m_height)
  295. {
  296. return true;
  297. }
  298. // Update frame timer
  299. int64_t now = bx::getHPCounter();
  300. static int64_t last = now;
  301. const int64_t frameTime = now - last;
  302. last = now;
  303. const double freq = double(bx::getHPFrequency());
  304. const float deltaTime = float(frameTime / freq);
  305. const bgfx::Caps* caps = bgfx::getCaps();
  306. if (m_size[0] != (int32_t)m_width
  307. || m_size[1] != (int32_t)m_height
  308. || m_recreateFrameBuffers)
  309. {
  310. destroyFramebuffers();
  311. createFramebuffers();
  312. m_recreateFrameBuffers = false;
  313. }
  314. // update animation time
  315. const float rotationSpeed = 0.75f;
  316. m_animationTime += deltaTime * rotationSpeed;
  317. if (bx::kPi2 < m_animationTime)
  318. {
  319. m_animationTime -= bx::kPi2;
  320. }
  321. // Update camera
  322. cameraUpdate(deltaTime*0.15f, m_mouseState, ImGui::MouseOverArea() );
  323. cameraGetViewMtx(m_view);
  324. updateUniforms();
  325. bx::mtxProj(m_proj, m_fovY, float(m_size[0]) / float(m_size[1]), 0.01f, 100.0f, caps->homogeneousDepth);
  326. bx::mtxProj(m_proj2, m_fovY, float(m_size[0]) / float(m_size[1]), 0.01f, 100.0f, false);
  327. bgfx::ViewId view = 0;
  328. // Draw models into scene
  329. {
  330. bgfx::setViewName(view, "forward scene");
  331. bgfx::setViewClear(view
  332. , BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH
  333. , 0x7fb8ffff // clear to a sky blue
  334. , 1.0f
  335. , 0
  336. );
  337. bgfx::setViewRect(view, 0, 0, uint16_t(m_size[0]), uint16_t(m_size[1]));
  338. bgfx::setViewTransform(view, m_view, m_proj);
  339. bgfx::setViewFrameBuffer(view, m_frameBuffer);
  340. bgfx::setState(0
  341. | BGFX_STATE_WRITE_RGB
  342. | BGFX_STATE_WRITE_A
  343. | BGFX_STATE_WRITE_Z
  344. | BGFX_STATE_DEPTH_TEST_LESS
  345. );
  346. drawAllModels(view, m_forwardProgram, m_modelUniforms);
  347. ++view;
  348. }
  349. float orthoProj[16];
  350. bx::mtxOrtho(orthoProj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, caps->homogeneousDepth);
  351. {
  352. // clear out transform stack
  353. float identity[16];
  354. bx::mtxIdentity(identity);
  355. bgfx::setTransform(identity);
  356. }
  357. // Convert depth to linear depth for shadow depth compare
  358. {
  359. bgfx::setViewName(view, "linear depth");
  360. bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height));
  361. bgfx::setViewTransform(view, NULL, orthoProj);
  362. bgfx::setViewFrameBuffer(view, m_linearDepth.m_buffer);
  363. bgfx::setState(0
  364. | BGFX_STATE_WRITE_RGB
  365. | BGFX_STATE_WRITE_A
  366. | BGFX_STATE_DEPTH_TEST_ALWAYS
  367. );
  368. bgfx::setTexture(0, s_depth, m_frameBufferTex[FRAMEBUFFER_RT_DEPTH]);
  369. m_uniforms.submit();
  370. screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft);
  371. bgfx::submit(view, m_linearDepthProgram);
  372. ++view;
  373. }
  374. // optionally, apply dof
  375. const bool useOrDebugDof = m_useBokehDof || m_showDebugVisualization;
  376. if (useOrDebugDof)
  377. {
  378. view = drawDepthOfField(view, m_frameBufferTex[FRAMEBUFFER_RT_COLOR], orthoProj, caps->originBottomLeft);
  379. }
  380. else
  381. {
  382. bgfx::setViewName(view, "display");
  383. bgfx::setViewClear(view
  384. , BGFX_CLEAR_NONE
  385. , 0
  386. , 1.0f
  387. , 0
  388. );
  389. bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height));
  390. bgfx::setViewTransform(view, NULL, orthoProj);
  391. bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE);
  392. bgfx::setState(0
  393. | BGFX_STATE_WRITE_RGB
  394. | BGFX_STATE_WRITE_A
  395. );
  396. bgfx::setTexture(0, s_color, m_frameBufferTex[FRAMEBUFFER_RT_COLOR]);
  397. screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft);
  398. bgfx::submit(view, m_copyLinearToGammaProgram);
  399. ++view;
  400. }
  401. // Draw UI
  402. imguiBeginFrame(m_mouseState.m_mx
  403. , m_mouseState.m_my
  404. , (m_mouseState.m_buttons[entry::MouseButton::Left] ? IMGUI_MBUT_LEFT : 0)
  405. | (m_mouseState.m_buttons[entry::MouseButton::Right] ? IMGUI_MBUT_RIGHT : 0)
  406. | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
  407. , m_mouseState.m_mz
  408. , uint16_t(m_width)
  409. , uint16_t(m_height)
  410. );
  411. showExampleDialog(this);
  412. ImGui::SetNextWindowPos(
  413. ImVec2(m_width - m_width / 4.0f - 10.0f, 10.0f)
  414. , ImGuiCond_FirstUseEver
  415. );
  416. ImGui::SetNextWindowSize(
  417. ImVec2(m_width / 4.0f, m_height / 1.35f)
  418. , ImGuiCond_FirstUseEver
  419. );
  420. ImGui::Begin("Settings"
  421. , NULL
  422. , 0
  423. );
  424. ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f);
  425. {
  426. ImGui::Checkbox("use bokeh dof", &m_useBokehDof);
  427. if (ImGui::IsItemHovered())
  428. ImGui::SetTooltip("turn effect on and off");
  429. ImGui::Checkbox("use single pass at full res", &m_useSinglePassBokehDof);
  430. if (ImGui::IsItemHovered())
  431. {
  432. ImGui::BeginTooltip();
  433. ImGui::Text("calculate in a single pass at full resolution or use");
  434. ImGui::Text("multiple passes to compute at lower res and composite");
  435. ImGui::EndTooltip();
  436. }
  437. ImGui::Checkbox("show debug vis", &m_showDebugVisualization);
  438. if (ImGui::IsItemHovered())
  439. {
  440. ImGui::BeginTooltip();
  441. ImGui::Text("apply coloration to screen. fades from grey to orange with");
  442. ImGui::Text("increasing foreground blur. from grey to blue in background");
  443. ImGui::EndTooltip();
  444. }
  445. ImGui::Separator();
  446. bool isChanged = false;
  447. ImGui::Text("blur controls:");
  448. isChanged |= ImGui::SliderFloat("max blur size", &m_maxBlurSize, 10.0f, 50.0f);
  449. if (ImGui::IsItemHovered())
  450. ImGui::SetTooltip("maximum blur size in screen pixels");
  451. ImGui::SliderFloat("focusPoint", &m_focusPoint, 1.0f, 20.0f);
  452. if (ImGui::IsItemHovered())
  453. ImGui::SetTooltip("distance to focus plane");
  454. ImGui::SliderFloat("focusScale", &m_focusScale, 0.0f, 10.0f);
  455. if (ImGui::IsItemHovered())
  456. ImGui::SetTooltip("multiply focus calculation, larger=tighter focus");
  457. ImGui::Separator();
  458. ImGui::Text("bokeh shape and sample controls:");
  459. isChanged |= ImGui::SliderFloat("radiusScale", &m_radiusScale, 0.5f, 4.0f);
  460. if (ImGui::IsItemHovered())
  461. ImGui::SetTooltip("controls number of samples taken");
  462. isChanged |= ImGui::SliderInt("lobe count", &m_lobeCount, 1, 8);
  463. if (ImGui::IsItemHovered())
  464. ImGui::SetTooltip("using triangle lobes to emulate aperture blades");
  465. isChanged |= ImGui::SliderFloat("lobe pinch", &m_lobePinch, 0.0f, 1.0f);
  466. if (ImGui::IsItemHovered())
  467. ImGui::SetTooltip("adjust lobe shape, 0=round, 1=starry");
  468. isChanged |= ImGui::SliderFloat("lobe rotation", &m_lobeRotation, -1.0f, 1.0f);
  469. if (isChanged)
  470. {
  471. updateDisplayBokehTexture(m_radiusScale, m_maxBlurSize, m_lobeCount, (1.0f-m_lobePinch), 1.0f, m_lobeRotation);
  472. }
  473. ImGui::Text("number of samples taken: %d", m_sampleCount);
  474. if (ImGui::IsItemHovered())
  475. ImGui::SetTooltip("number of sample taps as determined by radiusScale and maxBlurSize");
  476. ImGui::Image(m_bokehTexture, ImVec2(128.0f, 128.0f) );
  477. }
  478. ImGui::End();
  479. imguiEndFrame();
  480. // Advance to next frame. Rendering thread will be kicked to
  481. // process submitted rendering primitives.
  482. m_currFrame = bgfx::frame();
  483. return true;
  484. }
  485. return false;
  486. }
  487. void drawAllModels(bgfx::ViewId _pass, bgfx::ProgramHandle _program, ModelUniforms & _uniforms)
  488. {
  489. const int32_t width = 6;
  490. const int32_t length = 20;
  491. float c0[] = { 72.0f/255.0f, 126.0f/255.0f, 149.0f/255.0f }; // blue
  492. float c1[] = { 235.0f/255.0f, 146.0f/255.0f, 251.0f/255.0f }; // purple
  493. float c2[] = { 199.0f/255.0f, 0.0f/255.0f, 57.0f/255.0f }; // pink
  494. for (int32_t zz = 0; zz < length; ++zz)
  495. {
  496. // make a color gradient, nothing special about this for example
  497. float * ca = c0;
  498. float * cb = c1;
  499. float lerpVal = float(zz) / float(length);
  500. if (0.5f <= lerpVal)
  501. {
  502. ca = c1;
  503. cb = c2;
  504. }
  505. lerpVal = bx::fract(2.0f*lerpVal);
  506. float r = bx::lerp(ca[0], cb[0], lerpVal);
  507. float g = bx::lerp(ca[1], cb[1], lerpVal);
  508. float b = bx::lerp(ca[2], cb[2], lerpVal);
  509. for (int32_t xx = 0; xx < width; ++xx)
  510. {
  511. const float angle = m_animationTime + float(zz)*(bx::kPi2/length) + float(xx)*(bx::kPiHalf/width);
  512. const float posX = 2.0f * xx - width + 1.0f;
  513. const float posY = bx::sin(angle);
  514. const float posZ = 2.0f * zz - length + 1.0f;
  515. const float scale = s_meshScale[MeshHollowCube];
  516. float mtx[16];
  517. bx::mtxSRT(mtx
  518. , scale
  519. , scale
  520. , scale
  521. , 0.0f
  522. , 0.0f
  523. , 0.0f
  524. , posX
  525. , posY
  526. , posZ
  527. );
  528. bgfx::setTexture(0, s_albedo, m_groundTexture);
  529. bgfx::setTexture(1, s_normal, m_normalTexture);
  530. _uniforms.m_color[0] = r;
  531. _uniforms.m_color[1] = g;
  532. _uniforms.m_color[2] = b;
  533. _uniforms.submit();
  534. meshSubmit(m_meshes[MeshHollowCube], _pass, _program, mtx);
  535. }
  536. }
  537. // draw box as ground plane
  538. {
  539. const float posY = -2.0f;
  540. const float scale = length;
  541. float mtx[16];
  542. bx::mtxSRT(mtx
  543. , scale
  544. , scale
  545. , scale
  546. , 0.0f
  547. , 0.0f
  548. , 0.0f
  549. , 0.0f
  550. , -scale + posY
  551. , 0.0f
  552. );
  553. _uniforms.m_color[0] = 0.5f;
  554. _uniforms.m_color[1] = 0.5f;
  555. _uniforms.m_color[2] = 0.5f;
  556. _uniforms.submit();
  557. meshSubmit(m_meshes[MeshCube], _pass, m_gridProgram, mtx);
  558. }
  559. }
  560. bgfx::ViewId drawDepthOfField(bgfx::ViewId _pass, bgfx::TextureHandle _colorTexture, float* _orthoProj, bool _originBottomLeft)
  561. {
  562. bgfx::ViewId view = _pass;
  563. bgfx::TextureHandle lastTex = _colorTexture;
  564. if (m_showDebugVisualization)
  565. {
  566. bgfx::setViewName(view, "bokeh dof debug pass");
  567. bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height));
  568. bgfx::setViewTransform(view, NULL, _orthoProj);
  569. bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE);
  570. bgfx::setState(0
  571. | BGFX_STATE_WRITE_RGB
  572. | BGFX_STATE_WRITE_A
  573. | BGFX_STATE_DEPTH_TEST_ALWAYS
  574. );
  575. bgfx::setTexture(0, s_color, lastTex);
  576. bgfx::setTexture(1, s_depth, m_linearDepth.m_texture);
  577. m_uniforms.submit();
  578. screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, _originBottomLeft);
  579. bgfx::submit(view, m_dofDebugProgram);
  580. ++view;
  581. }
  582. else if (m_useSinglePassBokehDof)
  583. {
  584. bgfx::setViewName(view, "bokeh dof single pass");
  585. bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height));
  586. bgfx::setViewTransform(view, NULL, _orthoProj);
  587. bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE);
  588. bgfx::setState(0
  589. | BGFX_STATE_WRITE_RGB
  590. | BGFX_STATE_WRITE_A
  591. | BGFX_STATE_DEPTH_TEST_ALWAYS
  592. );
  593. bgfx::setTexture(0, s_color, lastTex);
  594. bgfx::setTexture(1, s_depth, m_linearDepth.m_texture);
  595. m_uniforms.submit();
  596. screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, _originBottomLeft);
  597. bgfx::submit(view, m_dofSinglePassProgram);
  598. ++view;
  599. }
  600. else
  601. {
  602. unsigned halfWidth = (m_width/2);
  603. unsigned halfHeight = (m_height/2);
  604. bgfx::setViewName(view, "bokeh dof downsample");
  605. bgfx::setViewRect(view, 0, 0, uint16_t(halfWidth), uint16_t(halfHeight));
  606. bgfx::setViewTransform(view, NULL, _orthoProj);
  607. bgfx::setViewFrameBuffer(view, m_dofQuarterInput.m_buffer);
  608. bgfx::setState(0
  609. | BGFX_STATE_WRITE_RGB
  610. | BGFX_STATE_WRITE_A
  611. | BGFX_STATE_DEPTH_TEST_ALWAYS
  612. );
  613. bgfx::setTexture(0, s_color, lastTex);
  614. bgfx::setTexture(1, s_depth, m_linearDepth.m_texture);
  615. m_uniforms.submit();
  616. screenSpaceQuad(float(halfWidth), float(halfHeight), m_texelHalf, _originBottomLeft);
  617. bgfx::submit(view, m_dofDownsampleProgram);
  618. ++view;
  619. lastTex = m_dofQuarterInput.m_texture;
  620. /*
  621. replace the copy with bokeh dof combine
  622. able to read circle of confusion and color from downsample pass
  623. along with full res color and depth?
  624. do we need half res depth? i'm confused about that...
  625. */
  626. bgfx::setViewName(view, "bokeh dof quarter");
  627. bgfx::setViewRect(view, 0, 0, uint16_t(halfWidth), uint16_t(halfHeight));
  628. bgfx::setViewTransform(view, NULL, _orthoProj);
  629. bgfx::setViewFrameBuffer(view, m_dofQuarterOutput.m_buffer);
  630. bgfx::setState(0
  631. | BGFX_STATE_WRITE_RGB
  632. | BGFX_STATE_WRITE_A
  633. | BGFX_STATE_DEPTH_TEST_ALWAYS
  634. );
  635. bgfx::setTexture(0, s_color, lastTex);
  636. m_uniforms.submit();
  637. screenSpaceQuad(float(halfWidth), float(halfHeight), m_texelHalf, _originBottomLeft);
  638. bgfx::submit(view, m_dofQuarterProgram);
  639. ++view;
  640. lastTex = m_dofQuarterOutput.m_texture;
  641. bgfx::setViewName(view, "bokeh dof combine");
  642. bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height));
  643. bgfx::setViewTransform(view, NULL, _orthoProj);
  644. bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE);
  645. bgfx::setState(0
  646. | BGFX_STATE_WRITE_RGB
  647. | BGFX_STATE_WRITE_A
  648. | BGFX_STATE_DEPTH_TEST_ALWAYS
  649. );
  650. bgfx::setTexture(0, s_color, _colorTexture);
  651. bgfx::setTexture(1, s_blurredColor, lastTex);
  652. m_uniforms.submit();
  653. screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, _originBottomLeft);
  654. bgfx::submit(view, m_dofCombineProgram);
  655. ++view;
  656. }
  657. return view;
  658. }
  659. void createFramebuffers()
  660. {
  661. m_size[0] = m_width;
  662. m_size[1] = m_height;
  663. const uint64_t bilinearFlags = 0
  664. | BGFX_TEXTURE_RT
  665. | BGFX_SAMPLER_U_CLAMP
  666. | BGFX_SAMPLER_V_CLAMP
  667. ;
  668. m_frameBufferTex[FRAMEBUFFER_RT_COLOR] = bgfx::createTexture2D(uint16_t(m_size[0]), uint16_t(m_size[1]), false, 1, bgfx::TextureFormat::RGBA16F, bilinearFlags);
  669. m_frameBufferTex[FRAMEBUFFER_RT_DEPTH] = bgfx::createTexture2D(uint16_t(m_size[0]), uint16_t(m_size[1]), false, 1, bgfx::TextureFormat::D32F, bilinearFlags);
  670. m_frameBuffer = bgfx::createFrameBuffer(BX_COUNTOF(m_frameBufferTex), m_frameBufferTex, true);
  671. m_linearDepth.init(m_size[0], m_size[1], bgfx::TextureFormat::R16F, bilinearFlags);
  672. unsigned halfWidth = m_size[0]/2;
  673. unsigned halfHeight = m_size[1]/2;
  674. m_dofQuarterInput.init(halfWidth, halfHeight, bgfx::TextureFormat::RGBA16F, bilinearFlags);
  675. m_dofQuarterOutput.init(halfWidth, halfHeight, bgfx::TextureFormat::RGBA16F, bilinearFlags);
  676. }
  677. // all buffers set to destroy their textures
  678. void destroyFramebuffers()
  679. {
  680. bgfx::destroy(m_frameBuffer);
  681. m_linearDepth.destroy();
  682. m_dofQuarterInput.destroy();
  683. m_dofQuarterOutput.destroy();
  684. }
  685. void updateUniforms()
  686. {
  687. // from assao sample, cs_assao_prepare_depths.sc
  688. {
  689. // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear );
  690. // float depthLinearizeAdd = clipFar / ( clipFar - clipNear );
  691. // correct the handedness issue. need to make sure this below is correct, but I think it is.
  692. float depthLinearizeMul = -m_proj2[3*4+2];
  693. float depthLinearizeAdd = m_proj2[2*4+2];
  694. if (depthLinearizeMul * depthLinearizeAdd < 0)
  695. {
  696. depthLinearizeAdd = -depthLinearizeAdd;
  697. }
  698. vec2Set(m_uniforms.m_depthUnpackConsts, depthLinearizeMul, depthLinearizeAdd);
  699. float tanHalfFOVY = 1.0f / m_proj2[1*4+1]; // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f );
  700. float tanHalfFOVX = 1.0F / m_proj2[0]; // = tanHalfFOVY * drawContext.Camera.GetAspect( );
  701. if (bgfx::getRendererType() == bgfx::RendererType::OpenGL)
  702. {
  703. vec2Set(m_uniforms.m_ndcToViewMul, tanHalfFOVX * 2.0f, tanHalfFOVY * 2.0f);
  704. vec2Set(m_uniforms.m_ndcToViewAdd, tanHalfFOVX * -1.0f, tanHalfFOVY * -1.0f);
  705. }
  706. else
  707. {
  708. vec2Set(m_uniforms.m_ndcToViewMul, tanHalfFOVX * 2.0f, tanHalfFOVY * -2.0f);
  709. vec2Set(m_uniforms.m_ndcToViewAdd, tanHalfFOVX * -1.0f, tanHalfFOVY * 1.0f);
  710. }
  711. }
  712. m_uniforms.m_frameIdx = float(m_currFrame % 8);
  713. {
  714. float lightPosition[] = { 0.0f, 6.0f, 10.0f };
  715. bx::memCopy(m_modelUniforms.m_lightPosition, lightPosition, 3*sizeof(float));
  716. }
  717. // bokeh depth of field
  718. {
  719. // reduce dimensions by half to go along with smaller render target
  720. const float blurScale = (m_useSinglePassBokehDof) ? 1.0f : 0.5f;
  721. m_uniforms.m_blurSteps = m_blurSteps;
  722. m_uniforms.m_lobeCount = float(m_lobeCount);
  723. m_uniforms.m_lobeRadiusMin = (1.0f - m_lobePinch);
  724. m_uniforms.m_lobeRadiusDelta2x = 2.0f * m_lobePinch;
  725. m_uniforms.m_maxBlurSize = m_maxBlurSize * blurScale;
  726. m_uniforms.m_focusPoint = m_focusPoint;
  727. m_uniforms.m_focusScale = m_focusScale;
  728. m_uniforms.m_radiusScale = m_radiusScale * blurScale;
  729. m_uniforms.m_lobeRotation = m_lobeRotation;
  730. }
  731. }
  732. static float bokehShapeFromAngle (int _lobeCount, float _radiusMin, float _radiusDelta2x, float _rotation, float _theta)
  733. {
  734. // don't shape for 0, 1 blades...
  735. if (_lobeCount <= 1)
  736. {
  737. return 1.0f;
  738. }
  739. // divide edge into some number of lobes
  740. const float invPeriod = float(_lobeCount) / (bx::kPi2);
  741. float periodFraction = bx::fract(_theta * invPeriod + _rotation);
  742. // apply triangle shape to each lobe to approximate blades of a camera aperture
  743. periodFraction = bx::abs(periodFraction - 0.5f);
  744. return periodFraction * _radiusDelta2x + _radiusMin;
  745. }
  746. void updateDisplayBokehTexture(
  747. float _radiusScale,
  748. float _maxBlurSize,
  749. int _lobeCount,
  750. float _lobeRadiusMin,
  751. float _lobeRadiusMax,
  752. float _lobeRotation)
  753. {
  754. if (m_bokehTexture.idx != bgfx::kInvalidHandle)
  755. {
  756. bgfx::destroy(m_bokehTexture);
  757. }
  758. BX_ASSERT(0 < _lobeCount, "");
  759. const int32_t bokehSize = 128;
  760. const bgfx::Memory* mem = bgfx::alloc(bokehSize*bokehSize*4);
  761. bx::memSet(mem->data, 0x00, bokehSize*bokehSize*4);
  762. const float thetaStep = 2.39996323f; // golden angle
  763. float loopValue = _radiusScale;
  764. const float loopEnd = _maxBlurSize;
  765. float theta = 0.0f;
  766. // bokeh shape function multiples this by half later
  767. const float radiusDelta2x = 2.0f * (_lobeRadiusMax - _lobeRadiusMin);
  768. int32_t counter = 0;
  769. while (loopValue < loopEnd)
  770. {
  771. float radius = loopValue;
  772. // apply shape to circular distribution
  773. const float shapeScale = bokehShapeFromAngle(_lobeCount, _lobeRadiusMin, radiusDelta2x, _lobeRotation, theta);
  774. BX_ASSERT(_lobeRadiusMin <= shapeScale, "");
  775. float spiralCoordX = bx::cos(theta) * (radius * shapeScale);
  776. float spiralCoordY = bx::sin(theta) * (radius * shapeScale);
  777. // normalize for texture display
  778. spiralCoordX /= _maxBlurSize;
  779. spiralCoordY /= _maxBlurSize;
  780. // scale from -1,1 into 0,1 normalized texture space
  781. spiralCoordX = spiralCoordX * 0.5f + 0.5f;
  782. spiralCoordY = spiralCoordY * 0.5f + 0.5f;
  783. // convert to pixel coordinates
  784. int32_t pixelCoordX = int32_t(bx::floor(spiralCoordX * float(bokehSize-1) + 0.5f));
  785. int32_t pixelCoordY = int32_t(bx::floor(spiralCoordY * float(bokehSize-1) + 0.5f));
  786. BX_ASSERT(0 <= pixelCoordX, "");
  787. BX_ASSERT(0 <= pixelCoordY, "");
  788. BX_ASSERT(pixelCoordX < bokehSize, "");
  789. BX_ASSERT(pixelCoordY < bokehSize, "");
  790. // plot sample position, track for total samples
  791. uint32_t offset = (pixelCoordY * bokehSize + pixelCoordX) * 4;
  792. mem->data[offset + 0] = 0xff;
  793. mem->data[offset + 1] = 0xff;
  794. mem->data[offset + 2] = 0xff;
  795. mem->data[offset + 3] = 0xff;
  796. ++counter;
  797. theta += thetaStep;
  798. loopValue += (_radiusScale / loopValue);
  799. }
  800. m_sampleCount = counter;
  801. // hoping texture deals with mem
  802. m_bokehTexture = bgfx::createTexture2D(bokehSize, bokehSize, false, 1
  803. , bgfx::TextureFormat::BGRA8
  804. , 0
  805. | BGFX_SAMPLER_MIN_POINT
  806. | BGFX_SAMPLER_MIP_POINT
  807. | BGFX_SAMPLER_MAG_POINT
  808. , mem
  809. );
  810. }
  811. uint32_t m_width;
  812. uint32_t m_height;
  813. uint32_t m_debug;
  814. uint32_t m_reset;
  815. entry::MouseState m_mouseState;
  816. // Resource handles
  817. bgfx::ProgramHandle m_forwardProgram;
  818. bgfx::ProgramHandle m_gridProgram;
  819. bgfx::ProgramHandle m_copyProgram;
  820. bgfx::ProgramHandle m_copyLinearToGammaProgram;
  821. bgfx::ProgramHandle m_linearDepthProgram;
  822. bgfx::ProgramHandle m_dofSinglePassProgram;
  823. bgfx::ProgramHandle m_dofDownsampleProgram;
  824. bgfx::ProgramHandle m_dofQuarterProgram;
  825. bgfx::ProgramHandle m_dofCombineProgram;
  826. bgfx::ProgramHandle m_dofDebugProgram;
  827. // Shader uniforms
  828. PassUniforms m_uniforms;
  829. ModelUniforms m_modelUniforms;
  830. // Uniforms to identify texture samplers
  831. bgfx::UniformHandle s_albedo;
  832. bgfx::UniformHandle s_color;
  833. bgfx::UniformHandle s_normal;
  834. bgfx::UniformHandle s_depth;
  835. bgfx::UniformHandle s_blurredColor;
  836. bgfx::FrameBufferHandle m_frameBuffer;
  837. bgfx::TextureHandle m_frameBufferTex[FRAMEBUFFER_RENDER_TARGETS];
  838. RenderTarget m_linearDepth;
  839. RenderTarget m_dofQuarterInput;
  840. RenderTarget m_dofQuarterOutput;
  841. struct Model
  842. {
  843. uint32_t mesh; // Index of mesh in m_meshes
  844. float position[3];
  845. };
  846. Mesh* m_meshes[BX_COUNTOF(s_meshPaths)];
  847. bgfx::TextureHandle m_groundTexture;
  848. bgfx::TextureHandle m_normalTexture;
  849. bgfx::TextureHandle m_bokehTexture;
  850. uint32_t m_currFrame;
  851. float m_lightRotation = 0.0f;
  852. float m_texelHalf = 0.0f;
  853. float m_fovY = 60.0f;
  854. bool m_recreateFrameBuffers = false;
  855. float m_animationTime = 0.0f;
  856. float m_view[16];
  857. float m_proj[16];
  858. float m_proj2[16];
  859. int32_t m_size[2];
  860. // UI parameters
  861. bool m_useBokehDof = true;
  862. bool m_useSinglePassBokehDof = false;
  863. float m_maxBlurSize = 20.0f;
  864. float m_focusPoint = 5.0f;
  865. float m_focusScale = 3.0f;
  866. float m_radiusScale = 0.5f;
  867. float m_blurSteps = 50.0f;
  868. bool m_showDebugVisualization = false;
  869. int32_t m_lobeCount = 6;
  870. float m_lobePinch = 0.2f;
  871. float m_lobeRotation = 0.0f;
  872. int32_t m_sampleCount = 0;
  873. };
  874. } // namespace
  875. ENTRY_IMPLEMENT_MAIN(ExampleBokeh, "45-bokeh", "Bokeh Depth of Field");