UNPKG

cione-comp-lib

Version:

`$ yarn add cione-comp-lib` 或者 `$ npm i cione-comp-lib -S`

190 lines (189 loc) 6.57 kB
import Texture2D from "../Texture2D.mjs"; import TextureCube from "../TextureCube.mjs"; import Texture from "../Texture.mjs"; import FrameBuffer from "../FrameBuffer.mjs"; import Pass from "../compositor/Pass.mjs"; import Material from "../Material.mjs"; import Shader from "../Shader.mjs"; import Skybox from "../plugin/Skybox.mjs"; import Scene from "../Scene.mjs"; import EnvironmentMapPass from "../prePass/EnvironmentMap.mjs"; import vendor from "../core/vendor.mjs"; import textureUtil from "./texture.mjs"; import integrateBRDFShaderCode from "./shader/integrateBRDF.glsl.mjs"; import prefilterFragCode from "./shader/prefilter.glsl.mjs"; var cubemapUtil = {}; var targets = ["px", "nx", "py", "ny", "pz", "nz"]; cubemapUtil.prefilterEnvironmentMap = function(renderer, envMap, textureOpts, normalDistribution, brdfLookup) { if (!brdfLookup || !normalDistribution) { normalDistribution = cubemapUtil.generateNormalDistribution(); brdfLookup = cubemapUtil.integrateBRDF(renderer, normalDistribution); } textureOpts = textureOpts || {}; var width = textureOpts.width || 64; var height = textureOpts.height || 64; var textureType = textureOpts.type || envMap.type; var prefilteredCubeMap = new TextureCube({ width, height, type: textureType, flipY: false, mipmaps: [] }); if (!prefilteredCubeMap.isPowerOfTwo()) { console.warn("Width and height must be power of two to enable mipmap."); } var size = Math.min(width, height); var mipmapNum = Math.log(size) / Math.log(2) + 1; var prefilterMaterial = new Material({ shader: new Shader({ vertex: Shader.source("clay.skybox.vertex"), fragment: prefilterFragCode }) }); prefilterMaterial.set("normalDistribution", normalDistribution); textureOpts.encodeRGBM && prefilterMaterial.define("fragment", "RGBM_ENCODE"); textureOpts.decodeRGBM && prefilterMaterial.define("fragment", "RGBM_DECODE"); var dummyScene = new Scene(); var skyEnv; if (envMap.textureType === "texture2D") { var envCubemap = new TextureCube({ width, height, type: textureType === Texture.FLOAT ? Texture.HALF_FLOAT : textureType }); textureUtil.panoramaToCubeMap(renderer, envMap, envCubemap, { encodeRGBM: textureOpts.decodeRGBM }); envMap = envCubemap; } skyEnv = new Skybox({ scene: dummyScene, material: prefilterMaterial }); skyEnv.material.set("environmentMap", envMap); var envMapPass = new EnvironmentMapPass({ texture: prefilteredCubeMap }); if (textureOpts.encodeRGBM) { textureType = prefilteredCubeMap.type = Texture.UNSIGNED_BYTE; } var renderTargetTmp = new Texture2D({ width, height, type: textureType }); var frameBuffer = new FrameBuffer({ depthBuffer: false }); var ArrayCtor = vendor[textureType === Texture.UNSIGNED_BYTE ? "Uint8Array" : "Float32Array"]; for (var i = 0; i < mipmapNum; i++) { prefilteredCubeMap.mipmaps[i] = { pixels: {} }; skyEnv.material.set("roughness", i / (mipmapNum - 1)); var n = renderTargetTmp.width; var fov = 2 * Math.atan(n / (n - 0.5)) / Math.PI * 180; for (var j = 0; j < targets.length; j++) { var pixels = new ArrayCtor(renderTargetTmp.width * renderTargetTmp.height * 4); frameBuffer.attach(renderTargetTmp); frameBuffer.bind(renderer); var camera = envMapPass.getCamera(targets[j]); camera.fov = fov; renderer.render(dummyScene, camera); renderer.gl.readPixels( 0, 0, renderTargetTmp.width, renderTargetTmp.height, Texture.RGBA, textureType, pixels ); frameBuffer.unbind(renderer); prefilteredCubeMap.mipmaps[i].pixels[targets[j]] = pixels; } renderTargetTmp.width /= 2; renderTargetTmp.height /= 2; renderTargetTmp.dirty(); } frameBuffer.dispose(renderer); renderTargetTmp.dispose(renderer); skyEnv.dispose(renderer); normalDistribution.dispose(renderer); return { environmentMap: prefilteredCubeMap, brdfLookup, normalDistribution, maxMipmapLevel: mipmapNum }; }; cubemapUtil.integrateBRDF = function(renderer, normalDistribution) { normalDistribution = normalDistribution || cubemapUtil.generateNormalDistribution(); var framebuffer = new FrameBuffer({ depthBuffer: false }); var pass = new Pass({ fragment: integrateBRDFShaderCode }); var texture = new Texture2D({ width: 512, height: 256, type: Texture.HALF_FLOAT, wrapS: Texture.CLAMP_TO_EDGE, wrapT: Texture.CLAMP_TO_EDGE, minFilter: Texture.NEAREST, magFilter: Texture.NEAREST, useMipmap: false }); pass.setUniform("normalDistribution", normalDistribution); pass.setUniform("viewportSize", [512, 256]); pass.attachOutput(texture); pass.render(renderer, framebuffer); framebuffer.dispose(renderer); return texture; }; cubemapUtil.generateNormalDistribution = function(roughnessLevels, sampleSize) { var roughnessLevels = roughnessLevels || 256; var sampleSize = sampleSize || 1024; var normalDistribution = new Texture2D({ width: roughnessLevels, height: sampleSize, type: Texture.FLOAT, minFilter: Texture.NEAREST, magFilter: Texture.NEAREST, wrapS: Texture.CLAMP_TO_EDGE, wrapT: Texture.CLAMP_TO_EDGE, useMipmap: false }); var pixels = new Float32Array(sampleSize * roughnessLevels * 4); var tmp = []; for (var j = 0; j < roughnessLevels; j++) { var roughness = j / roughnessLevels; var a = roughness * roughness; for (var i = 0; i < sampleSize; i++) { var y = (i << 16 | i >>> 16) >>> 0; y = ((y & 1431655765) << 1 | (y & 2863311530) >>> 1) >>> 0; y = ((y & 858993459) << 2 | (y & 3435973836) >>> 2) >>> 0; y = ((y & 252645135) << 4 | (y & 4042322160) >>> 4) >>> 0; y = (((y & 16711935) << 8 | (y & 4278255360) >>> 8) >>> 0) / 4294967296; var cosTheta = Math.sqrt((1 - y) / (1 + (a * a - 1) * y)); tmp[i] = cosTheta; } for (var i = 0; i < sampleSize; i++) { var offset = (i * roughnessLevels + j) * 4; var cosTheta = tmp[i]; var sinTheta = Math.sqrt(1 - cosTheta * cosTheta); var x = i / sampleSize; var phi = 2 * Math.PI * x; pixels[offset] = sinTheta * Math.cos(phi); pixels[offset + 1] = cosTheta; pixels[offset + 2] = sinTheta * Math.sin(phi); pixels[offset + 3] = 1; } } normalDistribution.pixels = pixels; return normalDistribution; }; var cubemapUtil$1 = cubemapUtil; export { cubemapUtil$1 as default };