WebGL画圆的几种方式

需求:

看了一段时间WebGL,现在想画一个圆,方式应该有很多,我现在列出自己练习的几个

分析:

WebGL只能画点、线、三角形,我现在要画一个圆,基本上就只能靠三角形模拟出来一个

下表的图形都是通过三角形来构造的“正多边形”几何体

发现只要三角形数量足够多,“正多边形”会越来越倾向于“圆”。

WebGL画圆的几种方式_第1张图片 WebGL画圆的几种方式_第2张图片 WebGL画圆的几种方式_第3张图片
6个三角形 15个三角形 36个三角形

具体实施方案:

为了简要表达,会省略不重要的代码

方案1和方案2使用的绘画方式为

gl.drawArrays(gl.TRIANGLE_FAN, 0, n);

方案1:JavaScript中直接构造好点的坐标

直接在JavaScript中算好各个点的坐标,然后赋值给顶点着色器渲染

代码比较简单,主要注意一下相关个数

//生成顶点着色器需要的所有点的位置
function initVertexBuffers(gl) {
    let circleCenter = [0, 0];
    let n = 36;
    let stepAngle = 360 / n;
    let arr = [circleCenter[0], circleCenter[1]];
    for (let i = 0; i < n; i++) {
        let xy = getXYByIndex(i, stepAngle);
        let {x, y} = xy;
        arr.push(x);
        arr.push(y);
    }
    //如果没有下面3行代码,会出现一个缺口
    let xyRight = getXYByIndex(0, n);
    arr.push(xyRight.x);
    arr.push(xyRight.y);
    let verticesColors = new Float32Array(arr);
    let buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
    let a_Position = gl.getAttribLocation(gl.program, "a_Position");
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_Position);
    return n + 2;
}

上述代码用到了一个自定义函数

function getXYByIndex(index, stepAngle) {
    let pai = 3.1415926;
    let circleRadius = 0.6;
    let angle = stepAngle * index;
    let angleInRadian = angle * pai / 180;
    let x = Math.cos(angleInRadian) * circleRadius;
    let y = Math.sin(angleInRadian) * circleRadius;
    return {x, y}
}

备注:代码逻辑不复杂,当要放到博客中,心里对代码质量就有点要求了,此时看上述2段代码,心里不是很满意。

方案2:JavaScript中只算好等间隔的角度,通过角度在顶点着色器中计算出坐标

顶点着色器如下

//这里定义了pai,而不使用glsl中的radians,是为了和1.1中的方案进行对比,
//用一个统一的pai,减少对比时候的其他干扰
#define pai 3.1415926
precision mediump float;
attribute float a_Angle;//圆上的某个点相对于Z轴的旋转角度
void main()
{
    //下述的180.0,如果去掉小数点以及其后的0,那么就会报错
    float x=cos(a_Angle * pai / 180.0);
    float y=sin(a_Angle * pai / 180.0);
    gl_Position=vec4(x, y, 0.0, 1.0);
}

JavaScript中生成“角度数组”,并传给顶点着色器

function initAngles(gl) {
    let n = 36;
    let step = 360 / n;
    let arr = [];
    for (let i = 0; i < n; i++) {
        arr.push(step * i);
    }
    let verticesColors = new Float32Array(arr);
    let buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
    let a_Angle = gl.getAttribLocation(gl.program, "a_Angle");
    gl.vertexAttribPointer(a_Angle, 1, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_Angle);
    return n;
}

方案3:顶点着色器绘制一个和canvas重合的矩形,利用gl_FragCoord的坐标来对每个片元进行圆内筛选

让我觉得可以在片元着色器中来画圆的想法,是来自于片元着色器中的gl_FragCoord对象

gl_FragCoord.xyz可以读取每个像素的x、y、z坐标

拿到坐标,我就可以根据distance()方法来画圆

因为顶点坐标是骨架,再借鉴shaderToy的做法,实现思路

先在顶点着色器中画一个覆盖整个canvas的一个矩形

然后在片元着色器中来画圆

precision mediump float;
void main(){
    //canvas是一个400px*400px的矩形,我要在中心点画一个圆
    vec2 center=vec2(200, 200);
    //gl_FragCoord是一个二维坐标系,坐标原点位于canvas的左下角
    vec2 uv=gl_FragCoord.xy;
    //画一个半径为150px的圆
    bool isInCircle=distance(center, uv)<=150.0;
    if (isInCircle){
        gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
    } else {
        //如果没有下述语句,即使设置了gl.clearColor(),圆外底色会变成白色
        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
}

 

你可能感兴趣的:(WebGL,webgl,shader)