WebGL渲染像素化的线

时间:2019-04-15 16:57:04

标签: javascript webgl shader

我试图渲染简单的形状(圆形,矩形和三角形,但是,当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/

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

感谢您的帮助。预先谢谢你。

2 个答案:

答案 0 :(得分:1)

正如this article中指出的那样,画布具有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>

答案 1 :(得分:0)

画布的默认宽度和高度为300x150像素。这些是<canvas>元素的属性,而不是CSS属性。通过按100vw缩放到100vh,您只需将300x150像素拉伸到整个屏幕即可。

要真正获得画布像素到屏幕像素的1:1映射,您需要将画布的宽度和高度设置为窗口的大小:

this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight

您可能还想听resize上的window事件,并相应地更新画布大小。

提琴:https://jsfiddle.net/kL1a2zpr/