playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
212 lines (211 loc) • 7.24 kB
JavaScript
var reproject_default = `
varying vUv0: vec2f;
#ifdef CUBEMAP_SOURCE
var sourceCube: texture_cube<f32>;
var sourceCubeSampler : sampler;
#else
var sourceTex: texture_2d<f32>;
var sourceTexSampler : sampler;
#endif
#ifdef USE_SAMPLES_TEX
var samplesTex: texture_2d<f32>;
var samplesTexSampler : sampler;
uniform samplesTexInverseSize: vec2f;
#endif
uniform params: vec3f;
fn targetFace() -> f32 { return uniform.params.x; }
fn targetTotalPixels() -> f32 { return uniform.params.y; }
fn sourceTotalPixels() -> f32 { return uniform.params.z; }
const PI: f32 = 3.141592653589793;
fn saturate(x: f32) -> f32 {
return clamp(x, 0.0, 1.0);
}
#include "decodePS"
#include "encodePS"
fn modifySeams(dir: vec3f, scale: f32) -> vec3f {
let adir = abs(dir);
let M = max(max(adir.x, adir.y), adir.z);
return dir / M * vec3f(
select(scale, 1.0, adir.x == M),
select(scale, 1.0, adir.y == M),
select(scale, 1.0, adir.z == M)
);
}
fn toSpherical(dir: vec3f) -> vec2f {
let nonZeroXZ = any(dir.xz != vec2f(0.0, 0.0));
return vec2f(select(0.0, atan2(dir.x, dir.z), nonZeroXZ), asin(dir.y));
}
fn fromSpherical(uv: vec2f) -> vec3f {
return vec3f(cos(uv.y) * sin(uv.x),
sin(uv.y),
cos(uv.y) * cos(uv.x));
}
fn getDirectionEquirect(uv: vec2f) -> vec3f {
return fromSpherical((vec2f(uv.x, 1.0 - uv.y) * 2.0 - 1.0) * vec2f(PI, PI * 0.5));
}
fn signNotZero(k: f32) -> f32 {
return select(-1.0, 1.0, k >= 0.0);
}
fn signNotZeroVec2(v: vec2f) -> vec2f {
return vec2f(signNotZero(v.x), signNotZero(v.y));
}
fn octDecode(o: vec2f) -> vec3f {
var v = vec3f(o.x, 1.0 - abs(o.x) - abs(o.y), o.y);
if (v.y < 0.0) {
var temp: vec2f = (1.0 - abs(v.zx)) * signNotZeroVec2(v.xz);
v = vec3f(temp.x, v.y, temp.y);
}
return normalize(v);
}
fn getDirectionOctahedral(uv: vec2f) -> vec3f {
return octDecode(vec2f(uv.x, 1.0 - uv.y) * 2.0 - 1.0);
}
fn octEncode(v: vec3f) -> vec2f {
let l1norm = abs(v.x) + abs(v.y) + abs(v.z);
var result = v.xz * (1.0 / l1norm);
if (v.y < 0.0) {
result = (1.0 - abs(result.yx)) * signNotZeroVec2(result.xy);
}
return result;
}
#ifdef CUBEMAP_SOURCE
fn sampleCubemapDir(dir: vec3f) -> vec4f {
return textureSample(sourceCube, sourceCubeSampler, modifySeams(dir, 1.0));
}
fn sampleCubemapSph(sph: vec2f) -> vec4f {
return sampleCubemapDir(fromSpherical(sph));
}
fn sampleCubemapDirLod(dir: vec3f, mipLevel: f32) -> vec4f {
return textureSampleLevel(sourceCube, sourceCubeSampler, modifySeams(dir, 1.0), mipLevel);
}
fn sampleCubemapSphLod(sph: vec2f, mipLevel: f32) -> vec4f {
return sampleCubemapDirLod(fromSpherical(sph), mipLevel);
}
#else
fn sampleEquirectSph(sph: vec2f) -> vec4f {
let uv = sph / vec2f(PI * 2.0, PI) + 0.5;
return textureSample(sourceTex, sourceTexSampler, vec2f(uv.x, 1.0 - uv.y));
}
fn sampleEquirectDir(dir: vec3f) -> vec4f {
return sampleEquirectSph(toSpherical(dir));
}
fn sampleEquirectSphLod(sph: vec2f, mipLevel: f32) -> vec4f {
let uv = sph / vec2f(PI * 2.0, PI) + 0.5;
return textureSampleLevel(sourceTex, sourceTexSampler, vec2f(uv.x, 1.0 - uv.y), mipLevel);
}
fn sampleEquirectDirLod(dir: vec3f, mipLevel: f32) -> vec4f {
return sampleEquirectSphLod(toSpherical(dir), mipLevel);
}
fn sampleOctahedralDir(dir: vec3f) -> vec4f {
let uv = octEncode(dir) * 0.5 + 0.5;
return textureSample(sourceTex, sourceTexSampler, vec2f(uv.x, 1.0 - uv.y));
}
fn sampleOctahedralSph(sph: vec2f) -> vec4f {
return sampleOctahedralDir(fromSpherical(sph));
}
fn sampleOctahedralDirLod(dir: vec3f, mipLevel: f32) -> vec4f {
let uv = octEncode(dir) * 0.5 + 0.5;
return textureSampleLevel(sourceTex, sourceTexSampler, vec2f(uv.x, 1.0 - uv.y), mipLevel);
}
fn sampleOctahedralSphLod(sph: vec2f, mipLevel: f32) -> vec4f {
return sampleOctahedralDirLod(fromSpherical(sph), mipLevel);
}
#endif
fn getDirectionCubemap(uv: vec2f) -> vec3f {
let st = uv * 2.0 - 1.0;
let face = targetFace();
var vec: vec3f;
if (face == 0.0) {
vec = vec3f(1, -st.y, -st.x);
} else if (face == 1.0) {
vec = vec3f(-1, -st.y, st.x);
} else if (face == 2.0) {
vec = vec3f(st.x, 1, st.y);
} else if (face == 3.0) {
vec = vec3f(st.x, -1, -st.y);
} else if (face == 4.0) {
vec = vec3f(st.x, -st.y, 1);
} else {
vec = vec3f(-st.x, -st.y, -1);
}
return normalize(modifySeams(vec, 1.0));
}
fn matrixFromVector(n: vec3f) -> mat3x3f {
let a = 1.0 / (1.0 + n.z);
let b = -n.x * n.y * a;
let b1 = vec3f(1.0 - n.x * n.x * a, b, -n.x);
let b2 = vec3f(b, 1.0 - n.y * n.y * a, -n.y);
return mat3x3f(b1, b2, n);
}
fn matrixFromVectorSlow(n: vec3f) -> mat3x3f {
let up = select(vec3f(0.0, 0.0, select(-1.0, 1.0, n.y > 0.0)), vec3f(0.0, 1.0, 0.0), abs(n.y) > 0.0000001);
let x = normalize(cross(up, n));
let y = cross(n, x);
return mat3x3f(x, y, n);
}
fn reproject(uv: vec2f) -> vec4f {
if ({NUM_SAMPLES} <= 1) {
return {ENCODE_FUNC}({DECODE_FUNC}({SOURCE_FUNC}Dir({TARGET_FUNC}(uv))));
} else {
let t = {TARGET_FUNC}(uv);
let tu = dpdx(t);
let tv = dpdy(t);
var result = vec3f(0.0);
for (var u = 0.0; u < {NUM_SAMPLES_SQRT}; u += 1.0) {
for (var v = 0.0; v < {NUM_SAMPLES_SQRT}; v += 1.0) {
result += {DECODE_FUNC}({SOURCE_FUNC}Dir(normalize(t +
tu * (u / {NUM_SAMPLES_SQRT} - 0.5) +
tv * (v / {NUM_SAMPLES_SQRT} - 0.5))));
}
}
return {ENCODE_FUNC}(result / ({NUM_SAMPLES_SQRT} * {NUM_SAMPLES_SQRT}));
}
}
const unpackFloat: vec4f = vec4f(1.0, 1.0 / 255.0, 1.0 / 65025.0, 1.0 / 16581375.0);
#ifdef USE_SAMPLES_TEX
fn unpackSample(i: i32, L: ptr<function, vec3f>, mipLevel: ptr<function, f32>) {
var u = (f32(i * 4) + 0.5) * uniform.samplesTexInverseSize.x;
var v = (floor(u) + 0.5) * uniform.samplesTexInverseSize.y;
var raw: vec4f;
raw.x = dot(textureSample(samplesTex, samplesTexSampler, vec2f(u, v)), unpackFloat); u += uniform.samplesTexInverseSize.x;
raw.y = dot(textureSample(samplesTex, samplesTexSampler, vec2f(u, v)), unpackFloat); u += uniform.samplesTexInverseSize.x;
raw.z = dot(textureSample(samplesTex, samplesTexSampler, vec2f(u, v)), unpackFloat); u += uniform.samplesTexInverseSize.x;
raw.w = dot(textureSample(samplesTex, samplesTexSampler, vec2f(u, v)), unpackFloat);
*L = raw.xyz * 2.0 - 1.0;
*mipLevel = raw.w * 8.0;
}
fn prefilterSamples(uv: vec2f) -> vec4f {
let vecSpace = matrixFromVectorSlow({TARGET_FUNC}(uv));
var L: vec3f;
var mipLevel: f32;
var result = vec3f(0.0);
var totalWeight = 0.0;
for (var i = 0; i < {NUM_SAMPLES}; i += 1) {
unpackSample(i, &L, &mipLevel);
result += {DECODE_FUNC}({SOURCE_FUNC}DirLod(vecSpace * L, mipLevel)) * L.z;
totalWeight += L.z;
}
return {ENCODE_FUNC}(result / totalWeight);
}
fn prefilterSamplesUnweighted(uv: vec2f) -> vec4f {
let vecSpace = matrixFromVectorSlow({TARGET_FUNC}(uv));
var L: vec3f;
var mipLevel: f32;
var result = vec3f(0.0);
for (var i = 0; i < {NUM_SAMPLES}; i += 1) {
unpackSample(i, &L, &mipLevel);
result += {DECODE_FUNC}({SOURCE_FUNC}DirLod(vecSpace * L, mipLevel));
}
return {ENCODE_FUNC}(result / f32({NUM_SAMPLES}));
}
#endif
@fragment
fn fragmentMain(input : FragmentInput) -> FragmentOutput {
var output: FragmentOutput;
output.color = {PROCESS_FUNC}(input.vUv0);
return output;
}
`;
export {
reproject_default as default
};