<十一>入门CocosShader之顶点着色器和片元着色器
创建项目
创建一个shader文件夹,在文件夹下创建一个html文件,一个js文件,两个文件都命名为shader.

编写代码
- 准备顶点数据
在开始编写图形绘制代码前必须先给WebGL一些顶点数据
假如需要渲染一个平面三角形,就需要输入3个顶点
![image]()
因为是平面三角形,所以没有深度z,使用二维位置定义即可,以数组的形式存储。
上图就是为绘制平面三角形定义的三个顶点坐标。
顶点着色器的主要功能就是坐标转换,把顶点坐标转换为裁剪坐标,然后GPU会通过裁剪和透体剔除法把裁剪坐标转换为标准化设备坐标NDC。
![image]()
透视剔除法会裁剪空间里三个顶点的分量都除以w,实现裁剪坐标转换为NDC,NDC的每一个分量都在(-1,1)之间,超出部分最终不会呈现,所以在游戏引擎中无论使用的是局部坐标还是世界坐标,都需要经过坐标转换,最终GPU会将NDC转换成屏幕坐标传入光栅器。
因为这里是示例教程,所以采用的不是局部坐标或世界坐标,而是采用裁剪坐标。
有了这样一组顶点数据,就可以将它发送给图形渲染管线的第一个阶段:顶点着色器,它会在GPU上创建内存存储顶点数据,接着结合顶点组合方式来解析这些内存。WebGL通过顶点缓冲对象VBO管理这个内存,它会在GPU内存储大量的顶点数据以供顶点着色器使用。
2. 编写代码
在html中启用代码
<body>
<script src="shader.js"></script>
</body>
在js中创建一个入口函数main
//创建着色器
function createShader(gl,type,source){
//创建顶点着色器对象 | 片段着色器
const shader = gl.createShader(type);
//将顶点着色器文本关联到顶点着色器对象 | xxx
gl.shaderSource(shader, source);
//编译顶点着色器 | xxx
gl.compileShader(shader);
//判断编译状态(编译成功才能使用)
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if(success){
return shader;
}
//编译失败,打印错误信息
console.log(gl.getShaderInfoLog(shader));
//删除顶点着色器 | xxx
gl.deleteShader(shader);
}
//创建着色程序
function createProgram(gl, vertexShader, fragmentShader){
//创建着色程序对象
const program = gl.createProgram();
//将顶点着色器关联到着色程序对象上
gl.attachShader(program, vertexShader);
//将片段着色器关联到着色程序对象上
gl.attachShader(program, fragmentShader);
//链接着色程序对象
gl.linkProgram(program);
//判断是否成功
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if(success){
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
function main(){
//WebGL大多数元素都是可以通过状态来描述
//状态是通过内置全局变量gl来调用切换
//gl指向的是绘图上下文
//绘图上下文需要通过canvas元素来获取
//so,第一步,创建canvas元素
const canvas = document.createElement('canvas');
//这里解释一下为什么不直接在html中创建canvas元素,而是在js中动态创建
//因为很多代码是在js中编写的,在这里定义的canvas当前脚本可以判断其类型,而这些属性在html中定义的canvas元素在js中获取是类型是any,无法使用其api代码提示
//将创建的cānvas元素添加到body中
document.getElementsByTagName('body')[0].appendChild(canvas)
//定义画图尺寸
canvas.width = 400;
canvas.height = 300;
//第二步,获取绘图上下文
const gl = canvas.getContext("webgl");
//判断当前浏览器是否支持webgl
if(!gl){
console.log('不支持webgl');
return;
}
//第三步,编辑顶点着色器
//定义着色器文本,因为在js中是无法直接使用GLSL语言,但是gl支持将文本转译成着色器,所以需要通过字符串的形式来编写
const vertexSource = `
attribute vec2 a_position;//接收一个顶点位置输入
void main(){
gl_Position = vec4(a_position,0.0,1.0);//gl_Position是一个Vec4,传入的顶点是一个vec2,所以需要转换成vec4,x,y取之a_position,没有深度所以z为0.0,w设置为1.0
}
`
//创建顶点着色器
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
//定义顶点数据
const positions = [
0,0,
0,0.5,
0.7,0
]
//创建缓冲区对象(顶点缓冲对象存储顶点数据)
const vertexBuffer = gl.createBuffer();
//在顶点着色器节点会在GPU上创建内存存储顶点数据,顶点缓冲对象就是为了管理这块内存
//它会在GPU内存储大量的顶点,使用缓冲对象的好处就是可以一次性将大量顶点数据传输到GPU上,而不是每个顶点发送一次
//顶点缓冲对象的类型是gl.ARRAY_BUFFER,WebGL允许同时绑定多个缓冲,只要类型不同,但同类型的Buffer会替换之前的Buffer
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
//将顶点数据传输到缓冲区对象中
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
//bufferData主要是初始化buffer对象的数据存储,第一个参数是缓冲区类型,第二个参数是设定数据存储区的大小(GPU内存通常不会很大,所以要合理分配内存),第三个参数是数据的使用方式
//gl.STATIC_DRAW表示数据不会改变,这里因为传入的是一个固定的顶点数据,所以选择gl.STATIC_DRAW
//gl.DYNAMIC_DRAW表示数据会频繁更新,常用于动态更新
//gl.STREAM_DRAW表示数据每次渲染都会更新
//第四步,创建片段着色器
//定义着色器文本
const fragmentSource = `
precision mediump float;
void main(){
gl_FragColor = vec4(1.0,0.0,0.5,1.0);//玫红色
}
`;
//创建片段着色器对象
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
//第五步,创建着色程序
const program = createProgram(gl, vertexShader, fragmentShader);
//第六步,设置视口,方便将NDC坐标映射到画布上
gl.viewport(0, 0, gl.canvas.width,gl.canvas.height);
//清除画布颜色和颜色缓冲
gl.clearColor(0, 0, 0, 1);//这里设置成黑色
//清除画布颜色和颜色缓冲,由于最终呈现在屏幕上的颜色都要从颜色缓冲中读取,所以每次渲染都需要清除颜色缓冲,否则之前的渲染结果会残留导致花屏
gl.clear(gl.COLOR_BUFFER_BIT);
//使用着色程序对象
gl.useProgram(program);
//获取顶点位置属性变量
const positonAttributeLocation = gl.getAttribLocation(program, 'a_position');
//启用顶点位置属性变量
gl.enableVertexAttribArray(positonAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
//将缓冲区对象绑定到顶点位置属性变量上,并设置如何读取数据
gl.vertexAttribPointer(positonAttributeLocation, 2, gl.FLOAT, false, 0, 0);
//绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
main();
保存后直接打开html可以运行调试,源代码也可以断点调试。



浙公网安备 33010602011771号