mache Posted June 10, 2016 Share Posted June 10, 2016 Hello, I need to set a bunch of textures array in my shaders, is there a simple way to achieve that in babylon js or should I use something like here => http://stackoverflow.com/questions/19592850/how-to-bind-an-array-of-textures-to-a-webgl-shader-uniform Cheers Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 13, 2016 Share Posted June 13, 2016 Hello! Are you using the ShaderMaterial? If yes, you can directly call setTexture to set a texture (!!!): https://github.com/BabylonJS/Babylon.js/blob/master/src/Materials/babylon.shaderMaterial.ts#L47 For a texture array, we can think about adding the same function Quote Link to comment Share on other sites More sharing options...
mache Posted June 14, 2016 Author Share Posted June 14, 2016 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. Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 14, 2016 Share Posted June 14, 2016 Agree this could be a cool PR:) Quote Link to comment Share on other sites More sharing options...
mache Posted June 17, 2016 Author Share Posted June 17, 2016 OK cool, I let you know about it. Quote Link to comment Share on other sites More sharing options...
mache Posted June 17, 2016 Author Share Posted June 17, 2016 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 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 17, 2016 Share Posted June 17, 2016 What about calling this function in a loop from the Effect? Quote Link to comment Share on other sites More sharing options...
mache Posted June 20, 2016 Author Share Posted June 20, 2016 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 ? Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 20, 2016 Share Posted June 20, 2016 can you share a small shader using textures array? I'll try to make it works Quote Link to comment Share on other sites More sharing options...
mache Posted June 21, 2016 Author Share Posted June 21, 2016 ok, I joined my .fx files. debug.fragment.fx debug.vertex.fx Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 21, 2016 Share Posted June 21, 2016 OK I'll work on it soon (tough week but I'll find some time) Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 21, 2016 Share Posted June 21, 2016 Done http://www.babylonjs-playground.com/#NJRT3#4 Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 21, 2016 Share Posted June 21, 2016 Wait no..I forgot one thing Quote Link to comment Share on other sites More sharing options...
GameMonetize Posted June 21, 2016 Share Posted June 21, 2016 Ok goooood now: http://www.babylonjs-playground.com/#NJRT3#6 Quote Link to comment Share on other sites More sharing options...
mache Posted June 22, 2016 Author Share Posted June 22, 2016 nice Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.