Jump to content

How to deal with samplers array in Babylon js


mache
 Share

Recommended Posts

Hello,

Yes Iam using ShaderMaterial (you could do the same with an Effect as long you can get the WebGlProgram), finally I did it this way:

//this._nbIndexTextures = length of each array

for (let i = 0; i < this._nbIndexTextures + 1; i++) {
	//ini texture units
	textureUnitsId.push(i);
	textureUnitsNormal.push(i + this._nbIndexTextures + 1);
	textureUnitsPosition.push(i + 2 * (this._nbIndexTextures + 1));
}

var program = materialBuffer._effect.getProgram();
this._gl.useProgram(program);
var idLocation = this._gl.getUniformLocation(program, "idSamplers[0]");
var normalLocation = this._gl.getUniformLocation(program, "normalSamplers[0]");
var positionLocation = this._gl.getUniformLocation(program, "positionSamplers[0]");

this._gl.uniform1iv(idLocation, textureUnitsId);
for (let i = 0; i < this._nbIndexTextures + 1; i++) {
	//id
	this._gl.activeTexture(this._gl.TEXTURE0 + i);
	this._gl.bindTexture(this._gl.TEXTURE_2D, this._idTextures[i]._texture);
}

this._gl.uniform1iv(normalLocation, textureUnitsNormal);
for (let i = this._nbIndexTextures + 1; i < 2 * (this._nbIndexTextures + 1); i++) {
	var index = i - (this._nbIndexTextures + 1);
	//normal
	this._gl.activeTexture(this._gl.TEXTURE0 + i);
	this._gl.bindTexture(this._gl.TEXTURE_2D, this._normalTextures[index]._texture);
}

this._gl.uniform1iv(positionLocation, textureUnitsPosition);
for (let i = 2 * (this._nbIndexTextures + 1); i < 3 * (this._nbIndexTextures + 1); i++) {
	var index = i - 2 * (this._nbIndexTextures + 1);
	//position
	this._gl.activeTexture(this._gl.TEXTURE0 + i);
	this._gl.bindTexture(this._gl.TEXTURE_2D, this._positionTextures[index]._texture);
}

Since iam not using babylon js to set my uniforms textures I had bug when using setTexture function, so I also set other single texture this way. (texture unit were not well allocated i guess).

this function could just looks like this => setTextureArray(name, textures) each time before binding babylon check textures.length and manage texture units.

 

Link to comment
Share on other sites

Hi,

So I took a look at the babylon code. It looks like setTexture and the hypothetic setTextures are going to be in conflict since they share texture units. For now I don't really know how to do it without breaking everything :p.

Here is the part which stuck me 

Engine.prototype.setTexture = function (channel, texture) {
            if (channel < 0) {
                return;
            }
            // Not ready?
            if (!texture || !texture.isReady()) {
                if (this._activeTexturesCache[channel] != null) {
                    this._gl.activeTexture(this._gl["TEXTURE" + channel]);
                    this._gl.bindTexture(this._gl.TEXTURE_2D, null);
                    this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
                    this._activeTexturesCache[channel] = null;
                }
                return;
            }
            // Video
            var alreadyActivated = false;
            if (texture instanceof BABYLON.VideoTexture) {
                this._gl.activeTexture(this._gl["TEXTURE" + channel]);
                alreadyActivated = true;
                texture.update();
            }
            else if (texture.delayLoadState === Engine.DELAYLOADSTATE_NOTLOADED) {
                texture.delayLoad();
                return;
            }
            if (this._activeTexturesCache[channel] === texture) {
                return;
            }
            this._activeTexturesCache[channel] = texture;
            var internalTexture = texture.getInternalTexture();
            if (!alreadyActivated) {
                this._gl.activeTexture(this._gl["TEXTURE" + channel]);
            }
            if (internalTexture.isCube) {
                this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, internalTexture);
                if (internalTexture._cachedCoordinatesMode !== texture.coordinatesMode) {
                    internalTexture._cachedCoordinatesMode = texture.coordinatesMode;
                    // CUBIC_MODE and SKYBOX_MODE both require CLAMP_TO_EDGE.  All other modes use REPEAT.
                    var textureWrapMode = (texture.coordinatesMode !== BABYLON.Texture.CUBIC_MODE && texture.coordinatesMode !== BABYLON.Texture.SKYBOX_MODE) ? this._gl.REPEAT : this._gl.CLAMP_TO_EDGE;
                    this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_WRAP_S, textureWrapMode);
                    this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_WRAP_T, textureWrapMode);
                }
                this._setAnisotropicLevel(this._gl.TEXTURE_CUBE_MAP, texture);
            }
            else {
                this._gl.bindTexture(this._gl.TEXTURE_2D, internalTexture);
                if (internalTexture._cachedWrapU !== texture.wrapU) {
                    internalTexture._cachedWrapU = texture.wrapU;
                    switch (texture.wrapU) {
                        case BABYLON.Texture.WRAP_ADDRESSMODE:
                            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.REPEAT);
                            break;
                        case BABYLON.Texture.CLAMP_ADDRESSMODE:
                            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE);
                            break;
                        case BABYLON.Texture.MIRROR_ADDRESSMODE:
                            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.MIRRORED_REPEAT);
                            break;
                    }
                }
                if (internalTexture._cachedWrapV !== texture.wrapV) {
                    internalTexture._cachedWrapV = texture.wrapV;
                    switch (texture.wrapV) {
                        case BABYLON.Texture.WRAP_ADDRESSMODE:
                            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.REPEAT);
                            break;
                        case BABYLON.Texture.CLAMP_ADDRESSMODE:
                            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE);
                            break;
                        case BABYLON.Texture.MIRROR_ADDRESSMODE:
                            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.MIRRORED_REPEAT);
                            break;
                    }
                }
                this._setAnisotropicLevel(this._gl.TEXTURE_2D, texture);
            }
        };

This is where texture are binded, so this part of code need to know if there is array of textures as Engine.prototype.setTextures need to know how many single texture are binded. The shaderMaterial which is calling effect function which is calling this function (engine) could know these informations, but I feel that to pass it as parameter is not the right way to do it... 

How do you think it should be done ?

Have a nice weekend :)

 

Link to comment
Share on other sites

Hello,

I tried a for loop into the effect function. Here are main changes into the code:

BABYLON.ShaderMaterial.prototype.setTextures = function(name, textures) {
	if (this._options.samplers.indexOf(name) === -1) {
		this._options.samplers.push(name);
		for (let i = 1; i < textures.length; i++) {
			this._options.samplers.push(name + i);//to match texture unit when binding
		}
	}
	this._textureArrays[name] = textures;
	return this;
};

BABYLON.Effect.prototype.setTextures = function(channel, textures) {
	var textureUnits = [];
	for (let i = 0; i < textures.length; i++) {
		textureUnits.push(this._samplers.indexOf(channel) + i);
	}
	var location = this._engine._gl.getUniformLocation(this._program, channel);
	this._engine._gl.uniform1iv(location, textureUnits);
	for (let i = 0; i < textures.length; i++) {
		this._engine.setTexture(textureUnits[i], textures[i]);
	}
};

//part of code added into ShaderMaterial.prototype.bind

for (name in this._textureArrays) {
    this._effect.setTextures(name, this._textureArrays[name]);
}

this code works fine when I only use setTextures function on a material, but not when I am using in addition a setTexture function. This is caused by :

this._samplers.indexOf(channel)

When I only use setTextures this.samplers is ok so I got the right one index of nameOfMyArray (0). If I used setTexture in addition this.samplers don't index my array (so I get -1) and texture unit are not well manage.

I tested to replace "this._samplers.indexOf(channel)" by the right index hard coded (if i added a single texture then my array index should be 1) and it's working.

Any ideas of why setTexture erase nameOfMyArray in this.samplers into Effect ?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...