学习WebGL:着色器、绘制一个点
WebGL使用着色器信息绘图,着色器使用OpenGL ES(GLSL)编写
着色器分为顶点着色器(Vertex shader)和片元着色器(Fragment shader),顶点着色器描述位置信息,片元着色器描述颜色信息
//顶点着色器 void main(){ gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 10.0; } //片元着色器 void main(){ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
gl_Position、gl_PointSize、gl_FragColor均为GLSL内置变量名
vec4描述了数据类型为4个浮点数,相应的vec1表示1个浮点数,vec2表示2个浮点数,vec3表示3个浮点数
在gl_Position中的4个数值中,前3个应该是三维坐标的x、y、z,目前还不太明白最后一个数值的意义
有了着色器信息后,就可以使用这些信息开始绘制了
<!DOCTYPE html> <html lang='zh-cmn-Hans'> <head> <meta charset='utf-8' /> <title>Canvas - WebGL</title> </head> <body> <h1>HTML5 - Canvas - WebGL</h1> <canvas id='glcanvas' width='600' height='600'></canvas> <p>在坐标原点(画布中央)绘制一个红色的点</p> <script id="vShader" type="x-shader/x-vertex"> void main(){ gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 10.0; } </script> <script id="fShader" type="x-shader/x-fragment"> void main(){ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } </script> <script type="text/javascript"> window.onload = function(){ init(); } function init() { var canvas, gl,
vShader, fShader, shaderProgram; canvas = document.getElementById('glcanvas'); gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); if (!gl) { console.log('WebGL Fails!'); return; } try { vShader = gl.createShader(gl.VERTEX_SHADER); //创建着色器 fShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vShader, document.getElementById('vShader').textContent); //设置着色器的源码 gl.shaderSource(fShader, document.getElementById('fShader').textContent); gl.compileShader(vShader); //编译着色器 gl.compileShader(fShader); shaderProgram = gl.createProgram(); //创建着色器程序 gl.attachShader(shaderProgram, vShader); //把着色器信息附加到着色器程序 gl.attachShader(shaderProgram, fShader); gl.linkProgram(shaderProgram); //连接着色器程序 gl.useProgram(shaderProgram); // gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); gl.drawArrays(gl.POINTS, 0, 1); } catch (e) { console.log('WebGL Error!') } } </script> </body> </html>
不理解的是shaderSource这个方法,为何WebGL不直接提供设置着色器的API,而要通过这样的方法
上面的例子中着色器信息是在绘制前就设置好了的,如果要在绘制过程中设置,则需使用attribute和uniform等变量
<script id="vShader" type="x-shader/x-vertex"> attribute vec4 vp; attribute float vps; void main(){ gl_Position = vp; gl_PointSize = vps; } </script> <script id="fShader" type="x-shader/x-fragment"> uniform vec4 fc; void main(){ gl_FragColor = fc; } </script>
然后取得这些变量,并赋值
vShader = gl.createShader(gl.VERTEX_SHADER); //创建着色器 fShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(vShader, document.getElementById('vShader').textContent); //设置着色器的源码 gl.shaderSource(fShader, document.getElementById('fShader').textContent); gl.compileShader(vShader); //编译着色器 gl.compileShader(fShader); shaderProgram = gl.createProgram(); //创建着色器程序 gl.attachShader(shaderProgram, vShader); //把着色器信息附加到着色器程序 gl.attachShader(shaderProgram, fShader); gl.linkProgram(shaderProgram); //连接着色器程序 gl.useProgram(shaderProgram); // var vp = gl.getAttribLocation(shaderProgram, 'vp'); //取得变量 var vps = gl.getAttribLocation(shaderProgram, 'vps'); var fc = gl.getUniformLocation(shaderProgram, 'fc'); gl.vertexAttrib4f(vp, 0.5, 0.5, 0.0, 1.0); //变量赋值 gl.vertexAttrib1f(vps, 10.0); gl.uniform4f(fc, 1.0, 0.0, 0.0, 1.0); gl.drawArrays(gl.POINTS, 0, 1); //绘制点
attribute变量用于与顶点相关的数据,uniform变量用于与顶点无关的数据。书上是这么写,但理解起来很别扭。我的理解是在3D绘图中,每个点的坐标都会经过坐标变换最后映射到2D画布上,这时点的坐标是经过变换后的坐标,那么这个坐标信息就要使用attribute变量,而不管怎么变换,点的颜色都是不变的,所以颜色信息使用uniform变量。