UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

255 lines (252 loc) 11 kB
import { Vec4 } from '../../core/math/vec4.js'; import { Texture } from '../../platform/graphics/texture.js'; import { reprojectTexture } from './reproject-texture.js'; import { ADDRESS_CLAMP_TO_EDGE, TEXTURETYPE_RGBP, TEXTURETYPE_DEFAULT, PIXELFORMAT_RGBA8, TEXTUREPROJECTION_EQUIRECT, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F } from '../../platform/graphics/constants.js'; import { DebugGraphics } from '../../platform/graphics/debug-graphics.js'; // calculate the number of mipmap levels given texture dimensions var calcLevels = (width, height)=>{ if (height === void 0) height = 0; return 1 + Math.floor(Math.log2(Math.max(width, height))); }; var supportsFloat16 = (device)=>{ return device.textureHalfFloatRenderable; }; var supportsFloat32 = (device)=>{ return device.textureFloatRenderable; }; // lighting source should be stored HDR var lightingSourcePixelFormat = (device)=>{ return supportsFloat16(device) ? PIXELFORMAT_RGBA16F : supportsFloat32(device) ? PIXELFORMAT_RGBA32F : PIXELFORMAT_RGBA8; }; // runtime lighting can be RGBM var lightingPixelFormat = (device)=>{ return PIXELFORMAT_RGBA8; }; var createCubemap = (device, size, format, mipmaps)=>{ return new Texture(device, { name: "lighting-" + size, cubemap: true, width: size, height: size, format: format, type: TEXTURETYPE_RGBP , addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, mipmaps: false }); }; /** * Helper functions to support prefiltering lighting data. * * @ignore */ class EnvLighting { /** * Generate a skybox cubemap in the correct pixel format from the source texture. * * @param {Texture} source - The source texture. This is either a 2d texture in equirect format * or a cubemap. * @param {number} [size] - Size of the resulting texture. Otherwise use automatic sizing. * @returns {Texture} The resulting cubemap. */ static generateSkyboxCubemap(source, size) { var device = source.device; DebugGraphics.pushGpuMarker(device, 'genSkyboxCubemap'); var result = createCubemap(device, size || (source.cubemap ? source.width : source.width / 4), PIXELFORMAT_RGBA8); reprojectTexture(source, result, { numSamples: 1024 }); DebugGraphics.popGpuMarker(device); return result; } /** * Create a texture in the format needed to precalculate lighting data. * * @param {Texture} source - The source texture. This is either a 2d texture in equirect format * or a cubemap. * @param {object} [options] - Specify generation options. * @param {Texture} [options.target] - The target texture. If one is not provided then a * new texture will be created and returned. * @param {number} [options.size] - Size of the lighting source cubemap texture. Only used * if target isn't specified. Defaults to 128. * @returns {Texture} The resulting cubemap. */ static generateLightingSource(source, options) { var device = source.device; DebugGraphics.pushGpuMarker(device, 'genLightingSource'); var format = lightingSourcePixelFormat(device); var result = (options == null ? void 0 : options.target) || new Texture(device, { name: 'lighting-source', cubemap: true, width: (options == null ? void 0 : options.size) || 128, height: (options == null ? void 0 : options.size) || 128, format: format, type: format === PIXELFORMAT_RGBA8 ? TEXTURETYPE_RGBP : TEXTURETYPE_DEFAULT, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, mipmaps: true }); // copy into top level reprojectTexture(source, result, { numSamples: source.mipmaps ? 1 : 1024 }); DebugGraphics.popGpuMarker(device); // generate mipmaps return result; } /** * Generate the environment lighting atlas containing prefiltered reflections and ambient. * * @param {Texture} source - The source lighting texture, generated by generateLightingSource. * @param {object} [options] - Specify prefilter options. * @param {Texture} [options.target] - The target texture. If one is not provided then a * new texture will be created and returned. * @param {number} [options.size] - Size of the target texture to create. Only used if * target isn't specified. Defaults to 512. * @param {number} [options.numReflectionSamples] - Number of samples to use when generating * rough reflections. Defaults to 1024. * @param {number} [options.numAmbientSamples] - Number of samples to use when generating ambient * lighting. Defaults to 2048. * @returns {Texture} The resulting atlas */ static generateAtlas(source, options) { var device = source.device; var format = lightingPixelFormat(); DebugGraphics.pushGpuMarker(device, 'genAtlas'); var result = (options == null ? void 0 : options.target) || new Texture(device, { name: 'envAtlas', width: (options == null ? void 0 : options.size) || 512, height: (options == null ? void 0 : options.size) || 512, format: format, type: TEXTURETYPE_RGBP , projection: TEXTUREPROJECTION_EQUIRECT, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, mipmaps: false }); DebugGraphics.pushGpuMarker(device, 'mipmaps'); var s = result.width / 512; // generate mipmaps var rect = new Vec4(0, 0, 512 * s, 256 * s); var levels = calcLevels(256) - calcLevels(4); for(var i = 0; i < levels; ++i){ reprojectTexture(source, result, { numSamples: 1, rect: rect, seamPixels: s }); rect.x += rect.w; rect.y += rect.w; rect.z = Math.max(1, Math.floor(rect.z * 0.5)); rect.w = Math.max(1, Math.floor(rect.w * 0.5)); } DebugGraphics.popGpuMarker(device); DebugGraphics.pushGpuMarker(device, 'reflections'); // generate blurry reflections rect.set(0, 256 * s, 256 * s, 128 * s); for(var i1 = 1; i1 < 7; ++i1){ reprojectTexture(source, result, { numSamples: (options == null ? void 0 : options.numReflectionSamples) || 1024, distribution: (options == null ? void 0 : options.distribution) || 'ggx', specularPower: Math.max(1, 2048 >> i1 * 2), rect: rect, seamPixels: s }); rect.y += rect.w; rect.z = Math.max(1, Math.floor(rect.z * 0.5)); rect.w = Math.max(1, Math.floor(rect.w * 0.5)); } DebugGraphics.popGpuMarker(device); DebugGraphics.pushGpuMarker(device, 'ambient'); // generate ambient rect.set(128 * s, (256 + 128) * s, 64 * s, 32 * s); reprojectTexture(source, result, { numSamples: (options == null ? void 0 : options.numAmbientSamples) || 2048, distribution: 'lambert', rect: rect, seamPixels: s }); DebugGraphics.popGpuMarker(device); DebugGraphics.popGpuMarker(device); return result; } /** * Generate the environment lighting atlas from prefiltered cubemap data. * * @param {Texture[]} sources - Array of 6 prefiltered textures. * @param {object} [options] - The options object * @param {Texture} [options.target] - The target texture. If one is not provided then a * new texture will be created and returned. * @param {number} [options.size] - Size of the target texture to create. Only used if * target isn't specified. Defaults to 512. * @param {boolean} [options.legacyAmbient] - Enable generating legacy ambient lighting. * Default is false. * @param {number} [options.numSamples] - Number of samples to use when generating ambient * lighting. Default is 2048. * @returns {Texture} The resulting atlas texture. */ static generatePrefilteredAtlas(sources, options) { var device = sources[0].device; var format = sources[0].format; var type = sources[0].type; DebugGraphics.pushGpuMarker(device, 'genPrefilteredAtlas'); var result = (options == null ? void 0 : options.target) || new Texture(device, { name: 'envPrefilteredAtlas', width: (options == null ? void 0 : options.size) || 512, height: (options == null ? void 0 : options.size) || 512, format: format, type: type, projection: TEXTUREPROJECTION_EQUIRECT, addressU: ADDRESS_CLAMP_TO_EDGE, addressV: ADDRESS_CLAMP_TO_EDGE, mipmaps: false }); DebugGraphics.pushGpuMarker(device, 'mipmaps'); var s = result.width / 512; // generate mipmaps var rect = new Vec4(0, 0, 512 * s, 256 * s); var levels = calcLevels(512); for(var i = 0; i < levels; ++i){ reprojectTexture(sources[0], result, { numSamples: 1, rect: rect, seamPixels: s }); rect.x += rect.w; rect.y += rect.w; rect.z = Math.max(1, Math.floor(rect.z * 0.5)); rect.w = Math.max(1, Math.floor(rect.w * 0.5)); } DebugGraphics.popGpuMarker(device); DebugGraphics.pushGpuMarker(device, 'reflections'); // copy blurry reflections rect.set(0, 256 * s, 256 * s, 128 * s); for(var i1 = 1; i1 < sources.length; ++i1){ reprojectTexture(sources[i1], result, { numSamples: 1, rect: rect, seamPixels: s }); rect.y += rect.w; rect.z = Math.max(1, Math.floor(rect.z * 0.5)); rect.w = Math.max(1, Math.floor(rect.w * 0.5)); } DebugGraphics.popGpuMarker(device); DebugGraphics.pushGpuMarker(device, 'ambient'); // generate ambient rect.set(128 * s, (256 + 128) * s, 64 * s, 32 * s); if (options == null ? void 0 : options.legacyAmbient) { reprojectTexture(sources[5], result, { numSamples: 1, rect: rect, seamPixels: s }); } else { reprojectTexture(sources[0], result, { numSamples: (options == null ? void 0 : options.numSamples) || 2048, distribution: 'lambert', rect: rect, seamPixels: s }); } DebugGraphics.popGpuMarker(device); DebugGraphics.popGpuMarker(device); return result; } } export { EnvLighting };