-1

I am working in a web viewer that has many shader programs rendering to the same page. I'm adding another program that renders a quad with a texture. The quad renders just fine. The texture is what's giving me an issue.

Mesh.prototype.initTexture = function(ctx) {

    this.texture = this.ctx.createTexture();
    this.texture.image = new Image();

    // ctx.bindTexture(this.ctx.TEXTURE_2D, this.texture);
    // ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, 1, 1, 0, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); // red

    mesh.texture.image.onload = function () {
        //Upon callback, 'this' is different, so we use the global variable for now
        mesh.handleLoadedTexture(mesh.texture,mesh.texture.image);
    }

    this.texture.image.src = "/path/to/images/nehe.gif";

}

That's my initTexture function. I can uncomment the two lines that load the red square (a la WebGL - wait for texture to load), then the texture renders as red and no errors pop up anymore, but my texture never loads. The texture itself is a renderable texture that I downloaded from LearningWebGL. I have a demo downloaded form that site that runs well on my localhost that's in parallel to this project.

Here's the handleLoadedTexture function

Mesh.prototype.handleLoadedTexture = function(texture, image) {

    mesh.ctx.bindTexture(mesh.ctx.TEXTURE_2D, texture);
    mesh.ctx.pixelStorei(mesh.ctx.UNPACK_FLIP_Y_WEBGL, true);
    mesh.ctx.texImage2D(mesh.ctx.TEXTURE_2D, 0, mesh.ctx.RGBA, this.ctx.RGBA, mesh.ctx.UNSIGNED_BYTE, image);
    mesh.ctx.texParameteri(mesh.ctx.TEXTURE_2D, mesh.ctx.TEXTURE_MAG_FILTER, mesh.ctx.NEAREST);
    mesh.ctx.texParameteri(mesh.ctx.TEXTURE_2D, mesh.ctx.TEXTURE_MIN_FILTER, mesh.ctx.NEAREST);
    mesh.ctx.generateMipmap(mesh.ctx.TEXTURE_2D);
    mesh.ctx.bindTexture(mesh.ctx.TEXTURE_2D, null);
}

Then we have the chunk that calls render of this in the overall render pipeline:

if (mesh != null) {
    mesh = new Mesh();
    ctx.useProgram(meshShader);
    // I set the camera and projection matrices here
    glCheck();
    if(!mesh.vbo) {
        mesh.loadGL(ctx);
    }
    if(!mesh.texture){
        mesh.initTexture(ctx);
    }
    mesh.render(meshShader);
    glCheck();
}

Nothing too revolutionary there.

Render function:

Mesh.prototype.render = function(program) {
    assert(this.vbo != null, "VBO is null");
    assert(this.tcbo != null, "TCBO is null");

    // Arguments here are (index, size, type, normalized(bool), stride, offset)
    this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.vbo);
    this.ctx.vertexAttribPointer(program.a.vertex, this.sizevertices, this.ctx.FLOAT, false, 0, 0);

    this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.tcbo);
    this.ctx.vertexAttribPointer(program.a.textureCoordinate, this.sizetexco, this.ctx.FLOAT, false, 0, 0);

    this.ctx.activeTexture(this.ctx.TEXTURE0);
    this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.texture);
    this.ctx.uniform1i(program.uSampler, 0);

    this.ctx.drawArrays(this.ctx.TRIANGLE_STRIP, 0, this.numvertices);
    render.glCheck();
}

Vertex Shader:

varying vec2 vTextureCoord;

attribute vec3 vertex;
attribute vec2 textureCoordinate;

//other unrelated uniforms

void main(void) {

//position settings omitted
    vTextureCoord = textureCoordinate;
}

Fragment Shader:

precision mediump float;

varying vec2 vTextureCoord;

uniform sampler2D uSampler;

void main(void) {
    gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
}
Community
  • 1
  • 1
MarkW
  • 75
  • 1
  • 10
  • There is no point in calling `mesh.ctx.generateMipmap(mesh.ctx.TEXTURE_2D);` if you are going to use a nearest-neighbor minification filter. All you have done is increase the size of your texture data by 33% for no reason. – Andon M. Coleman Feb 04 '14 at 19:24
  • Hmm, I see. I want to say that line was in there as a leftover of flailing at this problem. I removed that line, but the resultant failure to load the texture is the same. Thanks for the info though! – MarkW Feb 04 '14 at 19:54
  • Are you sure that `handleLoadedTexture` is called? Have you checked (with Chrome's WebGL Inspector) whether the texture has been loaded to WebGL? – virtualnobi Feb 05 '14 at 08:28
  • @virtualnobi Completely overlooked that fact. Nice. I would still suggest having flags to check something, better than comparing complete object against some value. – Dragan Okanovic Feb 05 '14 at 09:16
  • @AbstractAlgorithm I'm not sure what you mean - I was referring to some basic debugging checks to make sure the right code is executed and the texture is transferred to WebGL space... – virtualnobi Feb 05 '14 at 09:48
  • @virtualnobi I've done some basic checking that I omitted. Things like `console.log("this is executed")` and things like `console.log("Mesh: " + mesh)`, etc. etc. It is definitely run. In fact, if I load the 1x1 texture first, it stays that 1x1 texture in the scene, but then I can open the js console and ask for `mesh` and it'll return the WebGL object, complete with the texture size - 256 x 256. I know I'm missing something, but by all accounts it *should* be rendering. – MarkW Feb 06 '14 at 02:03

2 Answers2

1

I'm wondering which mesh you are referencing in the initTexture function - it surely is not defined anywhere in the code you provided...

Maybe you wanted to write

this.texture.image.onload = function....

You already learnt that this inside the function is different from outside. But I guess you need to use another variable, not global one, like so:

that = this;
this.texture.image.onload = function () {
    that.handleLoadedTexture(mesh.texture,mesh.texture.image);
}
virtualnobi
  • 1,140
  • 2
  • 11
  • 35
  • Correct! I didn't add that code, it's pretty boiler-plate stuff, but I can add it if you'd like. In the code, `mesh` is a globally defined variable, that I initially set to `null`. Once the other objects are loaded, I go into the js console and execute `mesh = 1`. At that point, `if (!mesh) {...` gets evaluated. I'm fairly new to js as well, but that would be lovely if I could do something like `that = this`. Is that standard procedure for establishing context? – MarkW Feb 06 '14 at 02:05
  • There's another possibility, namely a `bind` function that fixes the execution context for a function, but (I think) here it's easier to use a local variable. Just google "Javascript bind function" and pick your most trusted site... – virtualnobi Feb 06 '14 at 12:04
0
if(!mesh.texture) {    // isn't it 'undefined'
    mesh.initTexture(ctx);
}

Maybe it's better to have appropriate flag for whole thing.

// when creating mesh
mesh.texInit = false;

And when loading:

Mesh.prototype.initTexture = function(ctx) {

    this.texture = this.ctx.createTexture();
    this.texture.image = new Image();

    // ctx.bindTexture(this.ctx.TEXTURE_2D, this.texture);
    // ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, 1, 1, 0, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); // red

    mesh.texture.image.onload = function () {
        //Upon callback, 'this' is different, so we use the global variable for now
        mesh.handleLoadedTexture(mesh.texture,mesh.texture.image);
        mesh.texInit = true;
        // note ^ here
    }
    this.texture.image.src = "/path/to/images/nehe.gif";
}

And for rendering:

if(mesh.texInit) { doMagic(); } // aka render
Dragan Okanovic
  • 7,509
  • 3
  • 32
  • 48
  • Ah, so add a flag that says that the texture has been initialized? I tried that once, but I bet I missed something. I'm willing to bet that the only time its given the opportunity to render with the texture, it's not ready. I'll give this a try tomorrow and report back. Thanks!! – MarkW Feb 06 '14 at 02:07
  • Yeah, simply use the flag to know if you have completely finalized creating `WebGLTexture` object. It's much better than comparing the whole object against some value. Say how it went. – Dragan Okanovic Feb 06 '14 at 08:35
  • Hmm, still nothing. The texture is still my temporary 1x1 red. I added `mesh.texinit = false` to the object instantiation. I made sure the logic worked that the texture was only applied on texture load: ```if (mesh.texinit == false) { mesh.initTexture(ctx); }``` Furthermore, I checked in the console, `mesh.texinit` logs `true`, and `mesh.texture.image.height` logs `256`. No errors appear in this run. – MarkW Feb 06 '14 at 15:26
  • Your comment set me on the right path. I had logic that was overwriting my object equivalent to: ``` if (mesh != null) { mesh = new Mesh(); ...}``` That's embarassing. I fixed that and it loads wonderfully. Thanks for your help! – MarkW Feb 06 '14 at 15:44