无法渲染到帧缓冲区(纹理)

时间:2015-11-26 16:08:02

标签: javascript webgl render-to-texture

我尝试使用阴影贴图实现阴影,因此我需要将场景渲染到单独的帧缓冲区(纹理)。我无法让它正常工作,所以在剥离我的代码库后,我会留下一组相对简单的指令,这些指令应该将场景渲染到纹理,然后简单地渲染纹理。

该计划包括两个计划:

  1. 地面计划
  2. 茶壶计划
  3. 第一个应该渲染一个具有特定纹理的矩形。第二个应该渲染一个茶壶(颜色根据其位置)。 Eech渲染步骤执行以下操作(好吧,无论如何都是这个想法):

    1. 切换到帧缓冲区
    2. 渲染茶壶
    3. 切换到普通缓冲区
    4. 渲染茶壶
    5. 渲染地面
    6. 现在,地面片段着色器看起来像:

      gl_FragColor = texture2D(shadowMap, fTexCoord);
      

      '阴影贴图'是我在第2步中渲染的纹理。我期待看到一个浮动的茶壶,下面画着一个矩形。确实有效。现在,我也希望能有这样的基础。包含一个茶壶。毕竟,我们渲染了我们正在观看的场景,没有地面到帧缓冲区/纹理。

      代码

      
      
      var UNSIGNED_SHORT_SIZE = 2;
      
      // Variables filled by setup()
      var glCanvas;
      var gl, teapotProgram, groundProgram;
      var vBuffer, iBuffer, fBuffer;
      var vertices, indices, textures;
      
      var teapot = null;
      var model;
      var view;
      var light;
      var projection;
      
      var BASE_URL = "https://hmbastiaan.nl/martijn/webgl/W08P02_SO/";
      
      var WIDTH = 150, HEIGHT = 150;
      
      function makeTeapot(){
          var drawingInfo = teapot.getDrawingInfoObjects();
          var indices = drawingInfo.indices;
      
          for(var i=0; i < indices.length; i++){
              indices[i] += 4; // Add offset for 'ground'
          }
      
          return {
              indices: drawingInfo.indices,
              vertices: drawingInfo.vertices
          }
      }
      
      function makeRectangle(x1, x2, y1, y2, z1, z2){
          var x1 = -2,
              x2 =  2,
              y1 = -1,
              y2 = -1,
              z1 = -1,
              z2 = -5;
      
          var vertices = [
              vec4(x1, y2, z1, 1),
              vec4(x2, y1, z1, 1),
              vec4(x2, y1, z2, 1),
              vec4(x1, y2, z2, 1)
          ];
      
          var textures = [
              vec2(-1.0, -1.0),
              vec2( 1.0, -1.0),
              vec2( 1.0,  1.0),
              vec2(-1.0,  1.0)
          ];
      
          var indices = [
              0, 1, 2,
              0, 2, 3
          ];
      
          return {
              indices: indices,
              vertices: vertices,
              textures: textures
          }
      
      }
      
      function resetBuffers(){
          vertices = [];
          indices = [];
          textures = [];
      
          // Add rectangle
          var rectangle = makeRectangle();
          Array.prototype.push.apply(vertices, rectangle.vertices);
          Array.prototype.push.apply(indices, rectangle.indices);
          Array.prototype.push.apply(textures, rectangle.textures);
      
          // Add teapot
          var teapot = makeTeapot();
          Array.prototype.push.apply(vertices, teapot.vertices);
          Array.prototype.push.apply(indices, teapot.indices);
      
          console.log(vertices);
          console.log(indices);
          console.log(textures);
      
          // Send to GPU
          gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
      
          gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
          gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
      }
      
      function setup(){
          $.get(BASE_URL + "teapot.obj", function(teapot_obj_data){
              teapot = new OBJDoc(BASE_URL + "teapot.obj");
      
              if(!teapot.parse(teapot_obj_data, 1)){
                  alert("Parsing teapot.obj failed.");
                  return;
              }
      
              setup2();
          }).fail(function(){
              alert("Getting teapot.obj failed.");
          });
      }
      
      function setup2(){
          glCanvas = document.getElementById("gl-canvas");
      
          gl = WebGLUtils.setupWebGL(glCanvas, {stencil: true, alpha: false});
          gl.viewport(0, 0, WIDTH, HEIGHT);
      
          teapotProgram = initShaders(gl, BASE_URL + "vshader-teapot.glsl", BASE_URL + "fshader-teapot.glsl");
          groundProgram = initShaders(gl, BASE_URL + "vshader-ground.glsl", BASE_URL + "fshader-ground.glsl");
      
          light = vec3(0.0, 2.0, -2.0);
          view = lookAt(vec3(0, 0, 3), vec3(0,0,0), vec3(0,1,0));
          projection = perspective(45, 1.0, 1, 100.0);
      
          // Get teapot uniforms
          gl.useProgram(teapotProgram);
          teapotProgram.modelLoc      = gl.getUniformLocation(teapotProgram, "Model");
          teapotProgram.viewLoc       = gl.getUniformLocation(teapotProgram, "View");
          teapotProgram.projectionLoc = gl.getUniformLocation(teapotProgram, "Projection");
      
          // Upload uniforms
          gl.uniformMatrix4fv(teapotProgram.projectionLoc, false, flatten(projection));
          gl.uniformMatrix4fv(teapotProgram.viewLoc, false, flatten(view));
          gl.uniformMatrix4fv(teapotProgram.modelLoc, false, flatten(scalem(0.25, 0.25, 0.25)));
      
          // Get teapot attributes
          teapotProgram.vPosition = gl.getAttribLocation(teapotProgram, "vPosition");
      
          // Get ground uniforms
          gl.useProgram(groundProgram);
          groundProgram.modelLoc      = gl.getUniformLocation(groundProgram, "Model");
          groundProgram.viewLoc       = gl.getUniformLocation(groundProgram, "View");
          groundProgram.projectionLoc = gl.getUniformLocation(groundProgram, "Projection");
          groundProgram.shadowMap     = gl.getUniformLocation(groundProgram, "shadowMap");
      
          // Get ground attributes
          groundProgram.vTexCoord = gl.getAttribLocation(groundProgram, "vTexCoord");
          groundProgram.vPosition = gl.getAttribLocation(groundProgram, "vPosition");
      
          // Allocate and fill vertices buffer
          vBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
      
          gl.vertexAttribPointer(teapotProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
          gl.enableVertexAttribArray(teapotProgram.vPosition);
      
          gl.vertexAttribPointer(groundProgram.vPosition, 4, gl.FLOAT, false, 0, 0);
          gl.enableVertexAttribArray(groundProgram.vPosition);
      
          // Allocate indices buffer
          iBuffer = gl.createBuffer();
      
          // Setup FBO
          fBuffer = gl.createFramebuffer();
          gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
      
          fBuffer.renderbuffer = gl.createRenderbuffer();
          gl.bindRenderbuffer(gl.RENDERBUFFER, fBuffer.renderbuffer);
          gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 512, 512);
      
          fBuffer.texture = gl.createTexture();
          gl.activeTexture(gl.TEXTURE0);
          gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 512, 512, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
          gl.generateMipmap(gl.TEXTURE_2D);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
          gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      
          gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fBuffer.texture, 0);
          gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fBuffer.renderbuffer);
      
          // Sanity checking: framebuffer seems to throw now errors
          if (!gl.isFramebuffer(fBuffer)) {
              throw("Invalid framebuffer");
          }
      
          var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
          switch (status) {
              case gl.FRAMEBUFFER_COMPLETE:
                  break;
              case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
                  throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
                  break;
              case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
                  throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
                  break;
              case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
                  throw("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
                  break;
              case gl.FRAMEBUFFER_UNSUPPORTED:
                  throw("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");
                  break;
              default:
                  throw("Incomplete framebuffer: " + status);
          }
      
          // Set ground textures
          gl.uniform1i(groundProgram.shadowMap, 0);
      
          // Upload uniforms
          gl.uniformMatrix4fv(groundProgram.projectionLoc, false, flatten(projection));
          gl.uniformMatrix4fv(groundProgram.viewLoc, false, flatten(view));
          gl.uniformMatrix4fv(groundProgram.modelLoc, false, flatten(mat4()));
      
          // Restore default buffers
          gl.bindTexture(gl.TEXTURE_2D, null);
          gl.bindRenderbuffer(gl.RENDERBUFFER, null);
          gl.bindFramebuffer(gl.FRAMEBUFFER, null);
      
          // Set background colour
          gl.clearColor(0.3921, 0.5843, 0.9294, 1.0);
      
          gl.enable(gl.DEPTH_TEST);
          gl.enable(gl.CULL_FACE);
      
          resetBuffers();
      
          window.requestAnimationFrame(render);
      }
      
      function render(){
          var teapot = makeTeapot();
      
          gl.useProgram(teapotProgram);
          gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
      
          // Switch to framebuffer
          gl.bindFramebuffer(gl.FRAMEBUFFER, fBuffer);
      
          // Draw teapot
          teapot = makeTeapot();
          gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
      
          // Set framebuffer to defualt buffer (in-browser output)
          gl.bindFramebuffer(gl.FRAMEBUFFER, null);
      
          // Draw ground
          gl.useProgram(groundProgram);
          gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
      
          // Render teapot
          gl.useProgram(teapotProgram);
          gl.drawElements(gl.TRIANGLES, teapot.indices.length, gl.UNSIGNED_SHORT, 6 * UNSIGNED_SHORT_SIZE);
      }
      
      setup();
      &#13;
      <div>
          <br/>
          <canvas width="150" height="150" id="gl-canvas">Sorry :|</canvas>
      </div>
      <script type='text/javascript' src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
      <script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/webgl-utils.js"></script>
      <script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/initShaders2.js"></script>
      <script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/MV.js"></script>
      <script type='text/javascript' src="https://hmbastiaan.nl/martijn/webgl/angel/objParser.js"></script>
      &#13;
      &#13;
      &#13;

      感兴趣的功能:

      1. setup2():设置所有缓冲区和制服。
      2. render():渲染场景。
      3. 免责声明:这是一项作业,虽然这段代码已经简化,看起来根本不像原来的作业:)。

1 个答案:

答案 0 :(得分:2)

一目了然,有几个问题。

  1. 纹理绑定是全局的。因为在setup2中你取消绑定1纹理,这意味着它从未使用过。

    您需要在每次绘制调用之前绑定所需的任何纹理。换句话说,当您绘制地面时,您需要绑定茶壶纹理,如

    gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
    

    注意:这是对真正需要的过度简化。你真的需要

    1. 选择纹理单元将纹理绑定到

      var unit = 5;
      gl.activeTexture(gl.TEXTURE0 + unit);
      
    2. 将纹理绑定到该单位。

      gl.bindTexture(gl.TEXTURE_2D, fBuffer.texture);
      
    3. 将均匀采样器设置为该纹理单元

      gl.uniform1i(groundProgram.shadowMap, unit);
      
    4. 您不需要这些额外步骤的原因是因为(a)您只是 有1个纹理所以你使用纹理单元#0,默认和(b)因为 制服默认为0,因此shadowMap正在查看纹理单元#0。

    5. 因为你制作了一个mipmap纹理,只需渲染到0级就不会更新mips。

      换句话说,在渲染茶壶之后,你将有一个mip级别为0的茶壶,但是mip级别1,2,3,4,5等仍然没有任何内容。你需要打电话

      gl.generateMipmap(gl.TEXTURE_2D)
      

      在将茶壶渲染到它之后的纹理。要么停止使用mips

    6. 每次拨打gl.bindFramebuffer时都需要设置视口。

      gl.bindFramebuffer几乎总是应该调用gl.viewport,以使视口与您要呈现的内容的大小相匹配

      gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
      // set to size of fb
      gl.viewport(0, 0, widthOfFb, heightOfFb);
      
      renderSomething();
      
      gl.bindFramebuffer(gl.FRAMEBUFFER, null);
      // set to size of canvas's drawingBuffer
      gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
      
    7. 属性设置是全局的

      您设置了茶壶属性。然后你在纹理上画一个茶壶。然后你绘制地面,但你仍然使用茶壶属性。

      就像纹理一样,您需要在每次绘制调用之前设置属性。

    8. 我也猜测你真的不应该在渲染函数中调用makeTeapot,而应该在设置中调用它。

      您可能会找到this article useful

      您还应该考虑not putting properties on WebGL objects as it's arguably an anti-pattern

      同步XHR请求也不酷。您在JavaScript控制台中收到此消息

        

      主线程上的同步XMLHttpRequest已弃用,因为       其对最终用户体验的不利影响。更多       帮助,检查http://xhr.spec.whatwg.org/

相关问题