绘制webgl原语

时间:2015-02-11 11:26:06

标签: opengl-es webgl

我知道如何使用WebGL绘制正方形,矩形和三角形。但是,我对如何使用triangle_fan绘制圆圈感到有点困惑。我创建了一个包含顶点的数组,并将其绑定到缓冲区。

在我绘制三角形等的方式中,我创建了一个数组来填充它的顶点。

var circle= [...];

因此,当我这样做来创建一个圆圈时,似乎我必须放置很多顶点才能使它平滑。

有没有办法创建一个更简单,代码更少的圆圈?

2 个答案:

答案 0 :(得分:1)

一种方法是将列表中的前两个顶点设置为(0,0,0)(1,0,0)。然后,您可以使用cos()sin()计算剩余的顶点,因为它们将返回圆上点的x和y值。

var numTris = 100;

vertices = [
     0.0,  0.0,  0.0,
     1.0,  0.0,  0.0
];

var degPerTri = (2 * Math.PI) / numTris;

for(var i = 0; i < numTris; i++) {
    var index = 2*3 + i*3
    var angle = degPerTri * (i+1);

    vertices[index] = Math.cos(angle); 
    vertices[index+1] = Math.sin(angle); 
    vertices[index+2] = 0; 
}

这是一张图片,显示了TRIANGLE_FAN期望顶点所在的顺序: enter image description here

列表中的第一个顶点将由扇形中的每个三角形共享(因此它将是我们圆圈的中心)。并且扇形中的每个三角形由两个连续的顶点以及列表中的第一个顶点组成。请注意,顶点1和7位于相同的位置。

你可以在这里看到它:

&#13;
&#13;
var gl;
function initGL(canvas) {
    try {
        gl = canvas.getContext("experimental-webgl");
        gl.viewportWidth = canvas.width;
        gl.viewportHeight = canvas.height;
    } catch (e) {
    }
    if (!gl) {
        alert("Could not initialise WebGL, sorry :-(");
    }
}


function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
        return null;
    }

    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
        if (k.nodeType == 3) {
            str += k.textContent;
        }
        k = k.nextSibling;
    }

    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }

    gl.shaderSource(shader, str);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }

    return shader;
}


var shaderProgram;

function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");

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

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
}


var mvMatrix = mat4.create();
var pMatrix = mat4.create();

function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}



var squareVertexPositionBuffer;

function initBuffers() {
    squareVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);

    var numTris = 100;

    vertices = [
         0.0,  0.0,  0.0,
         1.0,  0.0,  0.0
    ];

    var degPerTri = (2 * Math.PI) / numTris;
    
    for(var i = 0; i < numTris; i++) {
        var index = 2*3 + i*3
        var angle = degPerTri * (i+1);

        vertices[index] = Math.cos(angle); 
        vertices[index+1] = Math.sin(angle); 
        vertices[index+2] = 0; 
    }

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    squareVertexPositionBuffer.itemSize = 3;
    squareVertexPositionBuffer.numItems = numTris + 2;
}


function drawScene() {
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

    mat4.identity(mvMatrix);

    mat4.translate(mvMatrix, [0.0, 0.0, -3.0]);
    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLE_FAN, 0, squareVertexPositionBuffer.numItems);
}



function webGLStart() {
    var canvas = document.getElementById("canvas");
    initGL(canvas);
    initShaders();
    initBuffers();

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);

    drawScene();
}
&#13;
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script type="text/javascript" src="http://antonpantev.com/so-related/attribsnotsetup/custom_lesson4_files/glMatrix-0.9.5.min.js"></script><style type="text/css">.cf-hidden { display: none; } .cf-invisible { visibility: hidden; }</style>

<script id="shader-fs" type="x-shader/x-fragment">
    precision mediump float;

    void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
</script>

<script id="shader-vs" type="x-shader/x-vertex">
    attribute vec3 aVertexPosition;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;

    void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    }
</script>

</head>
<body onload="webGLStart();">
    <canvas id="canvas" style="border: none;" width="500" height="500"></canvas>
</body>
</html>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

实际上,常见的方法是使用大量顶点来制作不完美的圆/球。由于我们使用像素来显示事物,因此圆/球永远不会完美,但看起来可能是完美的。

例如在three.js editor中,基本圆圈由32个三角形组成。您应该根据相机与圆圈的距离来增加或减少数量。

无论如何,还有另外一种方法可以创造出完美的形状,而不仅仅是圆形。根据具体情况,您的想象力或模式用于实现它。

例如,在webgl中,我们只有点和三角形。可以在着色器中修改最终外观:

  • 在绘制之前,您将建立要绘制的点/三角形集。
  • 然后顶点着色器转动。它可能会修改每个顶点的位置。现在可能没用,所以我现在就跳过说明。
  • 片段着色器转动。片段着色器说明应该在每个像素的三角形/点覆盖的位置上显示什么颜色。 现在重要的是,你可以使用aplha通道渲染透明色(或者你可以跳过绘制这个像素 - 不确定这个)。所以你能做的就是铭刻圆圈。如果使用顶点着色器更改顶点的位置,则还可以执行外接圆。 Example is here,它来自之前回答的问题Change color in middle of circle

片段着色器的核心部分:

varying vec4 pos;

uniform vec2 middle;
uniform float r;

void main() {
    // inscribed circle math
    if (distance(pos.xy, middle) > r) {
        discard;
    }
    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}

我不会说这是最好的选择,这是可能的。

广泛使用的是例如Bezier curves,它可以帮助您实现任何自定义形状。这通常不会用于实时渲染太慢,但对于预渲染的视频或电影很有用。

我应该提到的最后一件事是顶点法线。这是核心问题。您为每个顶点分配一个法向量。然后,您可以在着色器中使用此向量来修改光反射或更多。然后球看起来更好,因为反射没有锋利的边缘。 以this demo为例。如果您使用它一段时间,您将识别表面上相当大的三角形,或者您可以注意到,如果您专注于边缘。但如果你不专注,球看起来很完美。因为镜子会分散你的注意力。一个彩球看起来不像这样。 法线也可以与贝塞尔曲线一样使用。