# 一、从绘制一个点开始
绘制一个点有以下几个步骤
- 获取 canvas 以及上下文。webgl 需要使用 canvas 展示。
1 2 3 4 5
| <canvas id="canvas"></canvas> <script> let canvas = document.getElementById('canvas') let gl = canvas.getContext('webgl') </script>
|
- 创建着色器
着色器有两类分别为顶点着色器和片元着色器。众所周知,每一个 3d 模型都是由一个个三角形构成的,而三角形由三个顶点构成。顶点着色器就是记录顶点的数据,比如位置,大小,颜色。当处理完顶点就会进入光栅化,简单的讲就是将 3d 模型转化为 2d 的像素点,模型所占用的像素点就是片元着色器,记录着每一个像素点的颜色,用 rgba 表示,取值范围在 0 到 1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| //glsl的代码使用字符串储存,然后再通过shaderSource传递到创建的着色器,通过compileShader编译。 const vertexCode = ` //注意glsl的每一行的结尾都要写; void main(){ //顶点坐标 gl_Postion = vec4(0.0,0.0,0.0,1.0); //点的大小 gl_PointSion = 10.0; } const fragmentCode = ` void main(){ //片元颜色设置成红色 gl_FragColor = vec4(1.0,0.0,0.0,1.0); }; `
const vertexShader = gl.creatShader(gl_VERTEX_SHADER) //创建顶点着色器 gl.shaderSource(vertexShader,vertexCode) //将上面写的顶点着色器glsl代码,放到创建好的顶点着色器上。 gl.compileShader(vertexShader) //编译顶点着色器 //创建片元着色器与顶点着色器一致 const fragmentShader = gl.creatShader(gl_FRAGMENT_SHADER) gl.shaderSource(fragmentShader,fragmentCode) gl.compileShader(fragmentShader)
|
- 创建着色器程序
1 2 3 4 5
| const program = gl.createProgram() //创建着色器程序 gl.attachShader(program,vertexShader) //将顶点着色器传入着色器程序 gl.attachShader(program,fragmentShader) //将片元着色器传入着色器程序 gl.linkProgram(program) //连接着色器程序 gl.useProgram(program) //使用这个着色器程序
|
- 将点绘制出来
1
| gl.drawArrays(gl.POINTS,0,1)
|
- 完整代码,此外还可以设置背景颜色,canvas1 默认的背景颜色是透明的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| let canvas = document.getElementById('canvas') const gl = canvas.getContext('webgl')
const vertexCode = ` void main(){ gl_Position = vec4(0.0,0.0,0.0,1.0); gl_PointSize = 10.0; } ` const fragmentCode = ` void main(){ gl_FragColor = vec4(1.0,0.0,0.0,1.0); } `
gl.clearColor(0.0,0.0,0.0,1.0)//设置背景颜色,设置一次之后每次调用clear()前无需重复设置除非颜色发送改变 gl.clear(gl.COLOR_BUFFER_BIT)//清除颜色缓冲区
const vertexShader = gl.createShader(gl.VERTEX_SHADER) //创建顶点着色器 gl.shaderSource(vertexShader,vertexCode) //将上面写的顶点着色器glsl代码,放到创建好的顶点着色器上。 gl.compileShader(vertexShader) //编译顶点着色器 //创建片元着色器与顶点着色器一致 const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER) gl.shaderSource(fragmentShader,fragmentCode) gl.compileShader(fragmentShader)
const program = gl.createProgram(); //创建着色器程序 gl.attachShader(program,vertexShader) //将顶点着色器传入着色器程序 gl.attachShader(program,fragmentShader) //将片元着色器传入着色器程序 gl.linkProgram(program) //连接着色器程序 gl.useProgram(program) //使用这个着色器程序
gl.drawArrays(gl.POINTS,0,1)
|
# 二、修改点的数据
之前点的坐标,大小,片元的颜色是通过硬编码的形式设置好的无法修改。现在试着使用 JavaScript 的变量储存点的数据然后传递给 webgl。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const vertexCode =` //attribute为储存限定符只能用于储存顶点着色器的数据。 attribute vec4 a_Position; attribute float a_PointSize; void main(){ gl_Position = a_Postion; gl_PointSize = a_PointSize; } ` //获取顶点着色器的坐标以及大小地址 const a_Postion = gl.getAttribLocation(program,'a_Position') const a_PointSize = gl.getAttribLocation(program,'a_PointSize') //修改顶点着色器的坐标和大小的值,坐标有四个值,但是如果传入的值少于四个,w默认1.0,其他默认0.0 //vertexAttrib[1,2,3,4]f,表示传入1,2,3,4个参数为浮点数 gl.vertexAttrib2f(a_Postion,0.5,0.0) gl.vertexAttrib1f(a_PointSize,30.0)
|
# 三、使用鼠标绘制点
在此基础上,使用鼠标监听事件监听用户的鼠标坐标然后传入顶点就行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| canvas.addEventListener('click',(e)=>{ const width = canvas.width / 2 const height = canvas.height / 2 const glX = (e.offsetX-width) / width const glY = (height-e.offsetY) / height //获取顶点着色器的坐标以及大小地址 const a_Postion = gl.getAttribLocation(program,'a_Position') const a_PointSize = gl.getAttribLocation(program,'a_PointSize') //修改顶点着色器的坐标和大小的值,坐标有四个值,但是如果传入的值少于四个,w默认1.0,其他默认0.0 //vertexAttrib[1,2,3,4]f,表示传入1,2,3,4个参数为浮点数 gl.vertexAttrib2f(a_Postion,glX,glY) gl.vertexAttrib1f(a_PointSize,30.0) gl.drawArrays(gl.POINTS,0,1) })
|
可以发现每次绘制点的时候,不仅上个点没了,背景也变成默认的透明了,如果想要每次绘制的时候都有上个点,同时不用每次都重新绘制背景颜色的话。
1 2
| //在获取上下文的时候传入 { preserveDrawingBuffer: true }这个参数即可 gl = canvas.getContext('WebGL', { preserveDrawingBuffer: true })
|
# 四、绘制有颜色的点
可以使用给片元着色器上颜色改变点的颜色。与片元着色器相关的储存限定符有两个,一个是 uniform,另一个是 varying。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 1.使用uniform const fragmentCode = ` //指定颜色的精度,如果不指定的话,会有问题。lowp为低精度,mediump为中精度,highp为高精度。 precision mediump float; uniform vec4 u_FrageColor; void main (){ gl_FragColor = u_FrageColor; } ` //在鼠标点击事件上加上这两行,注意使用uniform的时候如果第四个参数不传默认是0.0,也即透明。 const u_FrageColor = gl.getUniformLocation(program,'u_FrageColor') gl.uniform4f(u_FrageColor,Math.random(),Math.random(),Math.random(),1.0)
2.使用varying const vertexCode = ` //attribute为储存限定符只能用于储存顶点着色器的数据。 attribute vec4 a_Position; attribute float a_PointSize; //v_Color这个变量顶点着色器和片元着色器同名,给这个传a_Color,然后修改a_Color的值,片元着色器的v_Color也会跟着改变 attribute vec4 a_Color; varying vec4 v_Color; void main (){ gl_Position = a_Position; gl_PointSize = a_PointSize; v_Color = a_Color; } ` const fragmentCode = ` precision mediump float; // uniform vec4 u_FrageColor; varying vec4 v_Color; void main (){ gl_FragColor = v_Color; } `
const a_Color = gl.getAttribLocation(program,'a_Color') gl.vertexAttrib4f(a_Color,Math.random(),Math.random(),Math.random(),1.0)
|
参考资料:
[2. WebGL 绘制点 | iceWebGL (ice-webgl.netlify.app)](https://ice-webgl.netlify.app/content/ 二、WebGL 基础 / 2. WebGL 绘制点.html)