WebGL渲染像素化线条

问题描述 投票:0回答:2

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

预期结果:圆滑圆

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

javascript webgl shader
2个回答
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>

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/

© www.soinside.com 2019 - 2024. All rights reserved.