WebGL简单使用

发布于 2021-02-01  119 次阅读


着色器的使用

着色器类型

顶点着色器(Vertexshader)

用来描述顶点特性(如位置、颜色等)的程序,通常是二维或者三维图形的端点或者焦点。

片元着色器(Fragmentshader)

进行逐片处理如光照的程序,可以理解为像素(图像的单元)。
类型 变量名 描述
vec4 gl_Position 表示顶点位置
float gl_PointSize 表示顶点的尺寸(像素数)

attribute: 只能用于顶点着色器(VertexShader)传值。

它规定了变量的存储类型,在变量前加入这个关键字,表示该变量是一个全局变量。在此约定,attribute类型的变量都以a_前缀开始。attribute变量的声明格式是<存储类型><数据类型><  变量>。如attribute vec4 a_Position 。

uniform:用于片元着色器(FragmentShader)。

在此约定,所有uniform 类型的变量都以u_前缀开始。uniform限定的变量要与attribute变量的类型一致。若已有attribute vec4 a_Position,则片元着色器中应该是 uniform vec4u_FragColor。

七种基本绘制方式

通过gl.drawArrays(mode, girst,count)方法,可以绘制七种基本图形,如下表所示

基本图形 参数 描述
gl.POINTS 一系列的点,绘制在 v0 ,v1,v2....
线段 gl.LINES 一系列单独的线段,绘制在(v0, v1),(v2,v3)...,若点个数使奇数,则最后一个点被忽略
线条 gl.LINE_STRIP 一系列连接的线段,绘制在(v0,v1),(v1,v2)...最后一个点使一条线段的终点
回路线条 gl.LINE_LOOP 一系列连接的线段,与gl.LINE_STRIP相比,最后一个点会与开始的点相连接(vn,v0),线段会闭合
三角形 gl.TRIANGLES 一系列单独的三角形,绘制在(v0, v1, v2),(v3, v4, v5)...,点个数若不是3的倍数,则剩下的被忽略
三角带 gl.TRIANGLE_STRIP 一些列条带状的三角形,前三个点构成第一个三角形,从第二个点开始的三个点构成第二个三角形,以此类推…,(v0,v1,v2),(v2,v1,v3),(v2,v3,v4)...
三角扇 gl.TRIANGLE_FAN 一系列三角形组成的类似扇形的图形,前三个点构成第一个三角形,接下来的一个点和前一个三角形的最后一条边组成接下来的一个三角形,被绘制在(v0, v1, v2),(v0, v2, v3),(v0, v3, v4)...

缓冲器与缓冲区

缓冲器(buffer)

指的是缓冲寄存器,它分输入缓冲器和输出缓冲器两种。前者的作用是将外设送来的数据暂时存放,以便处理器将它取走;后者的作用是用来暂时存放处理器送往外设的数据。

缓冲区又称为缓存

是在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。

缓冲区对象:

可以一次性向着色器传入多个顶点的数据。

缓冲区种类:

顶点缓冲区(VertexBuffer)、索引缓冲区(IndexBuffer)、帧缓冲(FrameBuffer)、深度缓冲(DepthBuffer)、颜色缓冲(ColorBuffer)和模型缓冲(ModelBuffer)

缓冲区使用流程

创建缓冲区对象(gl.createBuffer())
绑定缓冲区对象(gl.bindBuffer())
数据写入缓冲区(gl.bufferData())
将缓冲区对象分配给一个attribute变量(gl.vertexAttribute())
开启attribute变量(gl.enableVertexAttribArray())

wabgl小游戏实例

htmljs
    <canvas id="webgl" width="400" height="400">
      Please use a browser that supports "canvas"
    </canvas>
    <p>剩余时间:<span id="time">12</span></p>
    <p>清除病毒数:<span id="num">0</span></p>
    <script src="webgl-utils.js"></script>
    <script src="webgl-debug.js"></script>
    <script src="cuon-utils.js"></script>
    <script src="cuon-matrix.js"></script>
    <script src="RotatedTriangle_Matrix4.js"></script>
// 顶点着色器
var v_shader_source = '' +
'attribute vec4 a_Position;' +
'attribute float a_PointSize;' +
'void main(){' +
'   gl_Position = a_Position;' +
'   gl_PointSize = a_PointSize;' +
'}'
// 片元着色器
var f_shader_source = '' +
'precision mediump float;' +
'uniform vec4 u_FragColor;'+
'void main(){' +
// 点的中心坐标是 (0.5, 0.5)
'   float dist = distance(gl_PointCoord, vec2(0.5, 0.5));' +
'   if(dist < 0.5){' + //点的半径是 0.5
'       gl_FragColor = u_FragColor;' +
'   }else{' +
'       discard;' +
'   }' +
'}'
var canvas = document.getElementById('webgl');
var gl = getWebGLContext(canvas);
var allNUM=0,cricleNum=8,time=20,animation,animation1;
var g_points = [];
var g_colors = [];
var g_pointSize = []
document.getElementById('time').innerText=time;
document.getElementById('num').innerText=allNUM;
var r=0.5,x=0,y=0,data=[];
for(let j=0;j<cricleNum;j++){
    var r_n=180-Math.random()*360;
    var s_n=parseInt(Math.random()*50 + 20);
    data[0]=r * Math.cos(Math.PI/180 * r_n) + x;
    data[1]=r * Math.sin(Math.PI/180 * r_n) + y;
    g_points.push(data);
    g_colors.push([Math.random(),Math.random(),Math.random(),0.6]);
    g_pointSize.push(s_n);
    for(i=0;i<g_points.length-1;i++){ var xy = g_points[i]; var size_x,size_y,xy_x,xy_y; size_x=(g_pointSize[i]+s_n)/canvas.width; size_y=(g_pointSize[i]+s_n)/canvas.height; if(xy[0]>=data[0]){
            xy_x=xy[0]-data[0];
        }else{
            xy_x=data[0]-xy[0];
        }
        if(xy[1]>=data[1]){
            xy_y=xy[1]-data[1];
        }else{
            xy_y=data[1]-xy[1];
        }
        if(size_x>=xy_x && size_y>=xy_y){
            j--
            g_points.pop()
            g_colors.pop()
            g_pointSize.pop()
        }
    }
    data=[];
}
g_points.push([0,0]);
g_colors.push([0.8, 0.2, 0.4, 1.0]);
g_pointSize.push(200);
// console.log(g_points);
// console.log(g_colors);
// console.log(g_pointSize);
main();
timelaye();
function main() {
    if (!gl) {
        console.log('不能获取webgl 上下文');
        return false;
    }
    // 初始化渲染器
    if (!initShaders(gl, v_shader_source, f_shader_source)) {
        console.log('初始化着色器失败');
        return false;
    }
    var a_Position=gl.getAttribLocation(gl.program,'a_Position');
	if(a_Position<0){
		console.log('获取a_Position失败');
		return;
	}
    var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
	if(!u_FragColor){
		console.log('获取u_FragColor失败');
		return;
	}
    var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize'); 
	if(!a_PointSize)
	{
		console.log('获取a_PointSize失败');
		return;
	}
    // 设置顶点位置
    var n = cricleNum;
    if (n < 0) {
        console.log('初始化顶点信息失败');
        return false;
    }
    canvas.onmousedown = function(ev) {click(ev, gl, canvas, a_Position, u_FragColor ,a_PointSize ); };

    // 设置背景颜色
    gl.clearColor(1, 1, 1, 1.0);
    // 开启深度检测
    gl.enable(gl.DEPTH_TEST);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEFTH_BUFFER_BIT);

    for(var i = 0; i<n+1; i++){
		var xy = g_points[i];
		var rgba = g_colors[i];
		gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0);
    	gl.vertexAttrib1f(a_PointSize, g_pointSize[i]);
		gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
		gl.drawArrays(gl.POINTS, 0, 1);
	}
    if(n == 0){
        clearInterval(animation);
        alert('成功清除所有病毒');
        end();
    }
};
function click(ev, gl, canvas, a_Position,u_FragColor,a_PointSize){
    console.log(ev);
    var x = ev.clientX;
	var y = ev.clientY;
	var rect = ev.target.getBoundingClientRect();
	//transform
    x = ((x-rect.left) - canvas.width/2)/(canvas.width/2);
	y = (canvas.height/2 - (y-rect.top))/(canvas.height/2);
	console.log(x+'..'+y)
    for(let i=0;i<cricleNum;i++){ var xy = g_points[i]; var size_x,size_y; size_x=g_pointSize[i]/canvas.width; size_y=g_pointSize[i]/canvas.height; if(x >= xy[0]-size_x && x <= xy[0]+size_x && y >= xy[1]-size_y && y <= xy[1]+size_y){
            g_points.splice(i,1);
            g_colors.splice(i,1);
            g_pointSize.splice(i,1);
            cricleNum--;
            allNUM++;
            main();
            document.getElementById('num').innerText=allNUM;
        }
    }
}
function timelaye(){
    animation=setInterval(function(){
        time--;
        document.getElementById('time').innerText=time;
        if(time<=0){
            end();
        }
    },1000);
}
function end(){
    clearInterval(animation);
    clearInterval(animation1);
    alert('很遗憾您失败了');
    // gl.clearColor(1, 1, 1, 1.0);
    // gl.clear(gl.COLOR_BUFFER_BIT);
}
animation1=setInterval(function(){
    for(i=0;i<g_points.length-1;i++){
        console.log(Math.random()*5)
        g_pointSize[i]=g_pointSize[i]+Math.random()*5;
    }
    main();
    for(var i=0;i<g_points.length-1;i++){
        var xy = g_points[i];
        var size_x,size_y,xy_x,xy_y;
        for(var j=i+1;j<g_points.length-1;j++){ var data = g_points[j]; size_x=(g_pointSize[i]+g_pointSize[j])/canvas.width; size_y=(g_pointSize[i]+g_pointSize[j])/canvas.height; if(xy[0]>=data[0]){
                xy_x=xy[0]-data[0];
            }else{
                xy_x=data[0]-xy[0];
            }
            if(xy[1]>=data[1]){
                xy_y=xy[1]-data[1];
            }else{
                xy_y=data[1]-xy[1];
            }
            if(size_x>=xy_x && size_y>=xy_y){
                end();
                // console.log(size_x,size_y,xy_x,xy_y)
                // console.log(i,j)
                return
            }
        }
    }
},4000);

试一试


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。