首页 > 解决方案 > WebGL 渲染像素化线条

问题描述

我试图渲染简单的形状(圆形、矩形和三角形,但是,当 WebGL 渲染它们时,它们变得非常像素化。

着色器代码:

<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;

void main() {
 // convert the rectangle points from pixels to 0.0 to 1.0
 vec2 zeroToOne = a_position / u_resolution;

 // convert from 0->1 to 0->2
 vec2 zeroToTwo = zeroToOne * 2.0;

 // convert from 0->2 to -1->+1 (clipspace)
 vec2 clipSpace = zeroToTwo - 1.0;

 gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;

uniform vec4 u_color;

void main() {
   gl_FragColor = u_color;
}
</script>

这是我渲染圆圈的代码:

var WebGLRenderer = (function () {

  function WebGLRenderer() {
    this.canvas = document.getElementById('canvas')
    this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')
    if (!this.gl) {
      throw Error('Your browser does not support WebGL')
      return
    }

    // Programs
    this.rectangleProgram = webglUtils.createProgramFromScripts(this.gl, ['2d-vertex-shader', '2d-fragment-shader'])

    // Locations
    this.rectanglePoisitionLocation = this.gl.getAttribLocation(this.rectangleProgram, 'a_position')

    // Uniforms
    this.rectangleResolutionLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_resolution')
    this.rectangleColorLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_color')

    // this.positionBuffer = this.gl.createBuffer()
    this.rectanglePositionBuffer = this.gl.createBuffer()
    // this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)

    requestAnimationFrame(this.render.bind(this))
  }


  WebGLRenderer.prototype.clearCanvas = function (color) {
    var rgba = color.getColor()
    this.gl.clearColor(...rgba)
    this.gl.clear(this.gl.COLOR_BUFFER_BIT)
  }

  WebGLRenderer.prototype.drawCircle = function (x, y, radius, color) {
    // Render circle
    // For now user rectangleProgram
    this.gl.useProgram(this.rectangleProgram)
    this.gl.enableVertexAttribArray(this.rectanglePoisitionLocation)
    // this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
    this.circleBuffer = this.gl.createBuffer()
    // this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.rectanglePositionBuffer)
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.circleBuffer)

    // Setup circle
    var circleVertices = [x, y]
    var numFans = 360
    var anglePerFan = (2 * Math.PI) / numFans
    for (var i = 0; i <= numFans; i++) {
      var angle = anglePerFan * (i + 1)
      var angledX = x + Math.cos(angle) * radius
      var angledY = y + Math.sin(angle) * radius
      circleVertices.push(angledX, angledY)
      // circleVertices.push()
    }
    this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(circleVertices), this.gl.DYNAMIC_DRAW)
    // this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW)

    var size = 2
    var type = this.gl.FLOAT
    var normalize = false
    var stride = 0
    var offset = 0
    this.gl.vertexAttribPointer(this.rectanglePoisitionLocation, size, type, normalize, stride, offset)

    this.gl.uniform2f(this.rectangleResolutionLocation, this.gl.canvas.width, this.gl.canvas.height)

    // Color
    var colorArray = color.getColor()
    this.gl.uniform4fv(this.rectangleColorLocation, colorArray)

    // Draw rectangle
    var primitiveType = this.gl.TRIANGLE_FAN
    // var primitiveType = this.gl.POINTS
    var offset = 0
    var count = circleVertices.length / size
    // var count = positions.length / size
    this.gl.drawArrays(primitiveType, offset, count)
  }

  WebGLRenderer.prototype.render = function (time) {
    this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height)

    var delta = Math.sin(time / 1000) * 10
    this.clearCanvas(new Color(0, 0, 0, 255))
    var rectangleColor = new Color(0, 65, 255, 255)
    var width = 50
    var height = 50
    var circleColor = new Color(0, 167, 255, 255)
    this.drawCircle(10, 10, 10, circleColor)

    requestAnimationFrame(this.render.bind(this))
  }
  return WebGLRenderer
})()


function Color(r, g, b, a) {
  this.r = r
  this.g = g
  this.b = b
  this.a = a
  this.getColor = function () {
    return [r / 255, g / 255, b / 255, a / 255]
  }
}

var renderer = new WebGLRenderer()

结果:模糊的圆圈(我用 WebGL 渲染的一切都是模糊的)

结果见小提琴:https ://jsfiddle.net/xLwmngav/1/

预期结果:一个光滑的圆形

任何帮助表示赞赏。先感谢您。

标签: javascriptwebglshader

解决方案


正如本文所指出的,画布有 2 种尺寸,它们的分辨率(其中有多少像素)和它们显示的尺寸。

通常,您希望分辨率匹配或超过画布显示的大小。最好的方法是在渲染之前检查画布的分辨率是否与其显示的大小匹配,以及是否不使用这样的函数调整它的大小

  function resize(canvas) {
    // Lookup the size the browser is displaying the canvas.
    const desiredWidth  = canvas.clientWidth;
    const desiredHeight = canvas.clientHeight;

    // Check if the canvas is not the same size.
    if (canvas.width  !== desiredWidth ||
        canvas.height !== desiredHeight) {

      // Make the canvas the same size
      canvas.width  = desiredWidth;
      canvas.height = desiredHeight;
    }
  }

并像这样使用它

function render() {
  resize(canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  ... draw here ...

  ...

例子:

function resize(canvas) {
  // Lookup the size the browser is displaying the canvas.
  const desiredWidth  = canvas.clientWidth;
  const desiredHeight = canvas.clientHeight;

  // Check if the canvas is not the same size.
  if (canvas.width  !== desiredWidth ||
      canvas.height !== desiredHeight) {

    // Make the canvas the same size
    canvas.width  = desiredWidth;
    canvas.height = desiredHeight;
  }
}

var WebGLRenderer = (function () {

  function WebGLRenderer() {
    this.canvas = document.getElementById('canvas')
    this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')
    if (!this.gl) {
      throw Error('Your browser does not support WebGL')
      return
    }

    // Programs
    this.rectangleProgram = webglUtils.createProgramFromScripts(this.gl, ['2d-vertex-shader', '2d-fragment-shader'])

    // Locations
    this.rectanglePoisitionLocation = this.gl.getAttribLocation(this.rectangleProgram, 'a_position')

    // Uniforms
    this.rectangleResolutionLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_resolution')
    this.rectangleColorLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_color')

    // this.positionBuffer = this.gl.createBuffer()
    this.rectanglePositionBuffer = this.gl.createBuffer()
    // this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)

    requestAnimationFrame(this.render.bind(this))
  }


  WebGLRenderer.prototype.clearCanvas = function (color) {
    var rgba = color.getColor()
    this.gl.clearColor(...rgba)
    this.gl.clear(this.gl.COLOR_BUFFER_BIT)
  }

  WebGLRenderer.prototype.drawCircle = function (x, y, radius, color) {
    // Render circle
    // For now user rectangleProgram
    this.gl.useProgram(this.rectangleProgram)
    this.gl.enableVertexAttribArray(this.rectanglePoisitionLocation)
    // this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
    this.circleBuffer = this.gl.createBuffer()
    // this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.rectanglePositionBuffer)
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.circleBuffer)

    // Setup circle
    var circleVertices = [x, y]
    var numFans = 360
    var anglePerFan = (2 * Math.PI) / numFans
    for (var i = 0; i <= numFans; i++) {
      var angle = anglePerFan * (i + 1)
      var angledX = x + Math.cos(angle) * radius
      var angledY = y + Math.sin(angle) * radius
      circleVertices.push(angledX, angledY)
      // circleVertices.push()
    }
    /*var circleVertices = [
      x, y,
      15, 18,
      5, 18,
      0, 10,
      4, 1,
      14, 1,
      20, 9,
      15, 18
    ]*/
    // three 2d points
    // TODO: Research static draw
    this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(circleVertices), this.gl.DYNAMIC_DRAW)
    // this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW)

    var size = 2
    var type = this.gl.FLOAT
    var normalize = false
    var stride = 0
    var offset = 0
    this.gl.vertexAttribPointer(this.rectanglePoisitionLocation, size, type, normalize, stride, offset)

    this.gl.uniform2f(this.rectangleResolutionLocation, this.gl.canvas.width, this.gl.canvas.height)

    // Color
    var colorArray = color.getColor()
    this.gl.uniform4fv(this.rectangleColorLocation, colorArray)

    // Draw rectangle
    var primitiveType = this.gl.TRIANGLE_FAN
    // var primitiveType = this.gl.POINTS
    var offset = 0
    var count = circleVertices.length / size
    // var count = positions.length / size
    this.gl.drawArrays(primitiveType, offset, count)
  }

  WebGLRenderer.prototype.render = function (time) {
    resize(this.gl.canvas);
    this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height)

    var delta = Math.sin(time / 1000) * 10
    this.clearCanvas(new Color(0, 0, 0, 255))
    var rectangleColor = new Color(0, 65, 255, 255)
    var width = 50
    var height = 50
    var circleColor = new Color(0, 167, 255, 255)
    this.drawCircle(10, 10, 10, circleColor)

    requestAnimationFrame(this.render.bind(this))
  }
  return WebGLRenderer
})()


function Color(r, g, b, a) {
  this.r = r
  this.g = g
  this.b = b
  this.a = a
  this.getColor = function () {
    return [r / 255, g / 255, b / 255, a / 255]
  }
}

var renderer = new WebGLRenderer()

window.WebGLRenderer = WebGLRenderer
body {
  margin: 0;
}

#canvas {
  display: block;  /* prevents scrollbar */
  width: 100vw;
  height: 100vh;
}
<canvas id="canvas"></canvas>
  <!-- vertex shader -->
  <script id="2d-vertex-shader" type="x-shader/x-vertex">
  attribute vec2 a_position;
  
  uniform vec2 u_resolution;
  
  void main() {
     // convert the rectangle points from pixels to 0.0 to 1.0
     vec2 zeroToOne = a_position / u_resolution;
  
     // convert from 0->1 to 0->2
     vec2 zeroToTwo = zeroToOne * 2.0;
  
     // convert from 0->2 to -1->+1 (clipspace)
     vec2 clipSpace = zeroToTwo - 1.0;
  
     gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
  }
  </script>
  <!-- fragment shader -->
  <script id="2d-fragment-shader" type="x-shader/x-fragment">
  precision mediump float;
  
  uniform vec4 u_color;
  
  void main() {
     gl_FragColor = u_color;
  }
  </script>
  <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
  <script src="https://webglfundamentals.org/webgl/resources/m3.js"></script>


推荐阅读