Webgl 如何处理 3d 中的绘图顺序

Webgl how to handle drawing order in 3d

所以我开始在我的引擎中实现 3d 渲染,绘制单个立方体效果很好。所有面都正确渲染并且深度检查已打开。问题是我的渲染引擎有一个层渲染系统,它们的顺序保存在一个循环的数组中。所以我将例如 10 个 3d 盒子并排放置在同一层上,相机不知道先渲染哪个。

每个 3d 框彼此分开渲染,移动相机不会改变任何渲染顺序。深度测试已启用。实施画家算法似乎对系统造成太大负担,WebGL 有什么办法可以处理这个问题吗?

深度测试是 WebGL 处理可见性的方式。我不确定你是如何在你的引擎中处理它的,但你可能在某个地方犯了错误。

我在下面提供了一个示例,可能会让您走上正轨。

var sgl = function() {
 
 "use strict";
 
 var gl = null;
 var programs = [];
 var buffers = [];
 var textures = [];
 
 addEventListener("unload",function() {
  for (var i = 0; i < programs.length; ++i) {
   gl.deleteProgram(programs[i]);
  }
  
  for (var i = 0; i < buffers.length; ++i) {
   gl.deleteBuffer(buffers[i]);
  }
  
  for (var i = 0; i < textures.length; ++i) {
   gl.deleteTexture(textures[i]);
  }
  
  gl = null;
 });
 
 return {
  set ctx(_gl) {
   if (_gl instanceof WebGLRenderingContext) {
    gl = _gl;
   }
  },
  
  createProgram: function(vc,fc) {
   if (!gl) {
    return null;
   }
   
   var vs = gl.createShader(gl.VERTEX_SHADER);
   var fs = gl.createShader(gl.FRAGMENT_SHADER);
   
   gl.shaderSource(vs,vc);
   gl.shaderSource(fs,fc);
   gl.compileShader(vs);
   gl.compileShader(fs);
   
   try {
    if (!gl.getShaderParameter(vs,gl.COMPILE_STATUS)) {
     throw "VS: " + gl.getShaderInfoLog(vs);
    }
    
    if (!gl.getShaderParameter(fs,gl.COMPILE_STATUS)) {
     throw "FS: " + gl.getShaderInfoLog(fs);
    }
   } catch(log) {
    gl.deleteShader(vs);
    gl.deleteShader(fs);
    console.error(log);
   }
   
   var p = gl.createProgram();
   
   gl.attachShader(p,vs);
   gl.attachShader(p,fs);
   gl.linkProgram(p);
   gl.deleteShader(vs);
   gl.deleteShader(fs);
   
   programs.push(p);
   
   return p;
  },
  
  createIndexBuffer: function(d) {
   d = d instanceof Uint16Array ? d : new Uint16Array(d);
   
   var b = gl.createBuffer();
   
   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,b);
   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,d,gl.STATIC_DRAW);
   
   buffers.push(b);
   
   return b;
  },
  
  createVertexBuffer: function(d) {
   d = d instanceof Float32Array ? d : new Float32Array(d);
   
   var b = gl.createBuffer();
   
   gl.bindBuffer(gl.ARRAY_BUFFER,b);
   gl.bufferData(gl.ARRAY_BUFFER,d,gl.STATIC_DRAW);
   
   buffers.push(b);
   
   return b;
  },
  
  createTexture: function(pixels,width,height) {
   var t = gl.createTexture();
   
   gl.activeTexture(gl.TEXTURE0);
   gl.bindTexture(gl.TEXTURE_2D,t);
   gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);
   gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);
   gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);
   gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);
   gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,width,height,0,gl.RGBA,gl.UNSIGNED_BYTE,pixels);
   
   textures.push(t);
   
   return t;
  }
 };
 
}();

void function() {
 
 "use strict";
 
 var canvasWidth = 180;
 var canvasHeight = 160;
 var canvas = null;
 var gl = null;
 
 var program = null;
 
 var uLightDir = null;
 var uModel = null;
 var uView = null;
 var uProj = null;
 var uTexture = null;
 
 var buffer = null;
 var texture = null;
 
 var modelMatrix = mat4.create();
 var viewMatrix = mat4.create();
 var projMatrix = mat4.perspective(
  mat4.create(),
  1.0472,
  canvasWidth / canvasHeight,
  1.0,
  100.0
 );
 
 var instances = [];
 var cameraAngle = 0.0;
 var cameraDistance = 15.0;
 var light = vec3.create();
 var camera = vec3.fromValues(0,0,5);
 var center = vec3.fromValues(0,0,0);
 var up = vec3.fromValues(0,1,0);
 
 onload = function() {
  canvas = document.getElementById("canvas");
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;
  
  gl = canvas.getContext("webgl") || console.error("WebGL Not Supported.");
  
  sgl.ctx = gl;
  
  program = sgl.createProgram(`
   precision lowp float;
   
   const vec4 LIGHT_DIR = vec4(0.0,0.0,1.0,0.0);
   
   attribute vec3 aPosition;
   attribute vec3 aNormal;
   attribute vec2 aUV;
   
   varying float vDiffuse;
   varying vec2 vUV;
   
   uniform vec3 uLightDir;
   uniform mat4 uModel;
   uniform mat4 uView;
   uniform mat4 uProj;
   
   void main() {
    vUV = aUV;
    vDiffuse = max(0.4,dot(vec4(uLightDir,0.0),(uModel * vec4(aNormal,0.0))));
    gl_Position = uProj * uView * uModel * vec4(aPosition,1.0);
   }
  `,`
   precision lowp float;
   
   varying float vDiffuse;
   varying vec2 vUV;
   
   uniform sampler2D uTexture;
   
   void main() {
    gl_FragColor = texture2D(uTexture,vUV) * vDiffuse;
   }
  `);
  
  uLightDir = gl.getUniformLocation(program,"uLightDir");
  uModel = gl.getUniformLocation(program,"uModel");
  uView = gl.getUniformLocation(program,"uView");
  uProj = gl.getUniformLocation(program,"uProj");
  uTexture = gl.getUniformLocation(program,"uTexture");
  
  buffer = sgl.createVertexBuffer([
   // Position    Normal   UV
   
   // Front
    1.0, 1.0, 1.0,  0.0, 0.0, 1.0,  1.0,0.0,
   -1.0, 1.0, 1.0,  0.0, 0.0, 1.0,  0.0,0.0,
   -1.0,-1.0, 1.0,  0.0, 0.0, 1.0,  0.0,1.0,
   
   -1.0,-1.0, 1.0,  0.0, 0.0, 1.0,  0.0,1.0,
    1.0,-1.0, 1.0,  0.0, 0.0, 1.0,  1.0,1.0,
    1.0, 1.0, 1.0,  0.0, 0.0, 1.0,  1.0,0.0,
   
   // Back
   -1.0,-1.0,-1.0,  0.0, 0.0,-1.0,  1.0,0.0,
   -1.0, 1.0,-1.0,  0.0, 0.0,-1.0,  0.0,0.0,
    1.0, 1.0,-1.0,  0.0, 0.0,-1.0,  0.0,1.0,
   
    1.0, 1.0,-1.0,  0.0, 0.0,-1.0,  0.0,1.0,
    1.0,-1.0,-1.0,  0.0, 0.0,-1.0,  1.0,1.0,
   -1.0,-1.0,-1.0,  0.0, 0.0,-1.0,  1.0,0.0,
   
   // Left
   -1.0, 1.0, 1.0, -1.0, 0.0, 0.0,  0.0,0.0,
   -1.0,-1.0,-1.0, -1.0, 0.0, 0.0,  1.0,1.0,
   -1.0,-1.0, 1.0, -1.0, 0.0, 0.0,  0.0,1.0,
   
   -1.0, 1.0, 1.0, -1.0, 0.0, 0.0,  0.0,0.0,
   -1.0, 1.0,-1.0, -1.0, 0.0, 0.0,  1.0,0.0,
   -1.0,-1.0,-1.0, -1.0, 0.0, 0.0,  1.0,1.0,
   
   // Right
    1.0,-1.0, 1.0,  1.0, 0.0, 0.0,  1.0,0.0,
    1.0,-1.0,-1.0,  1.0, 0.0, 0.0,  0.0,0.0,
    1.0, 1.0, 1.0,  1.0, 0.0, 0.0,  1.0,1.0,
   
    1.0,-1.0,-1.0,  1.0, 0.0, 0.0,  0.0,0.0,
    1.0, 1.0,-1.0,  1.0, 0.0, 0.0,  1.0,0.0,
    1.0, 1.0, 1.0,  1.0, 0.0, 0.0,  1.0,1.0,
   
   // Top
    1.0, 1.0,-1.0,  0.0, 1.0, 0.0,  1.0,0.0,
   -1.0, 1.0,-1.0,  0.0, 1.0, 0.0,  0.0,0.0,
    1.0, 1.0, 1.0,  0.0, 1.0, 0.0,  1.0,1.0,
    
    1.0, 1.0, 1.0,  0.0, 1.0, 0.0,  1.0,1.0,
   -1.0, 1.0,-1.0,  0.0, 1.0, 0.0,  0.0,0.0,
   -1.0, 1.0, 1.0,  0.0, 1.0, 0.0,  0.0,1.0,
   
   // Bottom
    1.0,-1.0, 1.0,  0.0,-1.0, 0.0,  1.0,1.0,
   -1.0,-1.0,-1.0,  0.0,-1.0, 0.0,  0.0,0.0,
    1.0,-1.0,-1.0,  0.0,-1.0, 0.0,  1.0,0.0,
   
   -1.0,-1.0, 1.0,  0.0,-1.0, 0.0,  0.0,1.0,
   -1.0,-1.0,-1.0,  0.0,-1.0, 0.0,  0.0,0.0,
    1.0,-1.0, 1.0,  0.0,-1.0, 0.0,  1.0,1.0
  ]);
  
  var width = 10;
  var height = 10;
  var pixels = new Uint8Array((width * height) << 2);
  
  for (var i = 0; i < pixels.length; i += 4) {
   var c = Math.random() < 0.5 ? 255 : 0;
   
   pixels[i + 0] = c;
   pixels[i + 1] = c;
   pixels[i + 2] = c;
   pixels[i + 3] = 255;
  }
  
  for (var x = 0; x < width; ++x) {
   var index = x << 2;
   
   pixels[index + 0] = 255;
   pixels[index + 1] = 255;
   pixels[index + 2] = 0;
   pixels[index + 3] = 255;
   
   index = (x + (height - 1) * width) << 2;
   
   pixels[index + 0] = 255;
   pixels[index + 1] = 255;
   pixels[index + 2] = 0;
   pixels[index + 3] = 255;
  }
  
  for (var y = 0; y < height; ++y) {
   var index = (y * width) << 2;
   
   pixels[index + 0] = 255;
   pixels[index + 1] = 255;
   pixels[index + 2] = 0;
   pixels[index + 3] = 255;
   
   index = ((width - 1) + y * width) << 2;
   
   pixels[index + 0] = 255;
   pixels[index + 1] = 255;
   pixels[index + 2] = 0;
   pixels[index + 3] = 255;
  }
  
  texture = sgl.createTexture(pixels,width,height);
  
  gl.useProgram(program);
  gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
  gl.vertexAttribPointer(0,3,gl.FLOAT,false,32,0);
  gl.vertexAttribPointer(1,3,gl.FLOAT,false,32,12);
  gl.vertexAttribPointer(2,2,gl.FLOAT,false,32,24);
  gl.enableVertexAttribArray(0);
  gl.enableVertexAttribArray(1);
  gl.enableVertexAttribArray(2);
  
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D,texture);
  
  gl.enable(gl.CULL_FACE);
  gl.enable(gl.DEPTH_TEST);
  
  gl.clearColor(0.5,0.5,0.5,1.0);
  
  gl.uniformMatrix4fv(uModel,false,modelMatrix);
  gl.uniformMatrix4fv(uView,false,viewMatrix);
  gl.uniformMatrix4fv(uProj,false,projMatrix);
  
  var size = 5;
  var hSize = size >> 1;
  
  for (var x = -hSize << 1; x <= hSize << 1; x += 2) {
   for (var y = -hSize << 1; y <= hSize << 1; y += 2) {
    for (var z = -hSize << 1; z <= hSize << 1; z += 2) {
     if (Math.random() < 0.3) {
      instances.push(vec3.fromValues(x,y,z));
     }
    }
   }
  }
  
  loop();
 }
 
 function loop() {
  cameraAngle += 0.01;
  
  if (cameraAngle > 2.0 * Math.PI) {
   cameraAngle = 0.0;
  }
  
  camera[0] = Math.cos(cameraAngle) * cameraDistance;
  camera[1] = 1.0;
  camera[2] = Math.sin(cameraAngle) * cameraDistance;
  
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  
  vec3.sub(light,camera,center);
  vec3.normalize(light,light);
  gl.uniform3fv(uLightDir,light);
  
  mat4.lookAt(viewMatrix,camera,center,up);
  gl.uniformMatrix4fv(uView,false,viewMatrix);
  
  for (var i = 0; i < instances.length; ++i) {
   mat4.fromTranslation(modelMatrix,instances[i]);
   gl.uniformMatrix4fv(uModel,false,modelMatrix);
   gl.drawArrays(gl.TRIANGLES,0,36);
  }
  
  requestAnimationFrame(loop);
 }
 
}();
body {
 background-color: black;
}

canvas {
 display: block;
 margin: 30px auto 0px auto;
 border: solid 1px white;
 border-radius: 10px;
}

script {
 display: none;
}
<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
 </head>
 <body>
    <script type="application/javascript" src="https://cdn.rawgit.com/toji/gl-matrix/8226d776/dist/gl-matrix-min.js"></script>
  <canvas id="canvas"></canvas>
 </body>
</html>