WebGL 3D 透视

WebGL 3D perspective

我是 WebGL 的新手,我正在学习 https://webglfundamentals.org/ 中的 WebGL 教程。我在我的 canvas 中绘制了一个 3d 立方体,它工作正常。

然后我尝试了 this 的 3D 视角。之后我的立方体拉伸了,看起来像这样。 (我用了gl-matrix做矩阵计算)

const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vsSource = `
    attribute vec4 aVertexPosition;
    attribute vec4 aColour;

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;
    uniform vec2 uResolution;
    uniform float uFudgeFactor;
    varying vec4 vColour;

    void main() {
        vec4 position = uModelViewMatrix * uProjectionMatrix * aVertexPosition;
        vec4 zeroToOne = position / vec4(uResolution, uResolution.x, 1);
        vec4 zeroToTwo = zeroToOne * 2.0;
        vec4 clipSpace = zeroToTwo - 1.0;

        float zToDivideBy = 1.0 + clipSpace.z * uFudgeFactor;

        gl_Position = vec4(vec3(clipSpace.xy / zToDivideBy , clipSpace.z) * vec3(1, -1, 1), clipSpace.w);
        vColour = aColour;
    }
`
const fsSource = `
    precision mediump float;
    varying vec4 vColour;

    void main() {
      gl_FragColor = vColour;
    }
`

const program = initShaderProgram(vsSource, fsSource)
const programInfo = {
  program: program,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(program, "aVertexPosition"),
    color: gl.getAttribLocation(program, "aColour")
  },
  uniformLocations: {
    modelViewMatrix: gl.getUniformLocation(program, "uModelViewMatrix"),
    projectionMatrix: gl.getUniformLocation(program, "uProjectionMatrix"),
    resolution: gl.getUniformLocation(program, "uResolution"),
    fudgeFactor: gl.getUniformLocation(program, "uFudgeFactor")
  }
}

const model = mat4.create()
mat4.translate(model, model, [400, 200, 400])
const projectionMatrix = mat4.create()
mat4.perspective(projectionMatrix, Math.PI / 3, 2, 1, 1000)
let buffers = initBuffers();

requestAnimationFrame(drawScene);

function drawScene() {
  gl.viewport(0, 0, canvas.width, canvas.height)
  gl.clearColor(0, 0, 0, 1)
  gl.clearDepth(1)
  gl.enable(gl.DEPTH_TEST)
  gl.depthFunc(gl.LEQUAL)

  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  gl.useProgram(programInfo.program)

  {
    const numComponents = 3;
    const type = gl.FLOAT
    const normalize = false
    const stride = 0
    const offset = 0
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position)
    gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition)
    gl.vertexAttribPointer(programInfo.attribLocations.vertexPosition, numComponents, type, normalize, stride, offset)
    draw3DBox(0, 0, 0, 100, 100, 100)
  }

  {
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color)
    gl.enableVertexAttribArray(programInfo.attribLocations.color)
    gl.vertexAttribPointer(programInfo.attribLocations.color, 4, gl.FLOAT, false, 0, 0)
    setColours()
  }

  gl.uniformMatrix4fv(programInfo.uniformLocations.modelViewMatrix, false, model)
  gl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix, false, projectionMatrix)
  gl.uniform2f(programInfo.uniformLocations.resolution, canvas.width, canvas.height)
  gl.uniform1f(programInfo.uniformLocations.fudgeFactor, 0.4)
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 24)

  requestAnimationFrame(drawScene)
}

function initShaderProgram(vsSource, fsSource) {
  const vertexShader = loadShader(gl.VERTEX_SHADER, vsSource)
  const fragmentShader = loadShader(gl.FRAGMENT_SHADER, fsSource)

  const shaderProgram = gl.createProgram()
  gl.attachShader(shaderProgram, vertexShader)
  gl.attachShader(shaderProgram, fragmentShader)
  gl.linkProgram(shaderProgram)

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    console.log("Unable to initialize the shader program >> ", gl.getProgramInfoLog(shaderProgram))
    return null
  }
  return shaderProgram
}

function loadShader(type, source) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, source)
  gl.compileShader(shader)

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.log("[ERROR] error occoured while compiling the shaders >> " + gl.getShaderInfoLog(shader))
    gl.deleteShader(shader)
    return null
  }
  return shader
}

function initBuffers() {
  const positionBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
  const colorBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)

  return {
    color: colorBuffer,
    position: positionBuffer
  }
}

function draw3DBox(x, y, z, width, height, depth) {
  const positions = new Float32Array([
    // front
    x - width / 2, y - height / 2, z - depth / 2,
    x + width / 2, y - height / 2, z - depth / 2,
    x - width / 2, y + height / 2, z - depth / 2,
    x + width / 2, y + height / 2, z - depth / 2,
    // right
    x + width / 2, y + height / 2, z - depth / 2,
    x + width / 2, y - height / 2, z - depth / 2,
    x + width / 2, y + height / 2, z + depth / 2,
    x + width / 2, y - height / 2, z + depth / 2,
    // back
    x + width / 2, y - height / 2, z + depth / 2,
    x - width / 2, y - height / 2, z + depth / 2,
    x + width / 2, y + height / 2, z + depth / 2,
    x - width / 2, y + height / 2, z + depth / 2,
    // bottom
    x + width / 2, y + height / 2, z - depth / 2,
    x - width / 2, y + height / 2, z - depth / 2,
    x + width / 2, y + height / 2, z + depth / 2,
    x - width / 2, y + height / 2, z + depth / 2,
    // left
    x - width / 2, y + height / 2, z + depth / 2,
    x - width / 2, y + height / 2, z - depth / 2,
    x - width / 2, y - height / 2, z + depth / 2,
    x - width / 2, y - height / 2, z - depth / 2,
    // top
    x - width / 2, y - height / 2, z - depth / 2,
    x + width / 2, y - height / 2, z - depth / 2,
    x - width / 2, y - height / 2, z + depth / 2,
    x + width / 2, y - height / 2, z + depth / 2,
  ]);

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)
}

function setColours() {
  const colours = [
    // front
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    // right
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    // back
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    // bottom
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    // left
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    // top
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
  ]

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colours), gl.STATIC_DRAW)
}
<canvas width="800px" height="400px"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>

我该如何解决这个问题?我做错了什么吗?

谢谢。

透视着色器代码不正确。

这就是你所拥有的

    vec4 position = uModelViewMatrix * uProjectionMatrix * aVertexPosition;
    vec4 zeroToOne = position / vec4(uResolution, uResolution.x, 1);
    vec4 zeroToTwo = zeroToOne * 2.0;
    vec4 clipSpace = zeroToTwo - 1.0;

    float zToDivideBy = 1.0 + clipSpace.z * uFudgeFactor;

    gl_Position = vec4(vec3(clipSpace.xy / zToDivideBy , clipSpace.z) * vec3(1, -1, 1), clipSpace.w);

这就是你需要的。

    gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;

您说您是从 this 开始的,但是 none zeroToOneu_resolution 内容在那个页面上。事实上,该页面上的结果着色器只是

    gl_Position = u_matrix * position;

我还把立方体移到相机前面的中央。它在相机后面。相机俯视-Z默认

const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vsSource = `
    attribute vec4 aVertexPosition;
    attribute vec4 aColour;

    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;
    uniform vec2 uResolution;
    uniform float uFudgeFactor;
    varying vec4 vColour;

    void main() {
        gl_Position = uProjectionMatrix * uModelViewMatrix  * aVertexPosition;
        vColour = aColour;
    }
`
const fsSource = `
    precision mediump float;
    varying vec4 vColour;

    void main() {
      gl_FragColor = vColour;
    }
`

const program = initShaderProgram(vsSource, fsSource)
const programInfo = {
  program: program,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(program, "aVertexPosition"),
    color: gl.getAttribLocation(program, "aColour")
  },
  uniformLocations: {
    modelViewMatrix: gl.getUniformLocation(program, "uModelViewMatrix"),
    projectionMatrix: gl.getUniformLocation(program, "uProjectionMatrix"),
  }
}

const model = mat4.create()
mat4.translate(model, model, [0, 0, -400])
const projectionMatrix = mat4.create()
mat4.perspective(projectionMatrix, Math.PI / 3, 2, 1, 1000)
let buffers = initBuffers();

requestAnimationFrame(drawScene);

function drawScene() {
  gl.viewport(0, 0, canvas.width, canvas.height)
  gl.clearColor(0, 0, 0, 1)
  gl.clearDepth(1)
  gl.enable(gl.DEPTH_TEST)
  gl.depthFunc(gl.LEQUAL)

  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  gl.useProgram(programInfo.program)

  {
    const numComponents = 3;
    const type = gl.FLOAT
    const normalize = false
    const stride = 0
    const offset = 0
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position)
    gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition)
    gl.vertexAttribPointer(programInfo.attribLocations.vertexPosition, numComponents, type, normalize, stride, offset)
    draw3DBox(0, 0, 0, 100, 100, 100)
  }

  {
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color)
    gl.enableVertexAttribArray(programInfo.attribLocations.color)
    gl.vertexAttribPointer(programInfo.attribLocations.color, 4, gl.FLOAT, false, 0, 0)
    setColours()
  }

  gl.uniformMatrix4fv(programInfo.uniformLocations.modelViewMatrix, false, model)
  gl.uniformMatrix4fv(programInfo.uniformLocations.projectionMatrix, false, projectionMatrix)
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 24)

  requestAnimationFrame(drawScene)
}

function initShaderProgram(vsSource, fsSource) {
  const vertexShader = loadShader(gl.VERTEX_SHADER, vsSource)
  const fragmentShader = loadShader(gl.FRAGMENT_SHADER, fsSource)

  const shaderProgram = gl.createProgram()
  gl.attachShader(shaderProgram, vertexShader)
  gl.attachShader(shaderProgram, fragmentShader)
  gl.linkProgram(shaderProgram)

  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    console.log("Unable to initialize the shader program >> ", gl.getProgramInfoLog(shaderProgram))
    return null
  }
  return shaderProgram
}

function loadShader(type, source) {
  const shader = gl.createShader(type)
  gl.shaderSource(shader, source)
  gl.compileShader(shader)

  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.log("[ERROR] error occoured while compiling the shaders >> " + gl.getShaderInfoLog(shader))
    gl.deleteShader(shader)
    return null
  }
  return shader
}

function initBuffers() {
  const positionBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
  const colorBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)

  return {
    color: colorBuffer,
    position: positionBuffer
  }
}

function draw3DBox(x, y, z, width, height, depth) {
  const positions = new Float32Array([
    // front
    x - width / 2, y - height / 2, z - depth / 2,
    x + width / 2, y - height / 2, z - depth / 2,
    x - width / 2, y + height / 2, z - depth / 2,
    x + width / 2, y + height / 2, z - depth / 2,
    // right
    x + width / 2, y + height / 2, z - depth / 2,
    x + width / 2, y - height / 2, z - depth / 2,
    x + width / 2, y + height / 2, z + depth / 2,
    x + width / 2, y - height / 2, z + depth / 2,
    // back
    x + width / 2, y - height / 2, z + depth / 2,
    x - width / 2, y - height / 2, z + depth / 2,
    x + width / 2, y + height / 2, z + depth / 2,
    x - width / 2, y + height / 2, z + depth / 2,
    // bottom
    x + width / 2, y + height / 2, z - depth / 2,
    x - width / 2, y + height / 2, z - depth / 2,
    x + width / 2, y + height / 2, z + depth / 2,
    x - width / 2, y + height / 2, z + depth / 2,
    // left
    x - width / 2, y + height / 2, z + depth / 2,
    x - width / 2, y + height / 2, z - depth / 2,
    x - width / 2, y - height / 2, z + depth / 2,
    x - width / 2, y - height / 2, z - depth / 2,
    // top
    x - width / 2, y - height / 2, z - depth / 2,
    x + width / 2, y - height / 2, z - depth / 2,
    x - width / 2, y - height / 2, z + depth / 2,
    x + width / 2, y - height / 2, z + depth / 2,
  ]);

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW)
}

function setColours() {
  const colours = [
    // front
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    // right
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    // back
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    // bottom
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    // left
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    // top
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
  ]

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colours), gl.STATIC_DRAW)
}
<canvas width="800px" height="400px"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
</script>