背面剔除的法线变换

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(使用前将#替换为@)

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

  • 如何将 div (或任何元素)覆盖在表格行 (tr) 上?

    我想在恰好有多个列的表行 tr 标记 上覆盖一个 div 或任何可以使用的元素 我尝试了几种方法 似乎都不起作用 我在下面发布了我当前的代码 我确实得到了一个覆盖 但不是直接覆盖在该行上 我尝试将覆盖顶部设置为 div Bottom css
  • 循环内的局部变量会被垃圾收集吗?

    我想知道将循环内引用的任何变量放在循环外是否更有效 或者它们可以像函数内的变量一样被垃圾收集吗 var obj key val for var i 0 i lt 10 i console log obj or for var i 0 i l
  • 向 JS 计算器添加键盘支持时出现的问题

    我想为我的计算器添加键盘支持 当我用键盘按下操作 即 或 时 js将其视为数字 而不是操作 例如 当我通过点击计算 10 11 时 我将得到 21 作为结果 当我通过键盘输入时 我会得到 10 为什么会发生这种情况 是否可以改变它 div
  • NodeJs读取JSON文件

    我正在使用 NodeJs 读取 json 文件 我的代码非常基本 var obj require sample json console log obj 0 Sample json 文件包含这样的字符串化 JSON sample good
  • 是否可以将自定义 HTML 添加到传单图层组和图层控件

    有什么方法可以将自定义 HTML 注入图层组和图层控件中吗 在我们的应用程序中 我们实现了滑块 输入 范围 来调整不透明度设置 并且很明显 在其控制容器内部的基础层上使用专用滑块是有意义的 没有选项或参数可以修改此控件 理想情况下 我们希望
  • Webpack 和 Angular HTML 图像加载

    我一直对 webpack 和 Angular 感到头疼 这可能有一个简单的答案 但我无法弄清楚 我已经阅读了堆栈溢出中关于这个主题的几乎所有答案 但都无济于事 我有一个像这样的 html 页面 还有其他包含图像的模板 img
  • Amcharts 图表 - 图表列到自定义 URL 的超链接以在新选项卡/窗口中打开

    我正在尝试制作一个 amcharts 图表 其中的列链接到自定义网址 并希望网址在新选项卡 窗口中打开 我尝试将此代码添加到图形对象中 但它不起作用 它在同一选项卡 窗口中打开链接 listeners event clickItem met
  • 如何在下拉列表中选择一个选项

    我正在使用 AngularJS 指令 我需要在模板中设置下拉列表的选定选项
  • 变量前面加双下划线

    我的节点代码中有以下代码片段 var fs require fs fs readdir dirname function err files console log files 为什么变量 dirname 有双下划线 我知道一个下划线是私有
  • SVG 中三角形的圆角

    我正在尝试制作一个具有圆角的三角形 三角形将如下所示 左下角是唯一看起来相当容易制作的角 主要是因为这是一个 90 度的 转弯 该转弯是使用QSVG 中的命令具有以下参数 Q x y height x y height RADIUS从我正在
  • Famo.us 滚动视图高度

    我正在尝试使用著名的顺序布局在滚动视图下方添加图像 但滚动视图的高度有问题 这就是我创建滚动视图的方式 var scrollview new Scrollview direction Utility Direction X options
  • AngularJS - 服务、工厂、过滤器等中的依赖注入

    因此 我想在我的 Angular 应用程序中使用一些插件和库 目前 我只是引用这些函数 方法 因为它们是在 99 的应用程序中以完全忽略依赖注入的方式使用的 我有 例如 javascript 库 MomentJS 它处理格式化和验证日期 并
  • 使用 jQuery/JavaScript 将文本框值复制到剪贴板

    我有一个文本框和按钮 如下所示 div class col xs 11 style padding 20px 0 div
  • 使用 Javascript eval() 100% 安全吗?

    我正在编写一个生成 Javascript 代码的 PHP 库 Javascript 代码有许多名为component001 component002 etc 页面通过 AJAX 动态加载 我需要通过 URL 变量传递组件的名称 然后由脚本进
  • jspm / jQuery / TypeScript - 模块“jquery”没有默认导出

    我正在尝试使用 TypeScript 和 jspm system js 来引导 Web 应用程序进行模块加载 我还没有走多远 安装 jspm 后 并使用它来安装 jQuery jspm install jquery 以及基础知识 main
  • 自 2012 年 6 月 12 日以来,“未定义”随机附加在我网站上 1% 的请求网址中

    自 2012 年 6 月 12 日 11 20 TU 起 我在我的 varnish apache 日志中看到非常奇怪的错误 有时 当用户请求一页时 几秒钟后我会看到类似的请求 但 url 中最后一个 之后的所有字符串已被 未定义 替换 例子
  • 我可以在不使用 Jquery UI 的情况下获得 Jquery Pulsate Effect 吗?

    我遇到了由于某种原因无法使用 Jquery UI 的情况 我正在尝试在不使用 Jquery UI 的情况下获得 Jquery UI 脉冲效果 与此链接类似 http docs jquery com UI Effects Pulsate ht
  • ES6 Reflect API 的好处

    我一直在努力升级一些代码以使用 ES6 语法 我有以下代码行 delete this foo 我的 linter 提出了使用建议 Reflect deleteProperty this foo 您可以找到该方法的文档here https d
  • Morgan Logger + Express.js:写入文件并在控制台中显示

    我正在尝试将 Morgan 与 Express js 结合使用来编写日志文件 同时也在控制台上显示我的日志 我正在使用这段代码 var logger require morgan var accessLogStream fs createW
  • 将 Angular Web 组件 EventEmitter 监听到 javascript

    我在以下工具的帮助下创建了一个小型网络组件本文 https medium com IMM9O web components with angular d0205c9db08f使用角度元素 其中包括 Input and Output 我能够将

随机推荐