WebGL---1.第一个三角形

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wade333777/article/details/82745788

最近无意中接触到WebGL,之前上半年学了一些opengl方面的知识,但知识这东西就是这样只有温故才能知新,打算正好借助学习WebGL来复习一下opengl方面的知识,顺便比较两者之间的区别。


一、获取WebGL上下文

一般都把WebGL上下文对象命名为gl。这样做也可以保证 JavaScript 代码与 OpenGL 程序更相近。取得了 WebGL 上下文之后,就可以开始 3D 绘图了。

首先,新建一个html文件并加入以下代码:

<html>
	<canvas id='c' width='480' height='320'></canvas>
	<script>
		var canvas = document.getElementById('c');
        var names = ["webgl",
                  "experimental-webgl",
                  "webkit-3d", 
                  "moz-webgl"];

		for (var i = 0; i < names.length; ++i) {
			try {
			  	gl = canvas.getContext(names[i]);
			} 
			catch(e) {}
			if (gl) break;
		}

		if (gl == null){
		 	alert("WebGL is not available");
		}else{
			gl.clearColor(0,0,0.8,1);
			gl.clear(gl.COLOR_BUFFER_BIT);
		}
	</script>
</html>

使用浏览器运行,将会显示一块蓝色区域。


二、缓冲区

顶点信息保存在js类型化数组中,使用之前必须转换到WebGL的缓冲区。
要创建缓冲区,可以调用gl.createBuffer() ,然后调用**gl.bindbuffer()**绑定到WebGL上下文。
缓冲是发送到GPU的一些二进制数据序列,通常情况下缓冲数据包括位置,法向量,纹理坐标,顶点颜色值等。 你可以存储任何数据。

创建VBO并且准备顶点数据:

// 1.创建缓冲区
var buffer = gl.createBuffer();

// 2.将buffer设置为上下文的当前缓冲区,此后,所有缓冲区的操作都直接在buffer中执行
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

// 3.准备顶点数据
var vertices = [-0.5, -0.5, 0.5, -0.5, 0, 0.5];

// 4.把顶点数据从CPU复制到当前绑定的GPU缓冲区
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),gl.STATIC_DRAW)

三、着色器

WebGL中有两种着色器:顶点着色器和片段(或像素)着色器。
顶点着色器用于将3D顶点转换为渲染的2D顶点。
片段着色器用于准确计算要绘制的每个像素的颜色。

向着色器传递数据的4种方式:

1. 属性(attribute)

属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器。 例如你可能在缓冲中用三个32位的浮点型数据存储一个位置值。 对于一个确切的属性你需要告诉它从哪个缓冲中获取数据,获取什么类型的数据(三个32位的浮点数据), 起始偏移值是多少,到下一个位置的字节数是多少。

缓冲不是随意读取的。事实上顶点着色器运行的次数是一个指定的确切数字, 每一次运行属性会从指定的缓冲中按照指定规则依次获取下一个值。

2.全局变量(uniform)
全局变量在着色程序运行前赋值,在运行过程中全局有效。

3.可变量(varying)
可变量是一种顶点着色器给片断着色器传值的方式,依照渲染的图元是点, 线还是三角形,顶点着色器中设置的可变量会在片断着色器运行中获取不同的插值。

4.纹理(texture)
纹理是一个数据序列,可以在着色程序运行中随意读取其中的数据。 大多数情况存放的是图像数据,但是纹理仅仅是数据序列, 你也可以随意存放除了颜色数据以外的其它数据。

编写Vertex Shader和Fragment Shader

var vs = 'attribute vec2 pos;' +
'void main(){ gl_Position = vec4(pos,0,1);}';
var fs = 'precision mediump float;' +
'void main(){ gl_FragColor = vec4(0,0.8,0,1);}';

编写Shader Program
接下来,我们需要编译,链接并最终生成我们的shader program,具体代码如下:

function createShader(str, type){
  var shader = gl.createShader(type);
  gl.shaderSource(shader,str);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    throw gl.getShaderInfoLog(shader);
  }
  return shader;
}

function createProgram(vstr,fstr){
  var program = gl.createProgram();
  var vshader = createShader(vstr,gl.VERTEX_SHADER);
  var fshader = createShader(fstr, gl.FRAGMENT_SHADER);
  gl.attachShader(program,vshader);
  gl.attachShader(program,fshader);
  gl.linkProgram(program);
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    throw gl.getProgramInfoLog(program);
  }
  return program;
}

//创建shader program
var program = createProgram(vs,fs);
gl.useProgram(program);

发送顶点数据给Vertex Shader
首先,我们要获取Vertex attribute的location。接着,激活该attribute,最后调用vertexAttribPointer来传递顶点数据。

program.vertexPosAttrib = gl.getAttribLocation(program,'pos');
gl.enableVertexAttribArray(program.vertexPosAttrib);

var step = Float32Array.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(program.vertexPosAttrib, 2, gl.FLOAT, false, 2*step, 0);

glVertexAttribPointer是一个专门用来解析顶点数据的函数。
第一个参数指定我们要配置的顶点属性,这里传的是顶点属性的位置值。
第二个参数指定顶点属性的大小,这里的顶点由x轴和y轴组成,所以大小是2。
第三个参数指定数据的类型,这里是gl.FLOAT。
第四个个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为true,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为false。
第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在2个float之后,我们把步长设置为2 * step。
第六个参数是偏移量,它表示数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。

uniform变量传值

var uColor = gl.getUniformLocation(program, "uColor");
gl.uniform4fv(uColor, [0, 0, 0, 1]);

四、绘制三角形

WebGL 只能绘制三种形状:点、线和三角。其他所有形状都是由这三种基本形状合成之后,再绘 制到三维空间中的。执行绘图操作要调用 gl.drawArrays()或 gl.drawElements()方法,前者用于 数组缓冲区,后者用于元素数组缓冲区。

gl.drawArrays()或 gl.drawElements()的第一个参数都是一个常量,表示要绘制的形状。可 取值的常量范围包括以下这些:

  • gl.POINTS:将每个顶点当成一个点来绘制。

  • gl.LINES:将数组当成一系列顶点,在这些顶点间画线。每个顶点既是起点也是终点,因此数
    组中必须包含偶数个顶点才能完成绘制。

  • gl.LINE_LOOP:将数组当成一系列顶点,在这些顶点间画线。线条从第一个顶点到第二个顶点,
    再从第二个顶点到第三个顶点,依此类推,直至最后一个顶点。然后再从最后一个顶点到第一
    个顶点画一条线。结果就是一个形状的轮廓。

  • gl.LINE_STRIP:除了不画最后一个顶点与第一个顶点之间的线之外,其他与 gl.LINE_LOOP
    相同。

  • gl.TRIANGLES:将数组当成一系列顶点,在这些顶点间绘制三角形。除非明确指定,每个三角
    形都单独绘制,不与其他三角形共享顶点。

  • gl.TRIANGLES_STRIP:除了将前三个顶点之后的顶点当作第三个顶点与前两个顶点共同构成一个新三角形外,其他都与 gl.TRIANGLES 相同。例如,如果数组中包含 A、B、C、D 四个顶
    点,则第一个三角形连接 ABC,而第二个三角形连接 BCD。

  • gl.TRIANGLES_FAN:除了将前三个顶点之后的顶点当作第三个顶点与前一个顶点及第一个顶
    点共同构成一个新三角形外,其他都与 gl.TRIANGLES 相同。例如,如果数组中包含 A、B、C、D 四个顶点,则第一个三角形连接 ABC,而第二个三角形连接 ACD。

      gl.drawArrays(gl.TRIANGLES,0,3);
    

第一个参数选择上面的常量作为绘制类型。
第二个参数是数组缓冲区中的起始索引。
第三个参数是数组缓冲区中包含的顶点数(点的集合数)


五、实例代码

<html>
	<canvas id='c' width='480' height='320'></canvas>
	<script>
		var canvas = document.getElementById('c');
        var names = ["webgl",
                  "experimental-webgl",
                  "webkit-3d", 
                  "moz-webgl"];

		for (var i = 0; i < names.length; ++i) {
			try {
			  	gl = canvas.getContext(names[i]);
			} 
			catch(e) {}
			if (gl) break;
		}

		if (gl == null){
		 	alert("WebGL is not available");
		}else{
			gl.clearColor(0,0,0.8,1);
			gl.clear(gl.COLOR_BUFFER_BIT);
		}

		// 1.创建缓冲区
		var buffer = gl.createBuffer();

		// 2.将buffer设置为上下文的当前缓冲区,此后,所有缓冲区的操作都直接在buffer中执行
		gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

		// 3.准备顶点数据
		var vertices = [-0.5, -0.5, 0.5, -0.5, 0, 0.5];

		// 4.把顶点数据从CPU复制到当前绑定的GPU缓冲区
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),gl.STATIC_DRAW)

		var vs = 'attribute vec2 pos;' +
		'void main(){ gl_Position = vec4(pos,0,1);}';
		var fs = 'precision mediump float;' +
		'void main(){ gl_FragColor = vec4(0,0.8,0,1);}';

		function createShader(str, type){
		  var shader = gl.createShader(type);
		  gl.shaderSource(shader,str);
		  gl.compileShader(shader);
		  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		    throw gl.getShaderInfoLog(shader);
		  }
		  return shader;
		}

		function createProgram(vstr,fstr){
		  var program = gl.createProgram();
		  var vshader = createShader(vstr,gl.VERTEX_SHADER);
		  var fshader = createShader(fstr, gl.FRAGMENT_SHADER);
		  gl.attachShader(program,vshader);
		  gl.attachShader(program,fshader);
		  gl.linkProgram(program);
		  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
		    throw gl.getProgramInfoLog(program);
		  }
		  return program;
		}

		//创建shader program
		var program = createProgram(vs,fs);
		gl.useProgram(program);

		program.vertexPosAttrib = gl.getAttribLocation(program,'pos');
		gl.enableVertexAttribArray(program.vertexPosAttrib);

		var step = Float32Array.BYTES_PER_ELEMENT;
		gl.vertexAttribPointer(program.vertexPosAttrib, 2, gl.FLOAT, false, 2*step, 0);

		gl.drawArrays(gl.TRIANGLES,0,3);


	</script>
</html>

猜你喜欢

转载自blog.csdn.net/wade333777/article/details/82745788