From c50cc8493fdd51d6deaf604b9f10f2b827f77401 Mon Sep 17 00:00:00 2001 From: Apoorva Joshi Date: Tue, 4 Apr 2017 22:22:08 +0530 Subject: [PATCH] Add parallax occlusion mapping example (#1104) * Added example 33-pom * Fixed warning in 33-pom example on Windows * Deleted unused image files * Deleted runtime textures and shaders --- examples/33-pom/fs_pom.sc | 94 ++++++++ examples/33-pom/makefile | 10 + examples/33-pom/pom.cpp | 394 +++++++++++++++++++++++++++++++++ examples/33-pom/varying.def.sc | 13 ++ examples/33-pom/vs_pom.sc | 44 ++++ examples/makefile | 1 + scripts/genie.lua | 1 + 7 files changed, 557 insertions(+) create mode 100644 examples/33-pom/fs_pom.sc create mode 100644 examples/33-pom/makefile create mode 100644 examples/33-pom/pom.cpp create mode 100644 examples/33-pom/varying.def.sc create mode 100644 examples/33-pom/vs_pom.sc diff --git a/examples/33-pom/fs_pom.sc b/examples/33-pom/fs_pom.sc new file mode 100644 index 000000000..8a0293060 --- /dev/null +++ b/examples/33-pom/fs_pom.sc @@ -0,0 +1,94 @@ +$input v_texcoord0, v_ts_light_pos, v_ts_view_pos, v_ts_frag_pos + +#include "../common/common.sh" + +SAMPLER2D(s_texColor, 0); +SAMPLER2D(s_texNormal, 1); +SAMPLER2D(s_texDepth, 2); + +uniform int u_shading_type; +uniform int u_show_diffuse_texture; +uniform int u_parallax_scale; +uniform int u_num_steps; + +vec2 parallax_uv(vec2 uv, vec3 view_dir) +{ + float depth_scale = float(u_parallax_scale) / 1000.0; + if (u_shading_type == 2) + { + // Parallax mapping + float depth = texture2D(s_texDepth, uv).r; + vec2 p = view_dir.xy * (depth * depth_scale) / view_dir.z; + return uv - p; + } + else + { + float layer_depth = 1.0 / float(u_num_steps); + float cur_layer_depth = 0.0; + vec2 delta_uv = view_dir.xy * depth_scale / (view_dir.z * u_num_steps); + vec2 cur_uv = uv; + + float depth_from_tex = texture2D(s_texDepth, cur_uv).r; + + for (int i = 0; i < 32; i++) + { + cur_layer_depth += layer_depth; + cur_uv -= delta_uv; + depth_from_tex = texture2D(s_texDepth, cur_uv).r; + if (depth_from_tex < cur_layer_depth) + { + break; + } + } + + if (u_shading_type == 3) + { + // Steep parallax mapping + return cur_uv; + } + else + { + // Parallax occlusion mapping + vec2 prev_uv = cur_uv + delta_uv; + float next = depth_from_tex - cur_layer_depth; + float prev = texture2D(s_texDepth, prev_uv).r - cur_layer_depth + layer_depth; + float weight = next / (next - prev); + return mix(cur_uv, prev_uv, weight); + } + } +} + +void main() +{ + vec3 light_dir = normalize(v_ts_light_pos - v_ts_frag_pos); + vec3 view_dir = normalize(v_ts_view_pos - v_ts_frag_pos); + + // Only perturb the texture coordinates if a parallax technique is selected + vec2 uv = (u_shading_type < 2) ? v_texcoord0 : parallax_uv(v_texcoord0, view_dir); + + vec3 albedo; + if (u_show_diffuse_texture == 0) + { + albedo = vec3(1.0, 1.0, 1.0); + } + else + { + albedo = texture2D(s_texColor, uv).rgb; + } + + vec3 ambient = 0.3 * albedo; + + vec3 norm; + + if (u_shading_type == 0) + { + norm = vec3(0, 0, 1); + } + else + { + norm = normalize(texture2D(s_texNormal, uv).rgb * 2.0 - 1.0); + } + + float diffuse = max(dot(light_dir, norm), 0.0); + gl_FragColor = vec4(diffuse * albedo + ambient, 1.0); +} diff --git a/examples/33-pom/makefile b/examples/33-pom/makefile new file mode 100644 index 000000000..94d3aae70 --- /dev/null +++ b/examples/33-pom/makefile @@ -0,0 +1,10 @@ +# +# Copyright 2011-2017 Branimir Karadzic. All rights reserved. +# License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +# + +BGFX_DIR=../.. +RUNTIME_DIR=$(BGFX_DIR)/examples/runtime +BUILD_DIR=../../.build + +include $(BGFX_DIR)/scripts/shader.mk diff --git a/examples/33-pom/pom.cpp b/examples/33-pom/pom.cpp new file mode 100644 index 000000000..64f5331c6 --- /dev/null +++ b/examples/33-pom/pom.cpp @@ -0,0 +1,394 @@ +/* + * Copyright 2011-2017 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause + */ + +#include "common.h" +#include "bgfx_utils.h" + +#include + +struct PosTangentBitangentTexcoordVertex +{ + float m_x; + float m_y; + float m_z; + uint32_t m_tangent; + uint32_t m_bitangent; + float m_u; + float m_v; + + static void init() + { + ms_decl + .begin() + .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) + .add(bgfx::Attrib::Tangent, 4, bgfx::AttribType::Uint8, true, true) + .add(bgfx::Attrib::Bitangent, 4, bgfx::AttribType::Uint8, true, true) + .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float, true, true) + .end(); + } + + static bgfx::VertexDecl ms_decl; +}; + +bgfx::VertexDecl PosTangentBitangentTexcoordVertex::ms_decl; + +uint32_t packUint32(uint8_t _x, uint8_t _y, uint8_t _z, uint8_t _w) +{ + union + { + uint32_t ui32; + uint8_t arr[4]; + } un; + + un.arr[0] = _x; + un.arr[1] = _y; + un.arr[2] = _z; + un.arr[3] = _w; + + return un.ui32; +} + +uint32_t packF4u(float _x, float _y = 0.0f, float _z = 0.0f, float _w = 0.0f) +{ + const uint8_t xx = uint8_t(_x*127.0f + 128.0f); + const uint8_t yy = uint8_t(_y*127.0f + 128.0f); + const uint8_t zz = uint8_t(_z*127.0f + 128.0f); + const uint8_t ww = uint8_t(_w*127.0f + 128.0f); + return packUint32(xx, yy, zz, ww); +} + +static PosTangentBitangentTexcoordVertex s_cubeVertices[24] = +{ + {-1, -1, 1, packF4u(-1, 0, 0), packF4u( 0, -1, 0), 1, 1 }, // Back + { 1, 1, 1, packF4u(-1, 0, 0), packF4u( 0, -1, 0), 0, 0 }, + {-1, 1, 1, packF4u(-1, 0, 0), packF4u( 0, -1, 0), 1, 0 }, + { 1, -1, 1, packF4u(-1, 0, 0), packF4u( 0, -1, 0), 0, 1 }, + {-1, -1, -1, packF4u( 1, 0, 0), packF4u( 0, -1, 0), 0, 1 }, // Front + { 1, 1, -1, packF4u( 1, 0, 0), packF4u( 0, -1, 0), 1, 0 }, + {-1, 1, -1, packF4u( 1, 0, 0), packF4u( 0, -1, 0), 0, 0 }, + { 1, -1, -1, packF4u( 1, 0, 0), packF4u( 0, -1, 0), 1, 1 }, + { 1, -1, -1, packF4u( 0, 0, 1), packF4u( 0, -1, 0), 0, 1 }, // Right + { 1, 1, 1, packF4u( 0, 0, 1), packF4u( 0, -1, 0), 1, 0 }, + { 1, -1, 1, packF4u( 0, 0, 1), packF4u( 0, -1, 0), 1, 1 }, + { 1, 1, -1, packF4u( 0, 0, 1), packF4u( 0, -1, 0), 0, 0 }, + {-1, -1, -1, packF4u( 0, 0, -1), packF4u( 0, -1, 0), 1, 1 }, // Left + {-1, 1, 1, packF4u( 0, 0, -1), packF4u( 0, -1, 0), 0, 0 }, + {-1, -1, 1, packF4u( 0, 0, -1), packF4u( 0, -1, 0), 0, 1 }, + {-1, 1, -1, packF4u( 0, 0, -1), packF4u( 0, -1, 0), 1, 0 }, + {-1, 1, -1, packF4u( 1, 0, 0), packF4u( 0, 0, -1), 0, 1 }, // Top + { 1, 1, 1, packF4u( 1, 0, 0), packF4u( 0, 0, -1), 1, 0 }, + {-1, 1, 1, packF4u( 1, 0, 0), packF4u( 0, 0, -1), 0, 0 }, + { 1, 1, -1, packF4u( 1, 0, 0), packF4u( 0, 0, -1), 1, 1 }, + {-1, -1, -1, packF4u( 1, 0, 0), packF4u( 0, 0, 1), 0, 0 }, // Bottom + { 1, -1, 1, packF4u( 1, 0, 0), packF4u( 0, 0, 1), 1, 1 }, + {-1, -1, 1, packF4u( 1, 0, 0), packF4u( 0, 0, 1), 0, 1 }, + { 1, -1, -1, packF4u( 1, 0, 0), packF4u( 0, 0, 1), 1, 0 }, +}; + +static const uint16_t s_cubeIndices[36] = +{ + 0 , 1 , 2 , + 0 , 3 , 1 , + 4 , 6 , 5 , + 4 , 5 , 7 , + + 8 , 9 , 10, + 8 , 11, 9 , + 12, 14, 13, + 12, 13, 15, + + 16, 18, 17, + 16, 17, 19, + 20, 21, 22, + 20, 23, 21, +}; + +class ExamplePom : public entry::AppI +{ + void init(int _argc, char** _argv) BX_OVERRIDE + { + Args args(_argc, _argv); + + m_width = 1280; + m_height = 720; + m_debug = BGFX_DEBUG_TEXT; + m_reset = BGFX_RESET_VSYNC; + + bgfx::init(args.m_type, args.m_pciId); + bgfx::reset(m_width, m_height, m_reset); + + // Enable debug text. + bgfx::setDebug(m_debug); + + // Set view 0 clear state. + bgfx::setViewClear(0 + , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH + , 0x303030ff + , 1.0f + , 0 + ); + + // Create vertex stream declaration. + PosTangentBitangentTexcoordVertex::init(); + + // Create static vertex buffer. + m_vbh = bgfx::createVertexBuffer(bgfx::makeRef(s_cubeVertices, sizeof(s_cubeVertices) ), + PosTangentBitangentTexcoordVertex::ms_decl); + + // Create static index buffer. + m_ibh = bgfx::createIndexBuffer(bgfx::makeRef(s_cubeIndices, sizeof(s_cubeIndices) ) ); + + // Create texture sampler uniforms. + s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Int1); + s_texNormal = bgfx::createUniform("s_texNormal", bgfx::UniformType::Int1); + s_texDepth = bgfx::createUniform("s_texDepth", bgfx::UniformType::Int1); + + + u_light_pos = bgfx::createUniform("u_light_pos", bgfx::UniformType::Vec4); + u_norm_mtx = bgfx::createUniform("u_norm_mtx", bgfx::UniformType::Mat4); + u_shading_type = bgfx::createUniform("u_shading_type", bgfx::UniformType::Int1); + u_show_diffuse_texture = bgfx::createUniform("u_show_diffuse_texture", bgfx::UniformType::Int1); + u_parallax_scale = bgfx::createUniform("u_parallax_scale", bgfx::UniformType::Int1); + u_num_steps = bgfx::createUniform("u_num_steps", bgfx::UniformType::Int1); + + // Create program from shaders. + m_program = loadProgram("vs_pom", "fs_pom"); + + // Load diffuse texture. + m_textureColor = loadTexture("textures/parallax-d.png"); + + // Load normal texture. + m_textureNormal = loadTexture("textures/parallax-n.png"); + + // Load depth texture. + m_textureDepth = loadTexture("textures/parallax-h.png"); + + imguiCreate(); + + m_timeOffset = bx::getHPCounter(); + m_shading_type = 4; + m_show_diffuse_texture = true; + m_parallax_scale = 50; + m_num_steps = 4; + } + + virtual int shutdown() BX_OVERRIDE + { + // Cleanup. + bgfx::destroyIndexBuffer(m_ibh); + bgfx::destroyVertexBuffer(m_vbh); + bgfx::destroyProgram(m_program); + bgfx::destroyTexture(m_textureColor); + bgfx::destroyTexture(m_textureNormal); + bgfx::destroyTexture(m_textureDepth); + bgfx::destroyUniform(s_texColor); + bgfx::destroyUniform(s_texNormal); + bgfx::destroyUniform(s_texDepth); + bgfx::destroyUniform(u_light_pos); + bgfx::destroyUniform(u_norm_mtx); + bgfx::destroyUniform(u_shading_type); + bgfx::destroyUniform(u_show_diffuse_texture); + bgfx::destroyUniform(u_parallax_scale); + bgfx::destroyUniform(u_num_steps); + + imguiDestroy(); + + // Shutdown bgfx. + bgfx::shutdown(); + + return 0; + } + + bool update() BX_OVERRIDE + { + if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) ) + { + // Set view 0 default viewport. + bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) ); + + // This dummy draw call is here to make sure that view 0 is cleared + // if no other draw calls are submitted to view 0. + bgfx::touch(0); + + int64_t now = bx::getHPCounter(); + static int64_t last = now; + const int64_t frameTime = now - last; + last = now; + const double freq = double(bx::getHPFrequency() ); + const double toMs = 1000.0/freq; + + float time = (float)( (now-m_timeOffset)/freq); + + // Use debug font to print information about this example. + bgfx::dbgTextClear(); + bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/33-pom"); + bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: Parallax mapping."); + bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs); + + float at[3] = { 0.0f, 0.0f, 1.0f }; + float eye[3] = { 0.0f, 0.0f, 0.0f }; + + // Set view and projection matrix for view 0. + const bgfx::HMD* hmd = bgfx::getHMD(); + if (NULL != hmd && 0 != (hmd->flags & BGFX_HMD_RENDERING) ) + { + float view[16]; + bx::mtxQuatTranslationHMD(view, hmd->eye[0].rotation, eye); + bgfx::setViewTransform(0, view, hmd->eye[0].projection, BGFX_VIEW_STEREO, hmd->eye[1].projection); + + // Set view 0 default viewport. + // + // Use HMD's width/height since HMD's internal frame buffer size + // might be much larger than window size. + bgfx::setViewRect(0, 0, 0, hmd->width, hmd->height); + } + else + { + float view[16]; + bx::mtxLookAt(view, eye, at); + + float proj[16]; + bx::mtxProj(proj, 60.0f, float(m_width) / float(m_height), 0.1f, 100.0f, bgfx::getCaps()->homogeneousDepth); + bgfx::setViewTransform(0, view, proj); + + // Set view 0 default viewport. + bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) ); + } + + imguiBeginFrame( + m_mouseState.m_mx + , m_mouseState.m_my + , (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0) + | (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0) + | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0) + , m_mouseState.m_mz + , uint16_t(m_width) + , uint16_t(m_height) + ); + + ImGui::Begin("Properties"); + + ImGui::RadioButton("No bump mapping", &m_shading_type, 0); + ImGui::RadioButton("Normal mapping", &m_shading_type, 1); + ImGui::RadioButton("Parallax mapping", &m_shading_type, 2); + ImGui::RadioButton("Steep parallax mapping", &m_shading_type, 3); + ImGui::RadioButton("Parallax occlusion mapping", &m_shading_type, 4); + bgfx::setUniform(u_shading_type, &m_shading_type); + + ImGui::Separator(); + + ImGui::Checkbox("Show diffuse texture", &m_show_diffuse_texture); + bgfx::setUniform(u_show_diffuse_texture, &m_show_diffuse_texture); + + if (m_shading_type > 1) + { + ImGui::Separator(); + + float multiplier = 1000.0f; + float x = (float)m_parallax_scale / multiplier; + ImGui::SliderFloat("Parallax scale", &x, 0.0f, 0.1f); + m_parallax_scale = (int32_t)(x * multiplier); + bgfx::setUniform(u_parallax_scale, &m_parallax_scale); + } + + if (m_shading_type > 2) + { + ImGui::Separator(); + + ImGui::SliderInt("Number of steps", &m_num_steps, 1, 32); + bgfx::setUniform(u_num_steps, &m_num_steps); + } + + ImGui::End(); + + imguiEndFrame(); + + float light_pos[4] = { 1, 2, 0, 0 }; + bgfx::setUniform(u_light_pos, light_pos); + + float a[16]; + float b[16]; + float c[16]; + float d[16]; + float mtx[16]; + bx::mtxRotateY(a, time * 0.4f); + bx::mtxRotateX(b, 0.4f); + bx::mtxMul(c, a, b); + bx::mtxTranslate(d, 0, 0, 4); + bx::mtxMul(mtx, c, d); + + // Set transform for draw call. + bgfx::setTransform(mtx); + + // Set normal matrix uniform + float inv[16]; + float transpose[16]; + bx::mtxInverse(inv, mtx); + bx::mtxTranspose(transpose, inv); + bgfx::setUniform(u_norm_mtx, transpose); + + // Set vertex and index buffer. + bgfx::setVertexBuffer(m_vbh); + bgfx::setIndexBuffer(m_ibh); + + // Bind textures. + bgfx::setTexture(0, s_texColor, m_textureColor); + bgfx::setTexture(1, s_texNormal, m_textureNormal); + bgfx::setTexture(2, s_texDepth, m_textureDepth); + + // Set render states. + bgfx::setState(0 + | BGFX_STATE_RGB_WRITE + | BGFX_STATE_ALPHA_WRITE + | BGFX_STATE_DEPTH_WRITE + | BGFX_STATE_DEPTH_TEST_LESS + | BGFX_STATE_MSAA + ); + + // Submit primitive for rendering to view 0. + bgfx::submit(0, m_program); + + // Advance to next frame. Rendering thread will be kicked to + // process submitted rendering primitives. + bgfx::frame(); + + return true; + } + + return false; + } + + bgfx::VertexBufferHandle m_vbh; + bgfx::IndexBufferHandle m_ibh; + bgfx::UniformHandle s_texColor; + bgfx::UniformHandle s_texNormal; + bgfx::UniformHandle s_texDepth; + bgfx::UniformHandle u_light_pos; + bgfx::UniformHandle u_norm_mtx; + bgfx::UniformHandle u_shading_type; + bgfx::UniformHandle u_show_diffuse_texture; + bgfx::UniformHandle u_parallax_scale; + bgfx::UniformHandle u_num_steps; + bgfx::ProgramHandle m_program; + bgfx::TextureHandle m_textureColor; + bgfx::TextureHandle m_textureNormal; + bgfx::TextureHandle m_textureDepth; + bool m_instancingSupported; + + entry::MouseState m_mouseState; + + uint32_t m_width; + uint32_t m_height; + uint32_t m_debug; + uint32_t m_reset; + int64_t m_timeOffset; + + int32_t m_shading_type; + bool m_show_diffuse_texture; + int32_t m_parallax_scale; + int32_t m_num_steps; +}; + +ENTRY_IMPLEMENT_MAIN(ExamplePom); diff --git a/examples/33-pom/varying.def.sc b/examples/33-pom/varying.def.sc new file mode 100644 index 000000000..2b42f9cc8 --- /dev/null +++ b/examples/33-pom/varying.def.sc @@ -0,0 +1,13 @@ +vec2 v_texcoord0 : TEXCOORD0 = vec2(0.0, 0.0); +vec3 v_ts_light_pos : TEXCOORD1 = vec3(0.0, 0.0, 0.0); +vec3 v_ts_view_pos : TEXCOORD2 = vec3(0.0, 0.0, 0.0); +vec3 v_ts_frag_pos : TEXCOORD3 = vec3(0.0, 0.0, 0.0); + +vec3 a_position : POSITION; +vec3 a_tangent : TANGENT; +vec3 a_bitangent : BITANGENT; +vec2 a_texcoord0 : TEXCOORD0; +vec4 i_data0 : TEXCOORD7; +vec4 i_data1 : TEXCOORD6; +vec4 i_data2 : TEXCOORD5; +vec4 i_data3 : TEXCOORD4; diff --git a/examples/33-pom/vs_pom.sc b/examples/33-pom/vs_pom.sc new file mode 100644 index 000000000..0b3b2dc05 --- /dev/null +++ b/examples/33-pom/vs_pom.sc @@ -0,0 +1,44 @@ +$input a_position, a_tangent, a_bitangent, a_texcoord0 +$output v_texcoord0, v_ts_light_pos, v_ts_view_pos, v_ts_frag_pos + +uniform mat4 u_norm_mtx; +uniform vec4 u_light_pos; + +#include "../common/common.sh" + +mat3 transpose(mat3 inMatrix) +{ + vec3 i0 = inMatrix[0]; + vec3 i1 = inMatrix[1]; + vec3 i2 = inMatrix[2]; + + mat3 outMatrix = mat3( + vec3(i0.x, i1.x, i2.x), + vec3(i0.y, i1.y, i2.y), + vec3(i0.z, i1.z, i2.z) + ); + + return outMatrix; +} + +void main() +{ + vec3 wpos = mul(u_model[0], vec4(a_position, 1.0) ).xyz; + gl_Position = mul(u_viewProj, vec4(wpos, 1.0) ); + + vec3 tangent = a_tangent * 2.0 - 1.0; + vec3 bitangent = a_bitangent * 2.0 - 1.0; + vec3 normal = cross(tangent, bitangent); + + vec3 t = normalize(mat3(u_norm_mtx) * tangent); + vec3 b = normalize(mat3(u_norm_mtx) * bitangent); + vec3 n = normalize(mat3(u_norm_mtx) * normal); + mat3 tbn = transpose(mat3(t, b, n)); + + v_ts_light_pos = tbn * u_light_pos.xyz; + // Our camera is always at the origin + v_ts_view_pos = tbn * vec3(0, 0, 0); + v_ts_frag_pos = tbn * wpos; + + v_texcoord0 = a_texcoord0; +} diff --git a/examples/makefile b/examples/makefile index f39a5d4e9..0df96c2e7 100644 --- a/examples/makefile +++ b/examples/makefile @@ -33,6 +33,7 @@ rebuild: @make -s --no-print-directory rebuild -C 28-wireframe @make -s --no-print-directory rebuild -C 30-picking @make -s --no-print-directory rebuild -C 31-rsm + @make -s --no-print-directory rebuild -C 33-pom @make -s --no-print-directory rebuild -C common/debugdraw @make -s --no-print-directory rebuild -C common/font @make -s --no-print-directory rebuild -C common/imgui diff --git a/scripts/genie.lua b/scripts/genie.lua index 1c3e74813..327e6523d 100644 --- a/scripts/genie.lua +++ b/scripts/genie.lua @@ -410,6 +410,7 @@ if _OPTIONS["with-examples"] then exampleProject("30-picking") exampleProject("31-rsm") exampleProject("32-particles") + exampleProject("33-pom") -- C99 source doesn't compile under WinRT settings if not premake.vstudio.iswinrt() then