123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- import Cartesian3 from '../Core/Cartesian3.js';
- import ComponentDatatype from '../Core/ComponentDatatype.js';
- import defined from '../Core/defined.js';
- import defineProperties from '../Core/defineProperties.js';
- import destroyObject from '../Core/destroyObject.js';
- import IndexDatatype from '../Core/IndexDatatype.js';
- import loadKTX from '../Core/loadKTX.js';
- import PixelFormat from '../Core/PixelFormat.js';
- import Buffer from '../Renderer/Buffer.js';
- import BufferUsage from '../Renderer/BufferUsage.js';
- import ComputeCommand from '../Renderer/ComputeCommand.js';
- import CubeMap from '../Renderer/CubeMap.js';
- import PixelDatatype from '../Renderer/PixelDatatype.js';
- import ShaderProgram from '../Renderer/ShaderProgram.js';
- import Texture from '../Renderer/Texture.js';
- import VertexArray from '../Renderer/VertexArray.js';
- import OctahedralProjectionAtlasFS from '../Shaders/OctahedralProjectionAtlasFS.js';
- import OctahedralProjectionFS from '../Shaders/OctahedralProjectionFS.js';
- import OctahedralProjectionVS from '../Shaders/OctahedralProjectionVS.js';
- import when from '../ThirdParty/when.js';
- /**
- * Packs all mip levels of a cube map into a 2D texture atlas.
- *
- * Octahedral projection is a way of putting the cube maps onto a 2D texture
- * with minimal distortion and easy look up.
- * See Chapter 16 of WebGL Insights "HDR Image-Based Lighting on the Web" by Jeff Russell
- * and "Octahedron Environment Maps" for reference.
- *
- * @private
- */
- function OctahedralProjectedCubeMap(url) {
- this._url = url;
- this._cubeMapBuffers = undefined;
- this._cubeMaps = undefined;
- this._texture = undefined;
- this._mipTextures = undefined;
- this._va = undefined;
- this._sp = undefined;
- this._maximumMipmapLevel = undefined;
- this._loading = false;
- this._ready = false;
- this._readyPromise = when.defer();
- }
- defineProperties(OctahedralProjectedCubeMap.prototype, {
- /**
- * The url to the KTX file containing the specular environment map and convoluted mipmaps.
- * @memberof OctahedralProjectedCubeMap.prototype
- * @type {String}
- * @readonly
- */
- url : {
- get : function() {
- return this._url;
- }
- },
- /**
- * A texture containing all the packed convolutions.
- * @memberof OctahedralProjectedCubeMap.prototype
- * @type {Texture}
- * @readonly
- */
- texture : {
- get : function() {
- return this._texture;
- }
- },
- /**
- * The maximum number of mip levels.
- * @memberOf OctahedralProjectedCubeMap.prototype
- * @type {Number}
- * @readonly
- */
- maximumMipmapLevel : {
- get : function() {
- return this._maximumMipmapLevel;
- }
- },
- /**
- * Determines if the texture atlas is complete and ready to use.
- * @memberof OctahedralProjectedCubeMap.prototype
- * @type {Boolean}
- * @readonly
- */
- ready : {
- get : function() {
- return this._ready;
- }
- },
- /**
- * Gets a promise that resolves when the texture atlas is ready to use.
- * @memberof OctahedralProjectedCubeMap.prototype
- * @type {Promise}
- * @readonly
- */
- readyPromise : {
- get : function() {
- return this._readyPromise.promise;
- }
- }
- });
- OctahedralProjectedCubeMap.isSupported = function(context) {
- return (context.colorBufferHalfFloat && context.halfFloatingPointTexture) || (context.floatingPointTexture && context.colorBufferFloat);
- };
- // These vertices are based on figure 1 from "Octahedron Environment Maps".
- var v1 = new Cartesian3(1.0, 0.0, 0.0);
- var v2 = new Cartesian3(0.0, 0.0, 1.0);
- var v3 = new Cartesian3(-1.0, 0.0, 0.0);
- var v4 = new Cartesian3(0.0, 0.0, -1.0);
- var v5 = new Cartesian3(0.0, 1.0, 0.0);
- var v6 = new Cartesian3(0.0, -1.0, 0.0);
- // top left, left, top, center, right, top right, bottom, bottom left, bottom right
- var cubeMapCoordinates = [v5, v3, v2, v6, v1, v5, v4, v5, v5];
- var length = cubeMapCoordinates.length;
- var flatCubeMapCoordinates = new Float32Array(length * 3);
- var offset = 0;
- for (var i = 0; i < length; ++i, offset += 3) {
- Cartesian3.pack(cubeMapCoordinates[i], flatCubeMapCoordinates, offset);
- }
- var flatPositions = new Float32Array([
- -1.0, 1.0, // top left
- -1.0, 0.0, // left
- 0.0, 1.0, // top
- 0.0, 0.0, // center
- 1.0, 0.0, // right
- 1.0, 1.0, // top right
- 0.0, -1.0, // bottom
- -1.0, -1.0, // bottom left
- 1.0, -1.0 // bottom right
- ]);
- var indices = new Uint16Array([
- 0, 1, 2, // top left, left, top,
- 2, 3, 1, // top, center, left,
- 7, 6, 1, // bottom left, bottom, left,
- 3, 6, 1, // center, bottom, left,
- 2, 5, 4, // top, top right, right,
- 3, 4, 2, // center, right, top,
- 4, 8, 6, // right, bottom right, bottom,
- 3, 4, 6 //center, right, bottom
- ]);
- function createVertexArray(context) {
- var positionBuffer = Buffer.createVertexBuffer({
- context : context,
- typedArray : flatPositions,
- usage : BufferUsage.STATIC_DRAW
- });
- var cubeMapCoordinatesBuffer = Buffer.createVertexBuffer({
- context : context,
- typedArray : flatCubeMapCoordinates,
- usage : BufferUsage.STATIC_DRAW
- });
- var indexBuffer = Buffer.createIndexBuffer({
- context : context,
- typedArray : indices,
- usage : BufferUsage.STATIC_DRAW,
- indexDatatype : IndexDatatype.UNSIGNED_SHORT
- });
- var attributes = [{
- index : 0,
- vertexBuffer : positionBuffer,
- componentsPerAttribute : 2,
- componentDatatype : ComponentDatatype.FLOAT
- }, {
- index : 1,
- vertexBuffer : cubeMapCoordinatesBuffer,
- componentsPerAttribute : 3,
- componentDatatype : ComponentDatatype.FLOAT
- }];
- return new VertexArray({
- context : context,
- attributes : attributes,
- indexBuffer : indexBuffer
- });
- }
- function createUniformTexture(texture) {
- return function() {
- return texture;
- };
- }
- function cleanupResources(map) {
- map._va = map._va && map._va.destroy();
- map._sp = map._sp && map._sp.destroy();
- var i;
- var length;
- var cubeMaps = map._cubeMaps;
- if (defined(cubeMaps)) {
- length = cubeMaps.length;
- for (i = 0; i < length; ++i) {
- cubeMaps[i].destroy();
- }
- }
- var mipTextures = map._mipTextures;
- if (defined(mipTextures)) {
- length = mipTextures.length;
- for (i = 0; i < length; ++i) {
- mipTextures[i].destroy();
- }
- }
- map._va = undefined;
- map._sp = undefined;
- map._cubeMaps = undefined;
- map._cubeMapBuffers = undefined;
- map._mipTextures = undefined;
- }
- /**
- * Creates compute commands to generate octahedral projections of each cube map
- * and then renders them to an atlas.
- * <p>
- * Only needs to be called twice. The first call queues the compute commands to generate the atlas.
- * The second call cleans up unused resources. Every call afterwards is a no-op.
- * </p>
- *
- * @param {FrameState} frameState The frame state.
- *
- * @private
- */
- OctahedralProjectedCubeMap.prototype.update = function(frameState) {
- var context = frameState.context;
- if (!OctahedralProjectedCubeMap.isSupported(context)) {
- return;
- }
- if (defined(this._texture) && defined(this._va)) {
- cleanupResources(this);
- }
- if (defined(this._texture)) {
- return;
- }
- if (!defined(this._texture) && !this._loading) {
- var cachedTexture = context.textureCache.getTexture(this._url);
- if (defined(cachedTexture)) {
- cleanupResources(this);
- this._texture = cachedTexture;
- this._maximumMipmapLevel = this._texture.maximumMipmapLevel;
- this._ready = true;
- this._readyPromise.resolve();
- return;
- }
- }
- var cubeMapBuffers = this._cubeMapBuffers;
- if (!defined(cubeMapBuffers) && !this._loading) {
- var that = this;
- loadKTX(this._url).then(function(buffers) {
- that._cubeMapBuffers = buffers;
- that._loading = false;
- });
- this._loading = true;
- }
- if (!defined(this._cubeMapBuffers)) {
- return;
- }
- this._va = createVertexArray(context);
- this._sp = ShaderProgram.fromCache({
- context : context,
- vertexShaderSource : OctahedralProjectionVS,
- fragmentShaderSource : OctahedralProjectionFS,
- attributeLocations : {
- position : 0,
- cubeMapCoordinates : 1
- }
- });
- // We only need up to 6 mip levels to avoid artifacts.
- var length = Math.min(cubeMapBuffers.length, 6);
- this._maximumMipmapLevel = length - 1;
- var cubeMaps = this._cubeMaps = new Array(length);
- var mipTextures = this._mipTextures = new Array(length);
- var originalSize = cubeMapBuffers[0].positiveX.width * 2.0;
- var uniformMap = {
- originalSize : function() {
- return originalSize;
- }
- };
- var pixelDatatype = context.halfFloatingPointTexture ? PixelDatatype.HALF_FLOAT : PixelDatatype.FLOAT;
- var pixelFormat = PixelFormat.RGBA;
- // First we project each cubemap onto a flat octahedron, and write that to a texture.
- for (var i = 0; i < length; ++i) {
- // Swap +Y/-Y faces since the octahedral projection expects this order.
- var positiveY = cubeMapBuffers[i].positiveY;
- cubeMapBuffers[i].positiveY = cubeMapBuffers[i].negativeY;
- cubeMapBuffers[i].negativeY = positiveY;
- var cubeMap = cubeMaps[i] = new CubeMap({
- context : context,
- source : cubeMapBuffers[i]
- });
- var size = cubeMaps[i].width * 2;
- var mipTexture = mipTextures[i] = new Texture({
- context : context,
- width : size,
- height : size,
- pixelDatatype : pixelDatatype,
- pixelFormat : pixelFormat
- });
- var command = new ComputeCommand({
- vertexArray : this._va,
- shaderProgram : this._sp,
- uniformMap : {
- cubeMap : createUniformTexture(cubeMap)
- },
- outputTexture : mipTexture,
- persists : true,
- owner : this
- });
- frameState.commandList.push(command);
- uniformMap['texture' + i] = createUniformTexture(mipTexture);
- }
- this._texture = new Texture({
- context : context,
- width : originalSize * 1.5 + 2.0, // We add a 1 pixel border to avoid linear sampling artifacts.
- height : originalSize,
- pixelDatatype : pixelDatatype,
- pixelFormat : pixelFormat
- });
- this._texture.maximumMipmapLevel = this._maximumMipmapLevel;
- context.textureCache.addTexture(this._url, this._texture);
- var atlasCommand = new ComputeCommand({
- fragmentShaderSource : OctahedralProjectionAtlasFS,
- uniformMap : uniformMap,
- outputTexture : this._texture,
- persists : false,
- owner : this
- });
- frameState.commandList.push(atlasCommand);
- this._ready = true;
- this._readyPromise.resolve();
- };
- /**
- * Returns true if this object was destroyed; otherwise, false.
- * <p>
- * If this object was destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
- * </p>
- *
- * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
- *
- * @see OctahedralProjectedCubeMap#destroy
- */
- OctahedralProjectedCubeMap.prototype.isDestroyed = function() {
- return false;
- };
- /**
- * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
- * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
- * <p>
- * Once an object is destroyed, it should not be used; calling any function other than
- * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
- * assign the return value (<code>undefined</code>) to the object as done in the example.
- * </p>
- *
- * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
- *
- * @see OctahedralProjectedCubeMap#isDestroyed
- */
- OctahedralProjectedCubeMap.prototype.destroy = function() {
- cleanupResources(this);
- this._texture = this._texture && this._texture.destroy();
- return destroyObject(this);
- };
- export default OctahedralProjectedCubeMap;
|