从体素获取角度我做错了什么?

时间:2019-06-02 19:35:00

标签: webgl voxel

如果您对我的解释不清楚,请随时提出要求。这个业余游戏项目对我来说意义重大

我正在使用webgl制作体素渲染引擎。它使用gl.points为每个体素绘制正方形。我只是使用由摄像机位置平移的投影矩阵,然后由摄像机旋转旋转。

gl_Position = 
 uMatrix * uModelMatrix * vec4(aPixelPosition[0],-aPixelPosition[2],aPixelPosition[1],1.0);

modelviewmatrix只是默认的mat4.create(),由于某种原因,如果没有它,它将不会显示任何内容。 aPixelPosition只是体素的X,Z,Y(在webgl空间中)。

使用类似这样的内容:

gl_PointSize = (uScreenSize[1]*0.7) / (gl_Position[2]);

您可以根据体素到相机的距离来设置体素的大小。除去一个视觉错误,效果很好。

(图片来自一个大的空心立方体)

您可以看到后壁显示正常(因为它们都直接指向您),但是与您成一定角度显示的后壁需要增大尺寸。 因此,我使用了在您面对的位置与体素的位置减去您的相机位置之间的点乘积来获取每个块的角度并相应地对其进行着色。

vPosition=acos(dot( normalize(vec2(sin(uYRotate),-cos(uYRotate))) ,
  normalize(vec2(aPixelPosition[0],aPixelPosition[1])-
vec2(uCam[0],uCam[1]))));

then color the blocks with what this returns.

(墙壁与您的角度取决于墙壁从黑色变成白色)

此视觉演示显示了问题,背面的壁都与您成一定角度(除了直接看的壁之外),同一面侧面的壁与您的角度越来越大。

如果我使用它调整pointSize使其随角度增加,它将解决视觉上的毛刺,但会引入新的毛刺。

从这里开始,一切看起来都很不错,但是如果您真的很靠近一堵墙,然后左右移动

左右扫描时会有相当明显的起泡效果,因为视图侧面的气泡成一定角度(即使它们无论如何都应面对相同的方向)

很明显,我的数学不是最好的。我怎么会有它,所以只有侧面的墙才成角度?而后墙上的那些都不会返回任何角度。多谢。

我已经尝试过制作该点产品,以便始终检查体素X,就像它与相机一样,但这只是为了使每个体素都具有相同的颜色。

1 个答案:

答案 0 :(得分:0)

我不确定您是否可以实际执行代表体素(立方体)和2D正方形(gl.POINTS)的操作。

我不确定是否可以演示该问题。也许我应该编写一个程序来绘制它,以便您可以移动相机,但是...

考虑这6个立方体

6 cubes

仅在其预计的中心放置一个正方形是行不通的

在我看来,没有正方形能够以通用的方式表示这些多维数据集,并且没有空隙和其他问题。

为确保没有间隙,立方体要覆盖的每个像素都必须由正方形覆盖。因此,首先我们可以绘制覆盖每个立方体的矩形

cube screen extents

然后因为gl.POINTS是正方形,我们需要将每个区域扩展为正方形

squares

鉴于重叠的数量将有各种各样的问题。在极端的角度下,一个特殊的正方形需要大小才能覆盖它所代表的多维数据集的屏幕空间,这将变得非常大。然后,当一堆多维数据集的Z相同时,您会遇到Z冲突问题。例如,蓝色方块将出现在绿色方块的前面,在它们重叠的地方,绿色会出现一个小缺口。

我们可以在这里看到

enter image description here

每个绿色像素都与右侧一列和向下一个体素的棕色像素部分重叠,这是因为POINT在前面并且足够大以覆盖屏幕空间,棕色体素使它最终覆盖了绿色像素。左和上一个。

这是遵循上述算法的着色器。对于3D空间中的每个点,它都假定一个单位立方体。它计算多维数据集的8个点中的每个点的规范化设备坐标(NDC),并使用它们来获取最小和最大NDC坐标。据此,它可以计算出gl_PointSize需要覆盖这么大的区域。然后将点放置在该区域的中心。

'use strict';

/* global window, twgl, requestAnimationFrame, document */

const height = 120;
const width = 30
const position = [];
const color = [];
const normal = [];
for (let z = 0; z < width; ++z) {
  for (let x = 0; x < width; ++x) {
    position.push(x, 0, z);
    color.push(r(0.5), 1, r(0.5));
    normal.push(0, 1, 0);
  }
}
for (let y = 1; y < height ; ++y) {
  for (let x = 0; x < width; ++x) {
    position.push(x, -y, 0);
    color.push(0.6, 0.6, r(0.5));
    normal.push(0, 0, -1);
    
    position.push(x, -y, width - 1);
    color.push(0.6, 0.6, r(0.5));
    normal.push(0, 0, 1);
    
    position.push(0, -y, x);
    color.push(0.6, 0.6, r(0.5));
    normal.push(-1, 0, 0);
    
    position.push(width - 1, -y, x);
    color.push(0.6, 0.6, r(0.5));
    normal.push(1, 0, 0);
  }
}

function r(min, max) {
  if (max === undefined) {
    max = min;
    min = 0;
  }
  return Math.random() * (max - min) + min;
}

const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector('canvas').getContext('webgl');

const vs = `
attribute vec4 position;
attribute vec3 normal;
attribute vec3 color;

uniform mat4 projection;
uniform mat4 modelView;
uniform vec2 resolution;

varying vec3 v_normal;
varying vec3 v_color;

vec2 computeNDC(vec4 p, vec4 off) {
  vec4 clipspace = projection * modelView * (p + off);
  return clipspace.xy / clipspace.w;
}


void main() {
  vec2 p0 = computeNDC(position, vec4(-.5, -.5, -.5, 0));
  vec2 p1 = computeNDC(position, vec4( .5, -.5, -.5, 0));
  vec2 p2 = computeNDC(position, vec4(-.5,  .5, -.5, 0));
  vec2 p3 = computeNDC(position, vec4( .5,  .5, -.5, 0));
  vec2 p4 = computeNDC(position, vec4(-.5, -.5,  .5, 0));
  vec2 p5 = computeNDC(position, vec4( .5, -.5,  .5, 0));
  vec2 p6 = computeNDC(position, vec4(-.5,  .5,  .5, 0));
  vec2 p7 = computeNDC(position, vec4( .5,  .5,  .5, 0));
  
  vec2 minNDC = 
    min(p0, min(p1, min(p2, min(p3, min(p4, min(p5, min(p6, p7)))))));
  vec2 maxNDC = 
    max(p0, max(p1, max(p2, max(p3, max(p4, max(p5, max(p6, p7)))))));

  vec2 minScreen = (minNDC * 0.5 + 0.5) * resolution;
  vec2 maxScreen = (maxNDC * 0.5 + 0.5) * resolution;
  vec2 rangeScreen = ceil(maxScreen) - floor(minScreen);
  float sizeScreen = max(rangeScreen.x, rangeScreen.y);
  
  // sizeSize is now how large the point has to be to touch the 
  // corners
  
  gl_PointSize = sizeScreen;
  
  vec4 pos = projection * modelView * position;
  
  // clip ourselves
  if (pos.x < -pos.w || pos.x > pos.w) {
    gl_Position = vec4(0,0,-10,1);
    return;
  }
 
  // pos is the wrong place to put the point. The correct
  // place to put the point is the center of the extents
  // of the screen space points
  
  gl_Position = vec4(
    (minNDC + (maxNDC - minNDC) * 0.5) * pos.w,
    pos.z,
    pos.w);
    
  v_normal = mat3(modelView) * normal;
  v_color = color;
}
`;

const fs = `
precision highp float;

varying vec3 v_normal;
varying vec3 v_color;

void main() {
  vec3 lightDirection = normalize(vec3(1, 2, 3));  // arbitrary light direction
  
  float l = dot(lightDirection, normalize(v_normal)) * .5 + .5;
  gl_FragColor = vec4(v_color * l, 1);
  gl_FragColor.rgb *= gl_FragColor.a;
}
`;

// compile shader, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make some vertex data
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position,
  normal,
  color: { numComponents: 3, data: color },
});

let camera;
const eye = [10, 10, 55];
const target = [0, 0, 0];
const up = [0, 1, 0];
const speed = 0.5;

const kUp = 38;
const kDown = 40;
const kLeft = 37;
const kRight = 39;
const kForward = 87;
const kBackward = 83;
const kSlideLeft = 65;
const kSlideRight = 68;

const keyMove = new Map();
keyMove.set(kForward,    { ndx: 8, eye:  1, target: -1 });
keyMove.set(kBackward,   { ndx: 8, eye:  1, target:  1 });
keyMove.set(kSlideLeft,  { ndx: 0, eye:  1, target: -1 });
keyMove.set(kSlideRight, { ndx: 0, eye:  1, target:  1 });
keyMove.set(kLeft,       { ndx: 0, eye:  0, target: -1 });
keyMove.set(kRight,      { ndx: 0, eye:  0, target:  1 });
keyMove.set(kUp,         { ndx: 4, eye:  0, target: -1 });
keyMove.set(kDown,       { ndx: 4, eye:  0, target:  1 });

function render() {  
  twgl.resizeCanvasToDisplaySize(gl.canvas);
  
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);
  
  const fov = Math.PI * 0.25;
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const near = 0.1;
  const far = 1000;
  const projection = m4.perspective(fov, aspect, near, far);
  
  camera = m4.lookAt(eye, target, up);
  const view = m4.inverse(camera);
  const modelView = m4.translate(view, [width / -2, 0, width / -2]);
  
  gl.useProgram(programInfo.program);
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  
  // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
  twgl.setUniforms(programInfo, {
    projection,
    modelView,
    resolution: [gl.canvas.width, gl.canvas.height],
  });  
  
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo, gl.POINTS);
}
render();

window.addEventListener('keydown', (e) => {
  e.preventDefault();
  
  const move = keyMove.get(e.keyCode);
  if (move) {
    const dir = camera.slice(move.ndx, move.ndx + 3);
    const delta = v3.mulScalar(dir, speed * move.target);
    v3.add(target, delta, target);
	  if (move.eye) {
	    v3.add(eye, delta, eye);
    }    
    render();
  }
});
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
#i { position: absolute; top: 0; left: 5px; font-family: monospace; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
<div id="i">ASWD ⬆️⬇️⬅️➡️</div>

最重要的是,使用POINTS

还会遇到其他问题
  1. 最大磅值仅需为1。

    该规范说,实现可以选择它们支持的最大大小点,并且该点必须至少为1。换句话说,某些实现可能只支持点大小为1。Checking WebGLStats您可能还可以,但仍然...

  2. 一些实现将POINTS正确地裁剪,并且不太可能被修复

    请参见https://stackoverflow.com/a/56066386/128511