3
$\begingroup$

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>
$\endgroup$
  • 1
    $\begingroup$ 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. $\endgroup$ – PaulHK May 10 '17 at 3:50
  • 1
    $\begingroup$ Also texture coordinates are usually normalised (0...1). Your 'tex' array has values higher than 1 ? $\endgroup$ – PaulHK May 10 '17 at 3:53
  • $\begingroup$ 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.) $\endgroup$ – mago3421 May 10 '17 at 3:55
  • $\begingroup$ 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. $\endgroup$ – PaulHK May 10 '17 at 4:17
  • $\begingroup$ 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. $\endgroup$ – mago3421 May 10 '17 at 5:12
4
$\begingroup$

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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.