Rewrite Radiance and Reflection probes to use Octahedral maps.

Co-authored-by: clayjohn <claynjohn@gmail.com>
This commit is contained in:
Dario 2025-06-13 16:03:49 -03:00 committed by clayjohn
parent 25203e24c4
commit c78c3ba894
48 changed files with 1513 additions and 1557 deletions

View file

@ -4,6 +4,8 @@
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define FLAG_HORIZONTAL (1 << 0)
@ -35,16 +37,17 @@ layout(push_constant, std430) uniform Params {
// DOF.
float camera_z_far;
float camera_z_near;
uint pad2[2];
// Octmap.
vec2 octmap_border_size;
vec4 set_color;
}
params;
#ifdef MODE_CUBEMAP_ARRAY_TO_PANORAMA
layout(set = 0, binding = 0) uniform samplerCubeArray source_color;
#elif defined(MODE_CUBEMAP_TO_PANORAMA)
layout(set = 0, binding = 0) uniform samplerCube source_color;
#ifdef MODE_OCTMAP_ARRAY_TO_PANORAMA
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
#elif defined(MODE_OCTMAP_TO_PANORAMA)
layout(set = 0, binding = 0) uniform sampler2D source_color;
#elif !defined(MODE_SET_COLOR)
layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif
@ -262,7 +265,7 @@ void main() {
imageStore(dest_buffer, pos + params.target, color);
#endif // MODE_LINEARIZE_DEPTH_COPY
#if defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
#if defined(MODE_OCTMAP_TO_PANORAMA) || defined(MODE_OCTMAP_ARRAY_TO_PANORAMA)
const float PI = 3.14159265359;
vec2 uv = vec2(pos) / vec2(params.section.zw);
@ -277,13 +280,13 @@ void main() {
normal.y = cos(theta);
normal.z = cos(phi) * sin(theta) * -1.0;
#ifdef MODE_CUBEMAP_TO_PANORAMA
vec4 color = textureLod(source_color, normal, params.camera_z_far); //the biggest the lod the least the acne
#ifdef MODE_OCTMAP_TO_PANORAMA
vec4 color = textureLod(source_color, vec3_to_oct_with_border(normal, params.octmap_border_size), params.camera_z_far); //the biggest the lod the least the acne
#else
vec4 color = textureLod(source_color, vec4(normal, params.camera_z_far), 0.0); //the biggest the lod the least the acne
vec4 color = textureLod(source_color, vec3(vec3_to_oct_with_border(normal, params.octmap_border_size), params.camera_z_far), 0.0); //the biggest the lod the least the acne
#endif
imageStore(dest_buffer, pos + params.target, color * params.luminance_multiplier);
#endif // defined(MODE_CUBEMAP_TO_PANORAMA) || defined(MODE_CUBEMAP_ARRAY_TO_PANORAMA)
#endif // defined(MODE_OCTMAP_TO_PANORAMA) || defined(MODE_OCTMAP_ARRAY_TO_PANORAMA)
#ifdef MODE_SET_COLOR
imageStore(dest_buffer, pos + params.target, params.set_color);

View file

@ -0,0 +1,36 @@
#[vertex]
#version 450
#VERSION_DEFINES
layout(location = 0) out vec2 uv_interp;
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 0.0, 1.0);
}
#[fragment]
#version 450
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(push_constant, std430) uniform Params {
float border_size;
}
params;
void main() {
vec3 dir = oct_to_vec3_with_border(uv_interp * 0.5 + 0.5, params.border_size);
frag_color = vec4(texture(source_cube, dir).rgb, 1.0);
}

View file

@ -1,145 +0,0 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define BLOCK_SIZE 8
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
#include "cubemap_downsampler_inc.glsl"
void main() {
uvec3 id = gl_GlobalInvocationID;
uint face_size = params.face_size;
if (id.x < face_size && id.y < face_size) {
float inv_face_size = 1.0 / float(face_size);
float u0 = (float(id.x) * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
float u1 = (float(id.x) * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
float v0 = (float(id.y) * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
float v1 = (float(id.y) * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + .125;
}
vec3 dir;
vec4 color;
switch (id.z) {
case 0:
get_dir_0(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_0(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_0(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_0(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 1:
get_dir_1(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_1(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_1(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_1(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 2:
get_dir_2(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_2(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_2(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_2(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 3:
get_dir_3(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_3(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_3(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_3(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 4:
get_dir_4(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_4(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_4(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_4(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
default:
get_dir_5(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_5(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_5(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_5(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
}
imageStore(dest_cubemap, ivec3(id), color);
}
}

View file

@ -1,48 +0,0 @@
layout(push_constant, std430) uniform Params {
uint face_size;
uint face_id; // only used in raster shader
}
params;
#define M_PI 3.14159265359
void get_dir_0(out vec3 dir, in float u, in float v) {
dir[0] = 1.0;
dir[1] = v;
dir[2] = -u;
}
void get_dir_1(out vec3 dir, in float u, in float v) {
dir[0] = -1.0;
dir[1] = v;
dir[2] = u;
}
void get_dir_2(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = 1.0;
dir[2] = -v;
}
void get_dir_3(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = -1.0;
dir[2] = v;
}
void get_dir_4(out vec3 dir, in float u, in float v) {
dir[0] = u;
dir[1] = v;
dir[2] = 1.0;
}
void get_dir_5(out vec3 dir, in float u, in float v) {
dir[0] = -u;
dir[1] = v;
dir[2] = -1.0;
}
float calcWeight(float u, float v) {
float val = u * u + v * v + 1.0;
return val * sqrt(val);
}

View file

@ -1,161 +0,0 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* clang-format off */
#[vertex]
#version 450
#VERSION_DEFINES
#include "cubemap_downsampler_inc.glsl"
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0 * float(params.face_size); // saturate(x) * 2.0
}
/* clang-format off */
#[fragment]
#version 450
#VERSION_DEFINES
#include "cubemap_downsampler_inc.glsl"
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
/* clang-format on */
void main() {
// Converted from compute shader which uses absolute coordinates.
// Could possibly simplify this
float face_size = float(params.face_size);
float inv_face_size = 1.0 / face_size;
vec2 id = floor(uv_interp);
float u1 = (id.x * 2.0 + 1.0 + 0.75) * inv_face_size - 1.0;
float u0 = (id.x * 2.0 + 1.0 - 0.75) * inv_face_size - 1.0;
float v0 = (id.y * 2.0 + 1.0 - 0.75) * -inv_face_size + 1.0;
float v1 = (id.y * 2.0 + 1.0 + 0.75) * -inv_face_size + 1.0;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + .125;
}
vec3 dir;
vec4 color;
switch (params.face_id) {
case 0:
get_dir_0(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_0(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_0(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_0(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 1:
get_dir_1(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_1(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_1(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_1(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 2:
get_dir_2(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_2(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_2(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_2(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 3:
get_dir_3(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_3(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_3(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_3(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
case 4:
get_dir_4(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_4(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_4(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_4(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
default:
get_dir_5(dir, u0, v0);
color = textureLod(source_cubemap, normalize(dir), 0.0) * weights[0];
get_dir_5(dir, u1, v0);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[1];
get_dir_5(dir, u0, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[2];
get_dir_5(dir, u1, v1);
color += textureLod(source_cubemap, normalize(dir), 0.0) * weights[3];
break;
}
frag_color = color;
}

View file

@ -1,329 +0,0 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 64
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly imageCube dest_cubemap0;
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly imageCube dest_cubemap1;
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly imageCube dest_cubemap2;
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly imageCube dest_cubemap3;
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly imageCube dest_cubemap4;
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly imageCube dest_cubemap5;
layout(rgba16f, set = 2, binding = 6) uniform restrict writeonly imageCube dest_cubemap6;
#ifdef USE_HIGH_QUALITY
#define NUM_TAPS 32
#else
#define NUM_TAPS 8
#endif
#define BASE_RESOLUTION 128
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
}
data;
#else
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][6] coeffs;
}
data;
#endif
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
switch (face) {
case 0:
dir = vec3(1.0, uv[1], -uv[0]);
break;
case 1:
dir = vec3(-1.0, uv[1], uv[0]);
break;
case 2:
dir = vec3(uv[0], 1.0, -uv[1]);
break;
case 3:
dir = vec3(uv[0], -1.0, uv[1]);
break;
case 4:
dir = vec3(uv[0], uv[1], 1.0);
break;
default:
dir = vec3(-uv[0], uv[1], -1.0);
break;
}
}
void main() {
// INPUT:
// id.x = the linear address of the texel (ignoring face)
// id.y = the face
// -> use to index output texture
// id.x = texel x
// id.y = texel y
// id.z = face
uvec3 id = gl_GlobalInvocationID;
// determine which texel this is
#ifndef USE_TEXTURE_ARRAY
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
int mip_level = 0;
if (id.x < (128 * 128)) {
mip_level = 0;
} else if (id.x < (128 * 128 + 64 * 64)) {
mip_level = 1;
id.x -= (128 * 128);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32)) {
mip_level = 2;
id.x -= (128 * 128 + 64 * 64);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16)) {
mip_level = 3;
id.x -= (128 * 128 + 64 * 64 + 32 * 32);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8)) {
mip_level = 4;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4)) {
mip_level = 5;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8);
} else if (id.x < (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4 + 2 * 2)) {
mip_level = 6;
id.x -= (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 + 4 * 4);
} else {
return;
}
int res = BASE_RESOLUTION >> mip_level;
#else // Using Texture Arrays so all levels are the same resolution
int res = BASE_RESOLUTION;
int mip_level = int(id.x / (BASE_RESOLUTION * BASE_RESOLUTION));
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
#endif
// determine dir / pos for the texel
vec3 dir, adir, frameZ;
{
id.z = id.y;
id.y = id.x / res;
id.x -= id.y * res;
vec2 uv;
uv.x = (float(id.x) * 2.0 + 1.0) / float(res) - 1.0;
uv.y = -(float(id.y) * 2.0 + 1.0) / float(res) + 1.0;
get_dir(dir, uv, id.z);
frameZ = normalize(dir);
adir = abs(dir);
}
// GGX gather colors
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
switch (axis) {
case 0:
UpVector = vec3(1, 0, 0);
break;
case 1:
UpVector = vec3(0, 1, 0);
break;
default:
UpVector = vec3(0, 0, 1);
break;
}
vec3 frameX = normalize(cross(UpVector, frameZ));
vec3 frameY = cross(frameZ, frameX);
// calculate parametrization for polynomial
float Nx = dir[otherAxis0];
float Ny = dir[otherAxis1];
float Nz = adir[axis];
float NmaxXY = max(abs(Ny), abs(Nx));
Nx /= NmaxXY;
Ny /= NmaxXY;
float theta;
if (Ny < Nx) {
if (Ny <= -0.999) {
theta = Nx;
} else {
theta = Ny;
}
} else {
if (Ny >= 0.999) {
theta = -Nx;
} else {
theta = -Ny;
}
}
float phi;
if (Nz <= -0.999) {
phi = -NmaxXY;
} else if (Nz >= 0.999) {
phi = NmaxXY;
} else {
phi = Nz;
}
float theta2 = theta * theta;
float phi2 = phi * phi;
// sample
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
#ifdef USE_HIGH_QUALITY
vec4 coeffsDir0[3];
vec4 coeffsDir1[3];
vec4 coeffsDir2[3];
vec4 coeffsLevel[3];
vec4 coeffsWeight[3];
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
coeffsDir0[iCoeff] = data.coeffs[mip_level][0][iCoeff][index];
coeffsDir1[iCoeff] = data.coeffs[mip_level][1][iCoeff][index];
coeffsDir2[iCoeff] = data.coeffs[mip_level][2][iCoeff][index];
coeffsLevel[iCoeff] = data.coeffs[mip_level][3][iCoeff][index];
coeffsWeight[iCoeff] = data.coeffs[mip_level][4][iCoeff][index];
}
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
#else
vec4 coeffsDir0 = data.coeffs[mip_level][0][index];
vec4 coeffsDir1 = data.coeffs[mip_level][1][index];
vec4 coeffsDir2 = data.coeffs[mip_level][2][index];
vec4 coeffsLevel = data.coeffs[mip_level][3][index];
vec4 coeffsWeight = data.coeffs[mip_level][4][index];
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, mip_level)
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
float sample_level = coeffsLevel[iSubTap];
float sample_weight = coeffsWeight[iSubTap];
#endif
sample_weight *= frameweight;
// adjust for jacobian
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
#ifndef USE_TEXTURE_ARRAY
sample_level += float(mip_level) / 6.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
#endif
// sample cubemap
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
color.w += sample_weight;
}
}
}
}
color /= color.w;
// write color
color.xyz = max(vec3(0.0), color.xyz);
color.w = 1.0;
#ifdef USE_TEXTURE_ARRAY
id.xy *= uvec2(2, 2);
#endif
switch (mip_level) {
case 0:
imageStore(dest_cubemap0, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap0, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 1:
imageStore(dest_cubemap1, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap1, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 2:
imageStore(dest_cubemap2, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap2, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 3:
imageStore(dest_cubemap3, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap3, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 4:
imageStore(dest_cubemap4, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap4, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
case 5:
imageStore(dest_cubemap5, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap5, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
default:
imageStore(dest_cubemap6, ivec3(id), color);
#ifdef USE_TEXTURE_ARRAY
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 0.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(0.0, 1.0, 0.0), color);
imageStore(dest_cubemap6, ivec3(id) + ivec3(1.0, 1.0, 0.0), color);
#endif
break;
}
}

View file

@ -1,63 +0,0 @@
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 8
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly imageCube dest_cubemap;
#include "cubemap_roughness_inc.glsl"
void main() {
uvec3 id = gl_GlobalInvocationID;
id.z += params.face_id;
vec2 uv = ((vec2(id.xy) * 2.0 + 1.0) / (params.face_size) - 1.0);
vec3 N = texelCoordToVec(uv, id.z);
if (params.use_direct_write) {
imageStore(dest_cubemap, ivec3(id), vec4(texture(source_cube, N).rgb, 1.0));
} else {
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size);
float roughness2 = params.roughness * params.roughness;
float roughness4 = roughness2 * roughness2;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
mat3 T;
T[0] = normalize(cross(UpVector, N));
T[1] = cross(N, T[0]);
T[2] = N;
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
vec2 xi = Hammersley(sampleNum, params.sample_count);
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
float NdotH = dot(N, H);
vec3 L = (2.0 * NdotH * H - N);
float ndotl = clamp(dot(N, L), 0.0, 1.0);
if (ndotl > 0.0) {
float D = DistributionGGX(NdotH, roughness4);
float pdf = D * NdotH / (4.0 * NdotH) + 0.0001;
float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001);
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl;
sum.a += ndotl;
}
}
sum /= sum.a;
imageStore(dest_cubemap, ivec3(id), vec4(sum.rgb, 1.0));
}
}

View file

@ -1,84 +0,0 @@
#define M_PI 3.14159265359
layout(push_constant, std430) uniform Params {
uint face_id;
uint sample_count;
float roughness;
bool use_direct_write;
float face_size;
}
params;
vec3 texelCoordToVec(vec2 uv, uint faceID) {
mat3 faceUvVectors[6];
// -x
faceUvVectors[1][0] = vec3(0.0, 0.0, 1.0); // u -> +z
faceUvVectors[1][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[1][2] = vec3(-1.0, 0.0, 0.0); // -x face
// +x
faceUvVectors[0][0] = vec3(0.0, 0.0, -1.0); // u -> -z
faceUvVectors[0][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[0][2] = vec3(1.0, 0.0, 0.0); // +x face
// -y
faceUvVectors[3][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[3][1] = vec3(0.0, 0.0, -1.0); // v -> -z
faceUvVectors[3][2] = vec3(0.0, -1.0, 0.0); // -y face
// +y
faceUvVectors[2][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[2][1] = vec3(0.0, 0.0, 1.0); // v -> +z
faceUvVectors[2][2] = vec3(0.0, 1.0, 0.0); // +y face
// -z
faceUvVectors[5][0] = vec3(-1.0, 0.0, 0.0); // u -> -x
faceUvVectors[5][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[5][2] = vec3(0.0, 0.0, -1.0); // -z face
// +z
faceUvVectors[4][0] = vec3(1.0, 0.0, 0.0); // u -> +x
faceUvVectors[4][1] = vec3(0.0, -1.0, 0.0); // v -> -y
faceUvVectors[4][2] = vec3(0.0, 0.0, 1.0); // +z face
// out = u * s_faceUv[0] + v * s_faceUv[1] + s_faceUv[2].
vec3 result = (faceUvVectors[faceID][0] * uv.x) + (faceUvVectors[faceID][1] * uv.y) + faceUvVectors[faceID][2];
return normalize(result);
}
vec3 ImportanceSampleGGX(vec2 xi, float roughness4) {
// Compute distribution direction
float Phi = 2.0 * M_PI * xi.x;
float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
// Convert to spherical direction
vec3 H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
return H;
}
float DistributionGGX(float NdotH, float roughness4) {
float NdotH2 = NdotH * NdotH;
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
denom = M_PI * denom * denom;
return roughness4 / denom;
}
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N) {
return vec2(float(i) / float(N), radicalInverse_VdC(i));
}

View file

@ -0,0 +1,83 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define BLOCK_SIZE 8
#include "../oct_inc.glsl"
layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D dest_octmap;
layout(push_constant, std430) uniform Params {
float border_size;
uint size;
uint pad;
uint pad2;
}
params;
// Use an approximation of the Jacobian.
float calcWeight(float u, float v) {
vec3 d = oct_to_vec3_with_border(vec2(u, v), params.border_size);
return 1.0 / pow(abs(d.z) + 1.0, 3.0);
}
void main() {
uvec2 id = gl_GlobalInvocationID.xy;
if (id.x < params.size && id.y < params.size) {
float inv_size = 1.0 / float(params.size);
uvec2 sample_id = id;
#ifdef USE_HIGH_QUALITY
float u0 = (float(sample_id.x) * 2.0f + 1.0f - 0.75f) * inv_size - 1.0f;
float u1 = (float(sample_id.x) * 2.0f + 1.0f + 0.75f) * inv_size - 1.0f;
float v0 = (float(sample_id.y) * 2.0f + 1.0f - 0.75f) * inv_size - 1.0f;
float v1 = (float(sample_id.y) * 2.0f + 1.0f + 0.75f) * inv_size - 1.0f;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + 0.125;
}
vec4 color = textureLod(source_octmap, vec2(u0, v0) * 0.5f + 0.5f, 0.0) * weights[0];
color += textureLod(source_octmap, vec2(u1, v0) * 0.5f + 0.5f, 0.0) * weights[1];
color += textureLod(source_octmap, vec2(u0, v1) * 0.5f + 0.5f, 0.0) * weights[2];
color += textureLod(source_octmap, vec2(u1, v1) * 0.5f + 0.5f, 0.0) * weights[3];
imageStore(dest_octmap, ivec2(id), color);
#else
vec2 uv = (vec2(sample_id.xy) + 0.5) * inv_size;
imageStore(dest_octmap, ivec2(id), textureLod(source_octmap, uv, 0.0));
#endif
}
}

View file

@ -0,0 +1,94 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/* clang-format off */
#[vertex]
#version 450
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(push_constant, std430) uniform Params {
uint size;
}
params;
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
}
/* clang-format off */
#[fragment]
#version 450
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(push_constant, std430) uniform Params {
uint size;
}
params;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
/* clang-format on */
float calcWeight(float u, float v) {
float val = u * u + v * v + 1.0;
return val * sqrt(val);
}
void main() {
#ifdef USE_HIGH_QUALITY
float inv_size = 1.0 / float(params.size);
float u0 = (uv_interp.x * 2.0f - 0.75) * inv_size - 1.0f;
float u1 = (uv_interp.x * 2.0f + 0.75) * inv_size - 1.0f;
float v0 = (uv_interp.y * 2.0f - 0.75) * inv_size - 1.0f;
float v1 = (uv_interp.y * 2.0f + 0.75) * inv_size - 1.0f;
float weights[4];
weights[0] = calcWeight(u0, v0);
weights[1] = calcWeight(u1, v0);
weights[2] = calcWeight(u0, v1);
weights[3] = calcWeight(u1, v1);
const float wsum = 0.5 / (weights[0] + weights[1] + weights[2] + weights[3]);
for (int i = 0; i < 4; i++) {
weights[i] = weights[i] * wsum + 0.125;
}
frag_color = textureLod(source_octmap, vec2(u0, v0) * 0.5f + 0.5f, 0.0) * weights[0];
frag_color += textureLod(source_octmap, vec2(u1, v0) * 0.5f + 0.5f, 0.0) * weights[1];
frag_color += textureLod(source_octmap, vec2(u0, v1) * 0.5f + 0.5f, 0.0) * weights[2];
frag_color += textureLod(source_octmap, vec2(u1, v1) * 0.5f + 0.5f, 0.0) * weights[3];
#else
frag_color = textureLod(source_octmap, uv_interp, 0.0);
#endif
}

View file

@ -0,0 +1,258 @@
// Copyright 2016 Activision Publishing, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 64
#include "../oct_inc.glsl"
layout(local_size_x = GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(rgba16f, set = 2, binding = 0) uniform restrict writeonly image2D dest_octmap0;
layout(rgba16f, set = 2, binding = 1) uniform restrict writeonly image2D dest_octmap1;
layout(rgba16f, set = 2, binding = 2) uniform restrict writeonly image2D dest_octmap2;
layout(rgba16f, set = 2, binding = 3) uniform restrict writeonly image2D dest_octmap3;
layout(rgba16f, set = 2, binding = 4) uniform restrict writeonly image2D dest_octmap4;
layout(rgba16f, set = 2, binding = 5) uniform restrict writeonly image2D dest_octmap5;
#ifdef USE_HIGH_QUALITY
#define NUM_TAPS 32
#else
#define NUM_TAPS 8
#endif
layout(push_constant, std430) uniform Params {
vec2 border_size;
vec2 pad;
}
params;
#define BASE_RESOLUTION 320
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
}
data;
#else
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][6] coeffs;
}
data;
#endif
void main() {
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
uvec2 id = gl_GlobalInvocationID.xy;
uint mip_level = 0;
#ifndef USE_TEXTURE_ARRAY
uint res = BASE_RESOLUTION;
while ((id.x >= (res * res)) && (res > 1)) {
id.x -= res * res;
res = res >> 1;
mip_level++;
}
#else // Using Texture Arrays so all levels are the same resolution
uint res = BASE_RESOLUTION;
mip_level = id.x / (BASE_RESOLUTION * BASE_RESOLUTION);
id.x -= mip_level * BASE_RESOLUTION * BASE_RESOLUTION;
#endif
// Determine the direction from the texel's position.
id.y = id.x / res;
id.x -= id.y * res;
vec2 inv_res = 1.0 / vec2(res);
vec3 dir = oct_to_vec3_with_border((vec2(id.xy) + vec2(0.5)) * inv_res, params.border_size.y);
vec3 adir = abs(dir);
vec3 frameZ = dir;
// Gather colors using GGX.
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
const float lowerBound = 0.57735; // 1 / sqrt(3), magnitude for each component on a vector where all the components are equal.
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - lowerBound) / (1.0 - lowerBound);
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
switch (axis) {
case 0:
UpVector = vec3(1, 0, 0);
break;
case 1:
UpVector = vec3(0, 1, 0);
break;
default:
UpVector = vec3(0, 0, 1);
break;
}
vec3 frameX = normalize(cross(UpVector, frameZ));
vec3 frameY = cross(frameZ, frameX);
// Calculate parametrization for polynomial.
float Nx = dir[otherAxis0];
float Ny = dir[otherAxis1];
float Nz = adir[axis];
float NmaxXY = max(abs(Ny), abs(Nx));
Nx /= NmaxXY;
Ny /= NmaxXY;
float theta;
if (Ny < Nx) {
if (Ny <= -0.999) {
theta = Nx;
} else {
theta = Ny;
}
} else {
if (Ny >= 0.999) {
theta = -Nx;
} else {
theta = -Ny;
}
}
float phi;
if (Nz <= -0.999) {
phi = -NmaxXY;
} else if (Nz >= 0.999) {
phi = NmaxXY;
} else {
phi = Nz;
}
float theta2 = theta * theta;
float phi2 = phi * phi;
// Sample. The coefficient table was computed with less mip levels than required, so we clamp the maximum level.
uint coeff_mip_level = min(mip_level, 5);
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
#ifdef USE_HIGH_QUALITY
vec4 coeffsDir0[3];
vec4 coeffsDir1[3];
vec4 coeffsDir2[3];
vec4 coeffsLevel[3];
vec4 coeffsWeight[3];
for (int iCoeff = 0; iCoeff < 3; iCoeff++) {
coeffsDir0[iCoeff] = data.coeffs[coeff_mip_level][0][iCoeff][index];
coeffsDir1[iCoeff] = data.coeffs[coeff_mip_level][1][iCoeff][index];
coeffsDir2[iCoeff] = data.coeffs[coeff_mip_level][2][iCoeff][index];
coeffsLevel[iCoeff] = data.coeffs[coeff_mip_level][3][iCoeff][index];
coeffsWeight[iCoeff] = data.coeffs[coeff_mip_level][4][iCoeff][index];
}
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// Determine sample attributes (dir, weight, coeff_mip_level)
vec3 sample_dir = frameX * (coeffsDir0[0][iSubTap] + coeffsDir0[1][iSubTap] * theta2 + coeffsDir0[2][iSubTap] * phi2) + frameY * (coeffsDir1[0][iSubTap] + coeffsDir1[1][iSubTap] * theta2 + coeffsDir1[2][iSubTap] * phi2) + frameZ * (coeffsDir2[0][iSubTap] + coeffsDir2[1][iSubTap] * theta2 + coeffsDir2[2][iSubTap] * phi2);
float sample_level = coeffsLevel[0][iSubTap] + coeffsLevel[1][iSubTap] * theta2 + coeffsLevel[2][iSubTap] * phi2;
float sample_weight = coeffsWeight[0][iSubTap] + coeffsWeight[1][iSubTap] * theta2 + coeffsWeight[2][iSubTap] * phi2;
#else
vec4 coeffsDir0 = data.coeffs[coeff_mip_level][0][index];
vec4 coeffsDir1 = data.coeffs[coeff_mip_level][1][index];
vec4 coeffsDir2 = data.coeffs[coeff_mip_level][2][index];
vec4 coeffsLevel = data.coeffs[coeff_mip_level][3][index];
vec4 coeffsWeight = data.coeffs[coeff_mip_level][4][index];
for (int iSubTap = 0; iSubTap < 4; iSubTap++) {
// determine sample attributes (dir, weight, coeff_mip_level)
vec3 sample_dir = frameX * coeffsDir0[iSubTap] + frameY * coeffsDir1[iSubTap] + frameZ * coeffsDir2[iSubTap];
float sample_level = coeffsLevel[iSubTap];
float sample_weight = coeffsWeight[iSubTap];
#endif
sample_weight *= frameweight;
#ifdef USE_HIGH_QUALITY
// Adjust for Jacobian.
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
#endif
#ifndef USE_TEXTURE_ARRAY
sample_level += float(mip_level) / 5.0; // Hack to increase the perceived roughness and reduce upscaling artifacts
#endif
// Sample Octmap.
vec2 sample_uv = vec3_to_oct_with_border(normalize(sample_dir), params.border_size);
color.rgb += textureLod(source_octmap, sample_uv, sample_level).rgb * sample_weight;
color.a += sample_weight;
}
}
}
}
// Write out the result.
color = vec4(max(vec3(0.0), color.rgb / color.a), 1.0);
#ifdef USE_TEXTURE_ARRAY
id.xy *= uvec2(2, 2);
#endif
if (mip_level > 5) {
return;
}
#ifdef USE_TEXTURE_ARRAY
#define IMAGE_STORE(x) \
imageStore(x, ivec2(id), color); \
imageStore(x, ivec2(id) + ivec2(1, 0), color); \
imageStore(x, ivec2(id) + ivec2(0, 1), color); \
imageStore(x, ivec2(id) + ivec2(1, 1), color)
#else
#define IMAGE_STORE(x) imageStore(x, ivec2(id), color)
#endif
switch (mip_level) {
case 0:
IMAGE_STORE(dest_octmap0);
break;
case 1:
IMAGE_STORE(dest_octmap1);
break;
case 2:
IMAGE_STORE(dest_octmap2);
break;
case 3:
IMAGE_STORE(dest_octmap3);
break;
case 4:
IMAGE_STORE(dest_octmap4);
break;
case 5:
default:
IMAGE_STORE(dest_octmap5);
break;
}
}

View file

@ -26,8 +26,9 @@
#VERSION_DEFINES
layout(push_constant, std430) uniform Params {
vec2 border_size;
int mip_level;
uint face_id;
int pad;
}
params;
@ -35,9 +36,9 @@ layout(location = 0) out vec2 uv_interp;
/* clang-format on */
void main() {
vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0);
uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0
vec2 base_arr[3] = vec2[](vec2(-1.0, -3.0), vec2(-1.0, 1.0), vec2(3.0, 1.0));
uv_interp = base_arr[gl_VertexIndex];
gl_Position = vec4(uv_interp, 0.0, 1.0);
}
/* clang-format off */
@ -47,13 +48,16 @@ void main() {
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(push_constant, std430) uniform Params {
vec2 border_size;
int mip_level;
uint face_id;
int pad;
}
params;
layout(set = 0, binding = 0) uniform samplerCube source_cubemap;
layout(set = 0, binding = 0) uniform sampler2D source_octmap;
layout(location = 0) in vec2 uv_interp;
layout(location = 0) out vec4 frag_color;
@ -66,8 +70,6 @@ layout(location = 0) out vec4 frag_color;
#define NUM_TAPS 8
#endif
#define BASE_RESOLUTION 128
#ifdef USE_HIGH_QUALITY
layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
vec4[7][5][3][24] coeffs;
@ -80,67 +82,35 @@ layout(set = 1, binding = 0, std430) buffer restrict readonly Data {
data;
#endif
void get_dir(out vec3 dir, in vec2 uv, in uint face) {
switch (face) {
case 0:
dir = vec3(1.0, uv[1], -uv[0]);
break;
case 1:
dir = vec3(-1.0, uv[1], uv[0]);
break;
case 2:
dir = vec3(uv[0], 1.0, -uv[1]);
break;
case 3:
dir = vec3(uv[0], -1.0, uv[1]);
break;
case 4:
dir = vec3(uv[0], uv[1], 1.0);
break;
default:
dir = vec3(-uv[0], uv[1], -1.0);
break;
}
}
void main() {
// determine dir / pos for the texel
vec3 dir, adir, frameZ;
{
vec2 uv;
uv.x = uv_interp.x;
uv.y = 1.0 - uv_interp.y;
uv = uv * 2.0 - 1.0;
get_dir(dir, uv, params.face_id);
frameZ = normalize(dir);
adir = abs(dir);
}
// determine which texel this is
// NOTE (macOS/MoltenVK): Do not rename, "level" variable name conflicts with the Metal "level(float lod)" mipmap sampling function name.
int mip_level = 0;
vec2 uv = uv_interp * 0.5 + 0.5;
if (params.mip_level < 0) {
// return as is
frag_color.rgb = textureLod(source_cubemap, frameZ, 0.0).rgb;
// Just copy the octmap directly.
frag_color.rgb = textureLod(source_octmap, uv, 0.0).rgb;
frag_color.a = 1.0;
return;
} else if (params.mip_level > 6) {
// maximum level
mip_level = 6;
} else if (params.mip_level > 5) {
// Limit the level.
mip_level = 5;
} else {
// Use the level from the push constant.
mip_level = params.mip_level;
}
// GGX gather colors
// Determine the direction from the texel's position.
vec3 dir = oct_to_vec3_with_border(uv, params.border_size.y);
vec3 adir = abs(dir);
vec3 frameZ = dir;
// Gather colors using GGX.
vec4 color = vec4(0.0);
for (int axis = 0; axis < 3; axis++) {
const int otherAxis0 = 1 - (axis & 1) - (axis >> 1);
const int otherAxis1 = 2 - (axis >> 1);
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - .75) / .25;
const float lowerBound = 0.57735; // 1 / sqrt(3), magnitude for each component on a vector where all the components are equal.
float frameweight = (max(adir[otherAxis0], adir[otherAxis1]) - lowerBound) / (1.0 - lowerBound);
if (frameweight > 0.0) {
// determine frame
vec3 UpVector;
@ -195,7 +165,7 @@ void main() {
float theta2 = theta * theta;
float phi2 = phi * phi;
// sample
// Sample.
for (int iSuperTap = 0; iSuperTap < NUM_TAPS / 4; iSuperTap++) {
const int index = (NUM_TAPS / 4) * axis + iSuperTap;
@ -239,21 +209,15 @@ void main() {
sample_weight *= frameweight;
// adjust for jacobian
sample_dir /= max(abs(sample_dir[0]), max(abs(sample_dir[1]), abs(sample_dir[2])));
sample_level += 0.75 * log2(dot(sample_dir, sample_dir));
// sample cubemap
color.xyz += textureLod(source_cubemap, normalize(sample_dir), sample_level).xyz * sample_weight;
color.w += sample_weight;
// Sample Octmap.
vec2 sample_uv = vec3_to_oct_with_border(normalize(sample_dir), params.border_size);
color.rgb += textureLod(source_octmap, sample_uv, sample_level).rgb * sample_weight;
color.a += sample_weight;
}
}
}
}
color /= color.w;
// write color
color.xyz = max(vec3(0.0), color.xyz);
color.w = 1.0;
frag_color = color;
// Write out the result.
frag_color = vec4(max(vec3(0.0), color.rgb / color.a), 1.0);
}

View file

@ -0,0 +1,64 @@
#[compute]
#version 450
#VERSION_DEFINES
#define GROUP_SIZE 8
layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D source_oct;
layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D dest_octmap;
#include "../oct_inc.glsl"
#include "octmap_roughness_inc.glsl"
void main() {
uvec2 id = gl_GlobalInvocationID.xy;
if (id.x < params.dest_size && id.y < params.dest_size) {
vec2 inv_source_size = 1.0 / vec2(params.source_size);
vec2 inv_dest_size = 1.0 / vec2(params.dest_size);
vec2 uv = (vec2(id.xy) + 0.5) * inv_dest_size;
if (params.use_direct_write) {
imageStore(dest_octmap, ivec2(id), vec4(texture(source_oct, uv).rgb, 1.0));
} else {
vec3 N = oct_to_vec3_with_border(uv, params.border_size.y);
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
float solid_angle_texel = 4.0 * M_PI / float(params.dest_size * params.dest_size);
float roughness2 = params.roughness * params.roughness;
float roughness4 = roughness2 * roughness2;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
mat3 T;
T[0] = normalize(cross(UpVector, N));
T[1] = cross(N, T[0]);
T[2] = N;
for (uint sampleNum = 0u; sampleNum < params.sample_count; sampleNum++) {
vec2 xi = Hammersley(sampleNum, params.sample_count);
vec3 H = T * ImportanceSampleGGX(xi, roughness4);
float NdotH = dot(N, H);
vec3 L = (2.0 * NdotH * H - N);
float ndotl = clamp(dot(N, L), 0.0, 1.0);
if (ndotl > 0.0) {
float D = DistributionGGX(NdotH, roughness4);
float pdf = D * NdotH / (4.0 * NdotH) + 0.0001;
float solid_angle_sample = 1.0 / (float(params.sample_count) * pdf + 0.0001);
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
vec2 sample_uv = vec3_to_oct_with_border(L, params.border_size);
sum.rgb += textureLod(source_oct, sample_uv, mipLevel).rgb * ndotl;
sum.a += ndotl;
}
}
imageStore(dest_octmap, ivec2(id), vec4(sum.rgb / sum.a, 1.0));
}
}
}

View file

@ -0,0 +1,49 @@
#define M_PI 3.14159265359
layout(push_constant, std430) uniform Params {
uint sample_count;
float roughness;
uint source_size;
uint dest_size;
vec2 border_size;
bool use_direct_write;
uint pad;
}
params;
vec3 ImportanceSampleGGX(vec2 xi, float roughness4) {
// Compute distribution direction
float Phi = 2.0 * M_PI * xi.x;
float CosTheta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
float SinTheta = sqrt(1.0 - CosTheta * CosTheta);
// Convert to spherical direction
vec3 H;
H.x = SinTheta * cos(Phi);
H.y = SinTheta * sin(Phi);
H.z = CosTheta;
return H;
}
float DistributionGGX(float NdotH, float roughness4) {
float NdotH2 = NdotH * NdotH;
float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
denom = M_PI * denom * denom;
return roughness4 / denom;
}
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
vec2 Hammersley(uint i, uint N) {
return vec2(float(i) / float(N), radicalInverse_VdC(i));
}

View file

@ -5,7 +5,7 @@
#VERSION_DEFINES
#include "cubemap_roughness_inc.glsl"
#include "octmap_roughness_inc.glsl"
layout(location = 0) out vec2 uv_interp;
/* clang-format on */
@ -23,26 +23,24 @@ void main() {
#VERSION_DEFINES
#include "cubemap_roughness_inc.glsl"
#include "../oct_inc.glsl"
#include "octmap_roughness_inc.glsl"
layout(location = 0) in vec2 uv_interp;
layout(set = 0, binding = 0) uniform samplerCube source_cube;
layout(set = 0, binding = 0) uniform sampler2D source_oct;
layout(location = 0) out vec4 frag_color;
/* clang-format on */
void main() {
vec3 N = texelCoordToVec(uv_interp * 2.0 - 1.0, params.face_id);
//vec4 color = color_interp;
if (params.use_direct_write) {
frag_color = vec4(texture(source_cube, N).rgb, 1.0);
frag_color = vec4(texture(source_oct, uv_interp).rgb, 1.0);
} else {
vec2 inv_size = 1.0 / vec2(params.size);
vec3 N = oct_to_vec3_with_border(uv_interp, params.border_size.y);
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
float solid_angle_texel = 4.0 * M_PI / (6.0 * params.face_size * params.face_size);
float solid_angle_texel = 4.0 * M_PI / (params.size * params.size);
float roughness2 = params.roughness * params.roughness;
float roughness4 = roughness2 * roughness2;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
@ -68,12 +66,12 @@ void main() {
float mipLevel = params.roughness == 0.0 ? 0.0 : 0.5 * log2(solid_angle_sample / solid_angle_texel);
sum.rgb += textureLod(source_cube, L, mipLevel).rgb * ndotl;
vec2 sample_uv = vec3_to_oct_with_border(L, params.border_size);
sum.rgb += textureLod(source_oct, sample_uv, mipLevel).rgb * ndotl;
sum.a += ndotl;
}
}
sum /= sum.a;
frag_color = vec4(sum.rgb, 1.0);
frag_color = vec4(sum.rgb / sum.a, 1.0);
}
}

View file

@ -4,6 +4,8 @@
#VERSION_DEFINES
#include "../oct_inc.glsl"
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#define MAX_CASCADES 8
@ -40,10 +42,10 @@ layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_aver
layout(rgba16f, set = 0, binding = 14) uniform restrict writeonly image2DArray lightprobe_ambient_texture;
#ifdef USE_CUBEMAP_ARRAY
layout(set = 1, binding = 0) uniform textureCubeArray sky_irradiance;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 1, binding = 0) uniform texture2DArray sky_irradiance;
#else
layout(set = 1, binding = 0) uniform textureCube sky_irradiance;
layout(set = 1, binding = 0) uniform texture2D sky_irradiance;
#endif
layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps;
@ -75,8 +77,9 @@ layout(push_constant, std430) uniform Params {
vec3 sky_color_or_orientation;
float y_mult;
vec2 sky_irradiance_border_size;
bool store_ambient_texture;
uint pad[3];
uint pad;
}
params;
@ -271,10 +274,10 @@ void main() {
vec4 sky_quat = vec4(params.sky_color_or_orientation, sky_sign * sqrt(1.0 - dot(params.sky_color_or_orientation, params.sky_color_or_orientation)));
vec3 sky_dir = cross(sky_quat.xyz, ray_dir);
sky_dir = ray_dir + ((sky_dir * sky_quat.w) + cross(sky_quat.xyz, sky_dir)) * 2.0;
#ifdef USE_CUBEMAP_ARRAY
light.rgb = textureLod(samplerCubeArray(sky_irradiance, linear_sampler_mipmaps), vec4(sky_dir, 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
#ifdef USE_RADIANCE_OCTMAP_ARRAY
light.rgb = textureLod(sampler2DArray(sky_irradiance, linear_sampler_mipmaps), vec3(vec3_to_oct_with_border(sky_dir, params.sky_irradiance_border_size), 0.0), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
#else
light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), sky_dir, 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
light.rgb = textureLod(sampler2D(sky_irradiance, linear_sampler_mipmaps), vec3_to_oct_with_border(sky_dir, params.sky_irradiance_border_size), 2.0).rgb; // Use second mipmap because we don't usually throw a lot of rays, so this compensates.
#endif
light.rgb *= params.sky_energy;
light.a = 0.0;

View file

@ -29,6 +29,8 @@ void main() {
#VERSION_DEFINES
#include "../oct_inc.glsl"
#ifdef USE_MULTIVIEW
#extension GL_EXT_multiview : enable
#define ViewIndex gl_ViewIndex
@ -43,7 +45,7 @@ layout(push_constant, std430) uniform Params {
vec4 projection; // only applicable if not multiview
vec3 position;
float time;
vec2 pad;
vec2 border_size;
float luminance_multiplier;
float brightness_multiplier;
}
@ -100,10 +102,10 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms {
/* clang-format on */
#endif
layout(set = 2, binding = 0) uniform textureCube radiance;
layout(set = 2, binding = 0) uniform texture2D radiance;
#ifdef USE_CUBEMAP_PASS
layout(set = 2, binding = 1) uniform textureCube half_res;
layout(set = 2, binding = 2) uniform textureCube quarter_res;
layout(set = 2, binding = 1) uniform texture2D half_res;
layout(set = 2, binding = 2) uniform texture2D quarter_res;
#elif defined(USE_MULTIVIEW)
layout(set = 2, binding = 1) uniform texture2DArray half_res;
layout(set = 2, binding = 2) uniform texture2DArray quarter_res;
@ -199,7 +201,11 @@ float atan2_approx(float y, float x) {
}
void main() {
vec2 uv = uv_interp * 0.5 + 0.5;
vec3 cube_normal;
#ifdef USE_CUBEMAP_PASS
cube_normal = oct_to_vec3_with_border(uv, params.border_size.y);
#else
#ifdef USE_MULTIVIEW
// In multiview our projection matrices will contain positional and rotational offsets that we need to properly unproject.
vec4 unproject = vec4(uv_interp.x, uv_interp.y, 0.0, 1.0); // unproject at the far plane
@ -215,8 +221,7 @@ void main() {
#endif
cube_normal = mat3(params.orientation) * cube_normal;
cube_normal = normalize(cube_normal);
vec2 uv = uv_interp * 0.5 + 0.5;
#endif
vec2 panorama_coords = vec2(atan2_approx(cube_normal.x, -cube_normal.z), acos_approx(cube_normal.y));
@ -235,10 +240,10 @@ void main() {
#ifdef USE_CUBEMAP_PASS
#ifdef USES_HALF_RES_COLOR
half_res_color = texture(samplerCube(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
half_res_color = texture(sampler2D(half_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3_to_oct_with_border(cube_normal, params.border_size)) / params.luminance_multiplier;
#endif
#ifdef USES_QUARTER_RES_COLOR
quarter_res_color = texture(samplerCube(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_normal) / params.luminance_multiplier;
quarter_res_color = texture(sampler2D(quarter_res, SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3_to_oct_with_border(cube_normal, params.border_size)) / params.luminance_multiplier;
#endif
#else

View file

@ -16,6 +16,7 @@ layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
#include "../cluster_data_inc.glsl"
#include "../light_data_inc.glsl"
#include "../oct_inc.glsl"
#define M_PI 3.14159265359
@ -176,6 +177,9 @@ layout(set = 0, binding = 14, std140) uniform Params {
uint temporal_frame;
float temporal_blend;
vec2 sky_border_size;
vec2 pad;
mat3x4 cam_rotation;
mat4 to_prev_view;
@ -201,10 +205,10 @@ layout(r32ui, set = 0, binding = 17) uniform uimage3D light_only_map;
layout(r32ui, set = 0, binding = 18) uniform uimage3D emissive_only_map;
#endif
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
layout(set = 0, binding = 19) uniform textureCubeArray sky_texture;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 0, binding = 19) uniform texture2DArray sky_texture;
#else
layout(set = 0, binding = 19) uniform textureCube sky_texture;
layout(set = 0, binding = 19) uniform texture2D sky_texture;
#endif
#endif // MODE_COPY
@ -429,13 +433,13 @@ void main() {
if (params.sky_contribution > 0.0) {
float mip_bias = 2.0 + total_density * (MAX_SKY_LOD - 2.0); // Not physically based, but looks nice
vec3 scatter_direction = (params.radiance_inverse_xform * normalize(view_pos)) * sign(params.phase_g);
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
isotropic = texture(samplerCubeArray(sky_texture, linear_sampler_with_mipmaps), vec4(0.0, 1.0, 0.0, mip_bias)).rgb;
anisotropic = texture(samplerCubeArray(sky_texture, linear_sampler_with_mipmaps), vec4(scatter_direction, mip_bias)).rgb;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
isotropic = texture(sampler2DArray(sky_texture, linear_sampler_with_mipmaps), vec3(vec3_to_oct_with_border(vec3(0.0, 1.0, 0.0), params.sky_border_size), mip_bias)).rgb;
anisotropic = texture(sampler2DArray(sky_texture, linear_sampler_with_mipmaps), vec3(vec3_to_oct_with_border(scatter_direction, params.sky_border_size), mip_bias)).rgb;
#else
isotropic = textureLod(samplerCube(sky_texture, linear_sampler_with_mipmaps), vec3(0.0, 1.0, 0.0), mip_bias).rgb;
anisotropic = textureLod(samplerCube(sky_texture, linear_sampler_with_mipmaps), vec3(scatter_direction), mip_bias).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
isotropic = textureLod(sampler2D(sky_texture, linear_sampler_with_mipmaps), vec3_to_oct_with_border(vec3(0.0, 1.0, 0.0), params.sky_border_size), mip_bias).rgb;
anisotropic = textureLod(sampler2D(sky_texture, linear_sampler_with_mipmaps), vec3_to_oct_with_border(scatter_direction, params.sky_border_size), mip_bias).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
}
total_light += mix(params.ambient_color, mix(isotropic, anisotropic, abs(params.phase_g)), params.sky_contribution) * params.ambient_inject;

View file

@ -71,13 +71,6 @@ layout(location = 13) in vec4 previous_normal_attrib;
#endif // MOTION_VECTORS
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binormal, out vec3 normal) {
float c = cos(angle);
float s = sin(angle);
@ -1102,14 +1095,19 @@ vec4 fog_process(vec3 vertex) {
vec3 cube_view = scene_data_block.data.radiance_inverse_xform * vertex;
// mip_level always reads from the second mipmap and higher so the fog is always slightly blurred
float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data_block.data.z_near) / (scene_data_block.data.z_far - scene_data_block.data.z_near));
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
float lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod);
sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb;
sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend);
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float roughness_lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, roughness_lod);
float cube_lod = vec3_to_oct_lod(dFdx(cube_view), dFdy(cube_view), scene_data_block.data.radiance_pixel_size);
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 sky_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod), cube_lod).rgb;
vec3 sky_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod + 1), cube_lod).rgb;
sky_fog_color = mix(sky_sample_a, sky_sample_b, blend);
#else
sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
float roughness_lod = mip_level * MAX_ROUGHNESS_LOD;
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
sky_fog_color = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective);
}
@ -1693,18 +1691,24 @@ void fragment_shader(in SceneData scene_data) {
float horizon = min(1.0 + dot(ref_vec, normal), 1.0);
ref_vec = scene_data.radiance_inverse_xform * ref_vec;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod, blend;
float roughness_lod, blend;
blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, lod);
indirect_specular_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
indirect_specular_light = mix(indirect_specular_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
blend = modf(sqrt(roughness) * MAX_ROUGHNESS_LOD, roughness_lod);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 indirect_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, roughness_lod), ref_lod).rgb;
vec3 indirect_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, roughness_lod + 1), ref_lod).rgb;
indirect_specular_light = mix(indirect_sample_a, indirect_sample_b, blend);
#else
indirect_specular_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb;
float roughness_lod = sqrt(roughness) * MAX_ROUGHNESS_LOD;
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
indirect_specular_light = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light *= scene_data.IBL_exposure_normalization;
indirect_specular_light *= horizon * horizon;
indirect_specular_light *= scene_data.ambient_light_color_energy.a;
@ -1721,11 +1725,15 @@ void fragment_shader(in SceneData scene_data) {
if (bool(scene_data.flags & SCENE_DATA_FLAGS_USE_AMBIENT_CUBEMAP)) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * indirect_normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float ambient_lod = vec3_to_oct_lod(dFdx(ambient_dir), dFdy(ambient_dir), scene_data_block.data.radiance_pixel_size);
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 cubemap_ambient = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ambient_uv, MAX_ROUGHNESS_LOD), ambient_lod).rgb;
#else
vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
float roughness_lod = MAX_ROUGHNESS_LOD;
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 cubemap_ambient = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
cubemap_ambient *= scene_data.IBL_exposure_normalization;
ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
}
@ -1750,17 +1758,22 @@ void fragment_shader(in SceneData scene_data) {
float horizon = min(1.0 + dot(ref_vec, indirect_normal), 1.0);
ref_vec = scene_data.radiance_inverse_xform * ref_vec;
float roughness_lod = mix(0.001, 0.1, sqrt(clearcoat_roughness)) * MAX_ROUGHNESS_LOD;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod, blend;
blend = modf(roughness_lod, lod);
vec3 clearcoat_light = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb;
clearcoat_light = mix(clearcoat_light, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb, blend);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 clearcoat_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, lod), ref_lod).rgb;
vec3 clearcoat_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, lod + 1), ref_lod).rgb;
vec3 clearcoat_light = mix(clearcoat_sample_a, clearcoat_sample_b, blend);
#else
vec3 clearcoat_light = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_vec, roughness_lod).rgb;
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 clearcoat_light = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
}
#endif // LIGHT_CLEARCOAT_USED

View file

@ -10,6 +10,7 @@
#include "../cluster_data_inc.glsl"
#include "../decal_data_inc.glsl"
#include "../oct_inc.glsl"
#include "../scene_data_inc.glsl"
#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(MODE_RENDER_SDF) || defined(MODE_RENDER_NORMAL_ROUGHNESS) || defined(MODE_RENDER_VOXEL_GI) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
@ -352,17 +353,17 @@ layout(set = 1, binding = 2, std430) buffer restrict readonly InstanceDataBuffer
}
instances;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 1, binding = 3) uniform textureCubeArray radiance_cubemap;
layout(set = 1, binding = 3) uniform texture2DArray radiance_octmap;
#else
layout(set = 1, binding = 3) uniform textureCube radiance_cubemap;
layout(set = 1, binding = 3) uniform texture2D radiance_octmap;
#endif
layout(set = 1, binding = 4) uniform textureCubeArray reflection_atlas;
layout(set = 1, binding = 4) uniform texture2DArray reflection_atlas;
layout(set = 1, binding = 5) uniform texture2D shadow_atlas;

View file

@ -78,13 +78,6 @@ layout(location = 13) in vec4 previous_normal_attrib;
#endif // MODE_RENDER_MOTION_VECTORS
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
void axis_angle_to_tbn(vec3 axis, float angle, out vec3 tangent, out vec3 binormal, out vec3 normal) {
float c = cos(angle);
float s = sin(angle);
@ -1048,14 +1041,19 @@ hvec4 fog_process(vec3 vertex) {
vec3 cube_view = scene_data_block.data.radiance_inverse_xform * vertex;
// mip_level always reads from the second mipmap and higher so the fog is always slightly blurred
float mip_level = mix(1.0 / MAX_ROUGHNESS_LOD, 1.0, 1.0 - (abs(vertex.z) - scene_data_block.data.z_near) / (scene_data_block.data.z_far - scene_data_block.data.z_near));
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
float lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, lod);
sky_fog_color = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod)).rgb;
sky_fog_color = mix(sky_fog_color, texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(cube_view, lod + 1)).rgb, blend);
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float roughness_lod, blend;
blend = modf(mip_level * MAX_ROUGHNESS_LOD, roughness_lod);
float cube_lod = vec3_to_oct_lod(dFdx(cube_view), dFdy(cube_view), scene_data_block.data.radiance_pixel_size);
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
vec3 sky_sample_a = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod), cube_lod).rgb;
vec3 sky_sample_b = textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(cube_uv, roughness_lod + 1), cube_lod).rgb;
sky_fog_color = mix(sky_sample_a, sky_sample_b, blend);
#else
sky_fog_color = textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_view, mip_level * MAX_ROUGHNESS_LOD).rgb;
#endif //USE_RADIANCE_CUBEMAP_ARRAY
float roughness_lod = mip_level * MAX_ROUGHNESS_LOD;
vec2 cube_uv = vec3_to_oct_with_border(cube_view, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
sky_fog_color = textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), cube_uv, roughness_lod).rgb;
#endif //USE_RADIANCE_OCTMAP_ARRAY
fog_color = mix(fog_color, sky_fog_color, scene_data_block.data.fog_aerial_perspective);
}
@ -1595,20 +1593,20 @@ void main() {
#endif
half horizon = min(half(1.0) + dot(ref_vec, indirect_normal), half(1.0));
ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(ref_vec));
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod;
half blend = half(modf(float(sqrt(roughness) * half(MAX_ROUGHNESS_LOD)), lod));
half blend = half(modf(float(sqrt(roughness) * MAX_ROUGHNESS_LOD), lod));
hvec3 indirect_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(vec3(ref_vec), float(lod))).rgb);
hvec3 indirect_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(vec3(ref_vec), float(lod) + 1.0)).rgb);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 indirect_sample_a = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, float(lod)), ref_lod).rgb);
hvec3 indirect_sample_b = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_uv, float(lod) + 1.0), ref_lod).rgb);
indirect_specular_light = mix(indirect_sample_a, indirect_sample_b, blend);
#else // USE_RADIANCE_CUBEMAP_ARRAY
float lod = sqrt(roughness) * half(MAX_ROUGHNESS_LOD);
indirect_specular_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_vec), lod).rgb);
#endif //USE_RADIANCE_CUBEMAP_ARRAY
#else // USE_RADIANCE_OCTMAP_ARRAY
float lod = sqrt(roughness) * MAX_ROUGHNESS_LOD;
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
indirect_specular_light = hvec3(textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, lod).rgb);
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light *= REFLECTION_MULTIPLIER;
indirect_specular_light *= half(scene_data.IBL_exposure_normalization);
indirect_specular_light *= horizon * horizon;
@ -1626,14 +1624,18 @@ void main() {
if (sc_scene_use_ambient_cubemap()) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * indirect_normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
hvec3 cubemap_ambient = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb);
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float ambient_lod = vec3_to_oct_lod(dFdx(ambient_dir), dFdy(ambient_dir), scene_data_block.data.radiance_pixel_size);
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 octmap_ambient = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ambient_uv, MAX_ROUGHNESS_LOD), ambient_lod).rgb);
#else
hvec3 cubemap_ambient = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_dir, MAX_ROUGHNESS_LOD).rgb);
#endif //USE_RADIANCE_CUBEMAP_ARRAY
cubemap_ambient *= REFLECTION_MULTIPLIER;
cubemap_ambient *= half(scene_data.IBL_exposure_normalization);
ambient_light = mix(ambient_light, cubemap_ambient * half(scene_data.ambient_light_color_energy.a), half(scene_data.ambient_color_sky_mix));
float roughness_lod = MAX_ROUGHNESS_LOD;
vec2 ambient_uv = vec3_to_oct_with_border(ambient_dir, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 octmap_ambient = hvec3(textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ambient_uv, roughness_lod).rgb);
#endif //USE_RADIANCE_OCTMAP_ARRAY
octmap_ambient *= REFLECTION_MULTIPLIER;
octmap_ambient *= half(scene_data.IBL_exposure_normalization);
ambient_light = mix(ambient_light, octmap_ambient * half(scene_data.ambient_light_color_energy.a), half(scene_data.ambient_color_sky_mix));
}
}
#endif // !USE_LIGHTMAP
@ -1656,18 +1658,19 @@ void main() {
half horizon = min(half(1.0) + dot(ref_vec, indirect_normal), half(1.0));
ref_vec = hvec3(scene_data.radiance_inverse_xform * vec3(ref_vec));
float roughness_lod = mix(0.001, 0.1, sqrt(float(clearcoat_roughness))) * MAX_ROUGHNESS_LOD;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
float lod;
half blend = half(modf(roughness_lod, lod));
hvec3 clearcoat_sample_a = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod)).rgb);
hvec3 clearcoat_sample_b = hvec3(texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ref_vec, lod + 1)).rgb);
float ref_lod = vec3_to_oct_lod(dFdx(ref_vec), dFdy(ref_vec), scene_data_block.data.radiance_pixel_size);
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 clearcoat_sample_a = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, lod), ref_lod).rgb);
hvec3 clearcoat_sample_b = hvec3(textureLod(sampler2DArray(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, lod + 1), ref_lod).rgb);
hvec3 clearcoat_light = mix(clearcoat_sample_a, clearcoat_sample_b, blend);
#else
hvec3 clearcoat_light = hvec3(textureLod(samplerCube(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ref_vec), roughness_lod).rgb);
#endif //USE_RADIANCE_CUBEMAP_ARRAY
vec2 ref_uv = vec3_to_oct_with_border(ref_vec, vec2(scene_data_block.data.radiance_border_size, 1.0 - scene_data_block.data.radiance_border_size * 2.0));
hvec3 clearcoat_light = hvec3(textureLod(sampler2D(radiance_octmap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), ref_uv, roughness_lod).rgb);
#endif //USE_RADIANCE_OCTMAP_ARRAY
indirect_specular_light += clearcoat_light * horizon * horizon * Fc * half(scene_data.ambient_light_color_energy.a);
}
#endif // LIGHT_CLEARCOAT_USED

View file

@ -2,6 +2,7 @@
#define MAX_VIEWS 2
#include "../decal_data_inc.glsl"
#include "../oct_inc.glsl"
#include "../scene_data_inc.glsl"
#if !defined(MODE_RENDER_DEPTH) || defined(MODE_RENDER_MATERIAL) || defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(BENT_NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED)
@ -351,17 +352,17 @@ layout(set = 1, binding = 1, std430) buffer restrict readonly InstanceDataBuffer
}
instances;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
#ifdef USE_RADIANCE_OCTMAP_ARRAY
layout(set = 1, binding = 2) uniform textureCubeArray radiance_cubemap;
layout(set = 1, binding = 2) uniform texture2DArray radiance_octmap;
#else
layout(set = 1, binding = 2) uniform textureCube radiance_cubemap;
layout(set = 1, binding = 2) uniform texture2D radiance_octmap;
#endif
layout(set = 1, binding = 3) uniform textureCubeArray reflection_atlas;
layout(set = 1, binding = 3) uniform texture2DArray reflection_atlas;
layout(set = 1, binding = 4) uniform texture2D shadow_atlas;

View file

@ -0,0 +1,51 @@
vec3 oct_to_vec3(vec2 e) {
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
float t = max(-v.z, 0.0);
v.xy += t * -sign(v.xy);
return normalize(v);
}
// border_size: 1.0 - padding_in_uv_space * 2.0.
vec3 oct_to_vec3_with_border(vec2 uv, float border_size) {
// Convert into [-1,1] space and add border which extends beyond [-1,1].
uv = (uv - 0.5) * (2.0 / border_size);
// Calculate octahedral mirroring for values outside of [-1,1].
// Inspired by Timothy Lottes' code here: https://gpuopen.com/learn/fetching-from-cubes-and-octahedrons/
vec2 mask = step(vec2(1.0), abs(uv));
uv = 2.0 * clamp(uv, -1.0, 1.0) - uv;
uv = mix(uv, -uv, mask.yx);
return oct_to_vec3(uv);
}
vec2 oct_wrap(vec2 v) {
vec2 signVal;
signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
return (1.0 - abs(v.yx)) * signVal;
}
vec2 vec3_to_oct(vec3 n) {
// Reference: https://twitter.com/Stubbesaurus/status/937994790553227264
n /= (abs(n.x) + abs(n.y) + abs(n.z));
n.xy = (n.z >= 0.0) ? n.xy : oct_wrap(n.xy);
n.xy = n.xy * 0.5 + 0.5;
return n.xy;
}
// border_size.x: padding_in_uv_space
// border_size.y: 1.0 - padding_in_uv_space * 2.0
vec2 vec3_to_oct_with_border(vec3 n, vec2 border_size) {
vec2 uv = vec3_to_oct(n);
return uv * border_size.y + border_size.x;
}
float vec3_to_oct_lod(vec3 n_ddx, vec3 n_ddy, float pixel_size) {
// Approximate UV space derivatives by a factor of 0.5 because
// vec3_to_oct maps from [-1,1] to [0,1].
float pixel_size_sqr = 4.0 * pixel_size * pixel_size;
float ddx = dot(n_ddx, n_ddx) / pixel_size_sqr;
float ddy = dot(n_ddy, n_ddy) / pixel_size_sqr;
float dd_sqr = max(ddx, ddy);
return 0.25 * log2(dd_sqr + 1e-6f);
}

View file

@ -42,6 +42,10 @@ struct SceneData {
vec2 shadow_atlas_pixel_size;
vec2 directional_shadow_pixel_size;
float radiance_pixel_size;
float radiance_border_size;
vec2 reflection_atlas_border_size;
uint directional_light_count;
float dual_paraboloid_side;
float z_far;

View file

@ -937,6 +937,7 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal
blend = pow(blend_axes.x * blend_axes.y * blend_axes.z, half(2.0));
}
vec2 border_size = scene_data_block.data.reflection_atlas_border_size;
if (reflections.data[ref_index].intensity > 0.0 && reflection_accum.a < half(1.0)) { // compute reflection
vec3 local_ref_vec = (reflections.data[ref_index].local_matrix * vec4(ref_vec, 0.0)).xyz;
@ -957,7 +958,9 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal
hvec4 reflection;
half reflection_blend = max(half(0.0), blend - reflection_accum.a);
reflection.rgb = hvec3(textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_ref_vec, reflections.data[ref_index].index), sqrt(roughness) * MAX_ROUGHNESS_LOD).rgb) * REFLECTION_MULTIPLIER;
float roughness_lod = sqrt(roughness) * MAX_ROUGHNESS_LOD;
vec2 reflection_uv = vec3_to_oct_with_border(local_ref_vec, border_size);
reflection.rgb = hvec3(textureLod(sampler2DArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(reflection_uv, reflections.data[ref_index].index), roughness_lod).rgb) * REFLECTION_MULTIPLIER;
reflection.rgb *= half(reflections.data[ref_index].exposure_normalization);
reflection.a = reflection_blend;
@ -980,7 +983,9 @@ void reflection_process(uint ref_index, vec3 vertex, hvec3 ref_vec, hvec3 normal
hvec4 ambient_out;
half ambient_blend = max(half(0.0), blend - ambient_accum.a);
ambient_out.rgb = hvec3(textureLod(samplerCubeArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb);
float roughness_lod = MAX_ROUGHNESS_LOD;
vec2 ambient_uv = vec3_to_oct_with_border(local_amb_vec, border_size);
ambient_out.rgb = hvec3(textureLod(sampler2DArray(reflection_atlas, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec3(ambient_uv, reflections.data[ref_index].index), roughness_lod).rgb);
ambient_out.rgb *= half(reflections.data[ref_index].exposure_normalization);
ambient_out.a = ambient_blend;
ambient_out.rgb *= ambient_out.a;