Selaa lähdekoodia

Add support for procedural textures on nme

David `Deltakosh` Catuhe 5 vuotta sitten
vanhempi
commit
5a8cdc43ab

+ 2 - 1
dist/preview release/what's new.md

@@ -8,7 +8,8 @@
 - Added HDR texture filtering tools to the sandbox ([Sebavan](https://github.com/sebavan/))
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added SubSurfaceScattering on PBR materials ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
-- Added editing of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
+- Added edition of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
+- Added editiom of procedural texture in the node material editor ([Deltakosh](https://github.com/deltakosh))
 - Added Curve editor to manage entity's animations and edit animation groups in Inspector ([pixelspace](https://github.com/devpixelspace))
 - Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added support for **thin instances** for faster mesh instances. [Doc](https://doc.babylonjs.com/how_to/how_to_use_thininstances) ([Popov72](https://github.com/Popov72))

+ 3 - 0
nodeEditor/public/index.js

@@ -134,6 +134,9 @@ if (BABYLON.Engine.isSupported()) {
                 break;
             case BABYLON.NodeMaterialModes.Particle:
                 nodeMaterial.setToDefaultParticle();
+                break;                
+            case BABYLON.NodeMaterialModes.ProceduralTexture:
+                nodeMaterial.setToDefaultProceduralTexture();
                 break;
         }
         nodeMaterial.build(true);

+ 10 - 1
nodeEditor/src/components/nodeList/nodeListComponent.tsx

@@ -240,6 +240,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
             Particle: ["ParticleBlendMultiplyBlock", "ParticleColorBlock", "ParticlePositionWorldBlock", "ParticleRampGradientBlock", "ParticleTextureBlock", "ParticleTextureMaskBlock", "ParticleUVBlock"],
             PBR: ["PBRMetallicRoughnessBlock", "AmbientOcclusionBlock", "AnisotropyBlock", "ClearCoatBlock", "ReflectionBlock", "ReflectivityBlock", "RefractionBlock", "SheenBlock", "SubSurfaceBlock"],
             PostProcess: ["Position2DBlock", "CurrentScreenBlock"],
+            Procedural__Texture: ["Position2DBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             Scene: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ViewDirectionBlock"],
@@ -249,16 +250,24 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
             case NodeMaterialModes.Material:
                 delete allBlocks["PostProcess"];
                 delete allBlocks["Particle"];
+                delete allBlocks["Procedural__Texture"];
                 break;
             case NodeMaterialModes.PostProcess:
                 delete allBlocks["Animation"];
                 delete allBlocks["Mesh"];
                 delete allBlocks["Particle"];
+                delete allBlocks["Procedural__Texture"];
+                break;
+            case NodeMaterialModes.ProceduralTexture:
+                delete allBlocks["Animation"];
+                delete allBlocks["Mesh"];                
+                delete allBlocks["PostProcess"];
                 break;
             case NodeMaterialModes.Particle:
                 delete allBlocks["Animation"];
                 delete allBlocks["Mesh"];
-                delete allBlocks["PostProcess"];
+                delete allBlocks["PostProcess"];            
+                delete allBlocks["Procedural__Texture"];
                 allBlocks.Output_Nodes.splice(allBlocks.Output_Nodes.indexOf("VertexOutputBlock"), 1);
                 break;
         }

+ 4 - 2
nodeEditor/src/components/preview/previewManager.ts

@@ -229,7 +229,8 @@ export class PreviewManager {
                 this._handleAnimations();
                 break;
             }
-            case NodeMaterialModes.PostProcess: {
+            case NodeMaterialModes.PostProcess: 
+            case NodeMaterialModes.ProceduralTexture: {
                 this._camera.radius = 4;
                 this._camera.upperRadiusLimit = 10;
                 break;
@@ -421,7 +422,8 @@ export class PreviewManager {
             }
 
             switch (this._globalState.mode) {
-                case NodeMaterialModes.PostProcess: {
+                case NodeMaterialModes.PostProcess: 
+                case NodeMaterialModes.ProceduralTexture: {
                     this._globalState.onIsLoadingChanged.notifyObservers(false);
 
                     this._postprocess = tempMaterial.createPostProcess(this._camera, 1.0, Constants.TEXTURE_NEAREST_SAMPLINGMODE, this._engine);

+ 18 - 1
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -287,6 +287,9 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                 case NodeMaterialModes.Particle:
                     this.props.globalState.nodeMaterial!.setToDefaultParticle();
                     break;
+                case NodeMaterialModes.ProceduralTexture:
+                    this.props.globalState.nodeMaterial!.setToDefaultProceduralTexture();
+                    break;
             }
         }
 
@@ -350,6 +353,7 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
             { label: "Material", value: NodeMaterialModes.Material },
             { label: "Post Process", value: NodeMaterialModes.PostProcess },
             { label: "Particle", value: NodeMaterialModes.Particle },
+            { label: "Procedural", value: NodeMaterialModes.ProceduralTexture },
         ];
 
         return (
@@ -366,7 +370,20 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                         <TextLineComponent label="Version" value={Engine.Version}/>
                         <TextLineComponent label="Help" value="doc.babylonjs.com" underline={true} onLink={() => window.open('https://doc.babylonjs.com/how_to/node_material', '_blank')}/>
                         <ButtonLineComponent label="Reset to default" onClick={() => {
-                            this.props.globalState.nodeMaterial!.setToDefault();
+                            switch (this.props.globalState.mode) {
+                                case NodeMaterialModes.Material:
+                                    this.props.globalState.nodeMaterial!.setToDefault();
+                                    break;
+                                case NodeMaterialModes.PostProcess:
+                                    this.props.globalState.nodeMaterial!.setToDefaultPostProcess();
+                                    break;
+                                case NodeMaterialModes.Particle:
+                                    this.props.globalState.nodeMaterial!.setToDefaultParticle();
+                                    break;
+                                case NodeMaterialModes.ProceduralTexture:
+                                    this.props.globalState.nodeMaterial!.setToDefaultProceduralTexture();
+                                    break;
+                            }
                             this.props.globalState.onResetRequiredObservable.notifyObservers();
                         }} />
                     </LineContainerComponent>

+ 1 - 1
nodeEditor/src/diagram/display/inputDisplayManager.ts

@@ -18,7 +18,7 @@ const inputNameToAttributeValue: { [name: string] : string } = {
 };
 
 const inputNameToAttributeName: { [name: string] : string } = {
-    "position2d" : "postprocess",
+    "position2d" : "screen",
     "particle_uv" : "particle",
     "particle_color" : "particle",
     "particle_texturemask": "particle",

+ 1 - 0
nodeEditor/src/nodeEditor.ts

@@ -60,6 +60,7 @@ export class NodeEditor {
         if (options.customLoadObservable) {
             options.customLoadObservable.add(data => {
                 SerializationTools.Deserialize(data, globalState);
+                globalState.mode = options.nodeMaterial.mode;
                 globalState.onResetRequiredObservable.notifyObservers();
                 globalState.onBuiltObservable.notifyObservers();
             })

+ 2 - 0
src/Materials/Node/Enums/nodeMaterialModes.ts

@@ -8,4 +8,6 @@ export enum NodeMaterialModes {
     PostProcess = 1,
     /** For particle system */
     Particle = 2,
+    /** For procedural texture */
+    ProceduralTexture = 3,
 }

+ 153 - 41
src/Materials/Node/nodeMaterial.ts

@@ -3,7 +3,7 @@ import { PushMaterial } from '../pushMaterial';
 import { Scene } from '../../scene';
 import { AbstractMesh } from '../../Meshes/abstractMesh';
 import { Matrix, Vector2 } from '../../Maths/math.vector';
-import { Color4 } from '../../Maths/math.color';
+import { Color3, Color4 } from '../../Maths/math.color';
 import { Mesh } from '../../Meshes/mesh';
 import { Engine } from '../../Engines/engine';
 import { NodeMaterialBuildState } from './nodeMaterialBuildState';
@@ -47,6 +47,9 @@ import { IParticleSystem } from '../../Particles/IParticleSystem';
 import { BaseParticleSystem } from '../../Particles/baseParticleSystem';
 import { ColorSplitterBlock } from './Blocks/colorSplitterBlock';
 import { TimingTools } from '../../Misc/timingTools';
+import { ProceduralTexture } from '../Textures/Procedurals/proceduralTexture';
+import { AnimatedInputBlockTypes } from './Blocks/Input/animatedInputBlockTypes';
+import { TrigonometryBlock, TrigonometryBlockOperations } from './Blocks/trigonometryBlock';
 
 const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable<SubMesh> };
 
@@ -748,7 +751,7 @@ export class NodeMaterial extends PushMaterial {
     public createPostProcess(
         camera: Nullable<Camera>, options: number | PostProcessOptions = 1, samplingMode: number = Constants.TEXTURE_NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean,
         textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, textureFormat = Constants.TEXTUREFORMAT_RGBA): PostProcess {
-            return this._createEffectOrPostProcess(null, camera, options, samplingMode, engine, reusable, textureType, textureFormat);
+            return this._createEffectForPostProcess(null, camera, options, samplingMode, engine, reusable, textureType, textureFormat);
     }
 
     /**
@@ -756,10 +759,10 @@ export class NodeMaterial extends PushMaterial {
      * @param postProcess The post process to create the effect for
      */
     public createEffectForPostProcess(postProcess: PostProcess) {
-        this._createEffectOrPostProcess(postProcess);
+        this._createEffectForPostProcess(postProcess);
     }
 
-    private _createEffectOrPostProcess(postProcess: Nullable<PostProcess>,
+    private _createEffectForPostProcess(postProcess: Nullable<PostProcess>,
         camera?: Nullable<Camera>, options: number | PostProcessOptions = 1, samplingMode: number = Constants.TEXTURE_NEAREST_SAMPLINGMODE, engine?: Engine, reusable?: boolean,
         textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, textureFormat = Constants.TEXTUREFORMAT_RGBA): PostProcess {
         let tempName = this.name + this._buildId;
@@ -807,33 +810,78 @@ export class NodeMaterial extends PushMaterial {
                 );
             }
 
-            // Animated blocks
-            if (this._sharedData.animatedInputs) {
-                const scene = this.getScene();
+            this._checkInternals(effect);
+        });
 
-                let frameId = scene.getFrameId();
+        return postProcess;
+    }
 
-                if (this._animationFrame !== frameId) {
-                    for (var input of this._sharedData.animatedInputs) {
-                        input.animate(scene);
-                    }
+    /**
+     * Create a new procedural texture based on this node material
+     * @param size defines the size of the texture (which will be squared)
+     * @param scene defines the hosting scene
+     * @returns the new procedural texture attached to this node material
+     */
+    public createProceduralTexture(size: number, scene: Scene): ProceduralTexture {
+        let tempName = this.name + this._buildId;
 
-                    this._animationFrame = frameId;
-                }
-            }
+        let proceduralTexture = new ProceduralTexture(tempName, size, null, scene);
+
+        const dummyMesh = new AbstractMesh(tempName + "Procedural", this.getScene());
+        dummyMesh.reservedDataStore = {
+            hidden: true
+        };
+        
+        const defines = new NodeMaterialDefines();
+        let result = this._processDefines(dummyMesh, defines);
+        Effect.RegisterShader(tempName, this._fragmentCompilationState._builtCompilationString, this._vertexCompilationState._builtCompilationString);
 
-            // Bindable blocks
-            for (var block of this._sharedData.bindableBlocks) {
-                block.bind(effect, this);
+        let effect = this.getScene().getEngine().createEffect({
+                vertexElement: tempName,
+                fragmentElement: tempName
+            },
+            [VertexBuffer.PositionKind],
+            this._fragmentCompilationState.uniforms,
+            this._fragmentCompilationState.samplers,
+            defines.toString(), result?.fallbacks, undefined);
+
+        proceduralTexture.nodeMaterialSource = this;
+        proceduralTexture._effect = effect;
+
+        let buildId = this._buildId;
+        proceduralTexture.onBeforeGenerationObservable.add(() => {
+            if (buildId !== this._buildId) {
+                delete Effect.ShadersStore[tempName + "VertexShader"];
+                delete Effect.ShadersStore[tempName + "PixelShader"];
+
+                tempName = this.name + this._buildId;
+
+                defines.markAsUnprocessed();
+
+                buildId = this._buildId;
             }
 
-            // Connection points
-            for (var inputBlock of this._sharedData.inputBlocks) {
-                inputBlock._transmit(effect, this.getScene());
+            const result = this._processDefines(dummyMesh, defines);
+
+            if (result) {
+                Effect.RegisterShader(tempName, this._fragmentCompilationState._builtCompilationString, this._vertexCompilationState._builtCompilationString);
+
+                TimingTools.SetImmediate(() => {
+                    effect = this.getScene().getEngine().createEffect({
+                        vertexElement: tempName,
+                        fragmentElement: tempName
+                    },
+                    [VertexBuffer.PositionKind],
+                    this._fragmentCompilationState.uniforms,
+                    this._fragmentCompilationState.samplers,
+                    defines.toString(), result?.fallbacks, undefined);
+                });
             }
+
+            this._checkInternals(effect);
         });
 
-        return postProcess;
+        return proceduralTexture;
     }
 
     private _createEffectForParticles(particleSystem: IParticleSystem, blendMode: number, onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void, effect?: Effect, defines?: NodeMaterialDefines, dummyMesh?: Nullable<AbstractMesh>, particleSystemDefinesJoined_ = "") {
@@ -847,6 +895,9 @@ export class NodeMaterial extends PushMaterial {
             dummyMesh = this.getScene().getMeshByName(this.name + "Particle");
             if (!dummyMesh) {
                 dummyMesh = new AbstractMesh(this.name + "Particle", this.getScene());
+                dummyMesh.reservedDataStore = {
+                    hidden: true
+                };
             }
         }
 
@@ -902,31 +953,35 @@ export class NodeMaterial extends PushMaterial {
                 return;
             }
 
-            // Animated blocks
-            if (this._sharedData.animatedInputs) {
-                const scene = this.getScene();
+            this._checkInternals(effect);
+        });
+    }
 
-                let frameId = scene.getFrameId();
+    private _checkInternals(effect: Effect) {
+         // Animated blocks
+         if (this._sharedData.animatedInputs) {
+            const scene = this.getScene();
 
-                if (this._animationFrame !== frameId) {
-                    for (var input of this._sharedData.animatedInputs) {
-                        input.animate(scene);
-                    }
+            let frameId = scene.getFrameId();
 
-                    this._animationFrame = frameId;
+            if (this._animationFrame !== frameId) {
+                for (var input of this._sharedData.animatedInputs) {
+                    input.animate(scene);
                 }
-            }
 
-            // Bindable blocks
-            for (var block of this._sharedData.bindableBlocks) {
-                block.bind(effect, this);
+                this._animationFrame = frameId;
             }
+        }
 
-            // Connection points
-            for (var inputBlock of this._sharedData.inputBlocks) {
-                inputBlock._transmit(effect, this.getScene());
-            }
-        });
+        // Bindable blocks
+        for (var block of this._sharedData.bindableBlocks) {
+            block.bind(effect, this);
+        }
+
+        // Connection points
+        for (var inputBlock of this._sharedData.inputBlocks) {
+            inputBlock._transmit(effect, this.getScene());
+        }
     }
 
     /**
@@ -1351,7 +1406,7 @@ export class NodeMaterial extends PushMaterial {
     }
 
     /**
-     * Clear the current material and set it to a default state for post process
+     * Clear the current material and set it to a default state for procedural texture
      */
     public setToDefaultPostProcess() {
         this.clear();
@@ -1401,6 +1456,62 @@ export class NodeMaterial extends PushMaterial {
     }
 
     /**
+     * Clear the current material and set it to a default state for post process
+     */
+    public setToDefaultProceduralTexture() {
+        this.clear();
+
+        this.editorData = null;
+
+        const position = new InputBlock("Position");
+        position.setAsAttribute("position2d");
+
+        const const1 = new InputBlock("Constant1");
+        const1.isConstant = true;
+        const1.value = 1;
+
+        const vmerger = new VectorMergerBlock("Position3D");
+
+        position.connectTo(vmerger);
+        const1.connectTo(vmerger, { input: "w" });
+
+        const vertexOutput = new VertexOutputBlock("VertexOutput");
+        vmerger.connectTo(vertexOutput);
+
+        // Pixel
+        var time = new InputBlock("Time");
+        time.value = 0;
+        time.min = 0;
+        time.max = 0;
+        time.isBoolean = false;
+        time.matrixMode = 0;
+        time.animationType = AnimatedInputBlockTypes.Time;
+        time.isConstant = false;
+
+        const color = new InputBlock("Color3");
+        color.value = new Color3(1, 1, 1);
+        color.isConstant = false;
+        var fragmentOutput = new FragmentOutputBlock("FragmentOutput");
+
+        var vectorMerger = new VectorMergerBlock("VectorMerger");
+        vectorMerger.visibleInInspector = false
+        
+        var cos = new TrigonometryBlock("Cos");
+        cos.operation = TrigonometryBlockOperations.Cos;   
+        
+        position.connectTo(vectorMerger);
+        time.output.connectTo(cos.input);
+        cos.output.connectTo(vectorMerger.z);
+        vectorMerger.xyzOut.connectTo(fragmentOutput.rgb);
+
+        // Add to nodes
+        this.addOutputNode(vertexOutput);
+        this.addOutputNode(fragmentOutput);
+
+        this._mode = NodeMaterialModes.ProceduralTexture;
+    }
+
+    /**
      * Clear the current material and set it to a default state for particle
      */
     public setToDefaultParticle() {
@@ -1784,6 +1895,7 @@ export class NodeMaterial extends PushMaterial {
      */
     public static CreateDefault(name: string, scene?: Scene) {
         let newMaterial = new NodeMaterial(name, scene);
+
         newMaterial.setToDefault();
         newMaterial.build();
 

+ 62 - 36
src/Materials/Textures/Procedurals/proceduralTexture.ts

@@ -19,6 +19,7 @@ import "../../../Engines/Extensions/engine.renderTargetCube";
 import "../../../Shaders/procedural.vertex";
 import { DataBuffer } from '../../../Meshes/dataBuffer';
 import { _TypeStore } from '../../../Misc/typeStore';
+import { NodeMaterial } from '../../Node/nodeMaterial';
 
 /**
  * Procedural texturing is a way to programmatically create a texture. There are 2 types of procedural textures: code-only, and code that references some classic 2D images, sometimes calmpler' images.
@@ -48,6 +49,16 @@ export class ProceduralTexture extends Texture {
      */
     public onGeneratedObservable = new Observable<ProceduralTexture>();
 
+    /**
+     * Event raised before the texture is generated
+     */
+    public onBeforeGenerationObservable = new Observable<ProceduralTexture>();
+
+    /**
+     * Gets or sets the node material used to create this texture (null if the texture was manually created)
+     */
+    public nodeMaterialSource: Nullable<NodeMaterial> = null;
+
     /** @hidden */
     @serialize()
     public _generateMipMaps: boolean;
@@ -152,6 +163,11 @@ export class ProceduralTexture extends Texture {
         return this._effect;
     }
 
+    /** @hidden */
+    public _setEffect(effect: Effect) {
+        this._effect = effect;
+    }
+
     /**
      * Gets texture content (Use this function wisely as reading from a texture can be slow)
      * @returns an ArrayBufferView (Uint8Array or Float32Array)
@@ -221,6 +237,10 @@ export class ProceduralTexture extends Texture {
         var engine = this._fullEngine;
         var shaders;
 
+        if (this.nodeMaterialSource) {
+            return this._effect.isReady();
+        }
+
         if (!this._fragment) {
             return false;
         }
@@ -489,52 +509,55 @@ export class ProceduralTexture extends Texture {
 
         // Render
         engine.enableEffect(this._effect);
+        this.onBeforeGenerationObservable.notifyObservers(this);
         engine.setState(false);
 
-        // Texture
-        for (var name in this._textures) {
-            this._effect.setTexture(name, this._textures[name]);
-        }
+        if (!this.nodeMaterialSource) {
+            // Texture
+            for (var name in this._textures) {
+                this._effect.setTexture(name, this._textures[name]);
+            }
 
-        // Float
-        for (name in this._ints) {
-            this._effect.setInt(name, this._ints[name]);
-        }
+            // Float
+            for (name in this._ints) {
+                this._effect.setInt(name, this._ints[name]);
+            }
 
-        // Float
-        for (name in this._floats) {
-            this._effect.setFloat(name, this._floats[name]);
-        }
+            // Float
+            for (name in this._floats) {
+                this._effect.setFloat(name, this._floats[name]);
+            }
 
-        // Floats
-        for (name in this._floatsArrays) {
-            this._effect.setArray(name, this._floatsArrays[name]);
-        }
+            // Floats
+            for (name in this._floatsArrays) {
+                this._effect.setArray(name, this._floatsArrays[name]);
+            }
 
-        // Color3
-        for (name in this._colors3) {
-            this._effect.setColor3(name, this._colors3[name]);
-        }
+            // Color3
+            for (name in this._colors3) {
+                this._effect.setColor3(name, this._colors3[name]);
+            }
 
-        // Color4
-        for (name in this._colors4) {
-            var color = this._colors4[name];
-            this._effect.setFloat4(name, color.r, color.g, color.b, color.a);
-        }
+            // Color4
+            for (name in this._colors4) {
+                var color = this._colors4[name];
+                this._effect.setFloat4(name, color.r, color.g, color.b, color.a);
+            }
 
-        // Vector2
-        for (name in this._vectors2) {
-            this._effect.setVector2(name, this._vectors2[name]);
-        }
+            // Vector2
+            for (name in this._vectors2) {
+                this._effect.setVector2(name, this._vectors2[name]);
+            }
 
-        // Vector3
-        for (name in this._vectors3) {
-            this._effect.setVector3(name, this._vectors3[name]);
-        }
+            // Vector3
+            for (name in this._vectors3) {
+                this._effect.setVector3(name, this._vectors3[name]);
+            }
 
-        // Matrix
-        for (name in this._matrices) {
-            this._effect.setMatrix(name, this._matrices[name]);
+            // Matrix
+            for (name in this._matrices) {
+                this._effect.setMatrix(name, this._matrices[name]);
+            }
         }
 
         if (!this._texture) {
@@ -632,6 +655,9 @@ export class ProceduralTexture extends Texture {
             this._indexBuffer = null;
         }
 
+        this.onGeneratedObservable.clear();
+        this.onBeforeGenerationObservable.clear();
+
         super.dispose();
     }
 }

+ 1 - 1
src/PostProcesses/postProcess.ts

@@ -337,7 +337,7 @@ export class PostProcess {
      * @param textureType Type of textures used when performing the post process. (default: 0)
      * @param vertexUrl The url of the vertex shader to be used. (default: "postprocess")
      * @param indexParameters The index parameters to be used for babylons include syntax "#include<kernelBlurVaryingDeclaration>[0..varyingCount]". (default: undefined) See usage in babylon.blurPostProcess.ts and kernelBlur.vertex.fx
-     * @param blockCompilation If the shader should not be compiled imediatly. (default: false)
+     * @param blockCompilation If the shader should not be compiled immediatly. (default: false)
      * @param textureFormat Format of textures used when performing the post process. (default: TEXTUREFORMAT_RGBA)
      */
     constructor(