背面剔除的法线变换

2024-02-07

从本课开始:WebGL 3D 透视 https://webglfundamentals.org/webgl/lessons/webgl-3d-perspective.html我正在尝试实现没有魔法的背面剔除。

我正在动态计算对象空间中的面法线。之后,我将设置fudgeFactor inside m[2][3]获得观点除以 Z.

为了检查剪切矩阵是否有效,我直接使用从 WebGL 顶点着色器投影的顶点位置扩展了我的代码片段,并添加了“预计位置” flag.

现在,我尝试使用变换后的法线内的 z 分量来隐藏 normal.z 软糖系数为 0。为什么这对于透视投影不起作用,当我设置fudgeFactor我自己?

为了看看发生了什么,我编写了下面的最小示例来可视化法向量(感谢:Geeks3D https://www.geeks3d.com/20130905/exploring-glsl-normal-visualizer-with-geometry-shaders-shader-library/)并给它上色。Green正常:脸部可见,red正常:脸部被剔除。

我按照提示进行操作gman https://stackoverflow.com/users/128511/gman并实现了基本的多边形光栅化(在 WebGL 顶点着色器之外)作为概念证明。尝试拖动软糖因子 slider并检查是否正常工作。

'use strict';

function main() {
  function setupUI() {
    webglLessonsUI.setupUI(document.getElementById('ui'), settings, [
      { type: 'checkbox', key: 'projectedP', name: 'projected position', change: draw },
      { type: 'option', key: 'visible', options: settings.visibleOptions, name: 'visibility buffer', change: draw },
      { type: 'slider', key: 'fudgeFactor', change: draw, max: 2, step: 0.001, precision: 3 },
      { type: 'slider', key: 'tX', change: draw, min: -0.5 * gl.canvas.width, max: 0.5 * gl.canvas.width },
      { type: 'slider', key: 'tY', change: draw, min: -0.5 * gl.canvas.height, max: 0.5 * gl.canvas.height },
      { type: 'slider', key: 'tZ', change: draw, min: -gl.canvas.height, max: gl.canvas.height },
      { type: 'slider', key: 'rX', change: draw, max: 360 },
      { type: 'slider', key: 'rY', change: draw, max: 360 },
      { type: 'slider', key: 'rZ', change: draw, max: 360 },
      { type: 'slider', key: 'scale', change: draw, min: -5, max: 5, step: 0.01, precision: 2 },
    ]);
  }

  function draw() {
    // assignZToWMatrix
    perspMatrix[11] = settings.fudgeFactor;
    var w = gl.canvas.clientWidth, h = gl.canvas.clientHeight, d = 400;
    var projMatrix = m4.multiply(perspMatrix, m4.projector(w, h, d));
    var worldMatrix = m4.translation(settings.tX, settings.tY, settings.tZ);
    m4.xRotate(worldMatrix, (settings.rX * Math.PI) / 180, worldMatrix);
    m4.yRotate(worldMatrix, (settings.rY * Math.PI) / 180, worldMatrix);
    m4.zRotate(worldMatrix, (settings.rZ * Math.PI) / 180, worldMatrix);
    m4.scale(worldMatrix, settings.scale, settings.scale, settings.scale, worldMatrix);
    var matrix = m4.multiply(projMatrix, worldMatrix);
    
    // set the visibility flag using polygon area
    transformVertices(matrix);
    if(settings.visible == 1) {
      // try to set the visibility flag using an early check
      transformNormals(matrix);
    }
    
    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.enable(gl.CULL_FACE);
    gl.enable(gl.DEPTH_TEST);

    gl.useProgram(solidInfo.program);
    gl.bindBuffer(gl.ARRAY_BUFFER, solidBufferInfo.attribs.a_projectedP.buffer);
    gl.bufferData(gl.ARRAY_BUFFER, projectedVertices, gl.STATIC_DRAW);
    webglUtils.setBuffersAndAttributes(gl, solidInfo, solidBufferInfo);
    webglUtils.setUniforms(solidInfo, { u_matrix: matrix,
                                        u_projectedP: [settings.projectedP],
                                        u_fudgeFactor: settings.fudgeFactor });

    var count = 32 * 3; // <= geometries x points-each
    webglUtils.drawBufferInfo(gl, solidBufferInfo, gl.TRIANGLES);

    gl.useProgram(normalInfo.program);
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBufferInfo.attribs.a_visible.buffer);
    gl.bufferData(gl.ARRAY_BUFFER, visibility, gl.STATIC_DRAW);
    webglUtils.setBuffersAndAttributes(gl, normalInfo, normalBufferInfo);
    webglUtils.setUniforms(normalInfo, { u_matrix: matrix, 
                                         u_visible: [settings.visible] });
    webglUtils.drawBufferInfo(gl, normalBufferInfo, gl.LINES);
  }

  function calcFaceNormals(size) {
    var v = vertices, l = v.length;
    var nl = normalLines, n = normals;
    var i = 0, j = 0, k = 0;

    while (i < l) {
      var x0 = v[i++]; var y0 = v[i++]; var z0 = v[i++];
      var x1 = v[i++]; var y1 = v[i++]; var z1 = v[i++];
      var x2 = v[i++]; var y2 = v[i++]; var z2 = v[i++];

      var dx1 = x1 - x0; var dy1 = y1 - y0; var dz1 = z1 - z0;
      var dx2 = x2 - x0; var dy2 = y2 - y0; var dz2 = z2 - z0;

      var nx = dy1*dz2-dz1*dy2; var ny = dz1*dx2-dx1*dz2; var nz = dx1*dy2-dy1*dx2;

      // Normalize
      var len = Math.sqrt(nx * nx + ny * ny + nz * nz);
      nx /= len; ny /= len; nz /= len;

      // Center of the geometry
      var cx = (x0+x1+x2)/3; var cy = (y0+y1+y2)/3; var cz = (z0+z1+z2)/3;

      // Vector start point
      nl[j++] = cx; nl[j++] = cy; nl[j++] = cz;

      // Normal vector
      n[k++] = nx; n[k++] = ny; n[k++] = nz;

      // Assign a drawing length (scale) & end-point (translate)
      nl[j++] = nx*size+cx; nl[j++] = ny*size+cy; nl[j++] = nz*size+cz;
      
      // doublette needed by WebGL for single vertex processing
      n[k++] = nx; n[k++] = ny; n[k++] = nz;
    }
  }

  function transformVertices(m) {
    // same as m4.transformPoint
    function transformVertex(m,src,dst) {
      var x = src[0], y = src[1], z = src[2];
      var d = x * m[3] + y * m[7] + z * m[11] + m[15];
      dst[0] = (x * m[0] + y * m[4] + z * m[8]  + m[12]) / d;
      dst[1] = (x * m[1] + y * m[5] + z * m[9]  + m[13]) / d;
      dst[2] = (x * m[2] + y * m[6] + z * m[10] + m[14]) / d;
      return dst;
    }  

    // here we already have fudgeFactor inside m[11]
    var mv = vertices, l = mv.length, pv = projectedVertices;
    var mp0 = [0,0,0], mp1 = [0,0,0], mp2 = [0,0,0];
    var pp0 = [0,0,0], pp1 = [0,0,0], pp2 = [0,0,0];
    
    // "visible" flag inside the vertex shader visibility buffer
    var vb = visibility, j = 0;

    for(var i=0; i<l; i+=9) {
      mp0[0] = mv[i    ]; mp0[1] = mv[i + 1]; mp0[2] = mv[i + 2];
      mp1[0] = mv[i + 3]; mp1[1] = mv[i + 4]; mp1[2] = mv[i + 5];
      mp2[0] = mv[i + 6]; mp2[1] = mv[i + 7]; mp2[2] = mv[i + 8];
      // Project vertex coords
      pp0 = transformVertex(m,mp0,pp0);
      pp1 = transformVertex(m,mp1,pp1);
      pp2 = transformVertex(m,mp2,pp2);
      // Assign 
      pv[i    ] = pp0[0]; pv[i + 1] = pp0[1]; pv[i + 2] = pp0[2];
      pv[i + 3] = pp1[0]; pv[i + 4] = pp1[1]; pv[i + 5] = pp1[2]; 
      pv[i + 6] = pp2[0]; pv[i + 7] = pp2[1]; pv[i + 8] = pp2[2];

     // we need just only the sign, no need to divde as in 3.5.1
     var da = (pp0[0]*pp1[1] - pp1[0]*pp0[1]) + 
              (pp1[0]*pp2[1] - pp2[0]*pp1[1]) + 
              (pp2[0]*pp0[1] - pp0[0]*pp2[1]);
        
     // swap the sign because we inverted the Y-axis inside the projector matrix
     vb[j++] = -da;
     // doublette needed by WebGL for single vertex processing
     vb[j++] = -da;
    }
  }

  function transformNormals(m) {
    var n = normals, l = n.length;
    var x, y, z, w, tx, ty, tz, tw;
    var vb = visibility, j = 0; // Visibility buffer
    
    var t = transformedNormals, k = 0; // debug
    for(var i=0; i<l; i+=6) {
      // normal buffer doublettes are needed for WebGL single
      //  vertex processing, here we take just only one entry
      x = n[i]; y = n[i + 1]; z = n[i + 2]; w = 1;

      // fudgeFactor is inside m[11] - any way to shear normal?
      tx = x * m[0] + y * m[4] + z * m[8]  + w * m[12];
      ty = x * m[1] + y * m[5] + z * m[9]  + w * m[13];
      tz = x * m[2] + y * m[6] + z * m[10] + w * m[14];
      tw = x * m[3] + y * m[7] + z * m[11] + w * m[15]; 
      
      var idx =~ ~i/6; // triangle index
      // debug: index 21 & 22 are the top side of the middle rung
      // debug: keep the transformed vector to check what's happen
      t[k++] = tx; t[k++] = ty; t[k++] = tz; t[k++] = tw;

      // set the visibility flag
      vb[j++] = tz;
      // doublette for WebGL single vertex processing
      vb[j++] = tz;
    }
  }
  
  var canvas = document.querySelector('#canvas'),
    gl = canvas.getContext('webgl');
  var perspMatrix = m4.identity(), drawSize = 10;

  var settings = {
    visibleOptions:  [ '', 'T. NORMAL', 'POLY AREA' ],
    visible: 1,
    projectedP: true,
    fudgeFactor: 1.5,
    tX: -54, tY: -67, tZ: 0, 
    rX: 12, rY: 33, rZ: 8,
    scale: 1
  };

  var solidArrays = {
    position: { numComponents: 3, data: vertices },
    projectedP: { numComponents: 3, data: projectedVertices },
    color: { numComponents: 3, data: vertexColors }
  };

  var solidInfo = webglUtils.createProgramInfo(gl, ['vs-solid', 'fs-solid']);
  var solidBufferInfo = webglUtils.createBufferInfoFromArrays(gl, solidArrays);

  var normalArrays = {
    position: { numComponents: 3, data: normalLines },
    visible: { numComponents: 1, data: visibility },
    normal: { numComponents: 3, data: normals }
  };

  calcFaceNormals(drawSize);

  var normalInfo = webglUtils.createProgramInfo(gl, ['vs-normal', 'fs-normal']);
  var normalBufferInfo = webglUtils.createBufferInfoFromArrays(gl, normalArrays);

  draw();

  setupUI();
}

m4.projector = function (width, height, depth) {
  // Note: set 0,0 at canvas center
  return [
    2 / width, 0, 0, 0,
    0, -2 / height, 0, 0,
    0, 0, 2 / depth, 0,
    0, 0, 0, 1,
  ];
};

var vertices = new Float32Array([
  // left column front
  0,0,0,0,150,0,30,0,0,
  0,150,0,30,150,0,30,0,0,
  // top rung front
  30,0,0,30,30,0,100,0,0,
  30,30,0,100,30,0,100,0,0,
  // middle rung front
  30,60,0,30,90,0,67,60,0,
  30,90,0,67,90,0,67,60,0,
  // left column back
  0,0,30,30,0,30,0,150,30,
  0,150,30,30,0,30,30,150,30,
  // top rung back
  30,0,30,100,0,30,30,30,30,
  30,30,30,100,0,30,100,30,30,
  // middle rung back
  30,60,30,67,60,30,30,90,30,
  30,90,30,67,60,30,67,90,30,
  // top
  0,0,0,100,0,0,100,0,30,
  0,0,0,100,0,30,0,0,30,
  // top rung right
  100,0,0,100,30,0,100,30,30,
  100,0,0,100,30,30,100,0,30,
  // under top rung
  30,30,0,30,30,30,100,30,30,
  30,30,0,100,30,30,100,30,0,
  // between top rung and middle
  30,30,0,30,60,30,30,30,30,
  30,30,0,30,60,0,30,60,30,
  // top of middle rung
  30,60,0,67,60,30,30,60,30,
  30,60,0,67,60,0,67,60,30,
  // right of middle rung
  67,60,0,67,90,30,67,60,30,
  67,60,0,67,90,0,67,90,30,
  // bottom of middle rung.
  30,90,0,30,90,30,67,90,30,
  30,90,0,67,90,30,67,90,0,
  // right of bottom
  30,90,0,30,150,30,30,90,30,
  30,90,0,30,150,0,30,150,30,
  // bottom
  0,150,0,0,150,30,30,150,30,
  0,150,0,30,150,30,30,150,0,
  // left side
  0,0,0,0,0,30,0,150,30,
  0,0,0,0,150,30,0,150,0
]);

var vertexColors = new Uint8Array([
  // left column front
  100,35,60,100,35,60,100,35,60,
  200,70,120,200,70,120,200,70,120,
  // top rung front
  100,35,60,100,35,60,100,35,60,
  200,70,120,200,70,120,200,70,120,
  // middle rung front
  100,35,60,100,35,60,100,35,60,
  200,70,120,200,70,120,200,70,120,
  // left column back
  40,35,100,40,35,100,40,35,100,
  80,70,200,80,70,200,80,70,200,
  // top rung back
  40,35,100,40,35,100,40,35,100,
  80,70,200,80,70,200,80,70,200,
  // middle rung back
  40,35,100,40,35,100,40,35,100,
  80,70,200,80,70,200,80,70,200,
  // top
  35,100,105,35,100,105,35,100,105,
  70,200,210,70,200,210,70,200,210,
  // top rung right
  100,100,35,100,100,35,100,100,35,
  200,200,70,200,200,70,200,200,70,
  // under top rung
  105,50,35,105,50,35,105,50,35,
  210,100,70,210,100,70,210,100,70,
  // between top rung and middle
  105,80,35,105,80,35,105,80,35,
  210,160,70,210,160,70,210,160,70,
  // top of middle rung
  35,90,105,35,90,105,35,90,105,
  70,180,210,70,180,210,70,180,210,
  // right of middle rung
  50,35,105,50,35,105,50,35,105,
  100,70,210,100,70,210,100,70,210,
  // bottom of middle rung.
  38,105,50,38,105,50,38,105,50,
  76,210,100,76,210,100,76,210,100,
  // right of bottom
  70,105,40,70,105,40,70,105,40,
  140,210,80,140,210,80,140,210,80,
  // bottom
  45,65,55,45,65,55,45,65,55,
  90,130,110,90,130,110,90,130,110,
  // left side
  80,80,110,80,80,110,80,80,110,
  160,160,220,160,160,220,160,160,220
]);

// projection buffer: same as vertices
var projectedVertices = new Float32Array(vertices.length);
// normal buffer: 16 faces 2 triangles each, total 32 triangles => 128 elements + 128 doublettes => 64 components x 4 floats
var normalLines = new Float32Array((2 * vertices.length) / 3);
var normals = new Float32Array(2 * vertices.length / 3);
// visible buffer: 16 faces 2 triangles each, total 32 triangles => 64 components x 1 float
var visibility = new Float32Array(2 * vertices.length / 9);
// debug: normal transformation buffer has 4 components, to check also how the w is transformed
// 16 faces 2 triangles each, total 32 normals x 4 floats
var transformedNormals = new Float32Array(vertices.length / 9 * 4);

document.addEventListener('DOMContentLoaded', function (event) {
  setTimeout(main,100)
});
<!DOCTYPE html>
<html>
  <head>
    <link href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" type="text/css" rel="stylesheet"/>
    <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
    <script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
    <script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-ui.js"></script>
  </head>
  <body style="background-color: #fff;">
    <canvas id="canvas"></canvas>
    <div id="uiContainer">
      <div id="ui"></div>
    </div>

    <script id="vs-solid" type="x-shader/x-vertex">
      attribute vec3 a_position;
      attribute vec3 a_projectedP;
      attribute vec3 a_color;
      uniform bool u_projectedP;
      uniform mat4 u_matrix;
      uniform float u_fudgeFactor;
      varying vec4 v_color;
      void main() {
        vec4 pos;
        float zToDivideBy;
        if(u_projectedP) {
          pos = vec4(a_projectedP, 1.0);
          zToDivideBy = 1.0;
        } else {
          pos = u_matrix * vec4(a_position, 1.0);
          zToDivideBy = 1.0 + pos.z * u_fudgeFactor;
        }
        gl_Position = vec4(pos.xyz, zToDivideBy);
        v_color = vec4(a_color, 1.0);
      }
    </script>

    <script id="fs-solid" type="x-shader/x-fragment">
      precision mediump float;
      varying vec4 v_color;
      void main() {
         gl_FragColor = v_color;
      }
    </script>

    <script id="vs-normal" type="x-shader/x-vertex">
      attribute vec3 a_position;
      attribute vec3 a_normal;
      attribute float a_visible;
      uniform int u_visible;
      uniform mat4 u_matrix;
      varying vec4 v_color;
      void main() {
        // The x,y,z,w value we assign to gl_Position in our
        // vertex shader will be divided by w automatically.
        gl_Position = u_matrix * vec4(a_position, 1.0);
        float eps = -0.000001;
        bool vis;
        vec4 nrm = u_matrix * vec4(a_normal, 0.0);
        if(u_visible == 0) {
          // this is obviously wrong for fudgeFactor > 0 
          vis = nrm.z < eps;
        } else {
          // option 1: transformed normal.z
          // option 2: sign of polygon area
          vis = a_visible < eps;
        }
        // visible => green, hidden => red 
        v_color = vis ? vec4(0., 255., 0., 1.) : vec4(255., 0., 0., 1.);
      }
    </script>

    <script id="fs-normal" type="x-shader/x-fragment">
      precision mediump float;
      varying vec4 v_color;
      void main() {
         gl_FragColor = v_color;
      }
    </script>
  </body>
</html>

我希望我的法线会被变换矩阵剪切(旋转),但事实并非如此。

请注意:

  • 我知道我可以再次计算透视校正法线 使用屏幕空间顶点坐标(请参阅我的变换顶点函数),所以我不要求这个。
  • 此外:我知道法线通常用于着色 计算,对于我的问题来说这并不重要。

我试图了解如何转换法线以在后台实现背面剔除,在设置时使用矩阵乘法除以 Z我自己。


EDIT- 这里有一些参考:

对象空间中的背面剔除 https://www.cubic.org/docs/backcull.htm

一种紧凑的背面剔除方法 https://www.gamasutra.com/view/feature/131773/a_compact_method_for_backface_.php

背面剔除 - 点积/叉积 https://board.flashkit.com/board/showthread.php?656117.html

3D 图形的另一种视角:快速隐藏面去除(背面剔除) http://retro64.altervista.org/blog/another-look-at-3d-graphics-fast-hidden-faces-removal-back-face-culling/?doing_wp_cron=1611682367.0990099906921386718750


如果这不是您问题的答案,我很抱歉,但“背面剔除”与法线无关,并且您通常不会手动执行此操作。让 WebGL 为您做这件事

From the spec https://www.khronos.org/registry/OpenGL/specs/es/2.0/es_full_spec_2.0.pdf

3.5.1 基本多边形光栅化

多边形光栅化的第一步是确定多边形是否朝后 或正面。该确定是根据(剪裁或 未剪裁的)多边形面积以窗口坐标计算。一种计算方法 这个区域是

where xiw and yiw are the x and y window coordinates of the ith vertex of the n-vertex polygon (vertices are numbered starting at zero for purposes of this computation) and i⊕1 is (i+1) mod n. The interpretation of the sign of this value is controlled with

void FrontFace( enum dir );

将目录设置为CCW(对应于窗口坐标中投影多边形的逆时针方向)表示 a 的符号应在使用前反转。将目录设置为CW(对应顺时针方向) 使用 a 的符号如上面计算。正面判定需要一位 状态,并且最初设置为CCW.

如果面积的符号通过方程 3.4 计算(包括可能的反转) 最后一次调用所指示的该标志的正面) 为正,则多边形为 正面;否则,它是背面的。该决定与 与CullFace使能位和模式值来决定是否特定 多边形被光栅化。这CullFace模式通过调用设置

void CullFace( enum mode );

mode 是一个符号常量:其中之一FRONT, BACK or FRONT_AND_BACK。剔除 启用或禁用Enable or Disable使用符号常数CULL_FACE。如果禁用剔除或CullFace模式是BACK而背面多边形仅在剔除时才会被光栅化 被禁用或CullFace模式是FRONT。 CullFace的初始设置 模式是BACK。最初,剔除被禁用。

注意法线从未被提及,也没有被提及z提及。它完全发生在二维中。此外,尽管这并非不可能,但使用面法线(示例代码中显示的法线)进行背面剔除会很困难,因为每个顶点都是独立处理的。顶点着色器不处理三角形,而是处理单个顶点。那么,他们将如何计算三角形的法线以便剔除它呢?我想您可以传入每个顶点的法线,或者每次传入所有 3 个顶点,方法是将它们复制到单独的缓冲区中,但按三角形旋转它们的顺序。

在工作中看到背面剔除的最简单方法可能是绘制一些东西,但将面分开,以便您可以看到背面

'use strict';

function main() {
  const settings = {
    enabled: false,
    frontFace: 0,
    cullFace: 0,
    fudgeFactor: 1.42,
    tX: -34,
    tY: -47,
    tZ: 20,
    rX: 43,
    rY: 33,
    rZ: 8,
    scale: 1.77
  };
  const frontFaceOptions = [ "CCW", "CW" ];
  const cullFaceOptions = [ "BACK", "FRONT", "FRONT_AND_BACK" ];

  function setupUI() {
   
    webglLessonsUI.setupUI(document.querySelector('#ui'), settings, [
      { type: "checkbox", key: "enabled", name: "culling enabled", },
      { type: "option",   key: "frontFace", options: frontFaceOptions, },
      { type: "option",   key: "cullFace", options: cullFaceOptions, },
    ]);
  }

  function draw(time) {
    settings.rY = time * 0.01;
    // assignZToWMatrix
    perspMatrix[11] = settings.fudgeFactor;
    var projMatrix = m4.multiply(perspMatrix, m4.projector(gl.canvas.clientWidth, gl.canvas.clientHeight, 400));
    var worldMatrix = m4.translation(settings.tX, settings.tY, settings.tZ);
    m4.translate(worldMatrix, settings.tX, settings.tY, settings.tZ, worldMatrix);
    m4.xRotate(worldMatrix, (settings.rX * Math.PI) / 180, worldMatrix);
    m4.yRotate(worldMatrix, (settings.rY * Math.PI) / 180, worldMatrix);
    m4.zRotate(worldMatrix, (settings.rZ * Math.PI) / 180, worldMatrix);
    m4.scale(worldMatrix, settings.scale, settings.scale, settings.scale, worldMatrix);
    var matrix = m4.multiply(projMatrix, worldMatrix);

    webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    if (settings.enabled) {
      gl.enable(gl.CULL_FACE);
    } else {
      gl.disable(gl.CULL_FACE);
    }
    gl.cullFace(gl[cullFaceOptions[settings.cullFace]]);
    gl.frontFace(gl[frontFaceOptions[settings.frontFace]]);
    
    gl.enable(gl.DEPTH_TEST);

    gl.useProgram(solidInfo.program);
    webglUtils.setBuffersAndAttributes(gl, solidInfo, solidBufferInfo);
    webglUtils.setUniforms(solidInfo, {
      u_matrix: matrix
    });

    var count = 32 * 3; // <= geometries x points-each
    webglUtils.drawBufferInfo(gl, solidBufferInfo, gl.TRIANGLES);
    
    requestAnimationFrame(draw);
  }

  var canvas = document.querySelector('#canvas'),
    gl = canvas.getContext('webgl');
  var perspMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];

  var solidArrays = {
    position: {
      numComponents: 3,
      data: vertices
    },
    color: {
      numComponents: 3,
      data: vertexColors
    },
  };

  var solidInfo = webglUtils.createProgramInfo(gl, ['vs-solid', 'fs-solid']);
  var solidBufferInfo = webglUtils.createBufferInfoFromArrays(gl, solidArrays);

  setupUI();
  requestAnimationFrame(draw);
}

m4.projector = function(width, height, depth) {
  // Note: set 0,0 at canvas center
  return [
    2 / width, 0, 0, 0,
    0, -2 / height, 0, 0,
    0, 0, 2 / depth, 0,
    0, 0, 0, 1,
  ];
};

var vertices = new Float32Array([
  // left column front
  0, 0, 0, 0, 150, 0, 30, 0, 0,
  0, 150, 0, 30, 150, 0, 30, 0, 0,
  // top rung front
  30, 0, 0, 30, 30, 0, 100, 0, 0,
  30, 30, 0, 100, 30, 0, 100, 0, 0,
  // middle rung front
  30, 60, 0, 30, 90, 0, 67, 60, 0,
  30, 90, 0, 67, 90, 0, 67, 60, 0,
  // left column back
  0, 0, 30, 30, 0, 30, 0, 150, 30,
  0, 150, 30, 30, 0, 30, 30, 150, 30,
  // top rung back
  30, 0, 30, 100, 0, 30, 30, 30, 30,
  30, 30, 30, 100, 0, 30, 100, 30, 30,
  // middle rung back
  30, 60, 30, 67, 60, 30, 30, 90, 30,
  30, 90, 30, 67, 60, 30, 67, 90, 30,
  // top
  0, 0, 0, 100, 0, 0, 100, 0, 30,
  0, 0, 0, 100, 0, 30, 0, 0, 30,
  // top rung right
  100, 0, 0, 100, 30, 0, 100, 30, 30,
  100, 0, 0, 100, 30, 30, 100, 0, 30,
  // under top rung
  30, 30, 0, 30, 30, 30, 100, 30, 30,
  30, 30, 0, 100, 30, 30, 100, 30, 0,
  // between top rung and middle
  30, 30, 0, 30, 60, 30, 30, 30, 30,
  30, 30, 0, 30, 60, 0, 30, 60, 30,
  // top of middle rung
  30, 60, 0, 67, 60, 30, 30, 60, 30,
  30, 60, 0, 67, 60, 0, 67, 60, 30,
  // right of middle rung
  67, 60, 0, 67, 90, 30, 67, 60, 30,
  67, 60, 0, 67, 90, 0, 67, 90, 30,
  // bottom of middle rung.
  30, 90, 0, 30, 90, 30, 67, 90, 30,
  30, 90, 0, 67, 90, 30, 67, 90, 0,
  // right of bottom
  30, 90, 0, 30, 150, 30, 30, 90, 30,
  30, 90, 0, 30, 150, 0, 30, 150, 30,
  // bottom
  0, 150, 0, 0, 150, 30, 30, 150, 30,
  0, 150, 0, 30, 150, 30, 30, 150, 0,
  // left side
  0, 0, 0, 0, 0, 30, 0, 150, 30,
  0, 0, 0, 0, 150, 30, 0, 150, 0
]);

// spread the faces in the direction of their normals
for (let i = 0; i < vertices.length; i += 9) {
  // get a view of each position in place (works because it's a typedarray)
  const p0 = vertices.subarray(i + 0, i + 3);
  const p1 = vertices.subarray(i + 3, i + 6);
  const p2 = vertices.subarray(i + 6, i + 9);
  
  // compute the normal
  const n = m4.normalize(m4.cross(
     m4.subtractVectors(p1, p0),
     m4.subtractVectors(p2, p1)));
     
  // scale by 10
  m4.scaleVector(n, 10, n);
  
  // add the normal to each vertex of the triangle
  // this will move it in the direction of the normal
  m4.addVectors(p0, n, p0);
  m4.addVectors(p1, n, p1);
  m4.addVectors(p2, n, p2);
}

var vertexColors = new Uint8Array([
  // left column front
  100, 35, 60, 100, 35, 60, 100, 35, 60,
  200, 70, 120, 200, 70, 120, 200, 70, 120,
  // top rung front
  100, 35, 60, 100, 35, 60, 100, 35, 60,
  200, 70, 120, 200, 70, 120, 200, 70, 120,
  // middle rung front
  100, 35, 60, 100, 35, 60, 100, 35, 60,
  200, 70, 120, 200, 70, 120, 200, 70, 120,
  // left column back
  40, 35, 100, 40, 35, 100, 40, 35, 100,
  80, 70, 200, 80, 70, 200, 80, 70, 200,
  // top rung back
  40, 35, 100, 40, 35, 100, 40, 35, 100,
  80, 70, 200, 80, 70, 200, 80, 70, 200,
  // middle rung back
  40, 35, 100, 40, 35, 100, 40, 35, 100,
  80, 70, 200, 80, 70, 200, 80, 70, 200,
  // top
  35, 100, 105, 35, 100, 105, 35, 100, 105,
  70, 200, 210, 70, 200, 210, 70, 200, 210,
  // top rung right
  100, 100, 35, 100, 100, 35, 100, 100, 35,
  200, 200, 70, 200, 200, 70, 200, 200, 70,
  // under top rung
  105, 50, 35, 105, 50, 35, 105, 50, 35,
  210, 100, 70, 210, 100, 70, 210, 100, 70,
  // between top rung and middle
  105, 80, 35, 105, 80, 35, 105, 80, 35,
  210, 160, 70, 210, 160, 70, 210, 160, 70,
  // top of middle rung
  35, 90, 105, 35, 90, 105, 35, 90, 105,
  70, 180, 210, 70, 180, 210, 70, 180, 210,
  // right of middle rung
  50, 35, 105, 50, 35, 105, 50, 35, 105,
  100, 70, 210, 100, 70, 210, 100, 70, 210,
  // bottom of middle rung.
  38, 105, 50, 38, 105, 50, 38, 105, 50,
  76, 210, 100, 76, 210, 100, 76, 210, 100,
  // right of bottom
  70, 105, 40, 70, 105, 40, 70, 105, 40,
  140, 210, 80, 140, 210, 80, 140, 210, 80,
  // bottom
  45, 65, 55, 45, 65, 55, 45, 65, 55,
  90, 130, 110, 90, 130, 110, 90, 130, 110,
  // left side
  80, 80, 110, 80, 80, 110, 80, 80, 110,
  160, 160, 220, 160, 160, 220, 160, 160, 220
]);

main();
body {
  margin: 0;
}

canvas {
  border: none;
  width: 100vw;
  height: 100vh;
  display: block;
}
<link href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" type="text/css" rel="stylesheet"/>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-ui.js"></script>
<canvas id="canvas"></canvas>
<div id="uiContainer" style="top: 40px;">
  <div id="ui"></div>
</div>

<script id="vs-solid" type="x-shader/x-vertex">
  attribute vec4 a_position;
  attribute vec3 a_color;
  uniform mat4 u_matrix;
  varying vec4 v_color;
  void main() {
    gl_Position = u_matrix * a_position;
    v_color = vec4(a_color, 1.0);
  }
</script>

<script id="fs-solid" type="x-shader/x-fragment">
  precision mediump float;
  varying vec4 v_color;
  void main() {
     gl_FragColor = v_color;
  }
</script>

在评论中,您链接到 Commodore 64 上的软件渲染器。在 WebGL 中模拟软件渲染器有点困难,因为它的全部目的是替换该渲染器。该代码使用三角形进行操作,但在 WebGL 中我们不使用三角形进行操作,而是使用顶点进行操作,并且 WebGL 本身处理三角形。

无论如何,至于为什么你的代码不起作用,代码正在检查法线是否面向 Z 平面 (z 本文称其为surfaceToView vector.

因此,如果您想绘制一个线框框并像示例一样进行假隐藏线去除,则必须为每个顶点传递代表该顶点指向的方向的面法线。然后,您需要一个 modelView 矩阵(除了投影矩阵之外的所有内容)。计算点的视图位置并使用它来计算眼睛到点向量,您还可以通过同一矩阵定向法线并使用点积计算它们之间的角度。 0 会告诉您法线是背对还是朝向。example https://jsgist.org/?src=b0cbcc4d9ca214e5b87c1b284bf0b13f

该技术不适用于 3DF尽管。它仅适用于凸面形状,如立方体、球体、金字塔。 F 不是凸的,因此仍会绘制内部线。

在 WebGL 中去除隐藏线的最简单方法是绘制对象两次。一次使用三角形(填充深度缓冲区),然后再次使用线条。您可以将深度测试从LESS to LEQUAL当用线条画画时

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

背面剔除的法线变换 的相关文章

  • 如何检测浏览器是否支持自定义元素

    我正在查看 Modernizr 它应该有助于功能检测 这应该可以帮助确定您的网站是否与给定的 Web 浏览器兼容 但我没有看到任何表明我可以使用它来检测自定义 HTML 的内容我们在内容中创建和定义的元素 如果不是 Modernizr 我如
  • 如何测试 javascript 闭包内的函数

    这似乎是不可能的 也可能是 但我正在尝试更多的 TDD 但我总是在闭包方面碰壁 假设我有以下内容 function createSomething init function privateMethod param return init
  • React js Stripe 结账不起作用

    我正在尝试在 React js 应用程序中呈现条带结账默认表单
  • 不和谐机器人 |不和谐.js |类型错误:无法读取未定义的属性“长度”

    我正在制作一个 Discord 机器人 并且正在使用 CodeLyon 的视频作为参考 该错误位于我的 message js 文件中 该文件包含以下内容 require dotenv config create cooldowns map
  • 如何重定向到 instagram://user?username={username}

    我的 html 页面上有这个链接 可以在特定用户上打开 Instagram 应用程序 a href Link to Instagram Profile a 我一直在寻找自动运行 url instagram user username USE
  • 如何重置使用 JavaScript 更改的 CSS 属性?

    我的导航按钮的宽度从 100px 增加到 150px 当鼠标悬停在 nav li hover width 150px 但是使用 javascript 我已经做到了 无论选择哪个选项 宽度都将继续为 150px 当选择每个选项时 它会使其他选
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 使用 Angular 下载具有动态 src 的脚本

    Angular 提供了通过动态名称动态加载模板的方法ng include 该部分中的内联 JS 和 CSS 可以正常加载 但没有一个好的方法来下载带有动态 url 的脚本 我们需要下载脚本 相对于调用它们的 html 部分的路径 即我们有一
  • 如何抑制窗口鼠标滚轮滚动...?

    我正在开发嵌入页面中的画布应用程序 我有它 因此您可以使用鼠标滚轮放大绘图 但不幸的是 这会滚动页面 因为它是文章的一部分 当我在 dom 元素上滚动鼠标滚轮时 是否可以阻止鼠标滚轮在窗口上滚动 附加鼠标滚轮 不是 Gecko DOMMou
  • 从未用 @flow 标记的导入文件中获取类型定义

    TL DR我怎么告诉flow从未声明的导入模块导入类型定义 flow 加长版 流接缝能够从不使用流语法的文件中派生类型 请参阅示例 示例文件 flow js if Math random lt 0 5 var y hello else va
  • 标签获取 href 值

    我有以下 html div class threeimages a img alt Australia src Images Services 20button tcm7 9688 gif a div class text h2 a hre
  • JavaScript 重定向到新窗口

    我有以下代码 它根据下拉列表的值重定向到页面 我如何使其在新窗口中打开 function goto form var index form select selectedIndex if form select options index
  • 音频 blob 的 URL.createObjectURL 在 Firefox 中给出 TypeError

    我正在尝试从创建的音频 blob 创建对象 URLgetUserMedia 该代码在 Chrome 中可以运行 但在 Firefox 中存在问题 错误 当我打电话时stopAudioRecorder 它停在audio player src
  • 使用 KnockoutJs 映射插件进行递归模板化

    我正在尝试使用以下方法在树上进行递归模板化ko映射 插入 http knockoutjs com documentation plugins mapping html 但我无法渲染它 除非我定义separate每个级别的模板 在以下情况下
  • 表单计算器脚本基本价格未加载 OnLoad

    我的表单中有一个计算器来计算我的下拉选项选择 function select calculate on change calc input type checkbox calculate on click calc function cal
  • Firefox 书签探索未超过 Javascript 的第一级

    我已经编写了一些代码来探索我的 Firefox 书签 但我只获得了第一级书签 即我没有获得文件夹中的链接 e g 搜索引擎 雅虎网站 谷歌网站 在此示例中 我只能访问 Search engines 和 google com 不能访问 yah
  • 提交表单并重定向页面

    我在 SO 上看到了很多与此相关的其他问题 但没有一个对我有用 我正在尝试提交POST表单 然后将用户重定向到另一个页面 但我无法同时实现这两种情况 我可以获取重定向或帖子 但不能同时获取两者 这是我现在所拥有的
  • Electron - 为什么在关闭事件时将 BrowserWindow 实例设置为 null

    The 电子文档 https electronjs org docs api browser window 提供以下代码示例来创建新窗口 const BrowserWindow require electron let win new Br
  • 如何更改此 jquery 插件的时区/时间戳?

    我正在使用这个名为 timeago 的插件 在这里找到 timeago yarp com 它工作得很好 只是它在似乎不同的时区运行 我住在美国东部 费城时区 当我将准确的 EST 时间放入 timeago 插件时 比如 2011 05 28
  • JQuery 图像上传不适用于未来的活动

    我希望我的用户可以通过帖子上传图像 因此 每个回复表单都有一个上传表单 用户可以通过单击上传按钮上传图像 然后单击提交来提交帖子 现在我的上传表单可以上传第一个回复的图像 但第二个回复的上传不起作用 我的提交过程 Ajax 在 php 提交

随机推荐