Files
bgfx/examples/53-sky2/sky2.cpp
Matthew 527397d502 Atmospheric sky example v1 (#3861)
* Implemented terrain heightmap loading.

* Textured the terrain in sky2.

* Atmosphere implementation.

* Terrain lit, aerial perspective implemented.

* Shadow mapping for the terrain.

* Implemented hex-tiling.

im tired

* Cleanup

* Fixed OpenGL sampler issue.

* Brought back overridden shaders.
2026-08-02 02:29:51 +00:00

730 lines
23 KiB
C++

/*
* Copyright 2026 Mateusz Kozdrowicki. All rights reserved.
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
*/
#include "common.h"
#include "bgfx_utils.h"
#include "imgui/imgui.h"
#include "camera.h"
namespace
{
constexpr float kWorldSize = 96.0f; // (km)
constexpr float kAmplitude = 16.0f; // (km)
constexpr float kSunColor[3] = { 1.0f, 0.975f, 0.94f };
constexpr float kSunDiscCos = 0.9999f;
constexpr float kSunDiscScale = 60.0f;
constexpr uint16_t kTransmittanceW = 256;
constexpr uint16_t kTransmittanceH = 64;
constexpr uint16_t kMultiscatterSz = 32;
constexpr uint16_t kSkyviewW = 192;
constexpr uint16_t kSkyviewH = 108;
constexpr uint16_t kShadowMapSize = 2048;
struct PosNormalVertex
{
float m_x, m_y, m_z;
float m_nx, m_ny, m_nz;
static void init()
{
ms_layout
.begin()
.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float)
.end();
}
static bgfx::VertexLayout ms_layout;
};
bgfx::VertexLayout PosNormalVertex::ms_layout;
enum : bgfx::ViewId
{
kViewTransmittance = 0,
kViewMultiscatter = 1,
kViewSkyview = 2,
kViewShadow = 3,
kViewSky = 4,
kViewTerrain = 5,
};
class ExampleSky2 : public entry::AppI
{
public:
ExampleSky2(const char* _name, const char* _description, const char* _url)
: entry::AppI(_name, _description, _url)
{
}
void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
{
Args args(_argc, _argv);
m_width = _width;
m_height = _height;
m_debug = BGFX_DEBUG_TEXT;
m_reset = BGFX_RESET_VSYNC;
bgfx::Init init;
init.type = args.m_type;
init.vendorId = args.m_pciId;
init.platformData.nwh = entry::getNativeWindowHandle(entry::kDefaultWindowHandle);
init.platformData.ndt = entry::getNativeDisplayHandle();
init.platformData.type = entry::getNativeWindowHandleType();
init.resolution.width = m_width;
init.resolution.height = m_height;
init.resolution.reset = m_reset;
bgfx::init(init);
bgfx::setDebug(m_debug);
const bgfx::Caps* caps = bgfx::getCaps();
m_computeSupported = !!(caps->supported & BGFX_CAPS_COMPUTE);
if (!m_computeSupported)
{
return;
}
PosNormalVertex::init();
u_atmoRayleigh = bgfx::createUniform("u_atmoRayleigh", bgfx::UniformType::Vec4);
u_atmoMie = bgfx::createUniform("u_atmoMie", bgfx::UniformType::Vec4);
u_atmoOzone = bgfx::createUniform("u_atmoOzone", bgfx::UniformType::Vec4);
u_atmoPlanet = bgfx::createUniform("u_atmoPlanet", bgfx::UniformType::Vec4);
u_atmoGround = bgfx::createUniform("u_atmoGround", bgfx::UniformType::Vec4);
u_atmoParams = bgfx::createUniform("u_atmo_params", bgfx::UniformType::Vec4);
u_atmoParams2 = bgfx::createUniform("u_atmo_params2", bgfx::UniformType::Vec4);
u_skyParams = bgfx::createUniform("u_skyParams", bgfx::UniformType::Vec4);
u_sunDirection = bgfx::createUniform("u_sunDirection", bgfx::UniformType::Vec4);
u_sunRadiance = bgfx::createUniform("u_sunRadiance", bgfx::UniformType::Vec4);
u_invViewProj = bgfx::createUniform("u_skyInvViewProj", bgfx::UniformType::Mat4);
u_cameraPos = bgfx::createUniform("u_cameraPos", bgfx::UniformType::Vec4);
u_aerialParams = bgfx::createUniform("u_aerialParams", bgfx::UniformType::Vec4);
u_shadowParams = bgfx::createUniform("u_shadowParams", bgfx::UniformType::Vec4);
u_lightViewProj = bgfx::createUniform("u_lightViewProj", bgfx::UniformType::Mat4);
u_lightMtx = bgfx::createUniform("u_lightMtx", bgfx::UniformType::Mat4);
s_atmoTransmittance = bgfx::createUniform("s_atmo_transmittance", bgfx::UniformType::Sampler);
s_atmoMultiscatter = bgfx::createUniform("s_atmo_multiscatter", bgfx::UniformType::Sampler);
s_skyView = bgfx::createUniform("s_sky_view", bgfx::UniformType::Sampler);
s_shadowMap = bgfx::createUniform("s_shadowMap", bgfx::UniformType::Sampler);
s_grass = bgfx::createUniform("s_grass", bgfx::UniformType::Sampler);
s_rock = bgfx::createUniform("s_rock", bgfx::UniformType::Sampler);
m_terrainProgram = loadProgram("vs_sky2", "fs_sky2");
m_skyProgram = loadProgram("vs_sky2_bg", "fs_sky2_bg");
m_shadowProgram = loadProgram("vs_sky2_shadow", "fs_sky2_shadow");
m_csTransmittance = bgfx::createProgram(loadShader("cs_atmo_transmittance"), true);
m_csMultiscatter = bgfx::createProgram(loadShader("cs_atmo_multiscatter"), true);
m_csSkyview = bgfx::createProgram(loadShader("cs_atmo_skyview"), true);
const uint64_t lutFlags = BGFX_TEXTURE_COMPUTE_WRITE | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP;
m_transmittanceLut = bgfx::createTexture2D(kTransmittanceW, kTransmittanceH, false, 1, bgfx::TextureFormat::RGBA16F, lutFlags);
m_multiscatterLut = bgfx::createTexture2D(kMultiscatterSz, kMultiscatterSz, false, 1, bgfx::TextureFormat::RGBA16F, lutFlags);
m_skyviewLut = bgfx::createTexture2D(kSkyviewW, kSkyviewH, false, 1, bgfx::TextureFormat::RGBA16F, BGFX_TEXTURE_COMPUTE_WRITE | BGFX_SAMPLER_V_CLAMP);
m_shadowColor = bgfx::createTexture2D(kShadowMapSize, kShadowMapSize, false, 1, bgfx::TextureFormat::R32F, BGFX_TEXTURE_RT
| BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT
| BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP);
m_shadowDepth = bgfx::createTexture2D(kShadowMapSize, kShadowMapSize, false, 1, bgfx::TextureFormat::D16, BGFX_TEXTURE_RT_WRITE_ONLY);
bgfx::TextureHandle shadowAttachments[2] = { m_shadowColor, m_shadowDepth };
m_shadowFb = bgfx::createFrameBuffer(2, shadowAttachments, false);
// BC1 with mip chains
m_grassTx = loadTexture("textures/terrain_grass_1k_diff.dds");
m_rockTx = loadTexture("textures/terrain_rock_1k_diff.dds");
bx::mtxIdentity(m_lightMtx);
buildTerrain();
buildQuad();
cameraCreate();
cameraSetPosition({ -3.0f, 16.0f, 42.0f });
cameraSetHorizontalAngle(bx::kPi - bx::kPi / 20.0f);
cameraSetVerticalAngle(-bx::kPi / 12.0f);
bgfx::setViewClear(kViewShadow, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0xffffffff, 1.0f, 0);
bgfx::setViewClear(kViewSky, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000ff, 1.0f, 0);
m_frameTime.reset();
imguiCreate();
}
virtual int shutdown() override
{
if (!m_computeSupported)
{
bgfx::shutdown();
return 0;
}
cameraDestroy();
imguiDestroy();
bgfx::destroy(m_terrain_vbh);
bgfx::destroy(m_terrain_ibh);
bgfx::destroy(m_quad_vbh);
bgfx::destroy(m_terrainProgram);
bgfx::destroy(m_skyProgram);
bgfx::destroy(m_shadowProgram);
bgfx::destroy(m_csTransmittance);
bgfx::destroy(m_csMultiscatter);
bgfx::destroy(m_csSkyview);
bgfx::destroy(m_transmittanceLut);
bgfx::destroy(m_multiscatterLut);
bgfx::destroy(m_skyviewLut);
bgfx::destroy(m_shadowFb);
bgfx::destroy(m_shadowColor);
bgfx::destroy(m_shadowDepth);
bgfx::destroy(m_grassTx);
bgfx::destroy(m_rockTx);
bgfx::destroy(u_atmoRayleigh);
bgfx::destroy(u_atmoMie);
bgfx::destroy(u_atmoOzone);
bgfx::destroy(u_atmoPlanet);
bgfx::destroy(u_atmoGround);
bgfx::destroy(u_atmoParams);
bgfx::destroy(u_atmoParams2);
bgfx::destroy(u_skyParams);
bgfx::destroy(u_sunDirection);
bgfx::destroy(u_sunRadiance);
bgfx::destroy(u_invViewProj);
bgfx::destroy(u_cameraPos);
bgfx::destroy(u_aerialParams);
bgfx::destroy(u_shadowParams);
bgfx::destroy(u_lightViewProj);
bgfx::destroy(u_lightMtx);
bgfx::destroy(s_atmoTransmittance);
bgfx::destroy(s_atmoMultiscatter);
bgfx::destroy(s_skyView);
bgfx::destroy(s_shadowMap);
bgfx::destroy(s_grass);
bgfx::destroy(s_rock);
bgfx::shutdown();
return 0;
}
void setAtmosphereUniforms()
{
const float rayleigh[4] =
{
m_rayleighColor[0] * m_rayleighScale,
m_rayleighColor[1] * m_rayleighScale,
m_rayleighColor[2] * m_rayleighScale,
m_rayleighScaleHeight,
};
const float mie[4] = { m_mieScatter, m_mieScatter + m_mieAbsorption, m_mieScaleHeight, m_miePhaseG };
const float ozone[4] =
{
m_ozoneColor[0] * m_ozoneScale,
m_ozoneColor[1] * m_ozoneScale,
m_ozoneColor[2] * m_ozoneScale,
m_multiscatter,
};
const float planet[4] = { m_planetRadius, m_planetRadius + m_atmosHeight, m_ozoneCenter, m_ozoneHalf };
const float ground[4] = { m_groundAlbedo[0], m_groundAlbedo[1], m_groundAlbedo[2], 0.0f };
bgfx::setUniform(u_atmoRayleigh, rayleigh);
bgfx::setUniform(u_atmoMie, mie);
bgfx::setUniform(u_atmoOzone, ozone);
bgfx::setUniform(u_atmoPlanet, planet);
bgfx::setUniform(u_atmoGround, ground);
}
void drawComputeUnsupported()
{
bgfx::setViewClear(kViewSky, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000ff, 1.0f, 0);
bgfx::setViewRect(kViewSky, 0, 0, uint16_t(m_width), uint16_t(m_height) );
bgfx::touch(kViewSky);
bgfx::dbgTextClear();
bgfx::dbgTextPrintf(0, 0, 0x04, " Compute is not supported.");
}
void drawPanel()
{
ImGui::SetNextWindowPos(ImVec2(m_width - 340.0f, 10.0f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(330.0f, 480.0f), ImGuiCond_FirstUseEver);
ImGui::Begin("Settings");
ImGui::SeparatorText("Sun");
ImGui::SliderFloat("Elevation", &m_sunElevationDeg, -10.0f, 89.0f);
ImGui::SliderFloat("Azimuth", &m_sunAzimuthDeg, -180.0f, 180.0f);
ImGui::SliderFloat("Intensity", &m_sunIntensity, 0.0f, 30.0f);
ImGui::Checkbox("Shadow mapping", &m_shadowsEnabled);
ImGui::SeparatorText("Rayleigh");
ImGui::ColorEdit3("Ray color", m_rayleighColor);
ImGui::SliderFloat("Ray scale", &m_rayleighScale, 0.0f, 0.1f, "%.5f");
ImGui::SliderFloat("Ray height", &m_rayleighScaleHeight, 0.5f, 20.0f);
ImGui::SeparatorText("Mie");
ImGui::SliderFloat("Mie scatter", &m_mieScatter, 0.0f, 0.02f, "%.5f");
ImGui::SliderFloat("Mie absorb", &m_mieAbsorption, 0.0f, 0.02f, "%.5f");
ImGui::SliderFloat("Mie height", &m_mieScaleHeight, 0.2f, 8.0f);
ImGui::SliderFloat("Mie g", &m_miePhaseG, 0.0f, 0.99f);
ImGui::SeparatorText("Ozone");
ImGui::ColorEdit3("Ozone color", m_ozoneColor);
ImGui::SliderFloat("Ozone scale", &m_ozoneScale, 0.0f, 0.01f, "%.5f");
ImGui::SeparatorText("Planet");
ImGui::SliderFloat("Radius", &m_planetRadius, 1000.0f, 12000.0f);
ImGui::SliderFloat("Atmos height", &m_atmosHeight, 10.0f, 200.0f);
ImGui::ColorEdit3("Ground", m_groundAlbedo);
ImGui::SliderFloat("Multiscatter", &m_multiscatter, 0.0f, 5.0f);
ImGui::SeparatorText("Aerial perspective");
ImGui::SliderFloat("Inscatter", &m_aerialPerspectiveInscatter, 0.0f, 8.0f);
ImGui::SliderFloat("Density", &m_aerialPerspectiveDensity, 0.0f, 8.0f);
ImGui::SliderFloat("Ambient", &m_aerialPerspectiveambientStrength, 0.0f, 4.0f);
ImGui::End();
}
bool update() override
{
if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
{
if (!m_computeSupported)
{
drawComputeUnsupported();
bgfx::frame();
return true;
}
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)
);
showExampleDialog(this);
drawPanel();
imguiEndFrame();
m_frameTime.frame();
const float deltaTime = bx::toSeconds<float>(m_frameTime.getDeltaTime());
cameraUpdate(deltaTime, m_mouseState, ImGui::MouseOverArea());
const float elevation = bx::toRad(m_sunElevationDeg);
const float azimuth = bx::toRad(m_sunAzimuthDeg);
const bx::Vec3 toSun =
{
bx::cos(elevation) * bx::sin(azimuth),
bx::sin(elevation),
bx::cos(elevation) * bx::cos(azimuth),
};
const float sunDir[4] = { -toSun.x, -toSun.y, -toSun.z, 0.0f };
const float sunCosZenith = toSun.y;
const bx::Vec3 camPos = cameraGetPosition();
const float viewRadius = m_planetRadius + bx::max(camPos.y, 0.001f);
const float sunRadiance[4] =
{
kSunColor[0] * m_sunIntensity,
kSunColor[1] * m_sunIntensity,
kSunColor[2] * m_sunIntensity,
0.0f,
};
// --- LUT compute ---
setAtmosphereUniforms();
bgfx::setImage(0, m_transmittanceLut, 0, bgfx::Access::Write, bgfx::TextureFormat::RGBA16F);
bgfx::dispatch(kViewTransmittance, m_csTransmittance, kTransmittanceW/8, kTransmittanceH/8, 1);
setAtmosphereUniforms();
bgfx::setTexture(0, s_atmoTransmittance, m_transmittanceLut);
bgfx::setImage(1, m_multiscatterLut, 0, bgfx::Access::Write, bgfx::TextureFormat::RGBA16F);
bgfx::dispatch(kViewMultiscatter, m_csMultiscatter, kMultiscatterSz/8, kMultiscatterSz/8, 1);
const float atmoParams2[4] = { sunCosZenith, viewRadius, float(kSkyviewW), float(kSkyviewH) };
setAtmosphereUniforms();
bgfx::setUniform(u_atmoParams, sunRadiance);
bgfx::setUniform(u_atmoParams2, atmoParams2);
bgfx::setTexture(0, s_atmoTransmittance, m_transmittanceLut);
bgfx::setTexture(2, s_atmoMultiscatter, m_multiscatterLut);
bgfx::setImage(1, m_skyviewLut, 0, bgfx::Access::Write, bgfx::TextureFormat::RGBA16F);
bgfx::dispatch(kViewSkyview, m_csSkyview, (kSkyviewW + 7)/8, (kSkyviewH + 7)/8, 1);
float view[16];
cameraGetViewMtx(view);
float proj[16];
bx::mtxProj(proj, 60.0f, float(m_width) / float(m_height), 0.02f, 400.0f, bgfx::getCaps()->homogeneousDepth);
float viewProj[16];
bx::mtxMul(viewProj, view, proj);
float invViewProj[16];
bx::mtxInverse(invViewProj, viewProj);
const float skyParams[4] = { viewRadius, kSunDiscCos, kSunDiscScale, 0.0f };
// ortho half extent
const float shadowExtent = 0.8f * kWorldSize;
const float shadowTexel = 2.0f * shadowExtent / float(kShadowMapSize);
const float shadowParams[4] =
{
1.0f / float(kShadowMapSize),
shadowTexel,
m_shadowsEnabled ? 1.0f : 0.0f,
0.0f,
};
// --- Shadow map ---
if (m_shadowsEnabled)
{
const bx::Vec3 lightDir = bx::normalize(bx::Vec3{ sunDir[0], sunDir[1], sunDir[2] });
const bx::Vec3 center = { 0.0f, kAmplitude * 0.35f, 0.0f };
const float lightDist = kWorldSize * 1.2f;
const bx::Vec3 lightEye = bx::mad(lightDir, -lightDist, center);
const bx::Vec3 up = (bx::abs(lightDir.y) > 0.99f)
? bx::Vec3{ 0.0f, 0.0f, 1.0f }
: bx::Vec3{ 0.0f, 1.0f, 0.0f };
float lightView[16];
bx::mtxLookAt(lightView, lightEye, center, up);
float lightProj[16];
bx::mtxOrtho(lightProj, -shadowExtent, shadowExtent, -shadowExtent, shadowExtent, 0.1f, 2.0f*lightDist + 2.0f*kAmplitude, 0.0f, bgfx::getCaps()->homogeneousDepth);
float lightViewProj[16];
bx::mtxMul(lightViewProj, lightView, lightProj);
// bias matrix
const bool originBottomLeft = bgfx::getCaps()->originBottomLeft;
const bool homogeneousDepth = bgfx::getCaps()->homogeneousDepth;
const float sy = originBottomLeft ? 0.5f : -0.5f;
const float sz = homogeneousDepth ? 0.5f : 1.0f;
const float tz = homogeneousDepth ? 0.5f : 0.0f;
const float mtxCrop[16] =
{
0.5f, 0.0f, 0.0f, 0.0f,
0.0f, sy, 0.0f, 0.0f,
0.0f, 0.0f, sz, 0.0f,
0.5f, 0.5f, tz, 1.0f,
};
bx::mtxMul(m_lightMtx, lightViewProj, mtxCrop);
bgfx::setViewFrameBuffer(kViewShadow, m_shadowFb);
bgfx::setViewRect(kViewShadow, 0, 0, kShadowMapSize, kShadowMapSize);
bgfx::setUniform(u_lightViewProj, lightViewProj);
bgfx::setUniform(u_lightMtx, m_lightMtx);
bgfx::setVertexBuffer(0, m_terrain_vbh);
bgfx::setIndexBuffer(m_terrain_ibh);
bgfx::setState(BGFX_STATE_WRITE_R | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS);
bgfx::submit(kViewShadow, m_shadowProgram);
}
// --- Sky pass ---
bgfx::setViewRect(kViewSky, 0, 0, uint16_t(m_width), uint16_t(m_height) );
setAtmosphereUniforms();
bgfx::setUniform(u_invViewProj, invViewProj);
bgfx::setUniform(u_skyParams, skyParams);
bgfx::setUniform(u_sunDirection, sunDir);
bgfx::setUniform(u_sunRadiance, sunRadiance);
bgfx::setTexture(0, s_skyView, m_skyviewLut);
bgfx::setTexture(1, s_atmoTransmittance, m_transmittanceLut);
bgfx::setVertexBuffer(0, m_quad_vbh);
bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
bgfx::submit(kViewSky, m_skyProgram);
// --- Terrain pass ---
bgfx::setViewRect(kViewTerrain, 0, 0, uint16_t(m_width), uint16_t(m_height) );
bgfx::setViewTransform(kViewTerrain, view, proj);
const float camPos4[4] = { camPos.x, camPos.y, camPos.z, 0.0f };
const float aerial[4] = { m_aerialPerspectiveInscatter, m_aerialPerspectiveDensity, m_aerialPerspectiveambientStrength, 0.0f };
setAtmosphereUniforms();
bgfx::setUniform(u_sunDirection, sunDir);
bgfx::setUniform(u_sunRadiance, sunRadiance);
bgfx::setUniform(u_cameraPos, camPos4);
bgfx::setUniform(u_skyParams, skyParams);
bgfx::setUniform(u_aerialParams, aerial);
bgfx::setUniform(u_shadowParams, shadowParams);
bgfx::setUniform(u_lightMtx, m_lightMtx);
bgfx::setTexture(0, s_atmoTransmittance, m_transmittanceLut);
bgfx::setTexture(1, s_atmoMultiscatter, m_multiscatterLut);
bgfx::setTexture(2, s_skyView, m_skyviewLut);
bgfx::setTexture(3, s_grass, m_grassTx);
bgfx::setTexture(4, s_rock, m_rockTx);
bgfx::setTexture(5, s_shadowMap, m_shadowColor);
bgfx::setVertexBuffer(0, m_terrain_vbh);
bgfx::setIndexBuffer(m_terrain_ibh);
bgfx::setState(0
| BGFX_STATE_WRITE_RGB
| BGFX_STATE_WRITE_A
| BGFX_STATE_WRITE_Z
| BGFX_STATE_DEPTH_TEST_LESS
);
bgfx::submit(kViewTerrain, m_terrainProgram);
bgfx::frame();
return true;
}
return false;
}
void buildTerrain() {
bimg::ImageContainer* image = imageLoad("textures/heightmap.exr", bgfx::TextureFormat::R32F);
const uint32_t w = image->m_width;
const uint32_t h = image->m_height;
const float* src = (const float*)image->m_data;
float hmin = src[0];
float hmax = src[0];
for (uint32_t ii = 0, num = w * h; ii < num; ++ii)
{
hmin = bx::min(hmin, src[ii]);
hmax = bx::max(hmax, src[ii]);
}
float range = hmax - hmin;
if (range < 1e-6f)
{
range = 1.0f;
}
const float cellSize = kWorldSize / float(w - 1);
const float originX = -0.5f * kWorldSize;
const float originZ = -0.5f * cellSize * float(h - 1);
const uint32_t numVertices = w * h;
const bgfx::Memory* vbMem = bgfx::alloc(uint32_t(numVertices * sizeof(PosNormalVertex)));
PosNormalVertex* vertices = (PosNormalVertex*)vbMem->data;
for (uint32_t z = 0; z < h; ++z)
{
for (uint32_t x = 0; x < w; ++x)
{
const float height = (src[z * w + x]) / range * kAmplitude;
PosNormalVertex& v = vertices[z * w + x];
v.m_x = originX + float(x) * cellSize;
v.m_y = height;
v.m_z = originZ + float(z) * cellSize;
}
}
bimg::imageFree(image);
for (uint32_t z = 0; z < h; ++z)
{
for (uint32_t x = 0; x < w; ++x)
{
const uint32_t xl = (x > 0 ) ? x - 1 : x;
const uint32_t xr = (x < w - 1) ? x + 1 : x;
const uint32_t zd = (z > 0 ) ? z - 1 : z;
const uint32_t zu = (z < h - 1) ? z + 1 : z;
const float hL = vertices[z * w + xl].m_y;
const float hR = vertices[z * w + xr].m_y;
const float hD = vertices[zd * w + x].m_y;
const float hU = vertices[zu * w + x].m_y;
const float dx = hR - hL;
const float dz = hU - hD;
const bx::Vec3 normal = bx::normalize({ -dx, 2.0f * cellSize, -dz });
PosNormalVertex& v = vertices[z*w + x];
v.m_nx = normal.x;
v.m_ny = normal.y;
v.m_nz = normal.z;
}
}
const uint32_t numIndices = (w - 1) * (h - 1) * 6;
const bgfx::Memory* ibMem = bgfx::alloc(uint32_t(numIndices * sizeof(uint32_t)));
uint32_t* indices = (uint32_t*)ibMem->data;
uint32_t offset = 0;
for (uint32_t z = 0; z < h - 1; ++z)
{
for (uint32_t x = 0; x < w - 1; ++x)
{
const uint32_t i0 = z * w + x;
const uint32_t i1 = z * w + x + 1;
const uint32_t i2 = (z + 1) * w + x;
const uint32_t i3 = (z + 1) * w + x + 1;
indices[offset++] = i0;
indices[offset++] = i2;
indices[offset++] = i1;
indices[offset++] = i1;
indices[offset++] = i2;
indices[offset++] = i3;
}
}
m_terrain_vbh = bgfx::createVertexBuffer(vbMem, PosNormalVertex::ms_layout);
m_terrain_ibh = bgfx::createIndexBuffer(ibMem, BGFX_BUFFER_INDEX32);
}
void buildQuad() {
const bgfx::Memory* mem = bgfx::alloc(6 * sizeof(float) );
float* pos = (float*)mem->data;
pos[0] = -1.0f; pos[1] = -1.0f;
pos[2] = 3.0f; pos[3] = -1.0f;
pos[4] = -1.0f; pos[5] = 3.0f;
bgfx::VertexLayout layout;
layout
.begin()
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
.end();
m_quad_vbh = bgfx::createVertexBuffer(mem, layout);
}
entry::MouseState m_mouseState;
bgfx::VertexBufferHandle m_quad_vbh;
bgfx::VertexBufferHandle m_terrain_vbh;
bgfx::IndexBufferHandle m_terrain_ibh;
bgfx::ProgramHandle m_terrainProgram;
bgfx::ProgramHandle m_skyProgram;
bgfx::ProgramHandle m_shadowProgram;
bgfx::ProgramHandle m_csTransmittance;
bgfx::ProgramHandle m_csMultiscatter;
bgfx::ProgramHandle m_csSkyview;
bgfx::TextureHandle m_transmittanceLut;
bgfx::TextureHandle m_multiscatterLut;
bgfx::TextureHandle m_skyviewLut;
bgfx::TextureHandle m_grassTx;
bgfx::TextureHandle m_rockTx;
bgfx::TextureHandle m_shadowColor;
bgfx::TextureHandle m_shadowDepth;
bgfx::FrameBufferHandle m_shadowFb;
bgfx::UniformHandle u_atmoRayleigh;
bgfx::UniformHandle u_atmoMie;
bgfx::UniformHandle u_atmoOzone;
bgfx::UniformHandle u_atmoPlanet;
bgfx::UniformHandle u_atmoGround;
bgfx::UniformHandle u_atmoParams;
bgfx::UniformHandle u_atmoParams2;
bgfx::UniformHandle u_skyParams;
bgfx::UniformHandle u_sunDirection;
bgfx::UniformHandle u_sunRadiance;
bgfx::UniformHandle u_invViewProj;
bgfx::UniformHandle u_cameraPos;
bgfx::UniformHandle u_aerialParams;
bgfx::UniformHandle u_shadowParams;
bgfx::UniformHandle u_lightViewProj;
bgfx::UniformHandle u_lightMtx;
bgfx::UniformHandle s_atmoTransmittance;
bgfx::UniformHandle s_atmoMultiscatter;
bgfx::UniformHandle s_skyView;
bgfx::UniformHandle s_shadowMap;
bgfx::UniformHandle s_grass;
bgfx::UniformHandle s_rock;
float m_lightMtx[16];
FrameTime m_frameTime;
bool m_computeSupported = true;
float m_sunElevationDeg = 11.0f;
float m_sunAzimuthDeg = 155.0f;
float m_sunIntensity = 10.0f;
bool m_shadowsEnabled = true;
float m_rayleighColor[3] = { 0.1753f, 0.4096f, 1.0f }; // * scale = earth
float m_rayleighScale = 0.0331f;
float m_rayleighScaleHeight = 8.0f;
float m_mieScatter = 0.003996f;
float m_mieAbsorption = 0.000444f;
float m_mieScaleHeight = 1.2f;
float m_miePhaseG = 0.8f;
float m_ozoneColor[3] = { 0.3456f, 1.0f, 0.0452f }; // * scale = earth
float m_ozoneScale = 0.001881f;
float m_multiscatter = 1.0f;
float m_planetRadius = 6360.0f;
float m_atmosHeight = 100.0f;
float m_ozoneCenter = 25.0f;
float m_ozoneHalf = 15.0f;
float m_groundAlbedo[3] = { 0.3f, 0.3f, 0.3f };
float m_aerialPerspectiveInscatter = 1.0f;
float m_aerialPerspectiveDensity = 1.0f;
float m_aerialPerspectiveambientStrength = 1.0f;
uint32_t m_width;
uint32_t m_height;
uint32_t m_debug;
uint32_t m_reset;
};
} // namespace
ENTRY_IMPLEMENT_MAIN(
ExampleSky2
, "53-sky2"
, "Hillaire sky/atmosphere."
, "https://bkaradzic.github.io/bgfx/examples.html#sky2"
);