3

I am trying to write a simple WebGL script to display a pyramid with a brick texture and Phong lighting. The lighting works fine but when I incorporate the texture, the pyramid turns black. I've checked and set my parameters to use with power-of-two textures since my texture is 512x512. This scene should be straight out of the the Mozilla WebGL examples but some reason it is not working. I am now desperate and cannot figure out why adding textures objects black. Below is my code.

    <html>
    <head>
    <title>WebGL Simple Pyramid</

    CanvasMatrix class
    (c) Apple Inc
    -->
    <script src="CanvasMatrix.js" type="text/javascript"></script>

    <!--
      Vertex shader
    -->
    <script id="shader-vs" type="x-shader/x-vertex"> 
       precision highp float;
       attribute vec3 XYZ;
       attribute vec3 RGB;
       attribute vec3 NOR;
       attribute vec2 TEX;
       uniform mat4 ProjectionMatrix;
       uniform mat4 ModelviewMatrix;
       varying vec3 col;
       varying vec2 tex;
       vec3 ambient = vec3(0.3, 0.3, 0.3);
       vec3 diffuse = vec3(1.0, 1.0, 1.0);
       vec3 specular = vec3(1.0, 1.0, 1.0);
       vec4 position = vec4(-2.0, 1.0, -2.0, 1.0);
       void main(void)
       {
           //  P is the vertex coordinate on body
           vec3 P = vec3(ModelviewMatrix * vec4(XYZ, 1));
           //  N is the object normal at P
           vec4 norm = normalize(ModelviewMatrix * vec4(NOR, 1));
           vec3 N = vec3(norm.xyz);
           //  L is the light vector
           vec3 L = normalize(vec3(ModelviewMatrix*position) - P);
           //  Emission and ambient color
           vec3 color = ambient;
           //  Diffuse light intensity is cosine of light and normal vectors
           float Id = dot(L,N);
           if (Id>0.0)
           {
              //  Add diffuse
              color += Id*diffuse;
              //  R is the reflected light vector R = 2(L.N)N - L
              vec3 R = reflect(-L, N);
              //  V is the view vector (eye at the origin)
              vec3 V = normalize(-P);
              //  Specular is cosine of reflected and view vectors
              float Is = dot(R,V);
              if (Is>0.0) color += pow(Is,32.0)*specular;
           }
           //  Pass color to fragment shader
           col = color*RGB;
           //  Pass texture coordinates to fragment shader (will be interpolated)
           tex = TEX;
           //  Set transformed vertex location
           gl_Position = ProjectionMatrix * ModelviewMatrix *            vec4(XYZ,1);
       }
    </script> 

    <!--
      Fragment shader
      -->
    <script id="shader-fs" type="x-shader/x-fragment"> 
       precision highp float;
       varying vec3 col;
       varying vec2 tex;
       uniform sampler2D uSampler;
       void main(void)
       {
          gl_FragColor = vec4(col,1) * texture2D(uSampler, tex);
       }
    </script> 

    <!--
      WebGL program
      -->
    <script type="text/javascript"> 

    //
    //  Compile a shader
    //
    function CompileShader(gl,id)
    {
       //  Get shader by id
       var src = document.getElementById(id);
       //  Create shader based on type setting
       var shader;
       if (src.type == "x-shader/x-fragment")
          shader = gl.createShader(gl.FRAGMENT_SHADER);
       else if (src.type == "x-shader/x-vertex")
          shader = gl.createShader(gl.VERTEX_SHADER);
       else
          return null;
       //  Read source into str
       var str = "";
       var k = src.firstChild;
       while (k)
       {
          if (k.nodeType == 3) str += k.textContent;
          k = k.nextSibling;
       }
       gl.shaderSource(shader, str);
       //  Compile the shader
       gl.compileShader(shader);
       //  Check for errors
       if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0)
          alert(gl.getShaderInfoLog(shader));
       //  Return shader
       return shader;
    }

    //
    //  Compile shader program
    //
    function CompileShaderProg(gl,vert,frag)
    {
       //  Compile the program
       var prog  = gl.createProgram();
       gl.attachShader(prog , CompileShader(gl,vert));
       gl.attachShader(prog , CompileShader(gl,frag));
       gl.linkProgram(prog);
       //  Check for errors
       if (gl.getProgramParameter(prog, gl.LINK_STATUS) == 0)
          alert(gl.getProgramInfoLog(prog));
       //  Return program
       return prog;
    }

    var text;
    var image;
    function initTexture() 
    {
       text = gl.createTexture();
       image = new Image();
       image.onload = function() {handleTextureLoaded(text, image)}
       image.src = "wall.png";
    }

    function handleTextureLoaded(txt, img) 
    {
       gl.bindTexture(gl.TEXTURE_2D, txt);
       gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
       gl.generateMipmap(gl.TEXTURE_2D);
       gl.bindTexture(gl.TEXTURE_2D, null);
    }

    var gl,canvas;
    function webGLStart()
    {
       //  Set canvas
       canvas = document.getElementById("canvas");
       //  Select canvas size
       var size = Math.min(window.innerWidth,window.innerHeight)-10;
       canvas.width  = size;
       canvas.height = size;
       //  Start WebGL
       if (!window.WebGLRenderingContext)
       {
          alert("Your browser does not support WebGL. See http://get.webgl.org");
          return;
       }
       try
       {
          gl = canvas.getContext("experimental-webgl");
       }
       catch(e)
       {}
       if (!gl)
       {
          alert("Can't get WebGL");
          return;
       }

       //  Set viewport to entire canvas
       gl.viewport(0,0,size,size);

       //  Initialize textures
       initTexture();

       //  Load Shader
       var prog = CompileShaderProg(gl,"shader-vs","shader-fs");

       //  Set program
       gl.useProgram(prog);

       //  Set projection
       var ProjectionMatrix = new CanvasMatrix4();
       ProjectionMatrix.ortho(-2.5,+2.5,-2.5,+2.5,-2.5,+2.5);

       //  Vertex array count
       var n = 18;
       //  Pyramid vertex coordinates
       var xyz = 
       [
        -1,-1,-1,   1,-1,-1,    -1,-1,1,
        1,-1,-1,    -1,-1,1,    1,-1,1,
        0,1,0,      -1,-1,1,    1,-1,1,
        0,1,0,      1,-1,-1,    -1,-1,-1,
        0,1,0,      1,-1,1,     1,-1,-1,
        0,1,0,      -1,-1,-1,   -1,-1,1,
       ];
       var verts = gl.createBuffer();
       gl.bindBuffer(gl.ARRAY_BUFFER,verts);
       gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(xyz),gl.STATIC_DRAW);

       //  Pyramid colors
       var rgb = 
       [
        0.84,0.62,0.32, 0.84,0.62,0.32, 0.84,0.62,0.32,
        0.84,0.62,0.32, 0.84,0.62,0.32, 0.84,0.62,0.32,    
        0.84,0.62,0.32, 0.84,0.62,0.32, 0.84,0.62,0.32,
        0.84,0.62,0.32, 0.84,0.62,0.32, 0.84,0.62,0.32,
        0.84,0.62,0.32, 0.84,0.62,0.32, 0.84,0.62,0.32,
        0.84,0.62,0.32, 0.84,0.62,0.32, 0.84,0.62,0.32,
       ];
       var color = gl.createBuffer();
       gl.bindBuffer(gl.ARRAY_BUFFER,color);
       gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(rgb),gl.STATIC_DRAW);

       //  Pyramid texture coordinates
       var tex =
       [
        0,0,    4,0,    0,4,
        4,0,    0,4,    4,4,
        2,4,    0,0,    4,0,
        2,4,    0,0,    4,0,
        2,4,    0,0,    4,0,
        2,4,    0,0,    4,0,
       ];
       var texture = gl.createBuffer();
       gl.bindBuffer(gl.ARRAY_BUFFER,texture);
       gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(tex),gl.STATIC_DRAW); 

       //  Cube normals
       var nor =
       [
        0,-1, 0,                    0,-1, 0,                    0,-1, 0,
        0,-1, 0,                    0,-1, 0,                    0,-1, 0,
        0,0.4472136,0.89442719,     0,0.4472136,0.89442719,     0,0.4472136,0.89442719,
        0,0.4472136,-0.89442719,    0,0.4472136,-0.89442719,    0,0.4472136,-0.89442719,
        0.89442719,0.4472136,0,     0.89442719,0.4472136,0,     0.89442719,0.4472136,0,
        -0.89442719,0.4472136,0,    -0.89442719,0.4472136,0,    -0.89442719,0.4472136,0,
       ];
       var normals = gl.createBuffer();
       gl.bindBuffer(gl.ARRAY_BUFFER,normals);
       gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(nor),gl.STATIC_DRAW);

       //  Set state to draw scene
       gl.enable(gl.DEPTH_TEST);
       gl.clearColor(0.8,0.8,0.8,1);
       //  Mouse control variables
       var x0 = y0 = move  = 0;
       //  Rotation angles
       var th = ph = 15;
       //  Draw scene the first time
       Display();

       //
       //  Display the scene
       //
       function Display()
       {
          //  Clear the screen and Z buffer
          gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

          // Compute modelview matrix
          var ModelviewMatrix = new CanvasMatrix4();
          ModelviewMatrix.makeIdentity();
          ModelviewMatrix.rotate(ph,0,1,0);
          ModelviewMatrix.rotate(th,1,0,0);

          // Set shader
          gl.useProgram(prog);

          //  Set projection and modelview matrixes
                 gl.uniformMatrix4fv(gl.getUniformLocation(prog,"ProjectionMatrix") , false , new Float32Array(ProjectionMatrix.getAsArray()));
  gl.uniformMatrix4fv(gl.getUniformLocation(prog,"ModelviewMatrix")  , false , new Float32Array(ModelviewMatrix.getAsArray()));

  //  Set up 3D vertex array
  gl.bindBuffer(gl.ARRAY_BUFFER,verts);
  var XYZ = gl.getAttribLocation(prog,"XYZ");
  gl.enableVertexAttribArray(XYZ);
  gl.vertexAttribPointer(XYZ,3,gl.FLOAT,false,0,0);

  //  Set up 3D color array
  gl.bindBuffer(gl.ARRAY_BUFFER,color);
  var RGB = gl.getAttribLocation(prog,"RGB");
  gl.enableVertexAttribArray(RGB);
  gl.vertexAttribPointer(RGB,3,gl.FLOAT,false,0,0);

  //  Set up 3D normal array
  gl.bindBuffer(gl.ARRAY_BUFFER,normals);
  var NOR = gl.getAttribLocation(prog,"NOR");
  gl.enableVertexAttribArray(NOR);
  gl.vertexAttribPointer(NOR,3,gl.FLOAT,false,0,0);

  //  Set up 2D texture array
  gl.bindBuffer(gl.ARRAY_BUFFER,texture);
  var TEX = gl.getAttribLocation(prog,"TEX");
  gl.enableVertexAttribArray(TEX);
  gl.vertexAttribPointer(TEX,2,gl.FLOAT,false,0,0);

  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, text);
  gl.uniform1i(gl.getUniformLocation(prog, "uSampler"), 0);

  //  Draw all vertexes
  gl.drawArrays(gl.TRIANGLES,0,n);

  //  Disable vertex arrays
  gl.disableVertexAttribArray(XYZ);
  gl.disableVertexAttribArray(RGB);
  gl.disableVertexAttribArray(NOR);
  gl.disableVertexAttribArray(TEX);

  //  Flush
  gl.flush();
       }

       //
       //  Resize canvas
       //
       canvas.resize = function ()
       {
          var size = Math.min(window.innerWidth, window.innerHeight)-10;
          canvas.width  = size;
          canvas.height = size;
          gl.viewport(0,0,size,size);
          Display();
       }

       //
       //  Mouse button pressed
       //
       canvas.onmousedown = function (ev)
       {
          move  = 1;
          x0 = ev.clientX;
          y0 = ev.clientY;
       }

       //
       //  Mouse button released
       //
       canvas.onmouseup = function (ev)
       {
          move  = 0;
       }

       //
       //  Mouse movement
       //
       canvas.onmousemove = function (ev)
       {
          if (move==0) return;
          //  Update angles
          ph -= ev.clientX-x0;
          th += ev.clientY-y0;
          //  Store location
          x0 = ev.clientX;
          y0 = ev.clientY;
          //  Redisplay
          Display();
       }
    }
    </script> 

    <!--
      Web page
      -->
    </head>
    <body onload="webGLStart();" onresize="canvas.resize();"> 
    <H1>WebGL Simple Pyramid</H1>
    <canvas id="canvas" width="500" height="500"></canvas> 
    </body>
    </html>
mago3421
  • 71
  • 1
  • 4
  • 1
    First thing would be to check your texture coordinates are valid. This you can do by outputting the tex.xy to gl_FragColor,you should see a red/green gradient across your triangle. Once you confirm that is working you should also make sure your texture does not contain all 0 in its alpha as you're multiplying the A channel in your final colour, you can test this by not multiplying the alpha channel from the texture. – PaulHK May 10 '17 at 03:50
  • 1
    Also texture coordinates are usually normalised (0...1). Your 'tex' array has values higher than 1 ? – PaulHK May 10 '17 at 03:53
  • Hi @PaulHK, thanks for responding. I'll try that experiment. I set the texture coordinates so that the texture repeats (I'm not sure what this is called but I know this is how you use a texture to tile an object.) – mago3421 May 10 '17 at 03:55
  • I thought it may have been that, but I didn't see anything like glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); I think the default texture behaviour is clamping. – PaulHK May 10 '17 at 04:17
  • I added the function call to set the GL_TEXTURE_WRAP=GL_REPEAT but I still see black. I also set the texture coordinates to 0, 0.5, and 1 (from 0,2, and 4) but this did not work either. Also, WebGL inspector is not working (won't turn red on a page) butt I think I've seen before that my texture loads but for some reason is not sampled. – mago3421 May 10 '17 at 05:12

1 Answers1

4

Okay, I figured it out. It has to do with the texture not loading before the first display function is called. The texture displays after a mouse or keyboard event. To remedy this, I added a timer to refresh the scene. This only works on Firefox however. I'm not sure why the image does not display on Chrome.

mago3421
  • 71
  • 1
  • 4