Shader(着色器)是一种在计算机图形学中用于处理图形渲染的程序,它们运行在图形处理单元(GPU)上,通常用于定义绘制或变换图形的方式。Shader主要分为两类:顶点着色器(Vertex Shader)和片元着色器(Fragment Shader)。这两者一起工作以完成图形渲染的过程。
现在你需要绘制一个带颜色的正方体。
你可能也会像我一样,先画出轮廓结构,再对每个面进行上色处理。实际上shader也是这个过程:
顶点着色器负责轮廓的绘制,主要是确定轮廓在空间中的位置信息
片元着色器负责给轮廓加上颜色
先从简单的二维平面着手,例如我们现在需要绘制一个长 200 ,宽 200 的蓝色正方形。先准备一块画布
<div class="grid">
<canvas id="app" width="1000" height="700"></canvas>
</div>
顶点着色器只能绘制点,线条和三角形。任何图形,包括我们看到的三维图形都是由这三个要素构成。所以绘制一个正方形的话,我们可以需要使用两个三角形进行拼接而成。
// 创建顶点着色器
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
precision mediump float;
attribute vec2 a_Position;
void main(){
gl_Position = vec4(a_Position,0.0, 1.0);
}
`);
gl.compileShader(vertexShader);
// 创建片元着色器
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
precision mediump float;
void main(){
gl_FragColor = vec4(0.0,0.0,1.0,1.0);
}
`);
gl.compileShader(fragmentShader);
// 创建程序
var program = gl.createProgram();
//将顶点着色器挂载在着色器程序上。
gl.attachShader(program, vertexShader);
//将片元着色器挂载在着色器程序上。
gl.attachShader(program, fragmentShader);
//链接着色器程序
gl.linkProgram(program);
gl.useProgram(program);
const buffer = gl.createBuffer()
// 通过类型化数组来传递数据给webgl
const data = new Float32Array([
-200/500,200/350,
-200/500,-200/350,
200/500,-200/350,
-200/500,200/350,
200/500,200/350,
200/500,-200/350,
])
gl.bindBuffer(gl.ARRAY_BUFFER,buffer)
gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW)
const a_position_pointer = gl.getAttribLocation(program,'a_Position')
gl.vertexAttribPointer(a_position_pointer,2,gl.FLOAT,false,8,0)
gl.enableVertexAttribArray(a_position_pointer)
// 绘制三角形
gl.drawArrays(gl.TRIANGLES,0,6)
第34行 使用了Float32Array来定义了2个三角形的顶点,并传递给7行代码的顶点着色器。
gl_Position = vec4(a_Position,0.0, 1.0);
注意在 shader 中关于位置,只接受[-1,1]的范围,例如画布宽度为1000,在shader中你要定义 100 的单位长度,则需要改为 0.1
shader中的坐标参考:
此时,在 canvas 中已经存在2个三角形组成的图形轮廓了,接下来就是给图形上色,即片元着色器的作用了。片元,可以理解为屏幕的每一个像素,每个像素都是一个单独的个体,都会同时执行第17行代码:
gl_FragColor = vec4(0.0,0.0,1.0,1.0);
这句代码相当于告诉每个片元,你把你自己这个像素点渲染成蓝色吧。
渲染结果:
有两点需要关注:
- 没有落在顶点着色器区域内的片元不会渲染。例如1000,700这个位置不在轮廓内,所以片元着色器会忽略。
- 每个片元着色器都会并行同时执行一遍上述代码,注意是
同时,这也是 webgl 比canvas 2d 性能高的原因,在canvas 2d内我们需要for循环去遍历每隔元素。而shader是同时执行。
我们改一下片元着色器代码部分:
void main(){
if(gl_FragCoord.x>500.0){
gl_FragColor = vec4(0.0,0.0,1.0,1.0);
}else{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
}
gl_FragCoord 是shader提供的内置变量,gl_FragCoord.x 代表当前片元所处的水平位置。当水平位置大于500的片元会渲染成蓝色,否则渲染成红色。
有了 gl_FragCoord变量提供的动态变量,图形绘制有了更多可能,例如绘制圆形
void main(){
if((gl_FragCoord.x-500.0)*(gl_FragCoord.x-500.0) + (gl_FragCoord.y-350.0)*(gl_FragCoord.y-350.0) > 10000.0 ){
gl_FragColor = vec4(0.0,0.0,1.0,1.0);
}else{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
}
本节主要是对 shader 熟悉顶点着色器和片元着色器的概念作用有个简单认识,下节主要讲解shader的语法以及变量。
完整代码:
本节完